サンプルプログラムの概要

このサンプルプログラムは、ReactivePropertyをソースにしてPOCOをターゲットにして単方向・双方向のバインディングを行う方法をしめしています。WPFやWindows PhoneやWindows store appでは、XAMLのデータバインディングを使用するため、使用することはありませんが、XamarinやWindows Formsなどデータバインディングが無い(または、あったとしても貧弱な)環境での使用を想定しています。

このサンプルは、Windows FormsでReactivePropertyを使用しています。

サンプルプログラムの実行方法

ソリューションのNuGetパッケージの復元を有効化してリビルドしてください。そのあと、実行を行うとサンプルプログラムを実行できます。

サンプルプログラムの解説

このサンプルプログラムは、TextBoxに入力した内容が3秒遅れでLabelにすべて大文字になって表示される動作をします。

Windows Formsで使うための下準備

本題のBindingに入る前にWindows FormsでReactivePropertyを使うための下準備として、Windows FormsのUIスレッドにディスパッチしてくれるSchedulerを作成します。WindowsFormsSynchronizationContextを使ってSynchronizationContextSchedulerを作成すればOKです。

C#
スクリプトの編集|Remove
using System; 
using System.Reactive.Concurrency; 
using System.Windows.Forms; 
 
namespace WindowsFormsRxSampleApp 
{ 
    public static class WindowsFormUIDispatcher 
    { 
        private static Lazy<IScheduler> defaultValue = new Lazy<IScheduler>( 
            () => new SynchronizationContextScheduler(WindowsFormsSynchronizationContext.Current)); 
 
        public static IScheduler Default 
        { 
            get { return defaultValue.Value; } 
        } 
    } 
} 
 

ViewModelの作成

ViewModelは通常の場合と同じなので詳細な説明は省略します。注意点としてはDelayしたあとにUIスレッドに戻している点です。

C#
スクリプトの編集|Remove
using Codeplex.Reactive; 
using System; 
using System.Reactive.Linq; 
 
namespace WindowsFormsRxSampleApp 
{ 
    public class MainFormViewModel 
    { 
        public ReactiveProperty<string> Input { get; private set; } 
        public ReactiveProperty<string> Output { get; private set; } 
 
        public ReactiveCommand ClearCommand { get; private set; } 
 
        public MainFormViewModel() 
        { 
            this.Input = new ReactiveProperty<string>(); 
 
            this.Output = this.Input 
                .Where(x => x != null) 
                .Delay(TimeSpan.FromSeconds(3)) 
                .Select(x => x.ToUpper()) 
                .ObserveOn(WindowsFormUIDispatcher.Default) 
                .ToReactiveProperty(); 
 
            this.ClearCommand = new ReactiveCommand(); 
            this.ClearCommand.Subscribe(_ => this.Input.Value = "Clear value!!"); 
        } 
    } 
} 
 

データのバインド

本題のデータのバインドを行います。ReactivePropertyには、Codeplex.Reactive.Binding名前空間にReactivePropertyをバインドするための拡張メソッドが定義されています。

BindTo拡張メソッドを使うと、単方向・双方向のデータバインドが可能になります。ReactivePropertyから何かのオブジェクトへの一方通行の変換の場合は以下のように簡単に書けます。

C#
スクリプトの編集|Remove
private MainFormViewModel viewModel = new MainFormViewModel(); 
 
// ----------------------------------------------------- 
this.viewModel 
    .Output 
    .BindTo( 
        this.labelOutput, 
        x => x.Text); 
 

第一引数に、バインドの先になるオブジェクトを渡して、第二引数に、バインド先のプロパティを指定するラムダ式を指定します。これで、ReactivePropertyの値が変更されたら、バインド先のプロパティに値が反映されます。

双方向のデータバインドも基本的に同じ方法で指定しますが、バインド先からReactivePropertyの値の変更のトリガーとなるIObservable<Unit>を渡す点が異なります。サンプルプログラムでは、TextBoxのTextChangedイベントをトリガーに、ReactivePropertyに値を書き戻すように指定しているので、以下のようなコードになります。

C#
スクリプトの編集|Remove
this.viewModel 
    .Input 
    .BindTo( 
        this.textBoxInput, 
        x => x.Text, 
        mode: BindingMode.TwoWay, 
        targetUpdateTrigger: Observable.FromEvent<EventHandler, EventArgs>( 
            h => (s, e) => h(e), 
            h => this.textBoxInput.TextChanged += h, 
            h => this.textBoxInput.TextChanged -= h) 
            .ToUnit()); 
 

mode引数で双方向バインディングであることを指定してtargetUpdateTrigger引数で、変更のタイミングを指定しています。

最後に、ReactiveCommandをボタンのClickイベントに紐づけるためのToEventHandlerメソッドを紹介します。このメソッドはCommandをEvehtHandler型に変換するメソッドです。以下のように使用します。

C#
スクリプトの編集|Remove
this.buttonClear.Click += this.viewModel.ClearCommand.ToEventHandler();
 
あとで、イベントハンドラの解除を行う必要がある場合はToEventHandlerの結果を変数に保持して必要になった個所で -= でイベントの登録を解除するようにしてください。