65.9K
CodeProject 正在变化。 阅读更多。
Home

WPF 中的 ReactiveProperty 和 ReactiveCommand

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.25/5 (7投票s)

2015年12月20日

CPOL

6分钟阅读

viewsIcon

33102

downloadIcon

784

在本文中,我们将学习来自 ReactiveProperty.NET4 库的 ReactiveProperty 和 ReactiveCommand。它们用于在 XAML 应用程序中使用 MVVM 模式时创建响应式属性和命令。

引言

在本文中,我们将学习来自 ReactiveProperty.NET4 库的 ReactivePropertyReactiveCommand。它们用于在 XAML 应用程序中使用 MVVM 模式时创建响应式属性和命令。ReactiveProperty.NET4 库是基于 .NET 的响应式扩展构建的。在本文中,我们将创建一个演示,展示 ReactiveProperty 和 ReactiveCommand 如何通过减少代码行数和其他开销来帮助我们进行属性和命令绑定。

先决条件:要理解本文,您应该对 WPF 中的属性绑定和命令有基本的了解。如果您是 WPF 中 MVVM 和命令绑定的新手,请首先查看本文

目录

  • .NET 响应式扩展 (Rx) 是什么
  • ReactiveProperty 是什么
  • ReactiveCommand 是什么
  • 演示应用程序概述
  • 使用基本的 WPF 属性和命令
  • 使用 ReactiveProperty
  • 使用 ReactiveCommand

.NET 响应式扩展 (Rx) 是什么

Reactive Extensions (Rx) 是一个帮助我们编写异步和基于事件的程序的库。Rx 使用 LINQ 语法编写异步和事件处理查询或操作,以实现所需的功能。因此,无论何时我们需要进行多次异步调用和基于事件的编程,Reactive Extensions (Rx) 都能帮助我们。Reactive Extensions 有多种版本,例如 .NET 4.0、.NET 4.5、.NET 4.6、Windows Phone 8.0/8.1、Windows Store 应用 8.1、Xamarin.iOS、Xamarin.Android 的 Rx。除了 .Net,Rx 还有 Java 版本,称为 RxJava,以及 jQuery 的 Rx,称为 rxjs 等。有关 Rx 的更多学习,请访问Reactive Extensions

ReactiveProperty 是什么

ReactiveProperty 是一个在 MVVM 框架中使用 ReactiveProperty.NET4 库时使用的类。默认情况下,ReactiveProperty 实现了 INotifyPropertyChanged 以及 IReactiveProperty<T>, IReactiveProperty, IHasErrors, IObservable<T>, IDisposable, INotifyDataErrorInfo, IReadOnlyReactiveProperty<T> 和 IReadOnlyReactiveProperty。因此,当我们使用 ReactiveProperty 时,我们不需要实现 INotifyPropertyChanged 接口来实现双向数据绑定。它还提供了更多功能,因为它也实现了各种接口。有关 ReactiveProperty 的更多详细信息,请访问此 Codeplex 页面

ReactiveCommand 是什么

ReactiveCommand 是一个在 MVVM 框架中使用 ReactiveProperty.NET4 库时使用的类。它用于创建具有响应式功能的命令。默认情况下,ReactiveCommand 类继承自 ReactiveCommand<T>。ReactiveCommand<T> 实现了 ICommand、IObservable<T> 和 IDisposable 接口。因此,当我们使用 ReactiveCommand 时,我们不需要实现 ICommand 接口。

演示应用程序概述

在演示应用程序中,主视图有三个按钮,分别称为 使用 WPF 属性和命令演示、使用 ReactiveProperty 演示使用 ReactiveCommand 演示。点击每个按钮,关联的视图将打开。所有三个视图都具有相似的布局和执行计算的相同功能。但是,所有三个视图的实现方式不同,以实现相同的功能。UI XAML 代码有一些小的更改,但相应的 ViewModel 有重大更改。

每个子视图都有两个文本框用于接受输入值,一个 TextBlock 用于显示输出,以及四个单选按钮用于执行特定计算。四个单选按钮用于执行加法、减法、乘法和除法运算。计算将根据选中的单选按钮在输入更改时持续进行,结果将显示在输出 TextBlock 中。下面截图中显示了演示应用程序的快速概述。

FinalDemo
 

