none
Animating the Expand and Collapse of an Expander - once more with feeling! RRS feed

  • Question

  • I've trawled the net looking for some examples of how to animate the expansion and collapse of the content section of the Expander control (in either code or XAML) but it seems it's not a trivial task.

     

    I first attempted it in XAML and had success animating the Height property of the Expander based on the Expanded event which I used as the trigger for the animation. It didn't really do the trick and of course it didn't work with Collapsed because the event is fired after the Expander has been collapsed so you can't animate it.

     

    So I'm still looking for a way to do this.

     

    Should I create a Custom Control that inherits from Expander and try to intercept the Expand and Collapsed events to fire off some code beforehand?

     

    ... or what?

     

    Any guidance appreciated.

    Tuesday, September 11, 2007 2:18 PM

Answers

  • Hello, if you look at the default ControlTemplate of Expander (with the help of Blend), you’ll find it has a ContentPresenter named “ExpandSite”. Its default Visible value is Collapsed, and in a trigger, it sets Visibility to Visible:

    <ContentPresenter Focusable="false" Visibility="Collapsed" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" x:Name="ExpandSite" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" DockPanel.Dock="Bottom"/>

     

    <Trigger Property="IsExpanded" Value="true">

        <Setter Property="Visibility" TargetName="ExpandSite" Value="Visible"/>

    </Trigger>

     

    To create an animation, first you must first remove Visibility=”Collapsed” and that Setter, and set the ContentPresenter’s Height to 0 (if by default it's collapsed). Then you can use EnterActions and ExitActions of that Trigger to play the animations:

    <Trigger.EnterActions>

        <BeginStoryboard Storyboard="{StaticResource ExpandSb}"/>

    </Trigger.EnterActions>

    <Trigger.ExitActions>

        <BeginStoryboard x:Name="CollapseSb_BeginStoryboard" Storyboard="{StaticResource CollapseSb}"/>

    </Trigger.ExitActions>

     

    <ControlTemplate.Resources>

        <Storyboard x:Key="ExpandSb">

            <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="ExpandSite" Storyboard.TargetProperty="(FrameworkElement.Height)">

                <SplineDoubleKeyFrame KeyTime="00:00:00" Value="0"/>

                <SplineDoubleKeyFrame KeyTime="00:00:01" Value="100"/>

            </DoubleAnimationUsingKeyFrames>

        </Storyboard>

        <Storyboard x:Key="CollapseSb">

            <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="ExpandSite" Storyboard.TargetProperty="(FrameworkElement.Height)">

                <SplineDoubleKeyFrame KeyTime="00:00:00" Value="100"/>

                <SplineDoubleKeyFrame KeyTime="00:00:01" Value="0"/>

            </DoubleAnimationUsingKeyFrames>

        </Storyboard>

    </ControlTemplate.Resources>

     

    If you don’t want to hard code the Height value, you’ll have to do more work. For example, you can calculate the desired size in your code, and dynamically construct the Storyboards.

     

     

    Thursday, September 13, 2007 7:12 AM

All replies

  • Hello, if you look at the default ControlTemplate of Expander (with the help of Blend), you’ll find it has a ContentPresenter named “ExpandSite”. Its default Visible value is Collapsed, and in a trigger, it sets Visibility to Visible:

    <ContentPresenter Focusable="false" Visibility="Collapsed" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" x:Name="ExpandSite" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" DockPanel.Dock="Bottom"/>

     

    <Trigger Property="IsExpanded" Value="true">

        <Setter Property="Visibility" TargetName="ExpandSite" Value="Visible"/>

    </Trigger>

     

    To create an animation, first you must first remove Visibility=”Collapsed” and that Setter, and set the ContentPresenter’s Height to 0 (if by default it's collapsed). Then you can use EnterActions and ExitActions of that Trigger to play the animations:

    <Trigger.EnterActions>

        <BeginStoryboard Storyboard="{StaticResource ExpandSb}"/>

    </Trigger.EnterActions>

    <Trigger.ExitActions>

        <BeginStoryboard x:Name="CollapseSb_BeginStoryboard" Storyboard="{StaticResource CollapseSb}"/>

    </Trigger.ExitActions>

     

    <ControlTemplate.Resources>

        <Storyboard x:Key="ExpandSb">

            <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="ExpandSite" Storyboard.TargetProperty="(FrameworkElement.Height)">

                <SplineDoubleKeyFrame KeyTime="00:00:00" Value="0"/>

                <SplineDoubleKeyFrame KeyTime="00:00:01" Value="100"/>

            </DoubleAnimationUsingKeyFrames>

        </Storyboard>

        <Storyboard x:Key="CollapseSb">

            <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="ExpandSite" Storyboard.TargetProperty="(FrameworkElement.Height)">

                <SplineDoubleKeyFrame KeyTime="00:00:00" Value="100"/>

                <SplineDoubleKeyFrame KeyTime="00:00:01" Value="0"/>

            </DoubleAnimationUsingKeyFrames>

        </Storyboard>

    </ControlTemplate.Resources>

     

    If you don’t want to hard code the Height value, you’ll have to do more work. For example, you can calculate the desired size in your code, and dynamically construct the Storyboards.

     

     

    Thursday, September 13, 2007 7:12 AM
  • Yeah, hardcoding the height isn't going to work, and I really need to be able to achieve it in XAML rather than code.

     

    Maybe I could use binding somehow to bind the animation Value ... ?

    Wednesday, October 3, 2007 4:24 AM
  • Have you thought to use a LayoutTransform to animate the Y scale from 0 to 1? This way you do not have to calculate anything and you can do it all in XAML. What I do to get the same effect (and less messy XAML code since I am not using a style) is apply the LayoutTransform to the expanders content like so:

    Code Block

    <Expander x:Name="expander" Header="Instructions">
       <ListBox x:Name="listBox">
          <ListBoxItem Content="Instruction 1" />
          <ListBoxItem Content="Instruction 2" />
          <ListBoxItem Content="Instruction 3" />
          <ListBox.LayoutTransform>
             <ScaleTransform ScaleX="1" ScaleY="0"/>
          </ListBox.LayoutTransform>
       </ListBox>
    </Expander>



    And then apply an eventTrigger on the Expander.Expanded event that will fire off a doubleAnimation to animate the ScaleY to 1 to get the desired effect like so:

    Code Block

    <EventTrigger RoutedEvent="Expander.Expanded" SourceName="expander">
       <EventTrigger.Actions>
          <BeginStoryboard>
             <Storyboard>
                <DoubleAnimation From="0" To="1" Duration="0:0:0.25" Storyboard.TargetName="listBoxPhrasesInstructions" Storyboard.TargetProperty="(FrameworkElement.LayoutTransform).(ScaleTransform.ScaleY)"/>
             </Storyboard>
          </BeginStoryboard>
       </EventTrigger.Actions>
    </EventTrigger>


    Complete code can be found below:

    Code Block

    <StackPanel>
       <StackPanel.Triggers>
          <EventTrigger RoutedEvent="Expander.Expanded" SourceName="expander">
             <EventTrigger.Actions>
                <BeginStoryboard>
                   <Storyboard>
                      <DoubleAnimation From="0" To="1" Duration="0:0:0.25" Storyboard.TargetName="listBoxPhrasesInstructions" Storyboard.TargetProperty="(FrameworkElement.LayoutTransform).(ScaleTransform.ScaleY)"/>
                   </Storyboard>
                </BeginStoryboard>
             </EventTrigger.Actions>
          </EventTrigger>
       </StackPanel.Triggers>
       <Expander x:Name="expander" Header="Instructions">
          <ListBox x:Name="listBoxPhrasesInstructions">
             <ListBoxItem Content="Instruction 1" />
             <ListBoxItem Content="Instruction 2" />
             <ListBoxItem Content="Instruction 3" />
             <ListBox.LayoutTransform>
                <ScaleTransform ScaleX="1" ScaleY="0"/>
             </ListBox.LayoutTransform>
          </ListBox>
       </Expander>

     </StackPanel>


    There are other ways of doing this as well, but hopefully that helps for now!


    Thursday, October 4, 2007 7:41 PM
  • Please tell me I'm wrong, but I believe the problem with this approach is that you can't animate the collapsing of the Expander because the Collapsed event is raised after the IsExpanded property is set, which is the property that changes the visibility of the content section of the Expander.

     

    Friday, October 5, 2007 12:35 AM
  • You are correct, my example did not take into account the collapsed event, sorry about that. Yi-Lun Luo is correct in that you need to override the template and remove the collapsed setter and then apply your own triggers. But you can use a LayoutTransform to scale the expandSite from 0 to 1 for your animation without needing code behind or calculating a desired height.

     

    I have tried 3 times now to post a code answer but the forums keep running into an unknown error when I try to, so I wrote up a quick blog post that explains how to do it with a full XAML sample. Let me know if you need further help, best of luck!

    Friday, October 5, 2007 6:23 PM
  • Do you know to do it programmatically? Im not interested in using the scale transform. I want to slide the content in and out.

    Friday, April 11, 2008 12:54 AM
  • I have this same problem, but I do not get errors on a Buttons, only on CheckBoxes and RadioButtons. They all use basically the same style, so I am curious why Button does not throw the ContentPresenter errors on animations.
    Tuesday, September 2, 2008 8:16 PM
  • Check out my post here:

    http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/97fb41e8-218b-468d-9142-e44c990c61a3

    It's a solution that overrides the Expander template, and involves adding one ValueConverter to your solution.
    Friday, October 10, 2008 9:24 PM