none
Animating expander collapse/expand in xaml ?

    Question

  • Hi,
    I'm trying to make a custom template for Expander to animate the collapse and expand (as in windows xp). When i add the following trigger :

    <Trigger Property="IsExpanded" Value="True">
      <Trigger.EnterActions>
        <
    BeginStoryboard>
          <
    Storyboard>
            <
    DoubleAnimation Storyboard.TargetName="Content" Storyboard.TargetProperty="Height" To="{Binding ElementName=Content,Path=DesiredHeight}" Duration="0:0:1"/>
          </
    Storyboard>
        </
    BeginStoryboard>
      </
    Trigger.EnterActions>
      <
    Trigger.ExitActions>
        <
    BeginStoryboard>
          <
    Storyboard>
            <
    DoubleAnimation Storyboard.TargetName="Content" Storyboard.TargetProperty="Height" To="0" Duration="0:0:1"/>
          </
    Storyboard>
        </
    BeginStoryboard>
      </
    Trigger.ExitActions>
    </Trigger>

    I get the following error : "This Storyboard timeline tree could not be frozen".

    I understand why  the timeline can't be frozen (since i'm binding the value to a property that can change), so my question is : is there a way to animate the Expander without code beside ?

    Thanks,
    Guillaume

    Tuesday, May 23, 2006 9:23 AM

Answers

  • Short answer: no. We actually tried to get this work, but were limitted by the ability to key off the desired height before the item is actually created.
    Saturday, May 27, 2006 9:36 PM

