Data See, Data Do

Mike Hillberg's Blog on Wpf and Silverlight

Tip: Cannot animate '...' on an immutable object instance

Tip: Cannot animate '...' on an immutable object instance

Rate This
  • Comments 2

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="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://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();

    }

 

}

 

 

  • 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.

  • this is great helped me solve my problem immediately

    +1

Page 1 of 1 (2 items)
Leave a Comment
  • Please add 7 and 2 and type the answer here:
  • Post