WPF实现单帧动画 点击按钮触发失效及外部线程调用方案咨询
问题根因
- 你直接手动调用
DoubleAnimation_Completed回调方法,没有正确触发WPF动画的渲染流程:XAML中定义在Window.Loaded触发器内的DoubleAnimation实例da的作用域归所属Storyboard管理,直接调用da.BeginAnimation无法正确关联渲染上下文,动画不会实际执行,只会执行ShowImage的逻辑,且连续两次调用会让计数器ctr跳两步,看不到预期单帧效果。 - 原有自动循环逻辑是依赖动画完成事件触发下一次动画,手动调用时没有等待上一次动画完成,所有操作在UI线程同步执行,渲染队列被压到最后执行,看不到中间帧变化。
解决方案
第一步:修改XAML代码
把动画从Loaded触发器中移到资源字典,避免页面加载后自动循环播放,同时方便后台代码调用:
<Window x:Class="AnimatedFlag.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Indian Flag" Height="534.351" Width="525"> <Window.Resources> <!-- 把动画定义成窗口资源,方便后台调用 --> <DoubleAnimation x:Key="FrameAnimation" Storyboard.TargetName="flagImage" Storyboard.TargetProperty="Width" From="200" To="200" Duration="0:0:0.1" Completed="FrameAnimation_Completed"/> </Window.Resources> <Grid Margin="0,0,0,-102"> <Image Name="flagImage" Margin="12,12,12,263"/> <Button x:Name="button" Content="切换帧" HorizontalAlignment="Left" Margin="153,364,0,0" VerticalAlignment="Top" Width="75" Click="button_Click"/> </Grid> </Window>
第二步:修改C#后台代码
封装单帧触发逻辑,同时适配后续跨线程调用的需求:
public partial class MainWindow : Window { int ctr = 1; private DoubleAnimation _frameAnimation; public MainWindow() { InitializeComponent(); // 初始化时拿到动画资源 _frameAnimation = (DoubleAnimation)FindResource("FrameAnimation"); // 如果需要页面加载后自动播放可以打开下面注释 // flagImage.BeginAnimation(Image.WidthProperty, _frameAnimation); } private void FrameAnimation_Completed(object sender, EventArgs e) { // 动画完成后只更新一次帧,不要在这里循环触发 ShowImage(); } private void ShowImage() { string filename = "Images/Flag" + ctr + ".jpg"; BitmapImage image = new BitmapImage(); image.BeginInit(); image.UriSource = new Uri(filename, UriKind.Relative); image.EndInit(); flagImage.Source = image; ctr++; if (ctr > 6) { ctr = 1; } } /// <summary> /// 触发单帧动画的公共方法,支持跨线程调用 /// </summary> public void TriggerSingleFrame() { // 跨线程调用时自动调度到UI线程执行 if (!Dispatcher.CheckAccess()) { Dispatcher.Invoke(TriggerSingleFrame); return; } // 每次调用启动一次动画,动画完成后自动触发Completed更新帧 flagImage.BeginAnimation(Image.WidthProperty, _frameAnimation); } private void button_Click(object sender, RoutedEventArgs e) { // 点击按钮触发一次单帧,需要一次跳多帧可以多次调用该方法 TriggerSingleFrame(); } }
第三步:外部线程调用说明
后续如果要在非UI线程中触发帧动画,直接调用TriggerSingleFrame方法即可,方法内部已经做了UI线程调度处理,不会抛出跨线程访问异常。示例:
// 后台线程中触发动画示例 Task.Run(() => { // 执行业务逻辑 TriggerSingleFrame(); });
内容的提问来源于stack exchange,提问作者Alpha_33
相关产品推荐
相关产品推荐

