WinForms 中使用 ReactiveUI
2024-03-17 17:33:41

安装

安装 NuGet 包

1
ReactiveUI.WinForms

目前最新版是19.4.1。框架要求 .NET Framework 版本在4.6.2 及以上。

视图模型(ViewModel)

ViewModel应该从 ReactiveObject 继承,它实现了 INotifyPropertyChanged

1
public class FormViewModel : ReactiveObject {}

定义属性

定义可通知的属性

1
2
3
4
5
6
7
8
9
public class FormViewModel : ReactiveObject
{
private string _name;
public string Name
{
get => _name;
set => this.RaiseAndSetIfChanged(ref _name, value);
}
}

RaiseAndSetIfChanged 方法会尝试修改字段,如果修改成功则会发出变化的通知。
当有很多属性时,会存在很多相似的geter/seter代码,很不美观。
此时可以考虑引入ReactiveUI.Fody包,然后用[Reactive]注解可以节省很多代码。

1
2
3
4
5
public class FormViewModel : ReactiveObject
{
[Reactive]
public string Name { get; set; }
}

视图(View)

View应该从 IViewFor<T> 继承,这是个泛型接口,T就是具体的ViewModel类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
using MyApp.ViewModel;
using ReactiveUI;
using System;
using System.Diagnostics;
using System.Windows.Forms;

namespace MyApp
{
public partial class Form1 : Form, IViewFor<FormViewModel>
{
public Form1()
{
InitializeComponent();
this.WhenActivated(OnWhenActivated);
}

private void OnWhenActivated(Action<IDisposable> d)
{
Debug.Assert(ViewModel != null);
// 初始化代码
}

object IViewFor.ViewModel
{
get => ViewModel;
set => ViewModel = value as FormViewModel;
}

public FormViewModel ViewModel { get; set; } = new();
}
}

WhenActivated 是什么

WhenActivated 方法用于在视图被激活时执行特定的代码。基本上就是执行初始化的地方。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public partial class Form1 : Form, IViewFor<FormViewModel>
{
public Form1()
{
InitializeComponent();
this.WhenActivated(OnWhenActivated);
}

object IViewFor.ViewModel
{
get => ViewModel;
set => ViewModel = value as FormViewModel;
}

public FormViewModel ViewModel { get; set; } = new();

private void HandleActivation()
{
Debug.WriteLine(@"HandleActivation");
}

private void HandleDeactivation()
{
Debug.WriteLine(@"HandleDeactivation");
}

private void OnWhenActivated(CompositeDisposable d)
{
Debug.Assert(ViewModel != null);
HandleActivation();
Disposable.Create(HandleDeactivation).DisposeWith(d);
}
}

数据绑定

1
2
3
4
5
6
7
8
9
10
11
12
13
private void OnWhenActivated(CompositeDisposable d)
{
Debug.Assert(ViewModel != null);

// 从 ViewModel 上的属性到视图的单向绑定
this.OneWayBind(ViewModel, vm => vm.Description, v => v.label1.Text).DisposeWith(d);

// 双向绑定
this.Bind(ViewModel, vm => vm.Name, v => v.textBox1.Text).DisposeWith(d);

// 绑定命令
this.BindCommand(ViewModel, vm => vm.OpenCommand, v => v.btnOpen).DisposeWith(d);
}

这是最基础也最常见的用法。更多用法可以查看官方文档:https://www.reactiveui.net/docs/handbook/data-binding/

WhenAny

ReactiveUI 提供了一系列以 “WhenAny” 开头的方法,用于创建观察属性之间的关系和绑定。这些方法允许你监视属性的变化,并在这些属性变化时执行特定的操作。以下是一些常用的 “WhenAny” 方法:

  1. WhenAny:监视一个或多个属性的变化,并生成一个 IObservable,以便在这些属性中的任何一个发生变化时发出通知。
    1
    2
    this.WhenAnyValue(x => x.FirstName, x => x.LastName)
    .Subscribe(names => { /* 当 FirstName 或 LastName 变化时执行操作 */ });
  2. WhenAnyValue:与 WhenAny 类似,但专门用于监视属性值的变化。它会发出属性的当前值,并在属性值变化时发出新值。
    1
    2
    this.WhenAnyValue(x => x.Age)
    .Subscribe(age => { /* 当 Age 属性值变化时执行操作 */ });
  3. WhenAnyObservable:监视一个或多个 IObservable,并在其中任何一个发出通知时执行操作。
    1
    2
    this.WhenAnyObservable(x => x.LoadDataCommand.IsExecuting)
    .Subscribe(isExecuting => { /* 当 LoadDataCommand 的执行状态变化时执行操作 */ });
  4. WhenAnyDynamic:与 WhenAny 类似,但是允许动态选择属性,通常用于在字符串或 Lambda 表达式中引用属性。
    1
    2
    this.WhenAnyDynamic(x => x.GetPropertyByName(DynamicPropertyName))
    .Subscribe(value => { /* 当指定属性变化时执行操作 */ });

这些方法使你能够轻松地创建响应式的属性监视和绑定,以便在属性变化时执行相关操作。

参考资料

https://www.reactiveui.net/docs/handbook/view-models/
https://www.reactiveui.net/docs/handbook/when-any/
https://www.reactiveui.net/docs/handbook/commands/
https://www.cnblogs.com/aijiao/p/17328272.html