使用 ReactiveUI 进行 WinForms MVVM 设计






4.69/5 (39投票s)
一个简短的示例,展示了如何使用 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 技术的情况下执行测试。
精明的读者会注意到,此时 View
和 ViewModel
之间没有链接。 现在 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 string
s",这确实使代码维护变得困难,这一点尤其受欢迎。
关注点
ReactiveUI 框架目前的状态没有得到像希望的那样好的记录。 花费了大量时间从过时的示例和代码中断更改中找出含义。 我希望此示例将激励其他人深入研究 ReactiveUI 并提供更多示例和更好的文档。
历史
- 2014 年 7 月 29 日:初始草案
- 2017 年 9 月 29 日:修复了损坏的链接
- 2018 年 7 月 31 日:已更新到最近可用的 NuGet 包,添加了 GitHub 链接。