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

使用Prism Event Aggregator实现WPF代码后台到ViewModel通信失败如何解决

核心问题原因

你判断的完全正确,问题就出在EventAggregator的实例化逻辑上。Prism注入给ViewModel的IEventAggregator是DI容器管理的全局单例,你在代码后台直接new EventAggregator()生成的是全新的实例,两个实例的订阅池完全独立,所以你在新实例上发布事件,原来在单例上的订阅自然收不到通知。

修复EventAggregator调用的方案

你只需要确保代码后台获取的EventAggregator和ViewModel注入的是同一个实例即可,推荐两种获取方式:

  • 方式1:直接通过Prism的容器定位器获取
    不需要修改任何公共属性,在后台代码中直接调用即可:
    // 引用Prism.Ioc命名空间
    private readonly IEventAggregator _eventAggregator = ContainerLocator.Container.Resolve<IEventAggregator>();
    
  • 方式2:通过当前View的DataContext传递
    如果当前View的DataContext就是目标ViewModel,可以直接复用ViewModel里的实例,不需要额外从容器取:
    private void YourDataGrid_EndEditing(object sender, EventArgs e)
    {
        if (DataContext is YourViewModel vm)
        {
            vm.EventAggregator.GetEvent<AfterLineAmountPaidEvent>().Publish(new AfterLineAmountPaidEventArgs
            {
                InvoiceLinesSelectedAmount = InvoiceLinesDataGrid.ItemsSource
            });
        }
    }
    
更符合MVVM规范的替代通信方案

EventAggregator适合跨多个不相关ViewModel的通信,你这个场景是同页面的View和ViewModel通信,其实有更轻量、更易维护的方案:

方案1:封装附加行为(无后台代码)

你可以把自定义DataGrid的专属结束编辑事件封装成附加行为,直接绑定到ViewModel的ICommand,完全符合MVVM规范,不需要写任何业务相关的后台代码:

public static class DataGridAttach
{
    public static readonly DependencyProperty EndCellEditCommandProperty = DependencyProperty.RegisterAttached(
        "EndCellEditCommand", typeof(ICommand), typeof(DataGridAttach), new PropertyMetadata(default(ICommand), OnEndCellEditCommandChanged));

    private static void OnEndCellEditCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (d is not CustomDataGrid dataGrid) return;
        if (e.OldValue != null)
        {
            // 卸载旧事件
            dataGrid.AfterCellEdit -= OnDataGridAfterCellEdit;
        }
        if (e.NewValue != null)
        {
            // 挂载控件库的专属结束编辑事件
            dataGrid.AfterCellEdit += OnDataGridAfterCellEdit;
        }
    }

    private static void OnDataGridAfterCellEdit(object sender, AfterCellEditEventArgs e)
    {
        var dataGrid = (CustomDataGrid)sender;
        var command = GetEndCellEditCommand(dataGrid);
        if (command.CanExecute(dataGrid.ItemsSource))
        {
            command.Execute(dataGrid.ItemsSource);
        }
    }

    public static void SetEndCellEditCommand(DependencyObject element, ICommand value)
    {
        element.SetValue(EndCellEditCommandProperty, value);
    }

    public static ICommand GetEndCellEditCommand(DependencyObject element)
    {
        return (ICommand)element.GetValue(EndCellEditCommandProperty);
    }
}

然后在XAML中直接绑定即可:

<CustomDataGrid 
    ItemsSource="{Binding InvoiceLines}"
    local:DataGridAttach.EndCellEditCommand="{Binding LineAmountPaidChangedCommand}" />

方案2:直接后台转发调用(最简实现)

MVVM并不禁止后台代码,只要后台代码不包含业务逻辑,仅做View层的事件转发是完全合理的,代码更简单:

private void CustomDataGrid_AfterCellEdit(object sender, AfterCellEditEventArgs e)
{
    if (DataContext is YourViewModel vm)
    {
        vm.OnLineAmountPaidChanged(new AfterLineAmountPaidEventArgs
        {
            InvoiceLinesSelectedAmount = InvoiceLinesDataGrid.ItemsSource
        });
    }
}
注意事项
  • 使用EventAggregator订阅事件时,如果你的订阅方法需要操作UI,建议加上ThreadOption.UIThread参数避免跨线程异常:Subscribe(OnLineAmountPaidChanged, ThreadOption.UIThread)
  • 如果ViewModel会被多次创建销毁,记得保存订阅返回的SubscriptionToken,在ViewModel释放时调用Unsubscribe方法,避免内存泄漏

内容的提问来源于stack exchange,提问作者Clay Hess

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 02:54:07