本文为作者原创,转载请注明出处:https://www.cnblogs.com/zwvista/p/12468579.html
_文档
**安装
ViewModel
Command
UI
DynamicData
Validation
Log**
文档
安装
使用 ReactiveUI 需要安装平台所对应的包。
比如开发 WPF 应用程序需要下载 ReactiveUI 和 ReactiveUI.WPF。
ViewModel
自定义的 ViewModel 类应该继承 ReactiveObject 类。
public class ExampleViewModel : ReactiveObject { }
可读可写的属性
private string name;public string Name{get => name;set => this.RaiseAndSetIfChanged(ref name, value);}
只读属性
public ReactiveCommand<Object> PostTweet { get; }PostTweet = ReactiveCommand.Create(/*...*/);
只写属性
private readonly ObservableAsPropertyHelper<string> firstName;public string FirstName => firstName.Value;// Name 属性发生改变时// 如果属性值非空// 就提取该属性值中第一个空格前面的部分,// 并将其设置为 FirstNamethis.WhenAnyValue(x => x.Name).Where(x => !string.IsNullOrEmpty(x)).Select(x => x.Split(' ')[0]).ToProperty(this, x => x.FirstName, out firstName);
下载并使用 ReactiveUI.Fody 后代码可以简化
可读可写的属性
[Reactive]public string Name { get; set; }
只写属性
public string FirstName { [ObservableAsProperty] get; }this.WhenAnyValue(x => x.Name).Where(x => !string.IsNullOrEmpty(x)).Select(x => x.Split(' ')[0]).ToPropertyEx(this, x => x.FirstName);
Command
通过调用 ReactiveCommand 类的静态方法创建命令
- CreateFromObservable()
- CreateFromTask()
- Create()
- CreateCombined()
同步命令
ReactiveCommand<int,Unit> command = ReactiveCommand.Create<int>(integer => Console.WriteLine(integer));command.Execute(42).Subscribe();
异步命令
var command = ReactiveCommand.CreateFromObservable<Unit, int>(_ => Observable.Return(42).Delay(TimeSpan.FromSeconds(2)));command.Execute(Unit.Default).Subscribe();command.Subscribe(value => Console.WriteLine(value));
命令的可用性
var canExecute = this.WhenAnyValue(x => x.UserName, x => x.Password,(userName, password) =>!string.IsNullOrEmpty(userName) &&!string.IsNullOrEmpty(password));var command = ReactiveCommand.CreateFromTask(LogOnAsync, canExecute);
UI
在 Window, Page, UserControl 类里面创建 ViewModel, 并将其设置为 DataContext。
<Window x:Class="ReactiveDemo.MainWindow"...><!-- using traditional XAML markup bindings --></Window>
public partial class MainWindow : Window{public MainWindow(){InitializeComponent();DataContext = new AppViewModel();}}
DynamicData
数据集合应该采用 DynamicData 库中的集合类型:SourceList 和 SourceCache。
// 内部集合,用于实际操作SourceList<bool> __items = new SourceList<bool>();// 内部字段,用于绑定内部集合ReadOnlyObservableCollection<bool> _items;// 外部属性,用于绑定控件public ReadOnlyObservableCollection<bool> Items => _items;// 处理集合__items.Add(true);__items.RemoveAt(0);__items.Add(false);// 映射,过滤后再绑定到内部字段__items.Connect().Transform(x => !x).Filter(x => x).ObserveOn(RxApp.MainThreadScheduler).Bind(out _items).Subscribe();
Validation
数据验证需要额外安装一个 ReactiveUI.Validation 的包。
要进行数据验证,需要实现 IValidatableViewModel 接口或者继承 ReactiveValidationObject 类
ReactiveValidationObject 实现了 IValidatableViewModel 接口和 INotifyDataErrorInfo 接口
IValidatableViewModel 接口包含 ValidationContext 对象
INotifyDataErrorInfo 接口是 WPF 内部用于数据验证的接口,包含 HasErrors 属性,ErrorsChanged 事件以及 GetErrors 方法。
public class SampleViewModel : ReactiveObject, IValidatableViewModel{public ValidationContext ValidationContext { get; } = new ValidationContext();public ValidationHelper ComplexRule { get; }public ValidationHelper AgeRule { get; }[Reactive] public int Age { get; set; }[Reactive] public string Name { get; set; }public ReactiveCommand<Unit, Unit> Save { get; }public SampleViewModel(){this.ValidationRule(viewModel => viewModel.Name,name => !string.IsNullOrWhiteSpace(name),"You must specify a valid name");AgeRule = this.ValidationRule(viewModel => viewModel.Age,age => age >= 13 && age <= 100,age => $"{age} is a silly age");var nameAndAgeValid = this.WhenAnyValue(x => x.Age, x => x.Name, (age, name) => new { Age = age, Name = name }).Select(x => x.Age > 10 && !string.IsNullOrEmpty(x.Name));ComplexRule = this.ValidationRule(_ => nameAndAgeValid,(vm, state) => !state ? "That's a ridiculous name / age combination" : string.Empty);var canSave = this.IsValid();Save = ReactiveCommand.CreateFromTask(async unit => { }, canSave);}}
public class SampleViewModel : ReactiveValidationObject<SampleViewModel>{[Reactive]public string Name { get; set; } = string.Empty;public SampleViewModel(){this.ValidationRule(x => x.Name,name => !string.IsNullOrWhiteSpace(name),"Name shouldn't be empty.");}}
Log
输出日志需要另外下载日志专用的包
比如使用 Serilog 将日志输出到文件需要下载以下几个包
- Serilog
- Splat.Serilog
- Serilog.Sinks.File
在使用日志之前需要先创建配置并注册 Logger
using Serilog;using Splat;using Splat.Serilog;using System.Windows;public partial class App : Application{protected override void OnStartup(StartupEventArgs e){base.OnStartup(e);// 创建 LoggerLog.Logger = new LoggerConfiguration().WriteTo.File("log-.txt", rollingInterval: RollingInterval.Day).CreateLogger();// 注册 LoggerLocator.CurrentMutable.UseSerilogFullLogger();}}
使用 Logger
using Splat;using System.Windows;// 输出日志的类需要实现 IEnableLogger 接口public partial class MainWindow : Window, IEnableLogger{public MainWindow(){InitializeComponent();// 使用 Loggerthis.Log().Info("MainWindow Initialized.");}}
实际输出的日志
2020-06-23 18:47:45.365 +00:00 [INF] MainWindow Initialized.
