Há dias precisei de fazer uma animação em XAML que alterasse o tamanho de duas colunas de uma grid.
Para fazer esta animação poderiam-se colocar as duas colunas com o Width a Auto e alterar o tamanho dos componentes que estão em cada coluna, o que iria forçar a alteração do tamanho de cada coluna e assim fazer a animação pretendida. Mas esta abordagem tem a desvantagem de não se podermos usar, por exemplo, {0.8*} , já que não podemos usar um GridLength num width ou height de um FrameworkElement.
Para resolver este problema podemos alterar directamente o GridLength utilizando uma AnimationTimeline. Em baixo encontra-se uma a class GridLengthAnimation. Para usar esta class apenas teremos de definir a propriedade To que irá indicar qual será o novo tamanho da linha ou coluna. Também se pode definir a propriedade From, mas esta é opcional e caso não se defina é usado o valor actual como ponto de partida.
using System;
using System.Windows;
using System.Windows.Media.Animation;
namespace GridLengthAnimationTest
{
internal class GridLengthAnimation : AnimationTimeline
{
static GridLengthAnimation() { }
public GridLength? From
{
get
{
return (GridLength?)GetValue(FromProperty);
}
set
{
SetValue(FromProperty, value);
}
}
public static readonly DependencyProperty FromProperty =
DependencyProperty.Register("From", typeof(GridLength?), typeof(GridLengthAnimation), new PropertyMetadata(null));
public GridLength To
{
get
{
return (GridLength)GetValue(ToProperty);
}
set
{
SetValue(ToProperty, value);
}
}
public static readonly DependencyProperty ToProperty =
DependencyProperty.Register("To", typeof(GridLength), typeof(GridLengthAnimation));
public override Type TargetPropertyType
{
get
{
return typeof(GridLength);
}
}
protected override Freezable CreateInstanceCore()
{
return new GridLengthAnimation();
}
public override object GetCurrentValue(object defaultOriginValue, object defaultDestinationValue, AnimationClock animationClock)
{
double toVal = To.Value;
double fromVal;
if (this.From == null)
{
fromVal = defaultOriginValue != null && defaultOriginValue is GridLength ? ((GridLength)defaultOriginValue).Value : 0;
}
else
{
fromVal = this.From.GetValueOrDefault().Value;
}
if (animationClock.CurrentProgress != null)
{
if (fromVal > toVal)
{
return new GridLength((1 - animationClock.CurrentProgress.Value) * (fromVal - toVal) + toVal, To.IsStar ? GridUnitType.Star : GridUnitType.Pixel);
}
else
{
return new GridLength(animationClock.CurrentProgress.Value * (toVal - fromVal) + fromVal, To.IsStar ? GridUnitType.Star : GridUnitType.Pixel);
}
}
return null;
}
}
}
A seguir encontra-se um exemplo de como utilizar a animação em XAML. Este exemplo possui uma tabela com duas colunas e dois botões que permitem expandir ou restaurar o tamanho da primeira coluna.
<Window x:Class="GridLengthAnimationTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Local="clr-namespace:GridLengthAnimationTest"
Title="GridLengthAnimation" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition x:Name="FirstColumnDefinition" Width="150"/>
<ColumnDefinition x:Name="SecondColumnDefinition" Width="50"/>
</Grid.ColumnDefinitions>
<Border Background="Red" Grid.Column="0"/>
<Border Background="Blue" Grid.Column="1"/>
</Grid>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Grid.Row="1" >
<Button Content="Expandir" Margin="10" >
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<Local:GridLengthAnimation Duration="0:0:1" Storyboard.TargetName="FirstColumnDefinition" Storyboard.TargetProperty="Width" To="450" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
<Button Content="Restaurar" Margin="10">
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<Local:GridLengthAnimation Duration="0:0:1" Storyboard.TargetName="FirstColumnDefinition" Storyboard.TargetProperty="Width" To="150" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
</StackPanel>
</Grid>
</Window>
Se quiserem usar esta animação em code-behind, poderão fazer também de uma forma muito simples:
FirstColumnDefinition.BeginAnimation(
ColumnDefinition.WidthProperty,
new GridLengthAnimation()
{
To = new GridLength(0.8, GridUnitType.Star),
Duration = TimeSpan.FromSeconds(1)
});