かずきのBlog@hatena

日本マイクロソフトに勤めています。XAML + C#の組み合わせをメインに、たまにASP.NETやJavaなどの.NET系以外のことも書いています。掲載内容は個人の見解であり、所属する企業を代表するものではありません。

ReactiveProperty v2.0.0-pre3をリリースしました

NuGet Gallery | ReactiveProperty 2.0.0-pre3

今回は以下の変更を含んでます。

CountNotifyerのINotifyPropertyChangedの実装

CountNotifyerクラスがINotifyPropertyChangedインターフェースを実装してCountプロパティの変更通知を受け取れるようにしました。

EventToReactiveCommandの追加

イベントをReactiveCommandに接続するときに、間にRxのレイヤを1枚噛ますことができるEventToReactiveCommandクラスを追加しました。EventTrigger等と一緒に使います。要はコマンドを呼び出す前に任意の処理を挟んで、なおかつ処理の結果をコマンドに渡すことが出来るようになります。

Commandが呼び出される前の処理はReactiveConverter<T, U>かDelegateConverter<T, U>のどちらかを継承して作成します。ReactiveConverterのほうが、IObservable<T>を受け取ってIObservable<U>を返す処理を記述して、DelegateConverterのほうがTを受け取ってUを返す処理を記述します。

ReactivePropertyのサンプルプログラムに入っている例では、ファイルダイアログを出して選択したファイルをCommandに渡すようにしています。コードを以下に示します。

// Converter
public class OpenFileDialogConverter : ReactiveConverter<EventArgs, string>
{

    protected override IObservable<string> Convert(IObservable<EventArgs> source)
    {
        return source
            .Select(_ => new OpenFileDialog())
            .Do(x => x.Filter = "*.*|*.*")
            .Where(x => x.ShowDialog() == true) // Show dialog
            .Select(x => x.FileName); // convert to string
    }
}

EventArgsを受け取ったらダイアログを表示して、OKが押された場合のみ選択されたファイルのパスを流しています。

このConverterをCommandと接続するためにEventToReactiveCommandを使用します。EventToReactiveCommandのCommandプロパティに接続先のCommandをバインドして、先ほど作成したConverterをConverterプロパティ(コンテンツプロパティなので中に直接書けます)にセットします。

<Button Content="Select file">

    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Click">
            <!-- set the command, called after the converter -->
            <Interactivity:EventToReactiveCommand Command="{Binding SelectFileCommand, Mode=OneWay}">
                <Views:OpenFileDialogConverter/>
            </Interactivity:EventToReactiveCommand>
        </i:EventTrigger>
    </i:Interaction.Triggers>

</Button>

ViewModel側は以下のようになっています。

public class EventToReactiveCommandViewModel
{
    public ReactiveCommand<string> SelectFileCommand { get; private set; }

    public ReactiveProperty<string> Message { get; private set; }

    public EventToReactiveCommandViewModel()
    {
        // command called, after converter
        this.SelectFileCommand = new ReactiveCommand<string>();
        // create ReactiveProperty from ReactiveCommand
        this.Message = this.SelectFileCommand
            .Select(x => x + " selected.")
            .ToReactiveProperty();
    }
}

Windows Runtimeの場合

Windows Runtimeのようにダイアログが非同期呼び出しのケースでは、SelectManyでTaskをIObservableに変換することで、対応することができます。ここらへんRxだとさくっと書けていいですね。

public class SelectFileConverter : ReactiveConverter<RoutedEventArgs, string>
{
    protected override IObservable<string> Convert(IObservable<RoutedEventArgs> source)
    {
        return source
            .Select(_ => new FileOpenPicker()) // create picker
            .Do(x => x.FileTypeFilter.Add(".txt")) // set extensions
            .SelectMany(x => x.PickSingleFileAsync().AsTask().ToObservable()) // convert task to iobservable
            .Where(x => x != null) // filter
            .Select(x => x.Path); // convert
    }
}