Monday, March 29, 2010

WPF DoubleAnimationUsingKeyFrames Using C#

In this post I will show you how you can use the DoubleAnimationUsingKeyFrames in Code behind file.For this example code I have created menu and created six items of the menu, One green border which is used to move on the menu item when user takes mouse on that menu item and that border will then expand in height once the border reaches at the mouse over menu item. As you can see in the Image 1 and Image 2 as given below.

Image 1

Image 2

Let us start with code, below is the xaml of the grid which is used as the menu. Here I have inserted 6 columns of 100 with and 2 columns of with 5 on each side of the , so that equal space is maintained on both end of the grid. In the code there are some important point which I want to mention here. First is that I have one MouseEnter event handler for all the labels I have six label in the grid and I have use same event MouseEnter event handler. Second point is that I have used the Panel.ZIndex and set 1 for that labels and 0 (zero) for the moving border, so that the label are remain at top of the border, if you reverse the Panel.ZIndex of label to 0 (zero) and 1 for the border control, when border is expended in height then you can't see the label.

<Grid Width="610" Height="50">
<Grid.ColumnDefinitions >
<ColumnDefinition Width="5"></ColumnDefinition>
<ColumnDefinition Width="100"></ColumnDefinition>
<ColumnDefinition Width="100"></ColumnDefinition>
<ColumnDefinition Width="100"></ColumnDefinition>
<ColumnDefinition Width="100"></ColumnDefinition>
<ColumnDefinition Width="100"></ColumnDefinition>
<ColumnDefinition Width="100"></ColumnDefinition>
<ColumnDefinition Width="5"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Border Grid.ColumnSpan="8" CornerRadius="3" BorderBrush="Black" BorderThickness="2">
<Border.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FF6E6E6E" Offset="0"/>
<GradientStop Color="#FFCECCCC" Offset="0.974"/>
<GradientStop Color="#FF969595" Offset="0.151"/>
<GradientStop Color="#FFCECCCC"/>
</LinearGradientBrush>
</Border.Background>
</Border>
<Label Content="Menu Item 1" Grid.Column="1" Margin="2" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Panel.ZIndex="1" MouseEnter="Label_MouseEnter"/>
<Label Content="Menu Item 2" Grid.Column="2" Margin="0,2,0,2" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Panel.ZIndex="1" MouseEnter="Label_MouseEnter"/>
<Label Content="Menu Item 3" Grid.Column="3" Margin="0,2,0,2" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Panel.ZIndex="1" MouseEnter="Label_MouseEnter"/>
<Label Content="Menu Item 4" Grid.Column="4" Margin="0,2,0,2" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Panel.ZIndex="1" MouseEnter="Label_MouseEnter"/>
<Label Content="Menu Item 5" Grid.Column="5" Margin="0,2,0,2" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Panel.ZIndex="1" MouseEnter="Label_MouseEnter"/>
<Label Content="Menu Item 6" Grid.Column="6" Margin="0,2,0,2" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Panel.ZIndex="1" MouseEnter="Label_MouseEnter"/>
<Border Grid.ColumnSpan="8" BorderThickness="1" Grid.Column="1" Width="100" Height="10" VerticalAlignment="Top" HorizontalAlignment="Left" Background="#FF00DA0E" Margin="0,2,0,0" RenderTransformOrigin="0.5,0.5" x:Name="brdBorder" />
</Grid>

Third point is that I have set the width of the brbBorder control to 100 as I have set the column width of each label containing column to 100 so that brbBorder control will fill the current column. Forth point is that I have set the Grid.ColumnSpan of the brbBorder to 8 so that it can be moved in whole grid by changing the x position of the brbBorder control.
Below is the C# code which is used to animate (move the border control) to give location within the .2 second time interval.

int intGridColumn = Grid.GetColumn(lblSender) - 1;
TranslateTransform animatedTranslateTransform = new TranslateTransform();
brdBorder.RenderTransform = animatedTranslateTransform;
RegisterName("AnimatedTranslateTransform", animatedTranslateTransform);

DoubleAnimationUsingKeyFrames translationAnimation = new DoubleAnimationUsingKeyFrames();
translationAnimation.KeyFrames.Add(new LinearDoubleKeyFrame(intPreviousValue, KeyTime.FromTimeSpan(TimeSpan.FromSeconds(0))));
translationAnimation.KeyFrames.Add(new LinearDoubleKeyFrame(intGridColumn * 100, KeyTime.FromTimeSpan(TimeSpan.FromSeconds(.2))));

DoubleAnimation dblaHeight = new DoubleAnimation();
dblaHeight.BeginTime = TimeSpan.FromSeconds(.2);
dblaHeight.Duration = TimeSpan.FromSeconds(.1);
dblaHeight.To = 48;

intPreviousValue = intGridColumn * 100;

Storyboard.SetTargetName(translationAnimation, "AnimatedTranslateTransform");
Storyboard.SetTargetProperty(translationAnimation, new PropertyPath(TranslateTransform.XProperty));
Storyboard.SetTargetName(dblaHeight, brdBorder.Name);
Storyboard.SetTargetProperty(dblaHeight, new PropertyPath(Border.HeightProperty));

Storyboard strbStoryboard = new Storyboard();
strbStoryboard.Children.Add(translationAnimation);
strbStoryboard.Children.Add(dblaHeight);

strbStoryboard.Begin(this);
UnregisterName("AnimatedTranslateTransform");
In the above code I have uses an instance of the LinearDoubleKeyFrame class to move the green border along a path at a steady rate from its starting position to the new position which is calculated by getting the grid column of the mouse entered label and then multiple it with the column index. Linear key frames like LinearDoubleKeyFrame create a smooth linear transition between values. I have used two LinearDoubleKeyFrames which are added DoubleAnimationUsingKeyFrames object, First one is used to the border to the location where it was on last time ( with time interval of 0 (zero) seconds) and second one is used to move the border smoothly to new location. Next I have the doubleAnimtion object which is used to expand the height of the border when the border is moved to the new location, as I have set the begin time of .2 second which is the time to complete the movement of the border from one location to another location. I have also registered the name of the TranslateTransform object which is "AnimatedTranslateTransform" which is used to set the target of the DoubleAnimationUsingKeyFrames object and when I have started the animation I have unregistered the name of "AnimatedTranslateTransform" , So that when mouse point to new location on the menu it will create same name object and animate again, if I don't unregistered then it will execute on first time but for second time onward it will give exception and will not animate.
One more important point is that I have check the height of the border in MouseEnter event so that if the border has maximum height then it will reduce its height to original height which I have set is 10 pixel and when reduced height animation is complete then it will move to new location and then increase height. Hope you get some understanding of the DoubleAnimationUsingKeyFrames object to used it in C# code. You can download the source code from here
All and any comments / bugs / suggestions are welcomed!

3 comments: