Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
For the most part, you can animate any property in a WPF application. For example, the following is a rectangle that animates it’s fill color on mouse enter and leave:
<Window x:Class="Scratch.Window1"
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Scratch"
Foreground="Black"
Name="MyWindow"
>
<Rectangle Width="100" Height="100"
Fill="Green" >
<Rectangle.Triggers>
<EventTrigger RoutedEvent="Rectangle.MouseEnter">
<BeginStoryboard>
<Storyboard TargetProperty="Fill.Color">
<ColorAnimation To="Red" Duration="0:0:1" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="Rectangle.MouseLeave">
<BeginStoryboard>
<Storyboard TargetProperty="Fill.Color">
<ColorAnimation Duration="0:0:1" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Rectangle.Triggers>
</Rectangle>
</Window>
If you animate a property that’s databound, however, you might get the following exception: “Cannot animate 'Fill.Color' on an immutable object instance.” For example, you’ll get this if you change the rectangle’s fill above to:
Fill="{Binding ElementName=MyWindow, Path=Foreground}" >
The reason for this is a known issue where the animation is trying to make a copy of the Window’s “black” foreground brush, but is unable to because of the interaction with the binding.
As a workaround, you can update the binding to make a copy of the brush for the rectangle. That doesn’t interfere with the binding – any change to the window’s foreground will still be propagated to the rectangle – but the rectangle will make its own copy for a local animation. So the Fill ends up looking like this:
Fill="{Binding ElementName=MyWindow, Path=Foreground, Converter={x:Static local:MyCloneConverter.Instance}}"
… which is referencing an IValueConverter for the binding that looks like this:
internal class MyCloneConverter : IValueConverter
{
public static MyCloneConverter Instance = new MyCloneConverter();
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is Freezable)
{
value = (value as Freezable).Clone();
}
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
Comments
Anonymous
June 20, 2009
I actually found a little shortcut around this, by setting the value to anything other than 0. 5 works fine for me, but I guess that's only a patch on the tire, so to speak.Anonymous
August 07, 2014
this is great helped me solve my problem immediately +1