wpf - WPF 使用 DataTrigger 为 RenderTransform 设置动画
问题描述
我在使用数据触发器对 Canvas 对象的渲染转换进行动画处理时遇到了困难。作为一个测试用例,我在 Canvas 上有一个椭圆和两个按钮。当我按下每个按钮时,我希望椭圆的位置动画到该按钮的 X 坐标:
我在这个特定示例中使用按钮的事实既不是这里也不是那里,关键是我正在尝试使用绑定到我的视图模型中的属性的 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。它基本上只是一个包含一个椭圆和两个按钮的画布,并且椭圆有 2 个故事板,这些故事板响应后端属性设置为不同的字符串(即“左”和“右”)而被触发:
<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 中看到它。我也尝试过使用 FillBehaviours 和 HandoffBehaviors,但无济于事。有人可以解释为什么会发生这种情况以及如何解决吗?
解决方案
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
,Storyboard
但Stop
一旦Ellipse
动画完成,将恢复的位置。
恐怕不行。首先单击左然后右工作,但再次单击左会导致动画从位置 0 重新开始,即使我设置了 HandoffBehaviors 和 FillBehaviours。
摆脱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>
推荐阅读
- visual-studio - Visual Studio 不接受 Nugget 包的 UWP 版本?
- mysql - mysql查询缓存和缓冲池有什么区别?
- embedded-linux - 在嵌入式 Linux 上使用 can-bus 传输超过 8 个字节
- php - 无法在使用 Cpanel 托管时执行简单的 Laravel 的 cron 作业
- httpclient - 间歇性“发出 HTTP 请求时发生错误”
- vba - VBA:使用 DetectLanguage 方法时出现运行时错误 4198
- xamarin.forms - 如何在我的应用程序中与 ViewModel 进行通信?
- google-apps-script - 我需要为 Google 表格脚本编写一个 onEdit 函数
- python-3.x - FROM --> IMPORT 函数和返回值
- excel - 根据VBA中的条件将二维数组复制到另一个数组