代码之家  ›  专栏  ›  技术社区  ›  Mark Feldman

wpf使用datatrigger设置rendertransform动画

  •  1
  • Mark Feldman  · 技术社区  · 6 年前

    我很难使用数据触发器设置画布对象的渲染转换的动画。作为一个测试用例,我在画布上有一个椭圆和两个按钮。当我按下每个按钮时,我希望椭圆的位置以该按钮的X坐标为动画:

    enter image description here

    在这个特定的例子中,我使用的按钮既不在这里也不在那里,关键是我试图用绑定到视图模型中的属性的datatrigger来触发这些动画:

    // this is the property my DataTrigger will bind to
    private string _Anim;
    public string Anim
    {
        get { return this._Anim; }
        set
        {
            this._Anim = value;
            RaisePropertyChanged(() => this.Anim);
        }
    }
    
    // a command handler for the buttons that sets my animation property
    public ICommand AnimCommand { get { return new RelayCommand<string>(OnAnim); } }
    private void OnAnim(string value)
    {
        this.Anim = String.Empty;   // cancel any existing animation, probably not needed...
        this.Anim = value;
    }
    

    这是我用来做这个的xaml。它基本上只是一个包含一个椭圆和两个按钮的画布,椭圆有两个情节串连板,当后端属性被设置为不同的字符串(即“左”和“右”)时触发:

    <Viewbox xmlns:sys="clr-namespace:System;assembly=mscorlib">
        <Canvas Width="350" Height="150" Background="CornflowerBlue">
            <Ellipse x:Name="MyEllipse" Width="20" Height="20" Fill="Red">
                <Ellipse.RenderTransform>
                    <TranslateTransform x:Name="myTransform" X="50" Y="50" />
                </Ellipse.RenderTransform>
                <Ellipse.Style>
                    <Style TargetType="Ellipse">
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding Anim}" Value="Left">
                                <DataTrigger.EnterActions>
                                    <BeginStoryboard>
                                        <Storyboard>
                                            <DoubleAnimation Storyboard.TargetProperty="(Ellipse.RenderTransform).(TranslateTransform.X)" To="100" Duration="0:0:1" />
                                        </Storyboard>
                                    </BeginStoryboard>
                                </DataTrigger.EnterActions>
                            </DataTrigger>
                            <DataTrigger Binding="{Binding Anim}" Value="Right">
                                <DataTrigger.EnterActions>
                                    <BeginStoryboard>
                                        <Storyboard>
                                            <DoubleAnimation Storyboard.TargetProperty="(Ellipse.RenderTransform).(TranslateTransform.X)" To="200" Duration="0:0:1" />
                                        </Storyboard>
                                    </BeginStoryboard>
                                </DataTrigger.EnterActions>
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </Ellipse.Style>
            </Ellipse>
    
            <Button x:Name="btnLeft" Content="Left" Command="{Binding AnimCommand}" CommandParameter="Left" Canvas.Left="100" Canvas.Top="100" Width="32" Height="24" />
            <Button x:Name="btnRight" Content="Right" Command="{Binding AnimCommand}" CommandParameter="Right" Canvas.Left="200" Canvas.Top="100" Width="32" Height="24" />
    
        </Canvas>
    </Viewbox>
    

    当我运行这个,我可以点击第一个动画,然后点击第二个,并且行为都是正确的。然而,按相反的顺序点击它们是不起作用的…右边的按钮动画设置正确,左边的按钮什么也不起作用。就好像“正确的”动画仍在运行或锁定属性或其他东西一样,如果我交换xaml中脚本声明的顺序,情况就会逆转,这一事实进一步证明了这一点。

    这对eventtriggers很好,我只看到它对datatriggers。我也试过玩fillbehaviors和handoffbehaviors,但没用。有人能解释一下为什么会发生这样的事情吗?

    1 回复  |  直到 6 年前
        1
  •  1
  •   mm8    6 年前

    你应该移除 Storyboard 动画完成后。你可以用 RemoveStoryboard 上课做这个。就给我 BeginStoryboard 元素A Name 然后添加 RemoveStoryboard 元素到 ExitActions DataTrigger :

    <Style TargetType="Ellipse">
        <Style.Triggers>
            <DataTrigger Binding="{Binding Anim}" Value="Left">
                <DataTrigger.EnterActions>
                    <BeginStoryboard Name="sb">
                        <Storyboard>
                            <DoubleAnimation Storyboard.TargetProperty="(Ellipse.RenderTransform).(TranslateTransform.X)" To="100" Duration="0:0:1" />
                        </Storyboard>
                    </BeginStoryboard>
                </DataTrigger.EnterActions>
                <DataTrigger.ExitActions>
                    <RemoveStoryboard BeginStoryboardName="sb" />
                </DataTrigger.ExitActions>
            </DataTrigger>
            <DataTrigger Binding="{Binding Anim}" Value="Right">
                <DataTrigger.EnterActions>
                    <BeginStoryboard Name="sb2">
                        <Storyboard>
                            <DoubleAnimation Storyboard.TargetProperty="(Ellipse.RenderTransform).(TranslateTransform.X)" To="200" Duration="0:0:1" />
                        </Storyboard>
                    </BeginStoryboard>
                </DataTrigger.EnterActions>
                <DataTrigger.ExitActions>
                    <RemoveStoryboard BeginStoryboardName="sb2" />
                </DataTrigger.ExitActions>
            </DataTrigger>
        </Style.Triggers>
    </Style>
    

    另一个选项是 FillBehavior 故事板 Stop 但是 Ellipse 动画完成后将还原。

    恐怕不行。单击左键然后单击右键开始工作,但是再次单击左键会导致动画从位置0重新启动,即使我设置了handovbehaviors和fillbehaviors。

    摆脱 DataTriggers 然后在xaml中编写一些自定义代码。这工作:

    private void MyEllipse_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
    {
        INotifyPropertyChanged inpc = MyEllipse.DataContext as INotifyPropertyChanged;
        if (inpc != null)
            WeakEventManager<INotifyPropertyChanged, PropertyChangedEventArgs>.AddHandler(inpc, nameof(INotifyPropertyChanged.PropertyChanged), OnChanged);
    }
    
    private PropertyInfo _pi;
    private void OnChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == "Anim")
        {
            _pi = _pi ?? sender.GetType().GetProperty(e.PropertyName);
            string anim = _pi?.GetValue(sender) as string;
            switch (anim)
            {
                case "Left":
                    myTransform.BeginAnimation(TranslateTransform.XProperty, new DoubleAnimation() { To = 100.0, Duration = TimeSpan.FromSeconds(1) });
                    break;
                case "Right":
                    myTransform.BeginAnimation(TranslateTransform.XProperty, new DoubleAnimation() { To = 200.0, Duration = TimeSpan.FromSeconds(1) });
                    break;
            }
        }
    }
    

    XAML:

    <Viewbox xmlns:sys="clr-namespace:System;assembly=mscorlib">
        <Canvas Width="350" Height="150" Background="CornflowerBlue">
            <Ellipse x:Name="MyEllipse" Width="20" Height="20" Fill="Red"
                             DataContextChanged="MyEllipse_DataContextChanged">
                <Ellipse.RenderTransform>
                    <TranslateTransform x:Name="myTransform" X="50" Y="50" />
                </Ellipse.RenderTransform>
            </Ellipse>
    
            <Button x:Name="btnLeft" Content="Left" Command="{Binding AnimCommand}" CommandParameter="Left" Canvas.Left="100" Canvas.Top="100" Width="32" Height="24" />
            <Button x:Name="btnRight" Content="Right" Command="{Binding AnimCommand}" CommandParameter="Right" Canvas.Left="200" Canvas.Top="100" Width="32" Height="24" />
        </Canvas>
    </Viewbox>