Animate height of groupbox from 0 to auto
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 st开发者_JS百科oryboard. How could I know the auto height in advance. Expression blend does not enable me to animate to an auto.
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>
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.
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...
精彩评论