Silverlight 2 Timeline Panel

Sunday, October 19, 2008 7:43:08 PM (GMT Daylight Time, UTC+01:00)

A while back I posted on creating a custom layout panel in WPF that could arrange items based on an assigned date property to create a timeline view. Working through the creation of this panel taught me a great deal about how powerful the layout system was in WPF, and now how that power and flexibility extends into Silverlight 2.

The Silverlight 2 layout system is largely based on the use of panels, which includes the Canvas, Grid, and Stack Panel. Silverlight 2 includes a base Panel that you are able to derive from and override it’s default layout behavior. In addition to the basic functionality that comes with Panel, it provides two methods that you can easily override to create your own behavior. MeasureOverride and ArrangeOverride are the heart of the layout, determining the the position of elements. It’s essentially a to phase process where MeasureOverride examines the child elements of the panel to see what size they would “like to be”. The ArrangeOverride the tells the children the size and location they “are going to be”, based on whatever calculations you specify.

This example shows how using a custom attached property to specify an event date for any child element placed on the panel that ArrangeOverride can then use to position them.

 

Silverlight TimeLinePanel - Windows History Timeline

This view showing the history of Windows version release dates is created using this XAML markup:

 <local:TimelinePanel Width="800" Height="280" 
                             StartDate="1/1/1984" StopDate="12/12/2010" 
                             Background="LightGray">
       <TextBlock local:TimelinePanel.EventDate="11/1/1985">Windows 1.0</TextBlock>
       <TextBlock local:TimelinePanel.EventDate="12/1/1987">Windows 2.0</TextBlock>
       <TextBlock local:TimelinePanel.EventDate="5/1/1990">Windows 3.0</TextBlock>
       <TextBlock local:TimelinePanel.EventDate="4/1/1992">Windows 3.1</TextBlock>
       <TextBlock local:TimelinePanel.EventDate="5/30/1995">Windows NT 3.51</TextBlock>
       <TextBlock local:TimelinePanel.EventDate="8/24/1995" >Windows 95</TextBlock>
       <TextBlock local:TimelinePanel.EventDate="6/1/1996">Windows NT 4</TextBlock>
       <TextBlock local:TimelinePanel.EventDate="6/1/1998">Windows 98</TextBlock>
       <TextBlock local:TimelinePanel.EventDate="2/1/2000">Windows 2000</TextBlock>
       <TextBlock local:TimelinePanel.EventDate="7/1/2000">Windows Me</TextBlock>
       <TextBlock local:TimelinePanel.EventDate="10/1/2001">Windows XP</TextBlock>
       <TextBlock local:TimelinePanel.EventDate="1/1/2007">Windows Vista</TextBlock>
</local:TimelinePanel>

 

As you can see from the XAML markup, a Start and Stop date are specified for the date range that the panel will display. Each child element, in this case TextBlock’s have an EventDate property specified. It should be noted that the TextBlock itself has no concept of an event date coded into it. EventDate is an Attached Property, a dependency property that can be “attached” to any element.

“An attached property is a concept defined by XAML. An attached property is intended to be used as a type of global property that is settable on any object element in XAML. In Silverlight version 2, attached properties are typically defined as a specialized form of dependency property that does not have the conventional property wrapper in the object's CLR object model.”

MSDN has more details on attached properties in Silverlight 2 online.

In the ArrangeOverride code I simply scale the date based on the EventDate’s relative position to the StartDate and StopDate of the TimeLinePanel giving me a horizontal position. For the vertical position I track the position of the previous element and if it overlaps I place it below, creating a cascading effect for overlapping elements.

protected override Size ArrangeOverride(Size finalSize)
        {
            double lastX = 0;
            double lastY = 0;
            foreach (UIElement element in this.Children)
            {
                DateTime eventDate = (DateTime)element.GetValue(EventDateProperty);
                double pos = ScaleDate(eventDate);
                double left = finalSize.Width * pos;
                double top = (double)element.GetValue(VerticalOffsetProperty);
                if (double.IsNaN(top) == true)
                    top = 0.00;

                double width = element.DesiredSize.Width;
                double height = element.DesiredSize.Height;
                if (lastX + width > left)
                {
                    top = lastY + 20 + (double)element.GetValue(VerticalOffsetProperty);
                }
                else
                {
                    top = 0 + (double)element.GetValue(VerticalOffsetProperty);
                }
                element.Arrange(new Rect(left, top, width, height));
                lastX = left;
                lastY = top;
            }
            return finalSize;
        }

