Posted at

RxとReactivePropertyを使って、WPFでUnidirection Data Flowを実現する

More than 3 years have passed since last update.


要約

ブラウザ上の MVC的な分割を Node Stream API で行うの WPF+Rx+ReactiveProperty 版です


背景

WPFで、真面目にコードビハインドとViewModelを分離して、MVVMを実現するには、INotifyPropertyChangedとICommandの実装が面倒です。

また、個人的に双方向バインディングより、Unidirectional Data Flowが好きです。


作戦

幸い、技術的要素は揃っています。


  • イベントをRxで処理

  • ViewModelからViewへの変更通知はReactivePropertyで実現

  • ViewはXamlで実現

次のような処理の流れを考えます。


  1. Rxでイベントを受け取る

  2. Rxストリーム中でイベントを処理

  3. 結果をReactivePropertyに反映

  4. Viewを更新


作るアプリケーション

ボタンをクリックしたらカウンターの数値を加算します。


実装


View

TextBlockにカウンターの値をバインドします。

<Window x:Class="WpfApplication1.MainWindow"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication1"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<Button x:Name="button" Content="Button" />
<TextBlock x:Name="textBlock" Text="{Binding Count.Value}"/>
</StackPanel>
</Window>


コードビハインド

using Reactive.Bindings;

using System;
using System.Reactive.Linq;
using System.Windows;

namespace WpfApplication1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();

// DataContextにViewModelをバインド
var vm = new ViewModel();
DataContext = vm;

// イベントに処理をバインド
Observable.FromEvent<RoutedEventHandler, RoutedEventArgs>(
h => (s, e) => h(e),
h => button.Click += h,
h => button.Click -= h
)
.Subscribe(_ =>
{
// ViewModelを更新
vm.Count.Value++;
});
}
}

public class ViewModel
{
public ReactiveProperty<int> Count { get; } = new ReactiveProperty<int>();
}
}


  • DataContextにReactivePropertyを使ったViewModelを設定して、変更通知をバインド

  • RxのObservable.FromEventを使って、ボタンクリックイベントに処理をバインド


感想

ModelとContorllerは分離されていません。

ですが、Subscribe内の処理をServiceクラスに分離すればなんとでもなりそうです。

JavaScriptより簡単に実装できました、WPF(とRxとReactiveProperty)すごいです。

良さげ:sushi:


参考