在 WPF 中实现“下滑”动画

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8221230/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 23:11:31  来源:igfitidea点击:

Achieve "slide down" animation in WPF

wpfanimationcontroltemplate

提问by Andrew Shepherd

I am attempting to create my own template for an Expandercontrol. When the control is expanded, I want the content to slide down slowly.

我正在尝试为Expander控件创建自己的模板。当控件展开时,我希望内容缓慢向下滑动。

The desired height of the content is not known at compile time.

编译时不知道所需的内容高度。

I thought we could define the slide down as an animation:

我认为我们可以将下滑定义为动画:

<Storyboard x:Key="ExpandContent">

    <DoubleAnimation 
        Storyboard.TargetName="_expanderContent" 
        Storyboard.TargetProperty="Height" 
        From="0.0" 
        To="{Binding ElementName=_expanderContent,Path=DesiredHeight}"
        Duration="0:0:1.0" />
</Storyboard>

But Unfortunately not. We get an error

但不幸的是没有。我们得到一个错误

Cannot freeze this Storyboard timeline tree for use across threads.

无法冻结此 Storyboard 时间线树以跨线程使用。

It appears that we cannot use binding when defining animation parameters. (Discussed also in this question.)

看来我们在定义动画参数时不能使用绑定。(也在这个问题中讨论过。)

Does anyone have any ideas on how I can approach this? I'm wary of using LayoutTransform.ScaleY, because that would create a distorted image.

有没有人对我如何解决这个问题有任何想法?我对使用 持谨慎态度LayoutTransform.ScaleY,因为这会产生扭曲的图像。

This is similar to this question, but this question has an answer involved writing code-behind, which I don't think is possible in a control template. I'm wondering if a XAML-based solution is achievable.

这类似于这个问题,但是这个问题有一个涉及编写代码隐藏的答案,我认为这在控制模板中是不可能的。我想知道是否可以实现基于 XAML 的解决方案。



值得一提的是,这是我的控件模板的当前状态。

<ControlTemplate x:Key="ExpanderControlTemplate"  TargetType="{x:Type Expander}">
    <ControlTemplate.Resources>
            <!-- Here are the storyboards which don't work -->
            <Storyboard x:Key="ExpandContent">

                <DoubleAnimation 
                    Storyboard.TargetName="_expanderContent" 
                    Storyboard.TargetProperty="Height" 
                    From="0.0" 
                    To="{Binding ElementName=_expanderContent,Path=DesiredHeight}"
                    Duration="0:0:1.0" />
            </Storyboard>
            <Storyboard x:Key="ContractContent">

                <DoubleAnimation 
                    Storyboard.TargetName="_expanderContent" 
                    Storyboard.TargetProperty="Height" 
                    From="{Binding ElementName=_expanderContent,Path=DesiredHeight}"
                    To="0.0"
                    Duration="0:0:1.0" />

            </Storyboard>
        </ControlTemplate.Resources>
    <Grid Name="MainGrid" Background="White">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Name="ContentRow" Height="Auto" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <Border>
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*" />
                    <ColumnDefinition Width="Auto" />
                </Grid.ColumnDefinitions>
                <ContentPresenter ContentSource="Header" />
                <ToggleButton Template="{StaticResource ProductButtonExpand}"
                              Grid.Column="1"
                              IsChecked="{Binding Path=IsExpanded,Mode=TwoWay,RelativeSource={RelativeSource TemplatedParent}}" 
                              />
                <Rectangle Grid.ColumnSpan="2" Fill="#FFDADADA" Height="1" Margin="8,0,8,2" VerticalAlignment="Bottom"/>

            </Grid>
        </Border>

            <ContentPresenter Grid.Row="1" HorizontalAlignment="Stretch" Name="_expanderContent">

            </ContentPresenter>

    </Grid>
    <ControlTemplate.Triggers>
        <Trigger Property="IsExpanded" Value="True">
            <Setter TargetName="_expanderContent" Property="Height" Value="{Binding ElementName=_expanderContent,Path=DesiredHeight}" />

                <!-- Here is where I would activate the storyboard if they did work -->
                <Trigger.EnterActions>
                <!--<BeginStoryboard Storyboard="{StaticResource ExpandContent}"/>-->
            </Trigger.EnterActions>
                <Trigger.ExitActions>
                    <!--<BeginStoryboard x:Name="ContractContent_BeginStoryboard" Storyboard="{StaticResource ContractContent}"/>-->
                </Trigger.ExitActions>
        </Trigger>
            <Trigger Property="IsExpanded" Value="False">

                <Setter TargetName="_expanderContent" Property="Height" Value="0" />
            </Trigger>
        </ControlTemplate.Triggers>