The use of custom panels for layout in Silverlight 2 creates a world of exciting possibilities, giving developers and designers an incredibly flexible set of tools. With the ability for panels to be reused within other controls such as the ListBox (Shown in my previous Solar System ListBox Demo) advanced user experiences become much easier to create. By placing the TimeLinePanel into the ItemsPanelTemplate of ListBox and attaching the EventDate to the ListBoxItem, I now have all of the functionality of a ListBox, with the visual display of my TimeLinePanel.

<ListBox>
      <ListBox.ItemsPanel>
              <ItemsPanelTemplate>
                  <local:TimelinePanel Width="800" Height="280" 
                         StartDate="1/1/1984" StopDate="12/12/2010" Background="LightGray">
                  </local:TimelinePanel>
              </ItemsPanelTemplate>
      </ListBox.ItemsPanel>
      <ListBoxItem local:TimelinePanel.EventDate="11/1/1985"><TextBlock Text="Windows 1.0" /></ListBoxItem>
      <ListBoxItem local:TimelinePanel.EventDate="12/1/1987"><TextBlock Text="Windows 2.0" /></ListBoxItem>
      <ListBoxItem local:TimelinePanel.EventDate="5/1/1990"><TextBlock Text="Windows 3.0" /></ListBoxItem>
      <ListBoxItem local:TimelinePanel.EventDate="4/1/1992"><TextBlock Text="Windows 3.1" /></ListBoxItem>
      <ListBoxItem local:TimelinePanel.EventDate="5/30/1995"><TextBlock Text="Windows NT 3.51" /></ListBoxItem>
      <ListBoxItem local:TimelinePanel.EventDate="8/24/1995"><TextBlock Text="Windows 95" /></ListBoxItem>
      <ListBoxItem local:TimelinePanel.EventDate="6/1/1996"><TextBlock Text="Windows NT 4" /></ListBoxItem>
      <ListBoxItem local:TimelinePanel.EventDate="6/1/1998"><TextBlock Text="Windows 98" /></ListBoxItem>
      <ListBoxItem local:TimelinePanel.EventDate="2/1/2000"><TextBlock Text="Windows 2000" /></ListBoxItem>
      <ListBoxItem local:TimelinePanel.EventDate="7/1/2000"><TextBlock Text="Windows Me" /></ListBoxItem>
      <ListBoxItem local:TimelinePanel.EventDate="10/1/2001"><TextBlock Text="Windows XP" /></ListBoxItem>
      <ListBoxItem local:TimelinePanel.EventDate="1/1/2007"><TextBlock Text="Windows Vista" /></ListBoxItem>
</ListBox>

Silverlight TimeLinePanel replacing default ListBoxItemsPanel 

Live demo of TimeLinePanel

Silverlight 2 TimeLinePanel Demo Source Code

 

Technorati Tags: , ,

Posted in Silverlight  | Comments [2] 


Friday, October 24, 2008 7:44:57 PM (GMT Daylight Time, UTC+01:00)
Thanks for following up on your previous posts. I've been waiting for more details on this timeline for months. Now that you posted them, I'm embarassed by how easy it is!

Can you explain how I could set the attached property for each ListBoxItem if I'm binding to a data source?

I have a data template to represent each item in the collection. I tried binding to the object's Date property, like this:

local:TimelinePanel.EventDate="{Binding Path=Date}"

But, I really want to set the attached property on the ListBoxItem, not on the border of the data template.

Thank you.
Todd Beaulieu
Wednesday, November 05, 2008 12:30:42 AM (GMT Standard Time, UTC+00:00)
Thanks very much for the post. I would really like to see the answer to Todds question also.

Thankyou.
Chris
Comments are closed.