You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在WPF(.NET 8+Caliburn Micro)的ViewModel中启动Storyboard动画?

问题场景

使用WPF .NET 8 + Caliburn Micro实现MVVM架构,需要在菜单切换(点击首页按钮触发ActivateHomeView方法)前,启动MainView.xaml中预定义的MenuChangeStart Storyboard动画。当前两种尝试均失败:

  • 方法1:在ViewModel中调用FindResource失败(ViewModel无该方法)
  • 方法2:向ViewModel注入MainView实例时,实例始终为空

当前ActivateHomeView代码:

public void ActivateHomeView()
{
    CurrentMenuIcon = @"\Resources\home.png";
  
    // 此处需启动MenuChangeStart动画
    ActivateItemAsync(new HomeViewModel());
}
正确实现方式

方式1:Caliburn Micro事件聚合器(推荐,符合MVVM分离原则)

利用Caliburn内置的事件聚合器,让ViewModel发送动画触发消息,View订阅并执行动画,完全解耦视图与逻辑:

1. 定义消息类

public class MenuChangeAnimationMessage
{
    // 可添加参数区分不同动画类型
    public string AnimationName { get; set; }
}

2. ViewModel中发送消息

注入IEventAggregator,在切换菜单时发送动画触发指令:

private readonly IEventAggregator _eventAggregator;

public MainViewModel(IEventAggregator eventAggregator)
{
    _eventAggregator = eventAggregator;
}

public async void ActivateHomeView()
{
    CurrentMenuIcon = @"\Resources\home.png";
  
    // 发送启动动画的消息
    await _eventAggregator.PublishAsync(new MenuChangeAnimationMessage { AnimationName = "MenuChangeStart" });
  
    // 可选:等待动画完成后再切换视图(时长需匹配动画实际长度)
    await Task.Delay(300);
    await ActivateItemAsync(new HomeViewModel());
}

3. View中订阅消息并执行动画

在MainView.xaml.cs中订阅消息,收到后启动Storyboard:

public partial class MainView : Window
{
    private readonly IEventAggregator _eventAggregator;
    private IDisposable _subscription;

    public MainView(IEventAggregator eventAggregator)
    {
        InitializeComponent();
        _eventAggregator = eventAggregator;
        // 在UI线程订阅消息
        _subscription = _eventAggregator.SubscribeOnUIThread(this);
    }

    // Caliburn会自动匹配消息处理方法
    public void Handle(MenuChangeAnimationMessage message)
    {
        if (message.AnimationName == "MenuChangeStart")
        {
            var storyboard = FindResource("MenuChangeStart") as Storyboard;
            storyboard?.Begin();
        }
    }

    protected override void OnClosed(EventArgs e)
    {
        _subscription?.Dispose();
        base.OnClosed(e);
    }
}

方式2:DataTrigger绑定ViewModel属性(适合简单场景)

通过ViewModel的属性变化,触发View中的DataTrigger启动动画:

1. ViewModel添加动画触发属性

private bool _isMenuChanging;
public bool IsMenuChanging
{
    get => _isMenuChanging;
    set
    {
        _isMenuChanging = value;
        NotifyOfPropertyChange(() => IsMenuChanging);
    }
}

public async void ActivateHomeView()
{
    CurrentMenuIcon = @"\Resources\home.png";
  
    // 触发动画
    IsMenuChanging = true;
  
    // 等待动画完成
    await Task.Delay(300);
    IsMenuChanging = false;
  
    await ActivateItemAsync(new HomeViewModel());
}

2. View中配置DataTrigger

在MainView.xaml的根元素(如Window/Grid)中添加触发器:

<Window.Triggers>
    <DataTrigger Binding="{Binding IsMenuChanging}" Value="True">
        <BeginStoryboard Storyboard="{StaticResource MenuChangeStart}" />
    </DataTrigger>
</Window.Triggers>

方式3:通过IViewAware接口获取View实例(不推荐,存在耦合)

若需直接调用View方法,可实现Caliburn的IViewAware接口获取View实例(避免构造函数注入的时序问题):

1. ViewModel实现IViewAware接口

public class MainViewModel : Conductor<object>, IViewAware
{
    private MainView _view;

    public void AttachView(object view, object context = null)
    {
        _view = view as MainView;
    }

    public object GetView(object context = null)
    {
        return _view;
    }

    public event EventHandler<ViewAttachedEventArgs> ViewAttached;

    public async void ActivateHomeView()
    {
        CurrentMenuIcon = @"\Resources\home.png";
      
        if (_view != null)
        {
            _view.MenuChangeOpen();
        }
      
        await Task.Delay(300);
        await ActivateItemAsync(new HomeViewModel());
    }
}

2. View中实现动画启动方法

public partial class MainView : Window
{
    public void MenuChangeOpen()
    {
        var storyboard = FindResource("MenuChangeStart") as Storyboard;
        storyboard?.Begin();
    }
}

内容的提问来源于stack exchange,提问作者Prince Fowzan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 05:13:13