Monday, 31 May 2010

Simple animation sequences for Silverlight charts

In this post I would like to show a simple way to obtain an animation sequence when you load the ItemsSource of a Serie of a Chart in Silverlight (Toolkit April 2010).

I know that is available the AnimationSequence property but so far I couldn't make it work for all kinds of Series.
For example with the AreaSerie i have not been successful and it seems that I'm not the only one (http://betaforums.silverlight.net/forums/p/183878/418187.aspx#418187).

Now I'm gonna show how I changed the template of the AreaSeries to obtain our loading animations.

First of all let's see the xaml of the Usercontrol hosting the chart:
   1 <UserControl x:Class="XamlChart.MainPage"
   2     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
   5     xmlns:charting="clr-namespace:System.Windows.Controls.DataVisualization.Charting;assembly=System.Windows.Controls.DataVisualization.Toolkit"
   6     xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Toolkit"
   7     xmlns:my="clr-namespace:XamlChart"
   8     mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
   9     <UserControl.Resources>
  10         <my:ChartConverter x:Key="ChartConverter"/>
  11     </UserControl.Resources>
  12     <Grid x:Name="LayoutRoot">
  13         <charting:Chart Title="My Chart"  LegendTitle="Legend">
  14             <charting:AreaSeries Title="Series 1" DependentValueBinding="{Binding y, Converter={StaticResource ChartConverter}}"
  15                 IndependentValueBinding="{Binding x, Converter={StaticResource ChartConverter}}" 
  16                 Style="{StaticResource MySerieStyle}"  >
  17                 <charting:AreaSeries.ItemsSource>
  18                     <controls:ObjectCollection>
  19                         <my:MyPoint x="5" y="6"/>
  20                         <my:MyPoint x="6" y="7"/>
  21                         <my:MyPoint x="7" y="5.3"/>
  22                         <my:MyPoint x="8" y="9"/>
  23                     </controls:ObjectCollection>
  24                 </charting:AreaSeries.ItemsSource>
  25             </charting:AreaSeries>
  26             <charting:Chart.Axes>
  27                 <charting:LinearAxis Orientation="X" Title="X Axis" Location="Bottom"></charting:LinearAxis>
  28                 <charting:LinearAxis Orientation="Y" Title="Y Axis" Location="Left" ShowGridLines="True" ></charting:LinearAxis>
  29             </charting:Chart.Axes>
  30         </charting:Chart>
  31     </Grid>
  32 </UserControl>

Here We can see at line 16: Style="{StaticResource MySerieStyle}".

With this line We say that the new Template for our Serie has 'MySerieStyle' as key.

The Template is defined in another file called 'ResourceDictionary1.xaml'.
The link between our UserControl and the 'ResourceDictionary1.xaml' file is in the App.xaml file:
 1 <Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 2              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
 3              x:Class="XamlChart.App"
 4              >
 5     <Application.Resources>
 6         <ResourceDictionary>
 7                 <ResourceDictionary.MergedDictionaries>
 8                         <ResourceDictionary Source="ResourceDictionary1.xaml"/>
 9                 </ResourceDictionary.MergedDictionaries>
10         </ResourceDictionary>
11     </Application.Resources>
12 </Application>

