かずきのBlog@hatena

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

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>