</ControlTemplate>

采纳答案by H.B.

Ifyou can use Interactionswith FluidLayout(Blend 4 SDK) you are in luck, it's really useful for those fancy animation things.

如果您可以InteractionsFluidLayout( Blend 4 SDK)一起使用,那么您很幸运,它对那些花哨的动画非常有用。

First set the content CP's Height to 0:

首先设置内容CP的Height为0:

<ContentPresenter Grid.Row="1"
    HorizontalAlignment="Stretch"
    x:Name="_expanderContent"
    Height="0"/>

To animate this, the Heightjust needs to be animated to NaNin the VisualStatethat represents the expanded state (non-discrete animations would not let you use NaN):

要对此Height进行动画处理,只需要NaNVisualState表示展开状态的 中设置动画(非离散动画不允许您使用NaN):

xmlns:is="http://schemas.microsoft.com/expression/2010/interactions"
<Grid x:Name="MainGrid" Background="White">
    <VisualStateManager.CustomVisualStateManager>
        <is:ExtendedVisualStateManager/>
    </VisualStateManager.CustomVisualStateManager>
    <VisualStateManager.VisualStateGroups>
        <VisualStateGroup x:Name="ExpansionStates" is:ExtendedVisualStateManager.UseFluidLayout="True">
            <VisualStateGroup.Transitions>
                <VisualTransition GeneratedDuration="0:0:1"/>
            </VisualStateGroup.Transitions>
            <VisualState x:Name="Expanded">
                <Storyboard>
                    <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Height)"
                                                   Storyboard.TargetName="_expanderContent">
                        <DiscreteDoubleKeyFrame KeyTime="0" Value="NaN"/>
                    </DoubleAnimationUsingKeyFrames>
                </Storyboard>
            </VisualState>
            <VisualState x:Name="Collapsed"/>
        </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>
    <!-- ... --->

That should be all that is necessary, the fluid layout will create the transition for you from there.

这应该是所有必要的,流体布局将从那里为您创建过渡。



If you have a code-behind solution that would be fine, you can even use code-behind in dictionaries like this:

如果您有一个很好的代码隐藏解决方案,您甚至可以在字典中使用代码隐藏,如下所示:

<!-- TestDictionary.xaml -->
<ResourceDictionary x:Class="Test.TestDictionary"
                    ...>
//TestDictionary.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;

namespace Test
{
    partial class TestDictionary : ResourceDictionary
    {
        //Handlers and such here
    }
}

回答by Sverrir Sigmundarson

This is kind of an old question but I had problems with this today, so I guess posting my solution would be worth it:

这是一个老问题,但我今天遇到了这个问题,所以我想发布我的解决方案是值得的:

I had to animate the Height property of a grid row (sliding up and down), but needed dynamic binding so that the row would slide again to the same position as before.

我必须为网格行的 Height 属性设置动画(上下滑动),但需要动态绑定,以便该行再次滑动到与以前相同的位置。

I found this answer to be very helpful (after fruitlessly battling XAML): http://go4answers.webhost4life.com/Question/found-solution-work-protected-override-190845.aspx

我发现这个答案非常有帮助(在与 XAML 斗争无果之后):http: //go4answers.webhost4life.com/Question/found-solution-work-protected-override-190845.aspx

Sometimes doing things in the code-behind is just simpler:

有时在代码隐藏中做事更简单:

        Storyboard sb = new Storyboard();

        var animation = new GridLengthAnimation
        {
                Duration = new Duration(500.Milliseconds()),
                From = this.myGridRow.Height,
                To = new GridLength(IsGridRowVisible ? GridRowPreviousHeight : 0, GridUnitType.Pixel)
        };

        // Set the target of the animation
        Storyboard.SetTarget(animation, this.myGridRow);
        Storyboard.SetTargetProperty(animation, new PropertyPath("Height"));

        // Kick the animation off
        sb.Children.Add(animation);
        sb.Begin();

The GridLengthAnimation class can be found here: http://social.msdn.microsoft.com/forums/en-US/wpf/thread/da47a4b8-4d39-4d6e-a570-7dbe51a842e4/