Now We can look at the Template defined in the ResourceDictionary1.xaml file:
   1 <ResourceDictionary
   2         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
   3         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
   4     xmlns:chartingToolkit="clr-namespace:System.Windows.Controls.DataVisualization.Charting;assembly=System.Windows.Controls.DataVisualization.Toolkit"
   5     xmlns:visualizationToolkit="clr-namespace:System.Windows.Controls.DataVisualization;assembly=System.Windows.Controls.DataVisualization.Toolkit" 
   6     xmlns:System_Windows_Controls_DataVisualization_Charting_Primitives="clr-namespace:System.Windows.Controls.DataVisualization.Charting.Primitives;assembly=System.Windows.Controls.DataVisualization.Toolkit"
   7     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
   8     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d">
   9 
  10     <Style x:Key="MySerieStyle" TargetType="chartingToolkit:AreaSeries">
  11                 <Setter Property="IsTabStop" Value="False"/>
  12                 <Setter Property="PathStyle">
  13                         <Setter.Value>
  14                                 <Style TargetType="Path">
  15                                         <Setter Property="Opacity" Value="1"/>
  16                                 </Style>
  17                         </Setter.Value>
  18                 </Setter>
  19                 <Setter Property="Template">
  20                         <Setter.Value>
  21                                 <ControlTemplate TargetType="chartingToolkit:AreaSeries">
  22                                         <Canvas x:Name="PlotArea">
  23                                                 <Canvas.OpacityMask>
  24                             <LinearGradientBrush StartPoint="0,0" EndPoint="1,0" >
  25                                                                 <LinearGradientBrush.Transform>
  26                                                                         <RotateTransform Angle="0" />
  27                                                                 </LinearGradientBrush.Transform>
  28                                 <GradientStop Color="Black" Offset="0"/>
  29                                 <GradientStop x:Name="ColorStop" Color="Black" Offset="0"  />
  30                                 <GradientStop x:Name="ColorStop2" Color="Transparent" Offset="0.1"  />
  31                                 <GradientStop Color="Transparent" Offset="1"/>
  32                             </LinearGradientBrush>
  33                         </Canvas.OpacityMask>
  34                         <Canvas.Triggers>
  35                             <EventTrigger>
  36                                 <BeginStoryboard>
  37                                         <Storyboard x:Name="storyboard">
  38                                                 <DoubleAnimation
  39                                                         Storyboard.TargetName="ColorStop" Storyboard.TargetProperty="Offset"
  40                                                         From="0" To="1" Duration="0:0:4" BeginTime="00:00:00"  />
  41                                                 <DoubleAnimation
  42                                                         Storyboard.TargetName="ColorStop2" Storyboard.TargetProperty="Offset"
  43                                                         From="0.1" To="1" Duration="0:0:4" BeginTime="00:00:00"  />
  44                                         </Storyboard>
  45                                 </BeginStoryboard>
  46                             </EventTrigger>
  47                         </Canvas.Triggers>
  48                                                 <Path Fill="{TemplateBinding Background}"  Data="{TemplateBinding Geometry}" Style="{TemplateBinding PathStyle}"/>
  49                                         </Canvas>
  50                                 </ControlTemplate>
  51                         </Setter.Value>
  52                 </Setter>
  53         </Style>
  54     

I modified the Default Template of the AreaSeries.

As usual you can obtain the default one with Expression Blend right clicking the AreaSerie of the Chart and then doing Edit Template > Edit a Copy or examing the System.Windows.Controls.DataVisualization.Toolkit dll with Reflector as in the following snapshot:

The main idea is to use a LinearGradientBrush and a StoryBoard with two (or more) animations to move the GradientStops in the desired direction and speed.

