65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 ReactiveUI 进行 WinForms MVVM 设计

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.69/5 (39投票s)

2014 年 8 月 5 日

CPOL

3分钟阅读

viewsIcon

125316

一个简短的示例,展示了如何使用 Reactive UI 框架,使用 MVVM 来执行关注点分离。

引言

在本文中,我们将研究一种优雅的方式,将 WinForms 应用程序的*表示层*的 UI 关注点与使用 MVVM(模型-视图-视图模型)模式的应用程序业务逻辑分开。 这样,可以促进应用程序的测试驱动开发 (TDD)。

背景

在阅读 Peter Vogel 的一篇文章(参见 VSMagazine2014-05-01)时,我再次想起了 WinForm 应用程序设计人员在使用现代设计模式时遇到的困难。 我一直在寻找一种更简单的方法来实现 Brian Noyes 在 WinForms 中所完成的(参见 WinForms 的组合扩展),而无需使用从 Patterns and Practices Composite WPF Contrib 库中借用的代码(http://compositewpfcontrib.codeplex.com/)进行扭曲,该库难以理解且似乎已被放弃(就像互联网上的许多东西一样)。 然而,最近出现的 ReactiveUI 有望改善 WinForms 应用程序的开发。 ReactiveUI 是一个模型-视图-视图模型框架,与 Reactive Extensions 库一起用于构建可测试的 UI。

Using the Code

首先,本文的源代码可在 GitHub 上找到,供那些希望了解更多信息的人使用。

让我们通过一个简短的示例,了解如何使用 ReactiveUI 构建一个使用 MVVM 模式的 WinForms 应用程序。 我将使用 Visual Studio 2017 Community Edition 和默认的 WinForms 模板。 打开模板创建的主窗体并添加以下控件

  • 文本标签
  • 文本框
  • 确定按钮
  • 状态栏状态字符串

使用“工具”菜单下的 NuGet 包管理器并将 reactiveui-winforms 包添加到解决方案。 此包将添加我们需要的所有必要的引用和组件。

由于新的 System.Reactive 4.0 命名空间的重构,我发现我还需要添加对以下 NuGet 包的引用

此时,由于视图已部分构建,我们构建将处理数据和事件的 ViewModel。 让我们为视图上的控件设置带有后备字段的属性以及一个按钮命令。

//
// ViewModel example
//
namespace WinFormMVVM.ViewModels
{
	public class HomeViewModel : ReactiveUI.ReactiveObject
	{
		string ModelString;
		public string EnteredText
		{
			get { return ModelString; }
			set { this.RaiseAndSetIfChanged( ref ModelString, value);}
		}

		string statusString = "";
		public string Status
		{
			get{return statusString;}
			set{this.RaiseAndSetIfChanged(ref statusString,value);}
		}

		public ReactiveCommand OKCmd { get; private set; }

		public HomeViewModel
		{
            OKCmd = ReactiveCommand.Create(() => { Status = EnteredText + " is saved."; }
                , this.WhenAny(vm => vm.EnteredText, s => !string.IsNullOrWhiteSpace(s.Value)));
		}
	}
}

ViewModel 继承自 ReactiveObject 类。 此类授予您的类访问 RaiseAndSetIfChanged 辅助函数的权限。 这方便地将您的属性连接到 INotifyPropertyChanged/IObservable 接口,因此一步到位,您就可以拥有一个可以使用 Reactive Extensions 进行数据绑定和观察的类。

ViewModel 的构造函数中,我们设置了发生某些事情的条件,即,单击“确定”按钮时。 Create 函数的第一个参数是我们在单击按钮时希望执行的Action,它将更新状态消息。 这是我们对事件的*订阅*。 第二个参数是 canExecute 参数,它是从 WhenAny 扩展返回的 IObservable,它允许您观察对象上的一个或多个属性何时发生更改。 请注意,ViewModel 没有对任何 UI 元素的引用,这使我们能够在不担心所使用的特定 UI 技术的情况下执行测试。

精明的读者会注意到,此时 ViewViewModel 之间没有链接。 现在 ViewModel 已经构建好了,让我们来处理这个问题。 编辑 View 以继承自 interface IViewFor<T>,其中 T 是我们的 ViewModel

namespace WinFormMVVM
{
	public partial class HomeView : Form, IViewFor<HomeViewModel>
	{
		InitializeComponent();
		VM = new HomeViewModel();

		// Bind the view to the ReactiveUI viewmodel
		this.Bind(VM, x => x.EnteredText, x => x.textBoxMyInput.Text);
		this.Bind(VM, x => x.Status, x => x.toolStripMyStatusString.Text);
		this.BindCommand(VM, x => x.OKCmd, x => x.btnOK);
	}

	public HomeViewModel VM { get; set; }

	object IViewFor.ViewModel
	{
		get { return VM; }
		set { VM = (HomeViewModel)value; }
	}

	HomeViewModel IViewFor<HomeViewModel>.ViewModel
	{
		get { return VM; }
		set { VM = value; }
	}
}

我真正喜欢使用此框架的地方在于在编写 UI 元素和 ViewModel 属性之间的绑定时可以使用智能感知。 缺少在 XAML 或常规 WinForm 数据绑定中常见的 "magic strings",这确实使代码维护变得困难,这一点尤其受欢迎。

关注点

ReactiveUI 框架目前的状态没有得到像希望的那样好的记录。 花费了大量时间从过时的示例和代码中断更改中找出含义。 我希望此示例将激励其他人深入研究 ReactiveUI 并提供更多示例和更好的文档。

历史

  • 2014 年 7 月 29 日:初始草案
  • 2017 年 9 月 29 日:修复了损坏的链接
  • 2018 年 7 月 31 日:已更新到最近可用的 NuGet 包,添加了 GitHub 链接。
© . All rights reserved.