かずきのBlog@hatena

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

Reactive Extensions 応用「ドローイングツール」

入門ばっかりで飽きてきたので、応用的なものを作ってみました。因みにWPFアプリケーションです。

各種イベントを監視するObservableを用意します。

// マウスダウンイベント
var mouseDown = Observable.FromEvent(
    (EventHandler<MouseButtonEventArgs> h) => new MouseButtonEventHandler(h),
    h => this.MouseDown += h,
    h => this.MouseDown -= h);
// マウスむーぶイベント
var mouseMove = Observable.FromEvent(
    (EventHandler<MouseEventArgs> h) => new MouseEventHandler(h),
    h => this.MouseMove += h,
    h => this.MouseMove -= h);
// マウスアップイベント
var mouseUp = Observable.FromEvent(
    (EventHandler<MouseButtonEventArgs> h) => new MouseButtonEventHandler(h),
    h => this.MouseUp += h,
    h => this.MouseUp -= h);

これらのイベントをベースにいろいろ組み立てていきます。

// アイテムのZIndex
int zIndex = 0;
// ボタンダウン開始点
Point startPos = default(Point);

// クリック開始点の記録とマウスのキャプチャ
mouseDown
    .Select(e => e.EventArgs.GetPosition(this))
    .Subscribe(p =>
    {
        this.CaptureMouse();
        startPos = p;
    });

// マウスがリリースされたタイミングでキャプチャをリリース
// そして、矩形を作成してCanvasに追加
mouseUp.Select(e => e.EventArgs.GetPosition(this))
    .Subscribe(p =>
    {
        this.ReleaseMouseCapture();
        var r = CreateRectangle(startPos, p, zIndex++);
        layoutRoot.Children.Add(r);
    });

// マウスを動かしてる箇所を通知してくれる
var movePoints = mouseMove
    .Select(e => e.EventArgs.GetPosition(this));

// マウスが押されるまでスキップして、マウスがアップされるまで
// 通知する。そして、それを繰り返す。
var drag = movePoints
    .SkipUntil(mouseDown)
    .TakeUntil(mouseUp)
    .Repeat();

// ドラッグ中に、draggingという名前の半透明の矩形を移動させ続ける
drag.Subscribe(p => SetRectangle(dragging, startPos, p, zIndex));

とまぁ、こんな具合で動画のようなアプリを作るのにフィールド変数を1つも持たせることなく実装できます。とても凄いです・・・。

ついでにXAMLは、こんな感じです。

<Window x:Class="WpfPutRectSample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Canvas Name="layoutRoot">
        <Rectangle Name="dragging" Stroke="Black" Fill="Blue" Opacity="0.3"/>
    </Canvas>
</Window>

ソースは、以下からダウンロードできます。Reactive Extensionsをインストールしてればコンパイルできると思います。
WpfPutRectSample.zip