本文为作者原创,转载请注明出处:https://www.cnblogs.com/zwvista/p/12468579.html


_文档
**
安装
ViewModel
Command
UI
DynamicData
Validation
Log**

文档

Handbook

安装

使用 ReactiveUI 需要安装平台所对应的包。
比如开发 WPF 应用程序需要下载 ReactiveUI 和 ReactiveUI.WPF。

ViewModel

自定义的 ViewModel 类应该继承 ReactiveObject 类。
public class ExampleViewModel : ReactiveObject { }

可读可写的属性

  1. private string name;
  2. public string Name
  3. {
  4. get => name;
  5. set => this.RaiseAndSetIfChanged(ref name, value);
  6. }

只读属性

  1. public ReactiveCommand<Object> PostTweet { get; }
  2. PostTweet = ReactiveCommand.Create(/*...*/);

只写属性

  1. private readonly ObservableAsPropertyHelper<string> firstName;
  2. public string FirstName => firstName.Value;
  3. // Name 属性发生改变时
  4. // 如果属性值非空
  5. // 就提取该属性值中第一个空格前面的部分,
  6. // 并将其设置为 FirstName
  7. this.WhenAnyValue(x => x.Name)
  8. .Where(x => !string.IsNullOrEmpty(x))
  9. .Select(x => x.Split(' ')[0])
  10. .ToProperty(this, x => x.FirstName, out firstName);

下载并使用 ReactiveUI.Fody 后代码可以简化
可读可写的属性

  1. [Reactive]
  2. public string Name { get; set; }

只写属性

  1. public string FirstName { [ObservableAsProperty] get; }
  2. this.WhenAnyValue(x => x.Name)
  3. .Where(x => !string.IsNullOrEmpty(x))
  4. .Select(x => x.Split(' ')[0])
  5. .ToPropertyEx(this, x => x.FirstName);

Command

通过调用 ReactiveCommand 类的静态方法创建命令

  • CreateFromObservable()
  • CreateFromTask()
  • Create()
  • CreateCombined()

同步命令

  1. ReactiveCommand<int,Unit> command = ReactiveCommand.Create<int>(
  2. integer => Console.WriteLine(integer));
  3. command.Execute(42).Subscribe();

异步命令

  1. var command = ReactiveCommand.CreateFromObservable<Unit, int>(
  2. _ => Observable.Return(42).Delay(TimeSpan.FromSeconds(2)));
  3. command.Execute(Unit.Default).Subscribe();
  4. command.Subscribe(value => Console.WriteLine(value));

命令的可用性

  1. var canExecute = this.WhenAnyValue(
  2. x => x.UserName, x => x.Password,
  3. (userName, password) =>
  4. !string.IsNullOrEmpty(userName) &&
  5. !string.IsNullOrEmpty(password));
  6. var command = ReactiveCommand.CreateFromTask(LogOnAsync, canExecute);

UI

在 Window, Page, UserControl 类里面创建 ViewModel, 并将其设置为 DataContext。

  1. <Window x:Class="ReactiveDemo.MainWindow"...>
  2. <!-- using traditional XAML markup bindings -->
  3. </Window>
  1. public partial class MainWindow : Window
  2. {
  3. public MainWindow()
  4. {
  5. InitializeComponent();
  6. DataContext = new AppViewModel();
  7. }
  8. }

DynamicData

数据集合应该采用 DynamicData 库中的集合类型:SourceList 和 SourceCache。

  1. // 内部集合,用于实际操作
  2. SourceList<bool> __items = new SourceList<bool>();
  3. // 内部字段,用于绑定内部集合
  4. ReadOnlyObservableCollection<bool> _items;
  5. // 外部属性,用于绑定控件
  6. public ReadOnlyObservableCollection<bool> Items => _items;
  7. // 处理集合
  8. __items.Add(true);
  9. __items.RemoveAt(0);
  10. __items.Add(false);
  11. // 映射,过滤后再绑定到内部字段
  12. __items.Connect()
  13. .Transform(x => !x)
  14. .Filter(x => x)
  15. .ObserveOn(RxApp.MainThreadScheduler)
  16. .Bind(out _items)
  17. .Subscribe();