使用基本的 WPF 属性和命令

在上一篇文章中,我们通过点击四种按钮进行计算。在指定的按钮点击时,计算/结果值显示在输出 TextBlock 中。但在这篇文章中,我们已移除执行操作的按钮。取而代之的是,我们使用四个单选按钮。

运行下载的代码,然后点击“使用 WPF 属性和命令演示”按钮。在文本框中输入任意数字,然后勾选任意一个单选按钮。结果将显示在输出 TextBlock 中。

布局代码编写在 UsingWpfPropertyCommandView.xaml 文件中,它使用 WpfPropertyCommandViewModel。WpfPropertyCommandViewModel 包含 UsingWpfPropertyCommandView 所需的属性和命令代码。WpfPropertyCommandViewModel 继承自 ViewModelBase 类,该类实现了 INotifyPropertyChanged 接口(用于在 View 和 ViewModel 之间或反之进行属性的双向数据绑定)。RelayCommand 类实现了 ICommand 接口,用于实现 RadionButton 点击的命令。如果理解这两个文件的代码有任何困难,请查看 WPF 中的 ICommand 接口 文章以获取详细解释。

使用 ReactiveProperty

在本节中,我们将学习如何使用 ReactiveProperty 实现与上一节相同的功能。

首先,让我们添加所需的库。可以通过“管理 NuGet 包”添加所需的库,如下截图所示。它将添加 .NET Reactive Extension 的所有必需库。

FinalDemo

要在 UsingReactivePropertyView.xaml 中创建布局,几乎可以使用我们上面编写的相同 XAML 代码。只需要做两个额外的更改。第一个更改是,无论哪里有属性绑定,在属性名称中我们都必须添加 .Value,这样最终这些名称就变成了 Add.Value。这是因为 ReactiveProperty 以这种方式暴露其值。第二个更改是,在每次 RadioButton 点击时,RadionButtons 的 IsChecked 属性用于执行操作。RadioButtons 的 IsChecked 属性绑定到 ReactivePropertyViewModel 的 Add、Subtract、Multiply 和 Divide 属性。示例代码如下所示。

<StackPanel Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="4" Margin="25,10,0,15" >
    <Viewbox Height="30">
        <StackPanel Orientation="Horizontal">
         <RadioButton GroupName="RadioButtonGroup" Margin="25,0,0,0" 
                     IsChecked="{Binding Add.Value, Mode=TwoWay}">Add</RadioButton>
        <RadioButton GroupName="RadioButtonGroup"  Margin="25,0,0,0"
                     IsChecked="{Binding Substract.Value, Mode=TwoWay}">Substract</RadioButton>
        <RadioButton GroupName="RadioButtonGroup"  Margin="25,0 ,0 ,0"
                     IsChecked="{Binding Multiply.Value, Mode=TwoWay}">Multiply</RadioButton>
        <RadioButton GroupName="RadioButtonGroup"  Margin="25,0,25,0"
                     IsChecked="{Binding Divide.Value, Mode=TwoWay}">Divide</RadioButton>
        </StackPanel>
    </Viewbox>
</StackPanel>