The 'LinearGradientBrush.Transform' at line 25 is not mandatory here (note Angle="0") but I used it for other kind of animation as this one that shows the AreaSerie from the bottom up:
   1 <Style x:Key="AreaSeriesBottomUp" TargetType="chartingToolkit:AreaSeries">
   2                 <Setter Property="IsTabStop" Value="False"/>
   3                 <Setter Property="PathStyle">
   4                         <Setter.Value>
   5                                 <Style TargetType="Path">
   6                                         <Setter Property="Opacity" Value="1"/>
   7                                 </Style>
   8                         </Setter.Value>
   9                 </Setter>
  10                 <Setter Property="Template">
  11                         <Setter.Value>
  12                                 <ControlTemplate TargetType="chartingToolkit:AreaSeries">
  13                                         <Canvas x:Name="PlotArea">
  14                                                 <Canvas.OpacityMask>
  15                             <LinearGradientBrush StartPoint="1,0" EndPoint="0,0" >
  16                                                                 <LinearGradientBrush.Transform>
  17                                                                         <RotateTransform Angle="90" />
  18                                                                 </LinearGradientBrush.Transform>
  19                                 <GradientStop Color="Black" Offset="0"/>
  20                                 <GradientStop x:Name="ColorStop" Color="Black" Offset="0"  />
  21                                 <GradientStop x:Name="ColorStop2" Color="Transparent" Offset="0.1"  />
  22                                 <GradientStop Color="Transparent" Offset="1"/>
  23                             </LinearGradientBrush>
  24                         </Canvas.OpacityMask>
  25                         <Canvas.Triggers>
  26                             <EventTrigger>
  27                                 <BeginStoryboard>
  28                                         <Storyboard x:Name="storyboard">
  29                                                 <DoubleAnimation
  30                                                         Storyboard.TargetName="ColorStop" Storyboard.TargetProperty="Offset"
  31                                                         From="0" To="1" Duration="0:0:4" BeginTime="00:00:00"  />
  32                                                 <DoubleAnimation
  33                                                         Storyboard.TargetName="ColorStop2" Storyboard.TargetProperty="Offset"
  34                                                         From="0.1" To="1" Duration="0:0:4" BeginTime="00:00:00"  />
  35                                         </Storyboard>
  36                                 </BeginStoryboard>
  37                             </EventTrigger>
  38                         </Canvas.Triggers>
  39                                                 <Path Fill="{TemplateBinding Background}"  Data="{TemplateBinding Geometry}" Style="{TemplateBinding PathStyle}"/>
  40                                         </Canvas>
  41                                 </ControlTemplate>
  42                         </Setter.Value>
  43                 </Setter>
  44         </Style>

The last one animation shows the Area from the center to both the sides of the Area itself. Here I used two more GradientStops and two more Animations than before:
   1 <Style x:Key="AreaSeriesCenterSides" TargetType="chartingToolkit:AreaSeries">
   2                 <Setter Property="IsTabStop" Value="False"/>
   3                 <Setter Property="PathStyle">
   4                         <Setter.Value>
   5                                 <Style TargetType="Path">
   6                                         <Setter Property="Opacity" Value="1"/>
   7                                 </Style>
   8                         </Setter.Value>
   9                 </Setter>
  10                 <Setter Property="Template">
  11                         <Setter.Value>
  12                                 <ControlTemplate TargetType="chartingToolkit:AreaSeries">
  13                                         <Canvas x:Name="PlotArea">
  14                                                 <Canvas.OpacityMask>
  15                             <LinearGradientBrush StartPoint="0,0" EndPoint="1,0" >
  16                                                                 <LinearGradientBrush.Transform>
  17                                                                         <RotateTransform Angle="0" />
  18                                                                 </LinearGradientBrush.Transform>
  19                                 <GradientStop Color="Transparent" Offset="0"/>
  20                                 <GradientStop x:Name="ColorStop3" Color="Transparent" Offset="0.4"  />
  21                                 <GradientStop x:Name="ColorStop" Color="Black" Offset="0.5"   />
  22                                 <GradientStop x:Name="ColorStop2" Color="Black" Offset="0.5"  />                                                        
  23                                 <GradientStop x:Name="ColorStop4" Color="Transparent" Offset="0.6"  />
  24                                 <GradientStop Color="Transparent" Offset="1"/>
  25                             </LinearGradientBrush>
  26                         </Canvas.OpacityMask>
  27                         <Canvas.Triggers>
  28                             <EventTrigger>
  29                                 <BeginStoryboard>
  30                                         <Storyboard x:Name="storyboard">
  31                                                 <DoubleAnimation
  32                                                         Storyboard.TargetName="ColorStop" Storyboard.TargetProperty="Offset"
  33                                                         From="0.5" To="0" Duration="0:0:4" BeginTime="00:00:00"  />
  34                                                 <DoubleAnimation
  35                                                         Storyboard.TargetName="ColorStop2" Storyboard.TargetProperty="Offset"
  36                                                         From="0.5" To="1" Duration="0:0:4" BeginTime="00:00:00"  />
  37                                                                                 <DoubleAnimation
  38                                                         Storyboard.TargetName="ColorStop3" Storyboard.TargetProperty="Offset"
  39                                                         From="0.4" To="0" Duration="0:0:4" BeginTime="00:00:00"  />
  40                                                 <DoubleAnimation
  41                                                         Storyboard.TargetName="ColorStop4" Storyboard.TargetProperty="Offset"
  42                                                         From="0.6" To="1" Duration="0:0:4" BeginTime="00:00:00"  />
  43                                         </Storyboard>
  44                                 </BeginStoryboard>
  45                             </EventTrigger>
  46                         </Canvas.Triggers>
  47                                                 <Path Fill="{TemplateBinding Background}"  Data="{TemplateBinding Geometry}" Style="{TemplateBinding PathStyle}"/>
  48                                         </Canvas>
  49                                 </ControlTemplate>
  50                         </Setter.Value>
  51                 </Setter>
  52         </Style>

