C#のWPFでドラッグできるコントロールを作る

コントロールをドラッグさせる場合、Thumbコントロールを使うのがよさそう。

Thumbコントロールは以下のようなドラッグに関するイベントを持っている。

 DragStartedEvent  ・・・ ドラッグが開始された時

 DragCompletedEvent ・・・ ドラッグが終了した時

 DragDeltaEvent   ・・・ ドラッグでマウスが動いた時

 

 

Thumb自体は四角形の見た目だが、Templateを使ってカスタマイズしてしまえば色々な物が作れそう。

以下の例では、キャンバス内を自由にドラッグ移動できる画像を作ってみた。

XAML

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApplication1"
        mc:Ignorable="d"
        Title="MainWindow" Height="250" Width="250">
    <Canvas Margin="10" Background="AliceBlue">
        <Thumb Width="50" Height="50"
               Canvas.Left="0"
               Canvas.Top="0"
               DragStarted="Thumb_DragStarted"
               DragCompleted="Thumb_DragCompleted"
               DragDelta="Thumb_DragDelta">
            <Thumb.Template>
                <ControlTemplate>
                    <Border x:Name="Thumb_Border" BorderBrush="Red" BorderThickness="0">
                        <Image Source="testimage.jpg"/>
                    </Border>
                </ControlTemplate>
            </Thumb.Template>
        </Thumb>
    </Canvas>
</Window>

Canvasの中にThumbコントロールを作成 (10~14行目)

Thumbコントロールを配置して、DragStarted,DragCompleted,DragDeltaイベントを追加。

 

 

ControlTemplateでThumbをカスタマイズ (16~22行目)

Borderの中にImageを配置している。

ドラッグ中のみ赤枠を出すようコード側で制御する為、Borderに名前を付けている。

 

 

 

 

コード

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Thumb_DragStarted(object sender, DragStartedEventArgs e)
        {
            var thumb = sender as Thumb;
            if (null != thumb)
            {
                var border = thumb.Template.FindName("Thumb_Border", thumb) as Border;
                if (null != border)
                {
                    border.BorderThickness = new Thickness(1);
                }
            }
        }

        private void Thumb_DragCompleted(object sender, DragCompletedEventArgs e)
        {
            var thumb = sender as Thumb;
            if (null != thumb)
            {
                var border = thumb.Template.FindName("Thumb_Border", thumb) as Border;
                if (null != border)
                {
                    border.BorderThickness = new Thickness(0);
                }
            }
        }

        private void Thumb_DragDelta(object sender, DragDeltaEventArgs e)
        {
            var thumb = sender as Thumb;
            if (null != thumb)
            {
                var x = Canvas.GetLeft(thumb) + e.HorizontalChange;
                var y = Canvas.GetTop(thumb) + e.VerticalChange;

                var canvas = thumb.Parent as Canvas;
                if (null != canvas)
                {
                    x = Math.Max(x, 0);
                    y = Math.Max(y, 0);
                    x = Math.Min(x, canvas.ActualWidth - thumb.ActualWidth);
                    y = Math.Min(y, canvas.ActualHeight - thumb.ActualHeight);
                }

                Canvas.SetLeft(thumb, x);
                Canvas.SetTop(thumb, y);
            }
        }
    }

DragStartedイベント (8~19行目) 

ドラッグ中のみBorderで赤枠を出すように制御している。

"Thumb_Border"という名前でTemplateの中を検索してBorderコントロールを取得。

BorderTicknessを1にして枠を表示している。

 

 

DragCompletedイベント (21~32行目) 

ドラッグ中のみBorderで赤枠を出すように制御している。

"Thumb_Border"という名前でTemplateの中を検索してBorderコントロールを取得。

BorderTicknessを0にして枠を非表示にしている。

 

 

DragDeltaイベント (34~54行目) 

DragDeltaEventArgsのHorizontalChange,VerticalChangeにドラッグの移動量が入っているので、

それを使ってThumbの位置を移動させている。

 

また、親要素のCanvasを取得して、Canvasの外に出ないように移動後の座標を制御している。

(42~49行目)