かずきのBlog@hatena

すきな言語は C# + XAML の組み合わせ。Azure Functions も好き。最近は Go 言語勉強中。日本マイクロソフトで働いていますが、ここに書いていることは個人的なメモなので会社の公式見解ではありません。

ReactiveProperty 2.0.0-pre4をリリースしました

NuGet Gallery | ReactiveProperty 2.0.0-pre4

さっきpre3リリースしましたが、バージョンアップです。

メソッド名の変更

先ほど追加したEventToReactiveCommand用のReactiveConverterとDelegateConverterクラスのConvertメソッド名をConvertToに変更しました。Convertのままだと、実装しているインターフェースのConvertメソッドとかぶってオーバーライド出来ないことがあったので…。

EventToReactiveのObsolate化

この後紹介する機能とかぶるので非推奨にしました。

EventToReactivePropertyの追加

EventToReactiveCommandのReactiveProperty版です。

以下のようなConverterを定義して。

public class EventToReactivePropertyViewModel
{
    // binding from UI, event direct bind
    public ReactiveProperty<Unit> MouseDown { get; private set; }
    // binding from UI, event with converter
    public ReactiveProperty<Tuple<int, int>> MouseMove { get; private set; }
    // binding from UI, IgnoreEventArgs = true
    public ReactiveProperty<Unit> MouseEnter { get; private set; }

    public ReactiveProperty<string> CurrentPoint { get; private set; }
    public ReactiveProperty<string> Entered { get; private set; }

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

    public EventToReactivePropertyViewModel()
    {
        // mode off RaiseLatestValueOnSubscribe, because initialValue is null.
        // mode off DistinctUntilChanged, because if Unit no send any values.
        var none = ReactivePropertyMode.None;

        MouseMove = new ReactiveProperty<Tuple<int, int>>(mode: none);
        MouseDown = new ReactiveProperty<Unit>(mode: none);
        MouseEnter = new ReactiveProperty<Unit>(mode: none);

        CurrentPoint = MouseMove
            .Select(p => string.Format("X:{0} Y:{1}", p.Item1, p.Item2))
            .ToReactiveProperty();

        Entered = MouseEnter
            .Select(_ => Observable.Timer(TimeSpan.Zero, TimeSpan.FromSeconds(1)))
            .Switch()
            .Select(x => "entered:" + x + "sec")
            .ToReactiveProperty();

        this.AlertMessage = MouseDown.Select(_ => "MouseDown!").ToReactiveProperty(mode: none);
    }
}

// EventToReactiveProperty converter.
// Converter/IgnoreEventArgs is useful for unit testings.
// For example, MouseMovoe.Value = new Point(10, 10) is simulate MouseMove
// MouseEnter.Value = new Unit() is simulate raise MouseEnter event.
public class MouseEventToPointConverter : ReactiveConverter<dynamic, Tuple<int, int>>
{
    protected override IObservable<Tuple<int, int>> OnConvert(IObservable<dynamic> source)
    {
        return source
            .Select(x => x.GetPosition(null))
            .Select(x => Tuple.Create((int)x.X, (int)x.Y));
    }
}

EventTriggerなどと組み合わせて以下のように使います。

<Grid>
    <!-- Use Blend SDK's Interaction Trigger -->
    <!-- Event binding to ReactiveProperty -->
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="MouseMove">
            <!-- ConvertBack function is Func<object, object>-->
            <r:EventToReactiveProperty ReactiveProperty="{Binding MouseMove}">
                <vm:MouseEventToPointConverter/>
            </r:EventToReactiveProperty>
        </i:EventTrigger>
        <i:EventTrigger EventName="MouseDown">
            <!-- direct event bind -->
            <r:EventToReactiveProperty ReactiveProperty="{Binding MouseDown}" IgnoreEventArgs="True" />
        </i:EventTrigger>
        <i:EventTrigger EventName="MouseEnter">
            <!-- IgnoreEventArgs = true send Unit -->
            <r:EventToReactiveProperty ReactiveProperty="{Binding MouseEnter}" IgnoreEventArgs="true" />
        </i:EventTrigger>
    </i:Interaction.Triggers>
    <TextBlock Text="{Binding CurrentPoint.Value}" />
    <TextBlock Text="{Binding Entered.Value}" Margin="0,100,0,0" />
</Grid>