ReactiveUI使用笔记,
ReactiveUI使用笔记,教程总结
一、LINQ和Rx
LINQ(Language Intergrated Query)
对数据集合、关系数据、XML文件等对象进行查询和提取数据的技术,提供了统一的类似SQL的语法来对数据进行查询,而不用关心数据源的不同。
二、Rx(Reactive Extensions)
对LING的一种扩展,提供了一种新的组织和协调异步事件的方式,极大简化了代码的编写。他的目标是对异步的集合进行操作,也就是说,集合中的元素是异步填充的。
Rx最显著的特征是使用可观察集合(Observable Collection)来达到集成异步(composing asynchronous)和基于事件(event-based)的编程的效果。
- 组合(Composing): Reactive Extension的首要目标之一就是将多种异步操作组合起来是的代码更加简单。要做到这一点,数据流必须定义清楚,这样代码就很清晰集中,使得异步操作代码异步处理代码不会充斥整个应用程序。
- 异步(Asynchronous): 虽然Rx不仅仅能处理异步操作,但是使用Rx,大大简化了异步操作的实现,并且代码容易理解进而容易维护。
- 基于事件(Event-based): Rx简化了传统的异步编程方式,在后面的文章中我们会看到拖放(drag and drop)模式的实现
- 可观察集合(Observable collections): Rx的核心,它是一种集合,集合的元素在第一次访问的时候可能还没有填充。它对于Rx的重要性类似于enumerable集合对LINQ的重要性。
三、观察者模式(发布和订阅模式Publish and Subscribe ):
Observer
观察者
Observable
被观察对象
- 被观察对象:观察者 = 1:n
- 被观察对象有一些行为或者属性(状态信息),
- 观察者可以订阅某些感兴趣的属性或者行为,
- 被观察对象状态发生变化,会通知所有观察者,观察者将做出相应的反应(react)。
Why?
在程序设计中,很多代码是对改变的东西做出反应的,你或许可以使用 C#事件、回调、 switch 语句、委托、 lambdas、 observable、通知和绑定等实现,但这会使你的应用程序变得越来越复杂,难以理解。
反应式编程会将更改自动传播到整个系统中。
Rx将事件流抽象为Observable sequences(可观察序列)表示异步数据流,使用LINQ运算符查询异步数据流,并使用Scheduler
来控制异步数据流中的并发性。简单地说:Rx = Observables + LINQ + Schedulers。
- 一切皆为数据流
- Observable 是对数据流的抽象
- Observer是对Observable的响应
四、ViewModel:ReactiveObject
自定义的 ViewModel 类应该继承 ReactiveObject 类。
public class ExampleViewModel : ReactiveObject { }
属性定义
可读写的属性
private string _name; public string Name { get => _name; set => this.RaiseAndSetIfChanged(ref _name, value); }
只读属性:
private readonly ObservableAsPropertyHelper<string> firstName; public string FirstName => firstName.Value; //相当于 //public string FirstName //{ get {return 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);
五、Data Binding
Value Converters 值转换器:实现 IBindingTypeConverter 接口
// View 通过绑定以单向依赖的方式连接到 ViewModel。 // 手动实现IViewFor<tviewmodel>接口 // 对于UserControl,使用ReactiveUserControl<TViewmodel>封装IViewFor实现 public partial class TheView : Window, IViewFor<TheViewModel> { public static readonly DependencyProperty ViewModelProperty = DependencyProperty. Register(nameof(ViewModel), typeof(TheViewModel), typeof(TheView)); public TheView() { InitializeComponent(); ViewModel = new TheViewModel(); // Setup the bindings // Note: We have to use WhenActivated here, // since we need to dispose the bindings on XAML-based platforms, or else the bindings leak memory. this.WhenActivated(disposable => { // Bind // 设置 ViewModel 上的属性与 View 之间的双向绑定。 this.Bind(this.ViewModel, x => x.TheText, x => x.TheTextBox.Text) .DisposeWith(disposable); // OneWayBind // 设置从 ViewModel 上的属性到 View 的单向绑定 this.OneWayBind(this.ViewModel, x => x.TheText, x => x.TheTextBlock.Text) .DisposeWith(disposable); // BindCommand // 将一个 ICommand 绑定到一个控件, // 或者该控件上的一个特定事件(如何实现取决于 UI 框架) this.BindCommand(ViewModel, x => x.TheTextCommand, x => x.TheTextButton) .DisposeWith(disposable); }); } public TheViewModel ViewModel { get => (TheViewModel)GetValue(ViewModelProperty); set => SetValue(ViewModelProperty, value); } object IViewFor.ViewModel { get => ViewModel; set => ViewModel = (TheViewModel)value; } }
六、WhenAny
WhenAnyValue
// 返回一个可见的值,每当 Foo 发生变化时,它就会产生当前的值 this.WhenAnyValue(x => x.Foo); // 返回一个可见的值,每当任何属性发生更改时,该值都会生成一个新的 Color,其中包含最新的 RGB 值。 // 最后一个参数是一个选择器,它描述了如何组合三个观察到的属性 this.WhenAnyValue(x => x.Red, x => x.Green, x => x.Blue, (r, g, b) => new Color(r, g, b)); // 当任何变量都可以观察到嵌套属性的变化时 this.WhenAnyValue(x => x.Foo.Bar.Baz);
用法
// 一、订阅由 WhenAny 变量返回的观察值,并在值发生变化时获得通知 // 这将订阅当前对象的 SearchText 更改时,并将值打印 this.WhenAnyValue(x => x.SearchText) .Subscribe(x => Console.WriteLine(x)); // 二、 ToProperty 公开‘计算过的’属性 this.WhenAnyValue(x => x.SearchText, x => x.Length, (text, length) => text + "(" + length + ")") .ToProperty(this, // ToProperty用于创建一个“只读”计算属性 x => x.SearchTextLength, // Public out _searchTextLength);// OAPH // 三、 CanExecute 可执行性 var canCreateUser = this.WhenAnyValue( x => x.Username, x => x.Password, (user, pass) => !string.IsNullOrWhiteSpace(user) && !string.IsNullOrWhiteSpace(pass) && user.Length >= 3 && pass.Length >= 8) .DistinctUntilChanged(); CreateUserCommand = ReactiveCommand.CreateFromTask(CreateUser, canCreateUser); // 四、 Invoking commands 调用命令 // 1)用户触发按钮或控件时调用命令 2)属性值更改时调用命令。 // In the ViewModel. // 如果 SearchText 发生更改,并且自上次更改以来已经过去了0.25秒,则 InvokeCommand 将导致调用 SearchCommand。 this.WhenAnyValue(x => x.SearchText) .Where(x => !String.IsNullOrWhiteSpace(x)) .Throttle(TimeSpan.FromSeconds(.25)) .InvokeCommand(SearchCommand); // In the View. // 视图将绑定到 SearchText 属性,该属性将自动触发该命令。 this.Bind(ViewModel, vm => vm.SearchText, v => v.SearchTextField.Text); // 五、BindTo 将ViewModel中的值转换为View中的特定值 // 直接在View中执行转换 // 观察ShowToolTip 属性,将 true/false 值分别转换为1和0,然后将结果绑定到 ToolTipLabel 的 alpha 属性。 ViewModel.WhenAnyValue(x => x.ShowToolTip) .Select(show => show ? 1f : 0f) .BindTo(this, x => x.ToolTipLabel.Alpha); // 使用 OneWayBind 属性来执行绑定 this.OneWayBind(this.ViewModel, vm => vm.ShowToolTip, view => view.ToolTipLabel.Alpha, show => show ? 1f : 0f);
WhenAny
WhenAny
将传递一个 ObservedChange
对象,该对象会公开以下属性:
Value
- 更新后的值、Sender
- 属性发生更改的对象Expression
- 已更改的表达式。外部用户不经常需要。
// Sender 和 SelectedItem 的新属性值。 this.WhenAny(x => x.ComboBox.SelectedItem) .Subscribe(x => Console.WriteLine($"The {x.Sender} changed value to {x.Value}"));
WhenAnyObservable
WhenAnyObservable
观察一个或多个观察对象,并提供最新的观察值,处理新观察对象的自动订阅和以前观察对象的处理。
WhenAnyObservable
默认是一个延迟订阅。
// 每当文档被保存时,它将打印来自 IsSaved 的观察对象值。 // 当 Document 属性更改时,它将自动取消订阅并重新订阅。 public class MyViewModel { [Reactive] public Document Document { get; set; } public MyViewModel() { this.WhenAnyObservable(x => x.Document.IsSaved) .Subscribe(x => Console.WriteLine($"Document Saved: {x}")); } } public class Document { public IObservable<bool> IsSaved { get; } }