【问题标题】:Animate height of groupbox from 0 to auto将 groupbox 的高度从 0 设置为 auto
【发布时间】:2011-09-27 02:53:57
【问题描述】:

我在我的应用程序中使用了类似于扩展器的分组框。当我需要折叠一个组合框时,我将其高度设置为 0。当我需要展开它时,我将其高度设置为 auto (double.Nan) 是否可以使用情节提要执行此操作。我怎么能提前知道自动高度。表达式混合无法让我制作自动动画。

【问题讨论】:

    标签: c# wpf xaml animation storyboard


    【解决方案1】:

    我讨厌比例变换,因为我觉得它很丑,所以我寻找了另一种解决方案。

    好吧,我知道这是一篇旧帖子,并且存在许多解决方法,但我的很简单,即使有人确定找到它,我也没有在其他地方阅读过它。
    不是将高度从 X 设置为 Auto(这是不可能的),您可以让高度设置为 Auto 并为 MaxHeight 属性设置动画:

    <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>
    

    【讨论】:

    • 我还没有尝试过,但我不知道它是如何工作的。很聪明。
    • 我现在知道了:我不知道确切的条件,但是当我尝试将 To 绑定到我的窗口的 MaxHeight 或它的网格时,我得到了一个 InvalidOperationException。
    • 我也遇到了异常:“InvalidOperationException:无法冻结此 Storyboard 时间线树以供跨线程使用。”那是因为在里面使用了绑定
    • 这个解决方案太棒了!
    • 请注意,如果元素有边距,您可能还需要使用 ThicknessAnimation 为边距设置动画。
    【解决方案2】:

    您可以为此使用 ScaleTransform

    <GroupBox Header="GroupBox">
        <GroupBox.RenderTransform>
            <ScaleTransform ScaleY="1"/>
        </GroupBox.RenderTransform>
    </GroupBox>
    

    当折叠一个组合框时将 ScaleTransform.ScaleX 设置为 0。当展开时设置为 1。

    【讨论】:

    • 我实际上需要从特定高度动画到自动。因此,在运行动画之前,我将创建一个循环,以测试在多大的比例下我会得到 50 的近似高度(尝试使用 .01 然后 .02 等直到它关闭)并将该变量绑定到我的故事板。谢谢瑞克
    • 呃,比例变换压缩了视觉效果,看起来不太好。我更喜欢动画高度(和边距,如果有的话)。
    【解决方案3】:

    这是一个基于 StackPanel 的即用型示例(请随意将其替换为 GroupBox)。

    对于那些具有 «InvalidOperationException: 无法冻结此 Storyboard 时间线树以供跨线程使用。» 的情况,此异常发生在 Storyboard 中定义绑定时,该绑定本身定义在 ControlTemplate 或 Style 中。 在某种程度上,冻结动画是一种所有值都是静态的动画(假设是预先计算的)。

    只是为了感觉问题,想象一下 StackPanel 在动画过程中被添加或删除了一些控件,理论上要成功实现这个动画,动画引擎应该考虑到这些变化(通过绑定), 但不幸的是,WPF 实际上并不支持这种情况。

    要回到下面的解决方案,请注意我正在使用任何 ControlTemplate 或 Style 之外的绑定。 在这种情况下,动画参数是在触发事件时计算的,但一旦播放,它们不会在绑定属性更改时更新。

    因此,此解决方案(除非其他解决方案)存在 2 个非阻塞问题:

    1. 在动画结束时,如果 StackPanel 内容在动画期间发生变化,则 StackPanel 的大小可能会出错
    2. 在复选框上快速单击几次时,动画持续时间将始终相同,无论剩余空间折叠还是展开
    <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>
    

    到目前为止,似乎还没有完美而简单的解决方案来解决这个问题......

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-07-12
      • 2016-01-20
      • 2021-05-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-04-27
      相关资源
      最近更新 更多