在上面的代码中,Add、Subtract、Multiply 和 Divide 属性与 ReactivePropertyViewModel 中的 ReactiveProperty 绑定。ReactivePropertyViewModel 的全部代码如下所示。

 注意:ReactivePropertyViewModel 没有从 ViewModelBase 继承以实现双向数据绑定。

 public class ReactivePropertyViewModel : IDisposable
    {
        private bool isDisposed = false;
        private readonly SerialDisposable firstValueSubscriptionDisposable 
                                                    = new SerialDisposable();
        private readonly SerialDisposable secondValueSubscriptionDisposable 
                                                    = new SerialDisposable();

        public ReactiveProperty<double?> FirstValue { get; private set; }
        public ReactiveProperty<double?> SecondValue { get; private set; }
        public ReactiveProperty<double?> Output { get; private set; }
        public ReactiveProperty<bool> Add { get; private set; }
        public ReactiveProperty<bool> Substract { get; private set; }
        public ReactiveProperty<bool> Multiply { get; private set; }
        public ReactiveProperty<bool> Divide { get; private set; }       


        public ReactivePropertyViewModel()
        {
            FirstValue = new ReactiveProperty<double?>();
            SecondValue = new ReactiveProperty<double?>();
            Output = new ReactiveProperty<double?>();

            Add = new ReactiveProperty<bool>();
            Substract = new ReactiveProperty<bool>();
            Multiply = new ReactiveProperty<bool>();
            Divide = new ReactiveProperty<bool>();

            Add.Subscribe(x =>
            {
                if (x == true)
                {
                    firstValueSubscriptionDisposable.Disposable 
                                    = FirstValue.Subscribe(y => add());
                    secondValueSubscriptionDisposable.Disposable 
                                    = SecondValue.Subscribe(y => add());
                }
            });

            Substract.Subscribe(x =>
            {
                if (x == true)
                {
                    firstValueSubscriptionDisposable.Disposable 
                                    = FirstValue.Subscribe(y => substract());
                    secondValueSubscriptionDisposable.Disposable 
                                    = SecondValue.Subscribe(y => substract());
                }
            });

            Multiply.Subscribe(x =>
            {
                if (x == true)
                {
                    firstValueSubscriptionDisposable.Disposable 
                                    = FirstValue.Subscribe(y => multiply());
                    secondValueSubscriptionDisposable.Disposable 
                                    = SecondValue.Subscribe(y => multiply());
                }
            });

            Divide.Subscribe(x =>
            {
                if (x == true)
                {
                    firstValueSubscriptionDisposable.Disposable 
                                    = FirstValue.Subscribe(y => divide());
                    secondValueSubscriptionDisposable.Disposable 
                                    = SecondValue.Subscribe(y => divide());
                }
            });

        }

        private void add()
        {
            Output.Value = FirstValue.Value + SecondValue.Value;
        }

        private void substract()
        {
            Output.Value = FirstValue.Value - SecondValue.Value;
        }

        private void multiply()
        {
            Output.Value = FirstValue.Value * SecondValue.Value;
        }
        private void divide()
        {
            Output.Value = FirstValue.Value / SecondValue.Value;
        }

        public void Dispose()
        {
            if (isDisposed) return;

            isDisposed = true;
            firstValueSubscriptionDisposable.Dispose();
            secondValueSubscriptionDisposable.Dispose();
        }
    }


   

为了避免内存泄漏,我们必须清理任何可观察对象的订阅,如此处所述。在上面的代码中,我们使用 SerialDisposable 来释放 ReactiveProperties 的订阅。Reactive Core Library 在 System.Reactive.Disposables 命名空间下提供了许多 IDisposable 接口的实现。有关 Disposables 的更多详细信息,请查看 Lee Campbell 在线书籍的这一页

所以,根据以下几点,ReactivePropertyViewModel 的上述代码比 WpfPropertyCommandViewModel 更好、更简洁:

  • 在 ReactivePropertyViewModel 中,所有订阅代码都在一个地方,而在 WpfPropertyCommandViewModel 中,我们需要使用属性的 set 块来通知更改并调用方法。
  • 在 ReactivePropertyViewModel 中,我们不需要为属性创建私有后备字段。
  • 在 ReactivePropertyViewModel 中,代码更加简洁和自解释。
  • 无需创建自己的 ViewModelBase 和 RelayCommand 实现。

使用 ReactiveCommand

在本节中,我们将学习如何使用 ReactiveCommand 实现与上一节相同的功能。要创建布局,几乎可以使用我们上面编写的相同 XAML 代码。我们只需要做一个更改,即不再使用 RadioButton 的 IsChecked 属性来执行特定类型的操作。在本节中,我们将在所有四个 RadioButton 中使用一个单一命令,称为“OperationCommand”,来执行特定类型的操作。示例代码如下所示:

