Saturday, August 15, 2009

A Timeline Visualizer in Silverlight/C#: Part 1

I encountered a problem while developing an application for my current client: They needed to visualize a series of events on a timeline. Like a schedule or a Gantt chart. I couldn't find a canned solution for this, so I created one in Silverlight/C#. Ideally I want to create a templated control with declarative bindings, etc., but currently I just have it in the form of usercontrol. I figured it is still useful to share it here, since others will also have data that falls into this category, and could use ideas from this.

Here is a sample of how the control can be used. There is a bunch of UI functionality like click & drag, select/unselect item, mousewheel zoom in/zoom out, doubleclick to raise 'getdetail' event, add new item, etc. For some reason, Mousewheel works only in IE and not in Firefox. It works everywhere when you run it locally. I suspect the disparity is because of some site weirdness; I am using MS's free 10G silverlight streaming space to host the app. If anybody has any ideas, please let me know.
http://silverlight.services.live.com/invoke/105917/TimeLine%20Visualizer%20Demo/iframe.html



You can get the entire solution from filefactory: http://www.filefactory.com/file/ah13e8g/n/VisualTimelineDemo_zip.
Now, for the code. We will start looking at this from the outside in.
The relevant xaml in the page looks like this:

   <Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Border BorderBrush="#99000000" BorderThickness="1" CornerRadius="4,4,0,0" Margin="2" Grid.Row="0">
<TextBlock TextWrapping="Wrap" Margin="4,0,2,0" Text="Click on an item to select, click again to deselect. Double-Click outside any item to deselect globally. Click and drag to move. Use Mousewheel to zoom in/out. Doubleclick to request details of selected timeline item(s)."/>
</Border>
<local:TimeLineVisualizer Name="tl" Grid.Row="1"/>
<Border BorderBrush="#99000000" BorderThickness="1" CornerRadius="0,0,4,4" Margin="2" Grid.Row="2">
<StackPanel Orientation="Horizontal" Margin="4">
<TextBlock Margin="4,0,2,0" Text="TimeLine Item Name:"/>
<TextBox Name="ItemNameTextBox" Width="100"/>
<TextBlock Margin="4,0,2,0" Text="Start:"/>
<basics:DatePicker Name="StartDatePicker"/>
<TextBlock Margin="4,0,2,0" Text="End:"/>
<basics:DatePicker Name="EndDatePicker"/>
<Button Margin="4,0,4,0" Name="Btn_InsertTimeLine" Content="Add Item to TimeLine" Click="Button_Click"/>
</StackPanel>
</Border>
</Grid>

Class Diagram for TimeLineVisualizer:
Here is the code-behind in the page that deals with TimeLineVisualizer events:

     public Home()
{
------
tl.TimeLineChanged += (s, e) => SetTimeLineItemSource();
tl.DetailRequested += new EventHandler<DetailRequestedEventArgs>(tl_DetailRequested);
}
void tl_DetailRequested(object sender, DetailRequestedEventArgs e)
{
MessageBox.Show("'DetailRequested' event raised by TimeLine");
}
void Home_Loaded(object sender, RoutedEventArgs e)
{
SetTimeLineItemSource();
}
private void SetTimeLineItemSource()
{
tl.ItemsSource = from t in DataAccess.TimeLines
where ExtensionMethods.Intersects(t.Start, t.End, tl.StartDate.Value, tl.EndDate.Value)
select t;
}
The “ExtensionMethods.Intersects” method justs tells you if 2 date ranges intersect:

     //not really an extension method
public static bool Intersects(DateTime r1Start, DateTime r1End, DateTime r2Start, DateTime r2End)
{
return (r1Start == r2Start) || (r1Start > r2Start ? r1Start <= r2End : r2Start <= r1End);
}
In the next entry, we will look at the TimeLineVisualizer usercontrol.

14 comments:

  1. A really cool control! The only thing I miss is a possibility to zoom without mouse wheel because I'm at my laptop without a mouse :-O

    ReplyDelete
  2. You can actually zoom without the mousewheel, by changing the start date and end date (with a couple of datepickers), and setting the itemsource from the consumer of the control. I didn't add them because I thought the demo should be as barebones as possible. If I do have time, I will upload an app where the user can do that.

    Writing about the control itself, now. Will upload soon.

    -Jose

    ReplyDelete
  3. very, very, useful ! thank for your hard work!

    Can we change the timeline scale to range between hours/minutes instead of days?

    ReplyDelete
  4. Yes, with a little bit of effort. The method one would change is DrawAxis(). Currently there is code to write the days/date labels when the range is such that there is enough to space to do so. Enhance this to add time labels when the range/space makes sense. This was not a reuirement for the display for the client, but I think I will do this when I convert this into a templated control.

    ReplyDelete
  5. yeah that would be great -I need a silverlight control to do display a timeline of daily events in a 24 hour format -however the look and feel should be exactly as the one shown above

    thanks for this!

    ReplyDelete
  6. cannot download the solution... it runs a script that hangs IE

    ReplyDelete
  7. I dont have access to filefactory.com (which hosts the file) at work. Will take a look in the evening after I get home.

    ReplyDelete
  8. Is there a way to display more than one non overlapping TimeLineItem in one row? Ie could "Code Expenses Module" and "Code Timecard Module" be in one row?

    ReplyDelete
  9. I have problems with zoom in/out when TimeLine and TimeLineVisualizer are used in WPF app. Zoom in/out trigger does not happened. Any help highly appriciated: ivan.silni@gmail.com

    ReplyDelete
  10. Hi jose.

    Thank your for your excellence control. I want to know if I could use it in my business product. That is to say, is the control that you have developed obey the BSD licence?

    Thank your so much.

    ReplyDelete
  11. I am glad you find it useful. Feel free to use it in your product, without any fee.

    ReplyDelete
  12. The demo on http://silverlight.services.live.com and the solution on Fileserve are down. Looks interesting.

    ReplyDelete
  13. I cant download it, can you please reupload it again?

    ReplyDelete