使用Prism事件聚合器从代码隐藏到视图模型进行通信



我正在使用MVVM模式开发WPF应用程序。此外,我一直在使用Prism事件聚合器功能在视图模型之间进行通信。

我们使用的是一个控件库,其中一个控件(更改/自定义的数据网格(包含库作者创建的事件。例如,当单元格已结束编辑时。。。类似于损失焦点。我面临的问题是,库控件使用事件方法的代码隐藏,而不是视图模型。

我想我可以简单地利用事件聚合器让VM从代码后面了解事件。它不起作用。我的vm在构造函数中使用了一个简单的订阅。。。

_eventAggregator.GetEvent<AfterLineAmountPaidEvent>().Subscribe(OnLineAmountPaidChanged);

OnLineAmountPaidChanged方法永远不会被命中。

在后面的代码中,我正在发布事件。。。

_eventAggregator.GetEvent<AfterLineAmountPaidEvent>().Publish(
new AfterLineAmountPaidEventArgs
{
InvoiceLinesSelectedAmount = InvoiceLinesDataGrid.ItemsSource
});

我想知道这是否与Prism库和事件聚合器的实例化有关。在虚拟机中,我通过构造函数创建它。。。

IEventAggregator eventAggregator

我正在用基本VM扩展VM。。。

: base(eventAggregator, messageDialogService)

然后,我将实例化分配给一个私有的,如前面的代码所示。。。

private readonly IEventAggregator _eventAggregator;

在后面的代码中,我实例化事件聚合器如下。。。

private readonly IEventAggregator _eventAggregator = new EventAggregator();

当我使用断点遍历代码时,我注意到一旦代码到达后面的代码,订阅就会从2(两(更改为0(零(。这就是为什么我认为它正在以我使用库的方式在代码后面为应用程序重新实例化。

有没有一种不同/更好的方式来完成这种沟通?我是否错误地实例化了事件聚合器?

任何建议都是有用的。

@Joe。。。

我想发表一条评论,这样我就可以展示更多的代码,也许你可以指出我错在哪里。

我的虚拟机是用虚拟机工厂类创建的。这是一个片段。。。

public IReceiptDetailViewModel CreateReceiptDetailViewModel(
int? customerId,
Action<Guid> onTabClosed,
Action onReloadCustomerInvoicesRequested)
{
IReceiptDetailViewModel viewModel = new ReceiptDetailViewModel(
customerId,
_mapper,
_customerDataProvider,
_invoiceDataProvider,
_receiptDataProvider,
this,
_eventAggregator,
_messageDialogService,
onTabClosed,
onReloadCustomerInvoicesRequested,
_windowService);
return viewModel;
}

VM工厂代码使用传递给我的ConfigureServices方法的IServiceCollection intyerface从我的App.xaml.cs中引用。

services.AddTransient<IViewModelFactory, ViewModelFactory>();

我的应用程序构造函数如下。。。

ConfigureServices(serviceCollection);
_serviceProvider = serviceCollection.BuildServiceProvider();

我还添加了一个事件聚合器的单例。。。

services.AddSingleton<IEventAggregator, EventAggregator>();

那么我接线错误了吗?我应该采取不同的做法吗?

您的猜测是正确的。问题是您有两个EventAggregator对象。您的代码不应该实例化EventAggregator。它应该是棱镜送给你的。您的代码隐藏需要获得与视图模型相同的EventAggregator实例。

好的方面是,您可以通过构造函数注入的方式,将相同的EventAggregator注入到与视图模型相关的视图中。然后将其传递给任何其他代码bhiend。

下面是一个例子。我有一个叫ExploreModule的棱镜模块。在模块派生类中,我的RegisterTypes函数如下所示:

public void RegisterTypes(IContainerRegistry reg)
{
reg.RegisterForNavigation<ExploreView>(ModuleKey.Explore);
}

在我的应用程序中,ExploreView附带的视图模型称为ExploreVm。你看不到它被列在这里,因为我使用棱镜的";视图模型定位器";方法但基本上,每当Prism创建我的ExploreView时,它都会创建一个ExploreVm

这意味着我可以将我想要的任何注册服务添加到ExploreVmExploreView的构造函数中。包括IEventAggregator

所以我把我的ExploreVm编辑成IEventAggregator。这是我用的。它添加了IEventAggregator以及我个人创建和注册的另一项服务。由于Prism为我创建了这个视图模型,所以它只为我提供了两种服务。

public ExploreVm(ICaptureService  capSvc, IEventAggregator agg)
{
// ...
}

如果我想要,我也可以用同样的方式编辑我的ExploreView

public ExploreView(IEventAggregator aggregator)
{
Aggregator = aggregator;
InitializeComponent();
}

您应该有一个类似的视图/视图模型对,可以在其中执行相同的操作。

现在,如果我有一些子视图/控件(而不是由Prism创建的(需要访问IEventAggregator,那么我会在属性中公开IEventAggregator,或者使用其他方式将其传递下去。但这个由Prism创建的视图/视图模型是切入点。

无论如何,关键是您不创建EventAggregator。Prism做到了。

最新更新