かずきのBlog@hatena

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

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