Rxでドラッグ&ドロップの話を始める準備

WPFアプリでGUIのドラッグ&ドロップで矩形を動かすベタなコードを考えてみる。

MainWindow.xaml

<Window x:Class="BasicDragDrop.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">
    <Grid>
        <Rectangle Fill="Green" HorizontalAlignment="Left" Height="100" Stroke="Black"
                   VerticalAlignment="Top" Width="100"
                   MouseDown="Rectangle_MouseDown" MouseMove="Rectangle_MouseMove" MouseUp="Rectangle_MouseUp"/>
    </Grid>
</Window>

MainWindow.xaml.cs

using System.Diagnostics;
using System.Windows;
using System.Windows.Input;
using System.Windows.Shapes;

namespace BasicDragDrop
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        private Point _lastMousePosition;
        private bool _isDragging = false;

        public MainWindow()
        {
            InitializeComponent();
        }

        private void Rectangle_MouseDown(object sender, MouseButtonEventArgs e)
        {
            var rect = (Rectangle)sender;
            Debug.Assert(rect != null);
            Debug.Assert(_isDragging == false);

            rect.CaptureMouse();
            _lastMousePosition = e.GetPosition(null);
            _isDragging = true;
        }

        private void Rectangle_MouseMove(object sender, MouseEventArgs e)
        {
            if (_isDragging)
            {
                var newPos = e.GetPosition(null);
                var dx = newPos.X - _lastMousePosition.X;
                var dy = newPos.Y - _lastMousePosition.Y;

                var rect = (Rectangle)sender;
                Debug.Assert(rect != null);
                var current = rect.Margin;
                var moveTo = new Thickness()
                {
                    Left = current.Left + dx,
                    Top = current.Top + dy
                };
                rect.Margin = moveTo;
                _lastMousePosition = newPos;
            }
        }

        private void Rectangle_MouseUp(object sender, MouseButtonEventArgs e)
        {
            var rect = (Rectangle)sender;
            Debug.Assert(rect != null);

            if (_isDragging)
            {
                rect.ReleaseMouseCapture();
                _isDragging = false;
            }
        }
    }
}

まあ、これで緑の矩形をマウスでドラッグして動かせるようになる。
WPFでやる場合、座標位置の考え方がコンテナ依存なんでちょっとだけ面倒くさいコードになる。
この場合はGridがコンテナなんでMarginを使ってセットしてるけど、コンテナがCanvasの場合にはコードが変わる。
ぶっちゃけMoveToみたいなヘルパに委譲して吸収すれば良いんやけど、直観的なコードじゃ無くなるんでここではGrid依存で。

WPFWPFのデータバインディングでも、ラムダ式でも、LINQでも何でも良いんやけど、新しい概念を新しい記法で書くときに
慣れてない人間の目にはすごく難解な記号にうつるのな。
すごく単純化した例で言うと「5割る2」って数式を一番初めに小学校では
5÷2=
とならう。それを÷はかったるいから/で表現します。(定義する)ってなって急に「5/2」って書くようになる。
掛け算なら×が・になって最後に省略される。

・・・って何の話かっていうと、変換規則(省略や記号の定義も含む)をきちんと知らない(≠理解してない)とそのコードが
まるで意味不明な記号にしか見えなくなるのな。

プログラミングにおける黒魔術。動けば良いやって意味でネットからコードをコピペするだけで目的を達するんであれば
わからないまま黒魔術を自分のソースコード中に埋め込むのも良いのかも知れないけど応用できないし、実際問題動くか
どうかなんて運しだいなのな。残念ながら。

だからこそ、新たな概念の理解を進めるときは準備がいろいろと必要になる。

ドラッグ&ドロップの基本的なシーケンス(フロー?)は、

1.対象のオブジェクト上でマウスダウン
2.マウスアップまでの間マウスムーブの追跡
3.移動量に応じて対象オブジェクトの位置情報の更新
4.マウスアップでドロップ完了

こんな感じ。ドラッグ中のマウスムーブかフリーなマウスムーブかを見分けるためにドラッグ中フラグかドラッグ対象の
オブジェクトの参照(nullだとドラッグ中じゃない)を持つ必要がある。

で、俺っちがやりたいのはRxを使ってドラッグ&ドロップを一般化するってことなのな。言わばそれが直近の目標っつーか
第一のマイルストーンとなる。