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. 被观察对象:观察者 = 1:n
  2. 被观察对象有一些行为或者属性(状态信息),
  3. 观察者可以订阅某些感兴趣的属性或者行为,
  4. 被观察对象状态发生变化,会通知所有观察者,观察者将做出相应的反应(react)。

Why?

在程序设计中,很多代码是对改变的东西做出反应的,你或许可以使用 C#事件、回调、 switch 语句、委托、 lambdas、 observable、通知和绑定等实现,但这会使你的应用程序变得越来越复杂,难以理解。

反应式编程会将更改自动传播到整个系统中。

当A或B改变时,C会自动更新

Rx将事件流抽象为Observable sequences(可观察序列)表示异步数据流,使用LINQ运算符查询异步数据流,并使用Scheduler来控制异步数据流中的并发性。简单地说:Rx = Observables + LINQ + Schedulers。

  1. 一切皆为数据流
  2. Observable 是对数据流的抽象
  3. 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; }
}

 

 

 

 

 

 

 
posted on 2023-04-18 09:02  冰魂雪魄  阅读(1332)  评论(0编辑  收藏  举报

WPF框架交流群:C#.net. WPF.core 技术交流�      C#WPF技术交流群:C#.net. WPF.core 技术交流�     WPF技术大牛交流群:C#.net. WPF.core 技术交流