<StackPanel Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="4"  Margin="25,10,0,15" >
            <Viewbox Height="30">
                <StackPanel Orientation="Horizontal">
                    <RadioButton GroupName="RadioButtonGroup" Margin="25,0,0,0" 
                           Command="{Binding OperationCommand}" 
                           CommandParameter="{Binding Path=Content, 
                                RelativeSource={RelativeSource Self}}">Add
                    </RadioButton>
                    <RadioButton GroupName="RadioButtonGroup"  Margin="25,0,0,0" 
                           Command="{Binding OperationCommand}"
                           CommandParameter="{Binding Path=Content, 
                                RelativeSource={RelativeSource Self}}">Substract
                    </RadioButton>
                    <RadioButton GroupName="RadioButtonGroup"  Margin="25,0,0,0" 
                           Command="{Binding OperationCommand}"
                           CommandParameter="{Binding Path=Content, 
                                RelativeSource={RelativeSource Self}}">Multiply
                    </RadioButton>
                    <RadioButton GroupName="RadioButtonGroup"  Margin="25,0,25,0" 
                           Command="{Binding OperationCommand}"
                           CommandParameter="{Binding Path=Content, 
                                RelativeSource={RelativeSource Self}}">Divide
                     </RadioButton>
                </StackPanel>
            </Viewbox>
</StackPanel>
        

在上面的代码中,OperationCommand 绑定到 ReactiveCommandViewModel 的 ReactiveCommand。每当任何 RadioButton 被选中时,OperationCommand 都会执行。OperationCommand 将订阅特定操作并显示结果在输出文本块中。

    public class ReactiveCommandViewModel : IDisposable
    {
        private bool isDisposed = false;
        private readonly SerialDisposable firstValueSubscriptionDisposable 
                                                    = new SerialDisposable();
        private readonly SerialDisposable secondValueSubscriptionDisposable 
                                                    = new SerialDisposable();
        private readonly SingleAssignmentDisposable operationCommandSubscriptionDisposable 
                                                    = new SingleAssignmentDisposable();

        public ReactiveProperty<double?> FirstValue { get; private set; }
        public ReactiveProperty<double?> SecondValue { get; private set; }
        public ReactiveProperty<double?> Output { get; private set; }
        public ReactiveCommand<string> OperationCommand { get; set; }

        public ReactiveCommandViewModel()
        {
            FirstValue = new ReactiveProperty<double?>();
            SecondValue = new ReactiveProperty<double?>();
            Output = new ReactiveProperty<double?>();

            OperationCommand = new ReactiveCommand<string>();

            operationCommandSubscriptionDisposable.Disposable = OperationCommand.Subscribe(action =>
            {
                switch (action)
                {
                    case "Add":
                        firstValueSubscriptionDisposable.Disposable 
                                                = FirstValue.Subscribe(x => add());
                        secondValueSubscriptionDisposable.Disposable 
                                                = SecondValue.Subscribe(x => add());
                        break;

                    case "Substract":
                        firstValueSubscriptionDisposable.Disposable 
                                                = FirstValue.Subscribe(y => substract());
                        secondValueSubscriptionDisposable.Disposable 
                                                = SecondValue.Subscribe(y => substract());
                        break;

                    case "Multiply":
                        firstValueSubscriptionDisposable.Disposable 
                                                = FirstValue.Subscribe(y => multiply());
                        secondValueSubscriptionDisposable.Disposable 
                                                = SecondValue.Subscribe(y => multiply());
                        break;

                    case "Divide":
                        firstValueSubscriptionDisposable.Disposable 
                                                = FirstValue.Subscribe(y => divide());
                        secondValueSubscriptionDisposable.Disposable 
                                                = SecondValue.Subscribe(y => divide());
                        break;
                }

            });

        }

        private void add()
        {
            Output.Value = FirstValue.Value + SecondValue.Value;
        }

        private void substract()
        {
            Output.Value = FirstValue.Value - SecondValue.Value;
        }

        private void multiply()
        {
            Output.Value = FirstValue.Value * SecondValue.Value;
        }
        private void divide()
        {
            Output.Value = FirstValue.Value / SecondValue.Value;
        }

        public void Dispose()
        {
            if (isDisposed) return;

            isDisposed = true;
            firstValueSubscriptionDisposable.Dispose();
            secondValueSubscriptionDisposable.Dispose();
            operationCommandSubscriptionDisposable.Dispose();
        }
    }

因此,与 ReactivePropertyViewModel 代码相比,使用 ReactiveCommand 我们需要编写的代码要少得多。

结论

在本文中,我们学习了 ReactiveProperty.NET4 库中的 ReactivePropertyReactiveCommand。希望它能帮助您在处理基于 MVVM 的 WPF 应用程序时编写更好的代码。欢迎您的评论/建议和疑问。谢谢。

© . All rights reserved.