All replies

  • Short answer: no. We actually tried to get this work, but were limitted by the ability to key off the desired height before the item is actually created.
    Saturday, May 27, 2006 9:36 PM
  • I have this working, although I do have C# code.

    Basically, I built a decorator with a DependencyProperty ("Ratio") which determines how much of its Child to display.

    When the decorator's MeasureOverride method is called, it measures its Child and then multiplies the Child's DesiredWidth or DesiredHeight (depending on which direction we're expanding in) by the current Ratio value (0.0 <= Ratio <= 1.0) and returns the resulting value.

    In the decorator's ArrangeOverride method, it moves its Child so it's only partially visible, again according to the Ratio. The Child is given its full DesiredWidth and DesiredHeight -- it's just positioned "strangely". Oh, and the decorator has ClipToBounds=true so the part of the Child that's arranged outside the current size isn't visible. I realize ClipToBounds=true can have performance implications, but I couldn't find any other way to do it.

    Finally, it's only a matter of animating the Ratio value from 0.0 to 1.0.

    Sunday, May 28, 2006 6:37 AM
  • This is very simple to do. First, apply a LayoutTransform (of type ScaleTransform) to the ContentPresenter in the Expander Template:

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

    Then add this trigger to Expander's ControlTemplate.Triggers:

    <Trigger Property="Expander.IsExpanded" Value="True">
      <Trigger.EnterActions>
      <BeginStoryboard>
      <Storyboard>
      <DoubleAnimation Storyboard.TargetName ="ExpandSite" 
      Storyboard.TargetProperty ="LayoutTransform.ScaleY"
      To ="1" Duration ="0:0:0.25"/>
      <DoubleAnimation Storyboard.TargetName ="ExpandSite" 
      Storyboard.TargetProperty="Opacity"
      To ="1" Duration ="0:0:0.25"/>
      </Storyboard>
      </BeginStoryboard>
      </Trigger.EnterActions>
      <Trigger.ExitActions>
      <BeginStoryboard>
      <Storyboard>
      <DoubleAnimation Storyboard.TargetName ="ExpandSite" 
      Storyboard.TargetProperty ="LayoutTransform.ScaleY"
      Duration ="0:0:0.25"/>
      <DoubleAnimation Storyboard.TargetName ="ExpandSite" 
      Storyboard.TargetProperty="Opacity"
      Duration ="0:0:0.25"/>
      </Storyboard>
      </BeginStoryboard>
      </Trigger.ExitActions>
      </Trigger>

    That's it. The trigger will scale the ContentPresenter to represent Collapsed and Expanded states.

    • Proposed as answer by kainhart Thursday, September 03, 2009 2:16 PM
    Saturday, February 23, 2008 7:49 PM
  • Right.

     

    But the effects are subtly different. The effect I implemented is a "true slide" effect, where the element in question "slides" into place, at full scale the whole time. If anyone is interested, I'm probably willing to post the code (it's fairly simple, really).

     

    By animating the element's scale (ScaleX or ScaleY), the effect is more of a "grow" or "shrink" effect.

     

    It all depends on what you want.

    Sunday, February 24, 2008 8:42 AM
  • Matt would you care to show me how you do this? You can email me at justin@circuitreesolutions.com

    thanks!
    Tuesday, July 15, 2008 5:21 PM
  • I know it's months after but if you still have the code I'd like to take a look-see. Thanks
    Thursday, September 04, 2008 9:39 PM
  • I've managed to get an Expander to animate opening and closing with a "slide" action.  It does require having one converter available to the resource dictionary, but at least it does not require custom controls or XAML code on every Expander instance.  I did not accomodate for different ExpandDirections, but if you understand the code I'm sure you can flush that out on your own.  It does work for nested Expanders.

    Add this style to your ResourceDictionary or Window.Resources to apply it to all expanders.

    I started by editing a copy of the default Expander template.  Here is the part that I changed (I'm happy to post the whole code if anyone wants it):

    <Style TargetType="{x:Type Expander}"
        ... 
        <Setter Property="Template"
            <Setter.Value> 
                <ControlTemplate TargetType="{x:Type Expander}"
                    <Border SnapsToDevicePixels="true" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="3"
                        <DockPanel> 
                            <ToggleButton 
                                x:Name="HeaderSite" 
                                FocusVisualStyle="{StaticResource ExpanderHeaderFocusVisual}"  
                                Margin="1" MinHeight="0" MinWidth="0" 
                                Style="{StaticResource ExpanderDownHeaderStyle}"  
                                Content="{TemplateBinding Header}" 
                                ContentTemplate="{TemplateBinding HeaderTemplate}"  
                                ContentTemplateSelector="{TemplateBinding HeaderTemplateSelector}"  
                                FontFamily="{TemplateBinding FontFamily}"  
                                FontSize="{TemplateBinding FontSize}" 
                                FontStretch="{TemplateBinding FontStretch}"  
                                FontStyle="{TemplateBinding FontStyle}" 
                                FontWeight="{TemplateBinding FontWeight}" Foreground="{TemplateBinding Foreground}" 
                                HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"  
                                Padding="{TemplateBinding Padding}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" 
                                DockPanel.Dock="Top" IsChecked="{Binding Path=IsExpanded, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"/> 
                            <Grid 
                                x:Name="ExpandSiteContainer" 
                                Visibility="Visible" 
                                HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" 
                                Margin="{TemplateBinding Padding}" 
                                VerticalAlignment="{TemplateBinding VerticalContentAlignment}" 
                                DockPanel.Dock="Bottom"                                       
                                > 
                                <Grid.Height> 
                                    <MultiBinding Converter="{StaticResource multiplyConverter}"
                                        <Binding Path="ActualHeight" ElementName="ExpandSite"/> 
                                        <Binding Path="Tag" RelativeSource="{RelativeSource Self}" /> 
                                    </MultiBinding> 
                                </Grid.Height> 
                                <Grid.Tag> 
                                    <sys:Double>0.0</sys:Double> 
                                </Grid.Tag> 
                                <ScrollViewer 
                                    VerticalScrollBarVisibility="Hidden" 
                                    HorizontalScrollBarVisibility="Hidden" 
                                     
                                    > 
                                    <ContentPresenter 
                                        x:Name="ExpandSite" 
                                        Focusable="false" 
                                        VerticalAlignment="Top" 
                                        > 
                                    </ContentPresenter> 
                                </ScrollViewer> 
                            </Grid> 
                        </DockPanel> 
                    </Border> 
                    <ControlTemplate.Triggers> 
                        <Trigger Property="IsExpanded" Value="true"
                            <Trigger.EnterActions> 
                                <BeginStoryboard> 
                                    <Storyboard> 
                                        <DoubleAnimation Storyboard.TargetName ="ExpandSiteContainer"  
                                          Storyboard.TargetProperty ="Tag" 
                                          To="1.0" Duration ="0:0:0.25" /> 
                                    </Storyboard> 
                                </BeginStoryboard> 
                            </Trigger.EnterActions> 
                            <Trigger.ExitActions> 
                                <BeginStoryboard> 
                                    <Storyboard> 
                                        <DoubleAnimation Storyboard.TargetName ="ExpandSiteContainer"  
                                            Storyboard.TargetProperty ="Tag" 
                                            To="0" Duration ="0:0:0.25"/> 
                                    </Storyboard> 
                                </BeginStoryboard> 
                            </Trigger.ExitActions> 
                        </Trigger> 
                        <Trigger Property="ExpandDirection" Value="Right"
                            <Setter Property="DockPanel.Dock" TargetName="ExpandSite" Value="Right"/> 
                            <Setter Property="DockPanel.Dock" TargetName="HeaderSite" Value="Left"/> 
                            <Setter Property="Style" TargetName="HeaderSite" Value="{StaticResource ExpanderRightHeaderStyle}"/> 
                        </Trigger> 
                        <Trigger Property="ExpandDirection" Value="Up"
                            <Setter Property="DockPanel.Dock" TargetName="ExpandSite" Value="Top"/> 
                            <Setter Property="DockPanel.Dock" TargetName="HeaderSite" Value="Bottom"/> 
                            <Setter Property="Style" TargetName="HeaderSite" Value="{StaticResource ExpanderUpHeaderStyle}"/> 
                        </Trigger> 
                        <Trigger Property="ExpandDirection" Value="Left"
                            <Setter Property="DockPanel.Dock" TargetName="ExpandSite" Value="Left"/> 
                            <Setter Property="DockPanel.Dock" TargetName="HeaderSite" Value="Right"/> 
                            <Setter Property="Style" TargetName="HeaderSite" Value="{StaticResource ExpanderLeftHeaderStyle}"/> 
                        </Trigger> 
                        <Trigger Property="IsEnabled" Value="false"
                            <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/> 
                        </Trigger> 
                    </ControlTemplate.Triggers> 
                </ControlTemplate> 
            </Setter.Value> 
        </Setter> 
    </Style> 

    Add this include to your XAML file:
        xmlns:sys="clr-namespace:System;assembly=mscorlib" 
     

    And if you copy the whole Expander template from Expression Blend, you'll need this include as well:
        xmlns:Custom="http://schemas.microsoft.com/winfx/2006/xaml/composite-font" 
     


    I created a MultiConverter that multiplies values passed to it and returns the result.  This is the only code-behind you'll need:

    using System; 
    using System.Globalization; 
    using System.Windows.Data; 
     
    namespace MyNamespace 
        public class MultiplyConverter : IMultiValueConverter 
        { 
            public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) 
            { 
                double result = 1.0; 
                for (int i = 0; i < values.Length; i++) 
                    result *= (double)values[i]; 
     
                return result; 
            } 
     
            public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) 
            { 
                throw new Exception("Not implemented"); 
            } 
        } 

    Finally, add a XAML include for your local namespace and create an instance of the MultiConverter (I did all this in Window.Resources for the example):

        xmlns:local="clr-namespace:MyNamespace" 

        <Window.Resources> 
            <local:MultiplyConverter x:Key="multiplyConverter" /> 
            ... 
        </Window.Resources> 


    That's it!  Change the two "Duration" properties on the <DoubleAnimation> tags if you want the "slide" to happen more slowly or quickly.

    Justin
    • Proposed as answer by liveultimate Friday, October 10, 2008 8:45 PM
    Friday, October 10, 2008 8:43 PM
  • One additional note, that MultiValueConverter will crash the designer.  Try it like so, with the added IF statement:

    using System; 
    using System.Globalization; 
    using System.Windows.Data; 
     
    namespace MyNamespace
        public class MultiplyConverter : IMultiValueConverter 
        { 
            public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) 
            { 
                double result = 1.0; 
                for (int i = 0; i < values.Length; i++) 
                { 
                    if (values[i] is double) 
                        result *= (double)values[i]; 
                } 
     
                return result; 
            } 
     
            public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) 
            { 
                throw new Exception("Not implemented"); 
            } 
        } 

    Justin
    • Edited by liveultimate Friday, October 10, 2008 9:15 PM changed sample namespace
    Friday, October 10, 2008 9:14 PM
  • Thanks Justin for the code. Works like a champ. 

    I wanted my expander to look like the default xaml expander, transparent except for the graphic and text.  To do that I just cut and pasted the toggle button and its visual style from the default control template and pasted it into the dockpanel.  

    Now it looks like the regular expander control but has the cool animation.

    Kathy
    Monday, November 17, 2008 11:29 PM
  • Hi Justin,

    the code works pretty well although the expanders like ExpanderUpHeaderStyle  are missing.

    I do still have one dought:

    How does the Tag property work? Because it hasnt any relation with the width ( in my case) and actualy if I set it to 0 i both cases it still works.

    Could you explain how you use it?

    Realy thanks

    Eduardo
    Tuesday, November 25, 2008 2:24 PM
  • Kyrw said: the code works pretty well although the expanders like ExpanderUpHeaderStyle  are missing.


    liveultimate said:I did not accomodate for different ExpandDirections, but if you understand the code I'm sure you can flush that out on your own.



    Kyrw said: How does the Tag property work?

    http://msdn.microsoft.com/en-us/library/system.windows.frameworkelement.tag.aspx

    The Tag property is a custom property that you can use for anything you want.  It is of type(object).  WPF does not use it.  I am using it to store a percentage of the expander I want visible.  I animate the Tag property from 0 to 1 and multiply it by the ActualHeight to get a value for Height.

    Justin


    Tuesday, November 25, 2008 5:15 PM
  • Hi Justin.

    Can you please mail me the whole code?

    louisfrancios@msn.com

     

    Kind regards

    Monday, June 21, 2010 11:15 AM
  • @liveultimate

    Hi guy, i'm wondering how to implement this code.

    I made all the steps, exactly that you wrote, but it doesn't compile.

    First, when I add the xmlns:sys, all your expander's template has been blue underline by VS2010.

    So, I tried to compile without <sys:Double>0.0</sys:Double>, just writing 0.0 inside <Grid.Tag/>

    But in this way i yet can't compile. Too many erros about template names that I don't have.

     

    Do you have an empty code to post here? That starts from the zero.

    If you could and wasn't too much to ask, please post a zipped solution with one wpf form with expander with this animation inside it.

    Thankz at all.

    Friday, July 23, 2010 11:19 PM
  • Thieres,

    If you post your email address I will send you a zipped copy.

    Justin

    Tuesday, July 27, 2010 4:12 PM
  • Thank you guy.

    My email is thierestembra(at)gmail(dot)com and I'm using VS2010.

    Thankz again for your reply.

    Tuesday, August 03, 2010 12:19 AM
  • @ liveultimate: can you please send me the code at rishi.kasnia(at)gmail(dot)com i am also using vs2010.

     

    @ Thieres Tembra : Have you solved the peroblem .. could you please share it with me.

    Thursday, August 05, 2010 5:12 AM
  • @Rishi Kasnia

    Yes, I solved. My problem was my custom template that aren't compatible with liveultimate's one.

    If he didn't send to you, reply here and i send to u.

     

     

    @liveultimnate

    First, very thanks to your code. I made it work in my situation.

    But when I write <sys:Double>0.0</sys:Double> i got "input string was not in a correct format " and by this i can't edit window in design view.

    If i just remove this code and only put 0.0 inside the <Grid.Tag> i don't got this error and i can edit window in design view (but i have to write <sys... again when compile again, otherwise i got crash).

    Just to let you know, i have xmlns:sys="clr-namespace:System;assembly=mscorlib" in my ResourceDictionary.

    Do you know what could be it?

    Friday, August 06, 2010 2:09 PM
  • Hey Thieres, 

     

    could you email me the full code as well? I think it would be very useful. Email: kjanoudi[@]gmail[dot]com

     

     

    Monday, October 11, 2010 3:47 PM
  • Hi, I sent to you the liveultimate's code. I hope you make it useful.
    Monday, October 11, 2010 4:24 PM
  • I would love to have this code as well.

    magiancreative(at)live(dot)com.

    Monday, November 22, 2010 8:50 PM
  • Hi darrenlc,

    I sent liveultimate's code your way.

    Tuesday, November 23, 2010 6:10 PM
  • Hello Justin

     

    I want to ask you, if you were able to send me the whole code zipped to my mail? 

    pedekrogh@hotmail.com

    I appreciated it. Thanks..

    //regards Peter

    Monday, March 28, 2011 7:39 AM
  • Hi Peter,

     

    I just sent liveultimate's code your way.

    Monday, March 28, 2011 3:12 PM
  • Sorry for the "Me too"...but could someone send me the project as well?

    Thanks...

    jdaly@intermind-consulting.com

    Wednesday, April 27, 2011 2:11 PM
  • Hi Southern Gentleman,

    I just sent liveultimate's code your way.


    Wednesday, April 27, 2011 4:03 PM
  • Hi All!!

     sorry for joing the line  :(  but  could somebody  please send me the project as well ??

     

    Thanks

    panand@inhance.com

    Friday, April 29, 2011 6:06 PM
  • Sent it your way!
    Friday, April 29, 2011 7:47 PM
  • ksamurai: Awesome piece of code!<o:p></o:p>

    Some issues to notice:

    It gives constant warnings in the output window regarding the ActualHeight sometimes becomes NaN and that obviously causes exception since the ActualHeight is expected to be numeric for the animation to work.

    The thing that made me not use that code is the fact that it kills the whole app when you try to theme the application (removing and adding resource dictionary files) it throws some internal unhandled exception that looks like it’s coming from some WPF core files (maybe not but I couldn’t figure this one out), with method of elimination I could pin point that the issues was the animation that collapses / expends the expander.

    Had to go with Brian KC's solution although it uses Y scaling and the visual is not exactly the same as collapse/expand.<o:p></o:p>

    Just my 2 cents,<o:p></o:p>


    Thursday, February 09, 2012 5:55 PM
  • Hi,

    Can you please mail me the whole code.

    My email id is sharda.bhagwatkar@gmail.com.

    Wednesday, March 07, 2012 11:03 AM
  • Do like this simple way:

    <Expander>
    <Expander.Content>
    ...
    </Expander.Content>
    <Expander.Style>
    	<Style>
    		<Setter Property="Expander.BorderBrush" Value="LightBlue" />
    		<Setter Property="Expander.BorderThickness" Value="1" />
    		<Style.Triggers>
    			<Trigger Property="Expander.IsExpanded" Value="True">
    				<Trigger.EnterActions>
    					<BeginStoryboard>
    						<Storyboard>
    							<DoubleAnimation From="0" Duration="0:0:0.2" Storyboard.TargetProperty="Content.Height" />
    						</Storyboard>
    					</BeginStoryboard>
    				</Trigger.EnterActions>
    				<Trigger.ExitActions>
    					<BeginStoryboard>
    						<Storyboard>
    							<DoubleAnimation To="0" Duration="0:0:0.2" Storyboard.TargetProperty="Content.Height" />
    						</Storyboard>
    					</BeginStoryboard>
    				</Trigger.ExitActions>
    			</Trigger>
    		</Style.Triggers>
    	</Style>
    </Expander.Style>
    </Expander>

    Saturday, October 20, 2012 6:09 PM
  • I've managed to get an Expander to animate opening and closing with a "slide" action.  It does require having one converter available to the resource dictionary, but at least it does not require custom controls or XAML code on every Expander instance.  I did not accomodate for different ExpandDirections, but if you understand the code I'm sure you can flush that out on your own.  It does work for nested Expanders.
    ....
    That's it!  Change the two "Duration" properties on the <DoubleAnimation> tags if you want the "slide" to happen more slowly or quickly.

    Justin

    Your solution works really great but there's one major issue with it :

    - Scrolling with mouse wheel doesn't work any more.

    Any clues ?

    UPDATE

    Actually changing the inner ScrollViewer to a StackPanel solves the issue !



    • Edited by Aybe One Monday, June 10, 2013 5:30 PM
    Monday, June 10, 2013 5:00 PM
  • Hi folks

     

    This would be a great article [with code] for the TechNet Wiki Guru competition.

    Could you please spare us some time to post it?

    Read more in the stickies at the top of this forum, and here for June.

     

    C'mon folks, we need to show WPF still has some passionate developers!

     

    Regards,
    Pete


    #PEJL Got a good solution? If you invest your time in coding an elegant/novel or large answer on these MSDN forums, why not copy it over to our beloved TechNet Wiki, for future generations to benefit from!

    Sunday, June 16, 2013 10:46 PM
    Moderator