Reactive Extensionsでダブルクリックを実装してみる。

以前、データグリッドのダブルクリックイベントを実装してみました。

今回は、ダブルクリックイベントをReactive Extensionsで実装するとどうなるかを試してみました。ダブルクリック処理にフォーカスを当てるために、ButtonクラスのClickイベントに対してダブルクリック判定してみます。

// ButtonクラスのClickイベントをIObservable化する。
IObservable<IEvent<RoutedEventArgs>> click
    = Observable.FromEvent<RoutedEventArgs>(this.DoubleClieckButton, "Click");

// ダブルクリック検知処理開始。
click.Select(evt => DateTime.Now)
     .Scan((prev, current) => current.Subtract(prev) > TimeSpan.FromMilliseconds(500) ? current : DateTime.MinValue)
     .Where((time) => time == DateTime.MinValue)
     .Subscribe(time => System.Diagnostics.Debug.WriteLine("Double Click"));

Selectでイベントを別データに変換できます。Scanは一つ前の値と現在の値から処理を行います。WhereはLinqと同様フィルタリング処理を行います。Subscribeでイベントの監視を開始します。
具体的には、クリックされたときの時間をSelectで取得します。Scanで前回クリックされた時間と今回クリックされた時間の差を求め、差が一定時間内におさまっていた場合空時間(DateTime.MinValue)を、収まっていなかった場合今回クリックされた時間を返します。Whereで空時間だった場合のみSubscribeに通知するようにしています。

もうちょっと詳しく挙動を追うために、各メソッドが呼ばれるたびにデバッグ文を出力するようにしてみました。

// デバッグ用に書き換え。
click.Select((IEvent<RoutedEventArgs> evt) =>
{
    System.Diagnostics.Debug.WriteLine("Select");
    return DateTime.Now;
})
.Scan((DateTime prev, DateTime current) =>
{
    System.Diagnostics.Debug.WriteLine("Scan :" + prev + ", " + current);
    return current.Subtract(prev) > TimeSpan.FromMilliseconds(500) ? current : DateTime.MinValue;
})
.Where((DateTime time) =>
{
    System.Diagnostics.Debug.WriteLine("Where :" + time);
    return time == DateTime.MinValue;
})
.Subscribe((DateTime time) =>
{
    System.Diagnostics.Debug.WriteLine("Double Click");
});

ボタンを1クリックすると

Select
Where :2011/03/13 14:51:32

と出力されます。この段階ではScan以降のメソッドが呼ばれていないことがわかります。これは、Scanに2つの値を渡していない(prev引数がまだ存在していない)のが理由でしょう。
時間を十分おいてボタンを1クリックしてみます。

Select
Scan :2011/03/13 14:51:32, 2011/03/13 14:54:33
Where :2011/03/13 14:54:33

今度はWhereまで呼ばれましたが、Whereには空時間が渡されていないためSubscribeは呼ばれていません。
それでは、ボタンをダブルクリックしてみます。

Select
Scan :2011/03/13 14:54:33, 2011/03/13 14:56:04
Where :2011/03/13 14:56:04
Select
Scan :2011/03/13 14:56:04, 2011/03/13 14:56:04
Where :0001/01/01 0:00:00
Double Click

Subscribeメソッドがコールされています。ダブルクリック処理が実装されていることを確認できました。