ドラッグで移動する:Thumbコントロールを使う方法

先日のエントリーでは紹介したドラッグで移動可能なオブジェクトをThumbコントロールで作る方法のサンプルコードを紹介したい。

まずはXAMLはこんな感じ。

    <Canvas>
        <Thumb DragDelta="Thumb_DragDelta" Canvas.Left="0" Canvas.Top="0">
            <Thumb.Template>
                <ControlTemplate>
                    
                    <!--ドラッグ対象のオブジェクトを定義する-->
                    <Grid Width="100" Height="30">
                        <Ellipse Fill="LightBlue" Stroke="Blue" />
                        <TextBlock Text="動く物体" HorizontalAlignment="Center" VerticalAlignment="Center"/>
                    </Grid>
                    
                </ControlTemplate>
            </Thumb.Template>
        </Thumb>    
    </Canvas>

Thumbコントロールの親パネルはCanvasにしておく。なおCanvas.LeftおよびCanvas.Topで表示位置をCanvas上の相対座標で指定する。明示的にゼロで初期化しているのは、既定値がDouble.NaNのため後述の移動処理が効かなくなるのを避けるために入れてみた。
ThumbのTemplateプロパティ以下に表示したいXAMLを書けばよい。ここではシンプルな楕円にしているが通常の方法でカスタマイズできるので好きなように書けば良い。

次にイベントハンドラ

        //Thumbコントロールのドラッグイベント処理
        private void Thumb_DragDelta(object sender, DragDeltaEventArgs e) {
            
            var thumb = sender as Thumb;
            if (thumb == null) return;

            //ドラッグ量に応じてThumbコントロールを移動する
            Canvas.SetLeft(thumb, Canvas.GetLeft(thumb) + e.HorizontalChange);
            Canvas.SetTop(thumb, Canvas.GetTop(thumb) + e.VerticalChange);
        }

今回の用途では、Thumbコントロールでは難しいことを考えず1つのイベントハンドラで十分です。なおここではHorizontalChangeなどは直前のDragDeltaイベントからの水平差分移動量が入っていると思えば良い。
より正確に言えば、ドラッグ開始時の位置から今回イベント時の座標への相対移動量、つまり積算値が得られる。Thumbコントロール上の相対座標で得られるのだが、イベントの都度Thumbコントロールを移動しているので事実上の差分値として現れる。もし分身の術(反復横跳び?)のように表示されたり、逃げていくように表示されるバグが現れた場合にはこのあたりの座標系の理解が正しいか疑ってみると良いかもしれない。

実行してみるとこんな感じに表示される。楕円をドラッグすれば好きな位置に配置できる。

iPhoneアプリが出てきてこのような操作体系は完全に当たり前になってしまった。早い時期からRIAプラットフォームを標榜していたので、このような実装はExpressionBlendではなく素のWPFで提供しておいて欲しかったなぁと思う。