简体   繁体   English

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

[英]Use Prism Event Aggregator to Communicate from Code Behind to View Model

I am working on a WPF app using the MVVM pattern.我正在使用 MVVM 模式开发 WPF 应用程序。 Additionally, I have been utilizing the Prism Event Aggregator functionality to communicate between view models.此外,我一直在利用 Prism Event Aggregator 功能在视图模型之间进行通信。

We are using a library of controls and one of the controls we are using (an altered/customized datagrid) has events that the library author has created.我们正在使用一个控件库,我们正在使用的一个控件(一个更改/自定义的数据网格)具有库作者创建的事件。 For example, when a cell has ended editing...similar to a loss focus.例如,当一个单元格结束编辑时……类似于失去焦点。 The issue I am facing is that the library control utilizes the code behind instead of the view model for the event method.我面临的问题是库控件使用了背后的代码而不是事件方法的视图模型。

I figured I would simply utilize the event aggregator to let the VM know about the event from the code behind.我想我会简单地利用事件聚合器让 VM 从背后的代码中了解事件。 It is not working.它不工作。 My vm uses a simple subscribe in the constructor...我的虚拟机在构造函数中使用了一个简单的订阅...

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

The OnLineAmountPaidChanged method never gets hit. OnLineAmountPaidChanged 方法永远不会被命中。

In the code-behind, I am publishing the event...在代码隐藏中,我正在发布事件...

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

I am wondering if it has to do with the instantiation of the Prism library and the Event Aggregator.我想知道它是否与 Prism 库和事件聚合器的实例化有关。 In the VM, I am creating it via the constructor...在 VM 中,我通过构造函数创建它...

IEventAggregator eventAggregator

I am extending the VM with a base VM...我正在使用基本虚拟机扩展虚拟机...

: base(eventAggregator, messageDialogService)

I then assign the instantiation to a private that I use as shown in previous code...然后我将实例化分配给我使用的私有,如前面的代码所示......

private readonly IEventAggregator _eventAggregator;

In the code-behind, I instantiate the event aggregator as follows...在代码隐藏中,我按如下方式实例化事件聚合器...

private readonly IEventAggregator _eventAggregator = new EventAggregator();

When I step through the code using breakpoints, I notice that the subscriptions change once the code hits the code-behind from 2 (two) to 0 (zero).当我使用断点单步执行代码时,我注意到一旦代码命中代码隐藏,订阅就会从 2(二)变为 0(零)。 This is why I think that it is getting reinstantiated for the app in the code behind with the way I am utilizing the library.这就是为什么我认为它正在通过我使用库的方式在后面的代码中为应用程序重新实例化。

Is there a different/better way to accomplish this communication?是否有不同/更好的方式来完成这种沟通? Am I instantiating the event aggregator incorrectly?我是否错误地实例化了事件聚合器?

Any advice is helpful.任何建议都是有帮助的。

Your guess is correct.你的猜测是正确的。 The problem is you have two EventAggregator objects.问题是您有两个EventAggregator对象。 Your code is not supposed to instantiate the EventAggregator.您的代码不应实例化 EventAggregator。 It is supposed given to be given to you from Prism.它应该是从 Prism 给你的。 Your code-behind needs to get the same instance of EventAggregator that your view model gets.您的代码隐藏需要获得与您的视图模型相同的EventAggregator实例。

The nice thing is that you can inject that same EventAggregator to the view that goes with your viewmodel the same way that the viewmodel gets it.好消息是,您可以将相同的EventAggregator注入到与您的视图模型一起使用的视图中,就像视图模型获取它的方式一样。 Via constructor injection.通过构造函数注入。 Then pass it along to any other code-bhiend from there.然后将它传递给任何其他代码隐藏程序。

Here's an example.这是一个例子。 I have a Prism module called my ExploreModule .我有一个名为我的ExploreModule的 Prism 模块。 Inside the module-derived class my RegisterTypes function looks like this:在模块派生类中,我的 RegisterTypes 函数如下所示:

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

In my app, the view-model that goes with my ExploreView is called ExploreVm .在我的应用程序中,与我的ExploreView一起使用的视图模型称为ExploreVm You don't see it being listed here because I use Prism's "view-model-locator" approach.您没有看到它在此处列出,因为我使用 Prism 的“视图-模型-定位器”方法。 But basically whenever Prism creates my ExploreView , it creates an ExploreVm to go along with it.但基本上,无论何时 Prism 创建我的ExploreView ,它都会创建一个ExploreVm来配合它。

This means that I can add any registered-service I want to the constructor of either ExploreVm or ExploreView .这意味着我可以将任何我想要的注册服务添加到ExploreVmExploreView的构造函数中。 Including IEventAggregator包括IEventAggregator

So I edit my ExploreVm to take IEventAggregator .所以我编辑我的ExploreVm以获取IEventAggregator Here's the one I use.这是我使用的一种。 It adds IEventAggregator as well another service that I personally created and registered.它添加了IEventAggregator以及我个人创建和注册的另一个服务。 Since Prism creates this view model for me, it just handes me both services.由于 Prism 为我创建了这个视图模型,它只为我提供了两个服务。

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

And I can also edit my ExploreView the same way if I want如果我愿意,我也可以以相同的方式编辑我的ExploreView

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

You should have a similar view/view-model pair where you can do the same.您应该有一个类似的视图/视图-模型对,您可以在其中执行相同的操作。

Now if I had some child view/control ( not created by Prism) that I needed to get access to IEventAggregator , then I would expose the IEventAggregator in a property or use some other way to pass it down.现在,如果我有一些需要访问IEventAggregator子视图/控件(不是由 Prism 创建的),那么我将在属性中公开IEventAggregator或使用其他方式将其传递下去。 But this Prism-created view/view-model is the entry point.但是这个 Prism 创建的视图/视图模型是入口点。

Regardless, the key point is you do not create the EventAggregator.无论如何,关键是您没有创建 EventAggregator。 Prism does.棱镜可以。

@Joe... @乔...

I wanted to post a comment so I can show some more code where perhaps you might point out where I went wrong.我想发表评论,以便我可以展示更多代码,您可能会指出我哪里出错了。

My VMs are created with a VM factory class.我的 VM 是使用 VM 工厂类创建的。 Here is a snippet...这是一个片段...

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;
        }

The VM factory code gets referenced from my App.xaml.cs using the IServiceCollection intyerface passed to my ConfigureServices method.使用传递给我的 ConfigureServices 方法的 IServiceCollection 接口从我的 App.xaml.cs 引用 VM 工厂代码。

services.AddTransient<IViewModelFactory, ViewModelFactory>();

My App constructor is as follows...我的应用程序构造函数如下...

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

I also add a singleton of the event aggregator in there...我还在那里添加了一个事件聚合器的单例......

services.AddSingleton<IEventAggregator, EventAggregator>();

So am I wiring things sup incorrectly?所以我接线错误吗? Should I do it differently?我应该以不同的方式做吗?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM