Animating width and height when they are Double.NaN

A common problem in WPF when doing animation to width and height is that you cannot animate these properties (or any other properties of type double) if they are by default unset.

Here is a snippet that shows the code that would raise this kind of exception

   1: <Border Background="Red" HorizontalAlignment="Center" VerticalAlignment="Center">

   2:    <TextBlock Text="CLICK ME" VerticalAlignment="Center" HorizontalAlignment="Center"/>

   3:    <Border.Triggers>

   4:        <EventTrigger RoutedEvent="UIElement.MouseDown">

   5:            <BeginStoryboard>

   6:                <Storyboard>

   7:                    <DoubleAnimation Storyboard.TargetProperty="Height" To="300"/>

   8:                </Storyboard>

   9:            </BeginStoryboard>

  10:        </EventTrigger>

  11:    </Border.Triggers>

  12: </Border>

When you try to do this you get an exception like this one

>> Cannot animate the ‘Height’ property on a ‘System.Windows.Controls.Border’ using a ‘System.Windows.Media.Animation.DoubleAnimation’. For details see the inner exception.

and same for Width.

What I do in order to achieve the same behaviour without the need of setting a default width and height is to use a Scale Transform instead. Here is a snippet to get the same behaviour as the XAML above to animate the height

   1: <Border Background="Red" HorizontalAlignment="Center" VerticalAlignment="Center">

   2:    <TextBlock Text="CLICK ME" VerticalAlignment="Center" HorizontalAlignment="Center"/>

   3:    <Border.RenderTransform>

   4:        <ScaleTransform ScaleX="1" ScaleY="1" />

   5:    </Border.RenderTransform>

   6:    <Border.Triggers>

   7:        <EventTrigger RoutedEvent="UIElement.MouseDown">

   8:            <BeginStoryboard>

   9:                <Storyboard>

  10:                    <DoubleAnimation Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleY)" To="2"/>

  11:                </Storyboard>

  12:            </BeginStoryboard>

  13:        </EventTrigger>

  14:    </Border.Triggers>

  15: </Border>

Please note that there is a catch here. By using scale transform everything inside the actual control will be scaled. So if that is not what you want then you have a problem.

Yet of course there are solutions…. One of the solutions is to have an attached property to set the Height for you when the control is loaded. This can be achieved by setting the Height/Width to the ActualHeight/ActualWidth. Here is an attached property that I use in such cases.

   1: #region SetHeightToActual

   2:  

   3: /// <summary>

   4: /// SetHeightToActual Attached Dependency Property

   5: /// </summary>

   6: public static readonly DependencyProperty SetHeightToActualProperty =

   7:     DependencyProperty.RegisterAttached("SetHeightToActual", typeof(bool), typeof(WidthHeightBehaviour),

   8:         new FrameworkPropertyMetadata((bool)false,

   9:             new PropertyChangedCallback(OnSetHeightToActualChanged)));

  10:  

  11: /// <summary>

  12: /// Gets the SetHeightToActual property.  This dependency property 

  13: /// indicates ....

  14: /// </summary>

  15: public static bool GetSetHeightToActual(DependencyObject d)

  16: {

  17:     return (bool)d.GetValue(SetHeightToActualProperty);

  18: }

  19:  

  20: /// <summary>

  21: /// Sets the SetHeightToActual property.  This dependency property 

  22: /// indicates ....

  23: /// </summary>

  24: public static void SetSetHeightToActual(DependencyObject d, bool value)

  25: {

  26:     d.SetValue(SetHeightToActualProperty, value);

  27: }

  28:  

  29: /// <summary>

  30: /// Handles changes to the SetHeightToActual property.

  31: /// </summary>

  32: private static void OnSetHeightToActualChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)

  33: {

  34:     if ((bool)e.NewValue)

  35:     {

  36:         var element = (FrameworkElement)d;

  37:         RoutedEventHandler handler = null;

  38:         handler = delegate

  39:         {

  40:             element.Height = element.ActualHeight;

  41:             element.Loaded -= handler;

  42:         };

  43:         element.Loaded += handler;

  44:     }

  45: }

  46:  

  47: #endregion

And here is the XAML where I am using the attached behaviour. It is nearly seamless

   1: <Border Background="Red" HorizontalAlignment="Center" VerticalAlignment="Center" local:WidthHeightBehaviour.SetHeightToActual="True">

   2:    <TextBlock Text="CLICK ME" VerticalAlignment="Center" HorizontalAlignment="Center"/>

   3:    <Border.Triggers>

   4:        <EventTrigger RoutedEvent="UIElement.MouseDown">

   5:            <BeginStoryboard>

   6:                <Storyboard>

   7:                    <DoubleAnimation Storyboard.TargetProperty="Height" To="300"/>

   8:                </Storyboard>

   9:            </BeginStoryboard>

  10:        </EventTrigger>

  11:    </Border.Triggers>

  12: </Border>

You can do the same thing for the Width or any other property of this kind (for example I use this technique also for Center property of ScatterViewItems in Surface development).

Hope this tip/trick helps🙂

Happy coding

One thought on “Animating width and height when they are Double.NaN

  1. /// In my case it was not working because the “From” attribute was not set🙂

    <!– this line is working but it is resizing inner controls as well

    –>

    ///Jai SiyaRam

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s