The same effects just seen can be used with the other Series, for example we can look at the last animation seen applied to the ColumnSeries:
   1 <Style x:Key="ColumnSeriesStyle1" TargetType="chartingToolkit:ColumnSeries">
   2                 <Setter Property="IsTabStop" Value="False"/>
   3                 <Setter Property="Template">
   4                         <Setter.Value>
   5                                 <ControlTemplate TargetType="chartingToolkit:ColumnSeries">
   6                     <Canvas x:Name="PlotArea">
   7                         <Canvas.OpacityMask>
   8                             <LinearGradientBrush StartPoint="0,0" EndPoint="1,0" >
   9                                 <LinearGradientBrush.Transform>
  10                                     <RotateTransform Angle="0" />
  11                                 </LinearGradientBrush.Transform>
  12                                 <GradientStop Color="Transparent" Offset="0"/>
  13                                 <GradientStop x:Name="ColorStop3" Color="Transparent" Offset="0.4"  />
  14                                 <GradientStop x:Name="ColorStop" Color="Black" Offset="0.5"   />
  15                                 <GradientStop x:Name="ColorStop2" Color="Black" Offset="0.5"  />
  16                                 <GradientStop x:Name="ColorStop4" Color="Transparent" Offset="0.6"  />
  17                                 <GradientStop Color="Transparent" Offset="1"/>
  18                             </LinearGradientBrush>
  19                         </Canvas.OpacityMask>
  20                         <Canvas.Triggers>
  21                             <EventTrigger>
  22                                 <BeginStoryboard>
  23                                     <Storyboard x:Name="storyboard">
  24                                         <DoubleAnimation
  25                                                         Storyboard.TargetName="ColorStop" Storyboard.TargetProperty="Offset"
  26                                                         From="0.5" To="0" Duration="0:0:4" BeginTime="00:00:00"  />
  27                                         <DoubleAnimation
  28                                                         Storyboard.TargetName="ColorStop2" Storyboard.TargetProperty="Offset"
  29                                                         From="0.5" To="1" Duration="0:0:4" BeginTime="00:00:00"  />
  30                                         <DoubleAnimation
  31                                                         Storyboard.TargetName="ColorStop3" Storyboard.TargetProperty="Offset"
  32                                                         From="0.4" To="0" Duration="0:0:4" BeginTime="00:00:00"  />
  33                                         <DoubleAnimation
  34                                                         Storyboard.TargetName="ColorStop4" Storyboard.TargetProperty="Offset"
  35                                                         From="0.6" To="1" Duration="0:0:4" BeginTime="00:00:00"  />
  36                                     </Storyboard>
  37                                 </BeginStoryboard>
  38                             </EventTrigger>
  39                         </Canvas.Triggers>
  40                     </Canvas>
  41                 </ControlTemplate>
  42                         </Setter.Value>
  43                 </Setter>
  44         </Style>