GridLengthAnimation 类可以在这里找到:http: //social.msdn.microsoft.com/forums/en-US/wpf/thread/da47a4b8-4d39-4d6e-a570-7dbe51a842e4/

回答by JCH2k

There is a ready-to-use and XAML-only solution on CodeProject:

CodeProject 上有一个即用型和仅 XAML 的解决方案:

The Styles:

样式:

    <local:MultiplyConverter x:Key="MultiplyConverter" />
    <Style TargetType="Expander" x:Key="VerticalSlidingEmptyExpander">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Expander}">
                    <ScrollViewer x:Name="ExpanderContentScrollView"
                  HorizontalScrollBarVisibility="Hidden"
                  VerticalScrollBarVisibility="Hidden"
                  HorizontalContentAlignment="Stretch"
                  VerticalContentAlignment="Top"
                  >
                        <ScrollViewer.Tag>
                            <system:Double>0.0</system:Double>
                        </ScrollViewer.Tag>
                        <ScrollViewer.Height>
                            <MultiBinding Converter="{StaticResource MultiplyConverter}">
                                <Binding Path="ActualHeight" ElementName="ExpanderContent"/>
                                <Binding Path="Tag" RelativeSource="{RelativeSource Self}" />
                            </MultiBinding>
                        </ScrollViewer.Height>
                        <ContentPresenter x:Name="ExpanderContent" ContentSource="Content"/>
                    </ScrollViewer>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsExpanded" Value="True">
                            <Trigger.EnterActions>
                                <BeginStoryboard>
                                    <Storyboard>
                                        <DoubleAnimation 
                       Storyboard.TargetName="ExpanderContentScrollView"
                       Storyboard.TargetProperty="Tag"
                       To="1"
                       Duration="0:0:0.2"/>
                                    </Storyboard>
                                </BeginStoryboard>
                            </Trigger.EnterActions>
                            <Trigger.ExitActions>
                                <BeginStoryboard>
                                    <Storyboard>
                                        <DoubleAnimation 
                         Storyboard.TargetName="ExpanderContentScrollView"
                         Storyboard.TargetProperty="Tag"
                         To="0"
                         Duration="0:0:0.2"/>
                                    </Storyboard>
                                </BeginStoryboard>
                            </Trigger.ExitActions>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    <Style TargetType="Expander" x:Key="HorizontalSlidingEmptyExpander">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Expander}">
                    <ScrollViewer x:Name="ExpanderContentScrollView"
                  HorizontalScrollBarVisibility="Hidden"
                  VerticalScrollBarVisibility="Hidden"
                  HorizontalContentAlignment="Left"
                  VerticalContentAlignment="Stretch"
                  >
                        <ScrollViewer.Tag>
                            <system:Double>0.0</system:Double>
                        </ScrollViewer.Tag>
                        <ScrollViewer.Width>
                            <MultiBinding Converter="{StaticResource MultiplyConverter}">
                                <Binding Path="ActualWidth" ElementName="ExpanderContent"/>
                                <Binding Path="Tag" RelativeSource="{RelativeSource Self}" />
                            </MultiBinding>
                        </ScrollViewer.Width>
                        <ContentPresenter x:Name="ExpanderContent" ContentSource="Content"/>
                    </ScrollViewer>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsExpanded" Value="True">
                            <Trigger.EnterActions>
                                <BeginStoryboard>
                                    <Storyboard>
                                        <DoubleAnimation 
                       Storyboard.TargetName="ExpanderContentScrollView"
                       Storyboard.TargetProperty="Tag"
                       To="1"
                       Duration="0:0:0.2"/>
                                    </Storyboard>
                                </BeginStoryboard>
                            </Trigger.EnterActions>
                            <Trigger.ExitActions>
                                <BeginStoryboard>
                                    <Storyboard>
                                        <DoubleAnimation 
                         Storyboard.TargetName="ExpanderContentScrollView"
                         Storyboard.TargetProperty="Tag"
                         To="0"
                         Duration="0:0:0.2"/>
                                    </Storyboard>
                                </BeginStoryboard>
                            </Trigger.ExitActions>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

MultiplyConverter:

乘法转换器:

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");
    }
}

I duplicated the Style to have a horizontal and vertical version and omitted the ToggleButtons, but you can easily get that from the original post.

我复制了 Style 以具有水平和垂直版本并省略了 ToggleButtons,但您可以从原始帖子中轻松获得。