Friday, February 11, 2011

Animating Grid Column

Troubleshooting InvalidOperationException: DoubleAnimation cannot be used to animate property Width due to incompatible type.

Silverlight has great support for Animation and you might have experienced animating most of the controls on one or the other aspect. But animating ColumnDefinition.Width or RowDefinition.Height properties for Grid control is not straight forward. To achieve this one has to make a tweak.

Problem : InvalidOperationException: DoubleAnimation cannot be used to animate property Width due to incompatible type.

Reason : Width property of grid is not of type double but is of type GridLength. Further GridLength has a property called Value of type Double, but Value is ReadOnly so to change the width or height you have to create a new instance of the GridLength object and set it to the Width Property of the grid.

Solution: To achieve this we simply create a dependency property in our page and in the change event of that property we set the new instance of GridLength to width property of the grid.

[sourcecode language="html"]

<Grid x:Name="myGrid">
<Grid.Resources>
<Storyboard x:Name="myStoryboardC">
<DoubleAnimation From="150" To="0" Duration="00:00:1"
Storyboard.TargetName="myPageName"
Storyboard.TargetProperty="ColumnWidth">
<DoubleAnimation.EasingFunction>
<CubicEase EasingMode="EaseOut"/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
<Storyboard x:Name="myStoryboardE">
<DoubleAnimation From="0" To="150" Duration="00:00:1"
Storyboard.TargetName= “myPageName"
Storyboard.TargetProperty="ColumnWidth">
<DoubleAnimation.EasingFunction>
<CubicEase EasingMode="EaseOut"/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</Grid.Resources>
<Grid.ColumnDefinitions>
<ColumnDefinition x:Name="Column1" Width="150" MaxWidth="250" MinWidth="0">
</ColumnDefinition>
<ColumnDefinition Width="15" MaxWidth="15" MinWidth="15"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>

<Rectangle x:Name="myRectangle"
Fill="Blue" Width="200" Height="30" Grid.Column="0"/>

<layoutToolkit:Accordion x:Name="acc" SelectionMode="ZeroOrMore" Grid.Column="0">
<layoutToolkit:AccordionItem Header="hi">
<StackPanel>
<TextBlock Text="1"/>
<TextBlock Text="2"/>
<TextBlock Text="3"/>
</StackPanel>
</layoutToolkit:AccordionItem>
<layoutToolkit:AccordionItem Header="hi" Content="Task 2"/>
<layoutToolkit:AccordionItem Header="hi" Content="Task 3"/>
</layoutToolkit:Accordion>

<Button x:Name="btnEC" Content="&lt;" Grid.Column="1" Click="Button_Click" />

<data:DataGrid x:Name="dataGrid1" Margin="0" Grid.Column="2">
<data:DataGrid.RowDetailsTemplate>
<DataTemplate>
<StackPanel Background="LightBlue">
<StackPanel Orientation="Horizontal">
<TextBlock Text="This item has details." />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Here is some data: " />
<TextBlock Text="{Binding FirstName}" />
<TextBlock Text=" LastName" />
<TextBlock Text="{Binding LastName}" />
</StackPanel>
</StackPanel>
</DataTemplate>
</data:DataGrid.RowDetailsTemplate>
</data:DataGrid>

</Grid>

[/sourcecode]

[sourcecode language="csharp"]

public partial class MainPage : UserControl
{

bool toogle;
public MainPage()
{
InitializeComponent();
this.Name = "myPageName";
}

public static readonly DependencyProperty ColumnWidthProperty = DependencyProperty.Register("ColumnWidth",
typeof(double),
typeof(MainPage),
new PropertyMetadata
(ColumnWidthChanged));
public double ColumnWidth
{
get
{
return (double)GetValue(ColumnWidthProperty);
}
set
{
SetValue(ColumnWidthProperty, value);
}
}

private static void ColumnWidthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var mainPage = (MainPage)d;
mainPage.Column1.Width = new GridLength((double)e.NewValue);
}

private void Button_Click(object sender, RoutedEventArgs e)
{
if (!toogle)
{
myStoryboardC.Begin();
btnEC.Content = ">";
toogle = !toogle;
}
else
{
myStoryboardE.Begin();
btnEC.Content = "<";
toogle = !toogle;
}
}
}

[/sourcecode]

5 comments:

Marcio said...

Code seems nice, but I am not sure where to find the namespace for layouttoolkit:AccordionItem and also data:datagrid. So I can not use it yet.

Marcio 123 said...

Ok!
To compile you will need to add the two namespaces below:
xmlns:layoutToolkit="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Layout.Toolkit"
xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"
After that the code compiles OK. However, I am still getting an issue with system.string on code behind. I guess, I may be missing something else.

Dhaval Upadhyaya said...

Datagrid is the default control and if you drag the control from toolbox on silverlight page or usercontrol it will add the reference for you.

AccordionItem is not supplied with default control package so for that you will require Layout Toolkit available on codeproject. Once you install, add it to your toolbox and drag it on your page.

System.String is not used anywhere in my given snippet. If you have your own code than its fine. You have to add reference to xmlns:System="clr-namespace:System;assembly=mscorlib" namespace in your xaml.

Hope this answers your queries.

burt said...

Sorry for not understanding but I get error: Cannot resolve TargetName myPageName. What am I doing wrong?

Dhaval Upadhyaya said...

As shown in the code try to give Name for the page or user control you are using

public MainPage()
{
InitializeComponent();
this.Name = "myPageName";
}