WPF中ViewModels之间如何通信以及如何控制Views生命周期

Posted

技术标签:

【中文标题】WPF中ViewModels之间如何通信以及如何控制Views生命周期【英文标题】:How to communicate between ViewModels in WPF and how to control Views lifecycle 【发布时间】:2016-03-19 11:01:17 【问题描述】:

共有三个窗口 MainWindow、FirstWindow 和 SecondWindow。 MainWindow 可以打开 FirstWindow 和 SecondWindow。

现在我的问题是:

如何从 FirstWindow 打开 SecondWindow,并在 SecondWindow 打开时关闭 FirstWindow。这时候我可以控制SecondWindow但不能控制MainWindow,就像在MainWindow中使用SecondWindow.ShowDialog()一样。 点击SecondWindow 的“保存”按钮后,SecondWindow 关闭,MainWindow 的DataGrid 更新。如何从另一个 ViewModel 更新数据或在处理事件时如何返回数据?

【问题讨论】:

而不是使用多个窗口 -> 使用一个窗口并乘以代表第一个、第二个和第三个视图的 UserControls。例如带有 TabItems 的 TabControl 【参考方案1】:

你在这里要求多个东西。

基本上你需要两件事。用于在视图模型之间传递消息的事件聚合器(也称为信使)。有不同的框架来实现它,或者它们是 MVVM 框架的一部分。

您需要的第二个是导航服务,以将导航与视图模型分离,因为导航需要了解与视图相关的技术(WPF、UWP、Silverlight 等)

【讨论】:

【参考方案2】:

我同意Tseng's answer 并将尝试扩展他的答案。

第一部分

对于模块(不仅是 ViewModel)之间的低耦合通信,我们可以尝试实现EventAggregator 模式。事件聚合器有助于在低耦合应用中实现订阅者/发布者模式。我知道几个不同的实现。

第一个基于CodeProject post 并使用将帮助您防止内存泄漏的WeakReference。我不会发布整个代码,因为您可以下载源代码并使用它。在此实现中,您必须为订阅者实现 ISubscriber 接口。

第二个是Microsoft Prism 实现。这是一个开源项目,您可以看到interface、implementation 和base event class。在此实现中,您必须手动取消订阅该事件。

第三个也是最后一个是 MVVMLight 库及其Messenger 类。

如您所见,所有这些实现都使用单例模式来保存订阅者。

第二部分

第二部分是关于导航的。最简单的方法是使用Page navigation 基础设施。但在 MVVM 世界中,我们有许多不同的导航概念。

使用导航抽象的主要目的是将导航逻辑与具体的视图渲染技术(WPF、Silverlight、WinRT、Xamarin)分离。

例如,在Microsoft Prism 中,我们可以使用区域和RegionManager 在视图和窗口之间导航。这是一个非常笨重的导航框架,仅仅一篇文章就很难理解这个概念。

MVVM Light 也有自己的navigation mechanism。

对于我的项目,我通过Workspaces 使用我自己的导航实现。它是一种混合机制,结合了 .net 的页面导航原理和 Prism 的区域概念。

结论

这篇文章不是对您问题的回答。但希望对您理解 MVVM 概念有所帮助。

正如您在上面所读到的,有许多 MVVM 框架包含基础设施(不仅是 Messenger 和 NavigationService,还有基本命令实现、PopupService、转换器、INotifyPropertyChanged-helpers 和 base ViewModel 实现)来实现典型场景你的申请。

【讨论】:

您在使用 Prism 的 PubSubEvents 时不必自行取消订阅,除非您以 keepSubscriberReferenceAlive 订阅为 true。当设置为 false 时,Prism 将使用弱事件。 Prism 5 和 6 也是如此。不知道框架的旧版本【参考方案3】:

您需要使用表单类的实例来传递数据。请参阅下面我的简单的两个表单项目

表格 1

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApplication1

    public partial class Form1 : Form
    
        Form2 form2;
        public Form1()
        
            InitializeComponent();
            form2 = new Form2(this);
        

        private void button1_Click(object sender, EventArgs e)
        
            form2.Show();
            string  results = form2.GetData();
        
    

​

表格 2

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApplication1

    public partial class Form2 : Form
    
        Form1 form1;
        public Form2(Form1 nform1)
        
            InitializeComponent();

            this.FormClosing +=  new FormClosingEventHandler(Form2_FormClosing);
            form1 = nform1;
            form1.Hide();
        
        private void Form2_FormClosing(object sender, FormClosingEventArgs e)
        
            //stops form from closing
            e.Cancel = true;
            this.Hide();
        
        public string GetData()
        
            return "The quick brown fox jumped over the lazy dog";
        

    

​

【讨论】:

问题被标记为 WPF 和 MVVM,而不是 WinForms。 MVVM 旨在解耦代码以避免紧耦合和易于测试,并避免几乎所有代码落后。完全不同的设计理念

以上是关于WPF中ViewModels之间如何通信以及如何控制Views生命周期的主要内容,如果未能解决你的问题,请参考以下文章

WPF线程安​​全和页面之间的通信

如何在 MVVM 中的 UserControl 之间进行通信 - WPF 应用程序

如何使WPF的TreeView节点之间有连线

如何使用 caliburn micro 在 Wpf 中注入 EF DbContext

如何在不同项目中的视图/视图模型之间导航

WPF触控程序开发——整理的一些问题