I have groupboxes acting like expanders in my application. When I need to colapse a groupbox I set its height equal to 0. when I need to expand it I set it's height equal to auto (double.Nan) is it posible to do this with a storyboard. How could I know the auto height in advance. Expression blend does not enable me to animate to an auto.
3 Answers
As I hate scale transformation because I find it ugly, I looked for another solution.
Well, I know it is an old post and many workarounds exist, but mine is quite simple, and I didn't read it elsewhere even if someone found it for sure.
Instead of animating the height from X to Auto
(which is impossible), you could let the height to Auto
and animate the MaxHeight
property:
<MyControl x:Name="ctrlAutoHeight" Height="Auto">
<MyControl.Triggers>
<EventTrigger RoutedEvent="myRoutedEvent">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="ctrlAutoHeight"
Storyboard.TargetProperty="MaxHeight"
From="0.0"
To="{Binding ElementName=ParentControl, Path=ActualHeight}"
Duration="0:0:1"
AutoReverse="False"
/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</MyControl.Triggers>
</MyControl>
-
I didn't try it yet, but I don't see how it wouldn't work. Pretty smart. – Kilazur Jan 26 '15 at 14:20
-
6I do know now: i don't know the exact condition, but I get an InvalidOperationException when I try to bind `To` to my window's MaxHeight, or to its grid's. – Kilazur Jan 26 '15 at 14:43
-
7I also got the exception: "InvalidOperationException: Cannot freeze this Storyboard timeline tree for use across threads." That is because of using binding inside – Pavlo Hryza May 29 '17 at 15:00
-
This solution is glorious! – Skintkingle Oct 01 '18 at 10:26
-
Note you may also need to animate the margin if the element has one, using a ThicknessAnimation. – Chris Bordeman Oct 29 '18 at 02:44
-
Why so many people said: You can not use `binding` in animation? Why you can do this? https://stackoverflow.com/a/154227/3907561 – huang Mar 31 '21 at 11:35
You can use ScaleTransform for this
<GroupBox Header="GroupBox">
<GroupBox.RenderTransform>
<ScaleTransform ScaleY="1"/>
</GroupBox.RenderTransform>
</GroupBox>
When collapse a groupbox set ScaleTransform.ScaleX to 0. And when expand set to 1.

- 325
- 1
- 11
-
I actually need to animate from a specific height to auto. Therefore before running the animation I will create a loop that will test to see at what scale I would get an proximate height of 50 (try with .01 then .02 etc until it is close) and bind that variable to my storyboard. Thanks Rikker – Tono Nam Jul 15 '11 at 20:52
-
Ugh, scale transform compresses the visual, doesn't look good. I prefer to animate the height (and the margin, if any). – Chris Bordeman Oct 29 '18 at 02:43
Here is a ready-to-use example based on a StackPanel (feel free to replace it with a GroupBox).
For those having the «InvalidOperationException: Cannot freeze this Storyboard timeline tree for use across threads.», this exception occurs when a binding is defined inside a Storyboard, itself defined inside a ControlTemplate or a Style. In a way, a frozen animation is an animation where all values are static (let's say pre-calculated).
Just to feel the problem, imagine the StackPanel is being added or removed some controls during the animation, in theory to successfully fulfill this animation, the animation engine should take those changes into account (via bindings), but unfortunately, WPF doesn't actually support this case.
To come back to the solution below, note that I'm using bindings outside of any ControlTemplate or Style. In this case, the animation parameters are computed when events are triggered, but once playing they are not updated when bound properties change.
Therefore, this solution (unless others) suffers of 2 non blocking problems:
- At the end of an animation, the size of the StackPanel can be wrong if the StackPanel content changes during animation
- When fast clicking several times on the CheckBox the animation duration will always be the same whatever the remaining space to collapse or expand
<StackPanel>
<!-- CheckBox used for triggering the StackPanel animation -->
<CheckBox Content="Expand/Collapse">
<CheckBox.Triggers>
<EventTrigger RoutedEvent="CheckBox.Checked">
<!-- Expand animation -->
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="StackPanel"
Storyboard.TargetProperty="Height"
From="{Binding ElementName=StackPanel, Path=Height}"
To="{Binding ElementName=ContentSize, Path=DesiredSize.Height}"
Duration="0:0:1" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="CheckBox.Unchecked">
<!-- Collapse animation -->
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="StackPanel"
Storyboard.TargetProperty="Height"
From="{Binding ElementName=StackPanel, Path=Height}"
To="0"
Duration="0:0:1" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</CheckBox.Triggers>
</CheckBox>
<!-- The animated StackPanel -->
<StackPanel x:Name="StackPanel" Height="0">
<!-- The border below acts as a probe to get the expected StackPanel container size -->
<Border x:Name="ContentSize">
<TextBlock>Some Content</TextBlock>
</Border>
</StackPanel>
</StackPanel>
Until now, it appears that no perfect and simple solution exist to this problem...

- 765
- 8
- 18