Validation

数据验证需要额外安装一个 ReactiveUI.Validation 的包。
要进行数据验证,需要实现 IValidatableViewModel 接口或者继承 ReactiveValidationObject 类
ReactiveValidationObject 实现了 IValidatableViewModel 接口和 INotifyDataErrorInfo 接口
IValidatableViewModel 接口包含 ValidationContext 对象
INotifyDataErrorInfo 接口是 WPF 内部用于数据验证的接口,包含 HasErrors 属性,ErrorsChanged 事件以及 GetErrors 方法。

  1. public class SampleViewModel : ReactiveObject, IValidatableViewModel
  2. {
  3. public ValidationContext ValidationContext { get; } = new ValidationContext();
  4. public ValidationHelper ComplexRule { get; }
  5. public ValidationHelper AgeRule { get; }
  6. [Reactive] public int Age { get; set; }
  7. [Reactive] public string Name { get; set; }
  8. public ReactiveCommand<Unit, Unit> Save { get; }
  9. public SampleViewModel()
  10. {
  11. this.ValidationRule(
  12. viewModel => viewModel.Name,
  13. name => !string.IsNullOrWhiteSpace(name),
  14. "You must specify a valid name");
  15. AgeRule = this.ValidationRule(
  16. viewModel => viewModel.Age,
  17. age => age >= 13 && age <= 100,
  18. age => $"{age} is a silly age");
  19. var nameAndAgeValid = this
  20. .WhenAnyValue(x => x.Age, x => x.Name, (age, name) => new { Age = age, Name = name })
  21. .Select(x => x.Age > 10 && !string.IsNullOrEmpty(x.Name));
  22. ComplexRule = this.ValidationRule(
  23. _ => nameAndAgeValid,
  24. (vm, state) => !state ? "That's a ridiculous name / age combination" : string.Empty);
  25. var canSave = this.IsValid();
  26. Save = ReactiveCommand.CreateFromTask(async unit => { }, canSave);
  27. }
  28. }
  1. public class SampleViewModel : ReactiveValidationObject<SampleViewModel>
  2. {
  3. [Reactive]
  4. public string Name { get; set; } = string.Empty;
  5. public SampleViewModel()
  6. {
  7. this.ValidationRule(
  8. x => x.Name,
  9. name => !string.IsNullOrWhiteSpace(name),
  10. "Name shouldn't be empty.");
  11. }
  12. }

Log

输出日志需要另外下载日志专用的包
比如使用 Serilog 将日志输出到文件需要下载以下几个包

  • Serilog
  • Splat.Serilog
  • Serilog.Sinks.File

在使用日志之前需要先创建配置并注册 Logger

  1. using Serilog;
  2. using Splat;
  3. using Splat.Serilog;
  4. using System.Windows;
  5. public partial class App : Application
  6. {
  7. protected override void OnStartup(StartupEventArgs e)
  8. {
  9. base.OnStartup(e);
  10. // 创建 Logger
  11. Log.Logger = new LoggerConfiguration()
  12. .WriteTo.File("log-.txt", rollingInterval: RollingInterval.Day)
  13. .CreateLogger();
  14. // 注册 Logger
  15. Locator.CurrentMutable.UseSerilogFullLogger();
  16. }
  17. }

使用 Logger

  1. using Splat;
  2. using System.Windows;
  3. // 输出日志的类需要实现 IEnableLogger 接口
  4. public partial class MainWindow : Window, IEnableLogger
  5. {
  6. public MainWindow()
  7. {
  8. InitializeComponent();
  9. // 使用 Logger
  10. this.Log().Info("MainWindow Initialized.");
  11. }
  12. }

实际输出的日志

  1. 2020-06-23 18:47:45.365 +00:00 [INF] MainWindow Initialized.