添付ビヘイビアでドラッグアンドドロップ

最近は、M-V-VMパターンの勉強がてらWPFのアプリケーションを作っています。

M-V-VMパターンを使うとコードがスッキリかけるし、何よりViewModelがViewに依存していないおかげで、テストコードが書きやすいのがすばらしいですね。


いろいろな処理がきれいに書けるので、ドラッグアンドドロップでユーザーコントロールを移動するような処理もスッキリ書けないかなと考えていたのですが、添付ビヘイビアが使えそうだと思い試してみました。

まずは、以下のようなドラッグアンドドロップでThumbを移動させるビヘイビアを記述します。

using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;

namespace DragAndDropSample.Behaviors
{
    public class DragAndDropBehavior
    {
        public static bool GetIsEnabled(Thumb thumb)
        {
            return (bool)thumb.GetValue(IsEnabledProperty);
        }

        public static void SetIsEnabled(Thumb thumb, bool value)
        {
            thumb.SetValue(IsEnabledProperty, value);
        }

        public static readonly DependencyProperty IsEnabledProperty =
            DependencyProperty.RegisterAttached(
                "IsEnabled", typeof (bool), typeof (DragAndDropBehavior),
                new UIPropertyMetadata(false, OnIsEnabledChanged));

        static void OnIsEnabledChanged(DependencyObject depObj, 
                                       DependencyPropertyChangedEventArgs e)
        {
            Thumb thumb = depObj as Thumb;
            if (thumb == null)
            {
                return;
            }

            if (e.NewValue is bool == false)
            {
                return;
            }

            bool isEnabled = (bool)e.NewValue;

            if (isEnabled)
            {
                thumb.DragStarted += ThumbDragStarted;
                thumb.DragDelta += ThumbDragDelta;
                thumb.DragCompleted += ThumbDragCompleted;
            }
            else
            {
                thumb.DragStarted -= ThumbDragStarted;
                thumb.DragDelta -= ThumbDragDelta;
                thumb.DragCompleted -= ThumbDragCompleted;
            }
        }
        static void ThumbDragStarted(object sender, DragStartedEventArgs e)
        {
            Thumb thumb = sender as Thumb;
            if (thumb == null)
            {
                return;
            }
            thumb.Cursor = Cursors.Hand;
        }

        static void ThumbDragCompleted(object sender, DragCompletedEventArgs e)
        {
            Thumb thumb = sender as Thumb;
            if (thumb == null)
            {
                return;
            }
            thumb.Cursor = Cursors.Arrow;
        }

        static void ThumbDragDelta(object sender, DragDeltaEventArgs e)
        {
            Thumb thumb = sender as Thumb;
            if (thumb == null)
            {
                return;
            }

            double left = (double)thumb.GetValue(Canvas.LeftProperty);
            double top = (double)thumb.GetValue(Canvas.TopProperty);

            thumb.SetValue(Canvas.LeftProperty, left + e.HorizontalChange);
            thumb.SetValue(Canvas.TopProperty, top + e.VerticalChange);
        }
    }
}

あとはXAML側からビヘイビアを指定するだけ。

<Window x:Class="DragAndDropSample.Views.MainView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:Behaviors="clr-namespace:DragAndDropSample.Behaviors"
    Title="Main Window" Height="480" Width="640">

    <Canvas>
        <Thumb Behaviors:DragAndDropBehavior.IsEnabled="true"
               Canvas.Left="100" Canvas.Top="100" Height="20" Width="20">
            <Thumb.Template>
                <ControlTemplate TargetType="Thumb">
                    <Rectangle Fill="Blue"/>
                </ControlTemplate>
            </Thumb.Template>
        </Thumb>
    </Canvas>
</Window>

これでドラッグアンドドロップの処理を再利用できるわけですね。