文檔
安裝
使用 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 屬性發生改變時
// 如果屬性值非空
// 就提取該屬性值中第一個空格前面的部分,
// 並將其設置為 FirstName
this.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);
// 創建 Logger
Log.Logger = new LoggerConfiguration()
.WriteTo.File("log-.txt", rollingInterval: RollingInterval.Day)
.CreateLogger();
// 注冊 Logger
Locator.CurrentMutable.UseSerilogFullLogger();
}
}
使用 Logger
using Splat;
using System.Windows;
// 輸出日志的類需要實現 IEnableLogger 接口
public partial class MainWindow : Window, IEnableLogger
{
public MainWindow()
{
InitializeComponent();
// 使用 Logger
this.Log().Info("MainWindow Initialized.");
}
}
實際輸出的日志
2020-06-23 18:47:45.365 +00:00 [INF] MainWindow Initialized.