WPFでコントロールをドラッグ(1)

Thumbコントロールでドラッグ
http://www.moonmile.net/blog/archives/698

で、スクロールバーで使われる Thumb コントロールを使ってドラッグを試してみましたが、このままでは単純な四角(決められたスタイル)しかドラッグできません。
Thum コントロールを少し工夫して、色々な形のコントロールも移動ができるようにする、ことは可能だとは思うのですが、いささか面倒です。つーか、もともとやりたいことは、普通のコントロールのドラッグ、あとはコントロールを配置してマウスで移動、なんてのを想定しているので、Thumb コントロールでは都合が悪いのです。コードの見通は悪くないんだけどね、XAMLが奇妙な感じになりそう。

そんな訳で、普通のコントロール(Ellipseコントロール)を移動させてみます。

■Window1.xaml

<Window x:Class="SampleDrag.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="400">
    <Canvas Name="board">
        <TextBlock Canvas.Left="0" Canvas.Top="0" Height="21" Name="textPos" Width="119" Text="x:0 y:0" />
        <Ellipse Name="mark0" Canvas.Left="28" Canvas.Top="43"
                 Height="30" Stroke="Black" Width="30" Fill="Pink"
                 MouseLeftButtonDown="mark0_MouseLeftButtonDown"
                 MouseLeftButtonUp="mark0_MouseLeftButtonUp"
                 MouseMove="mark0_MouseMove"
                 />
    </Canvas>
</Window>

■Windows1.xaml.cs

/// <summary>
/// Window1.xaml の相互作用ロジック
/// </summary>
public partial class Window1 : Window
{
    public Window1()
    {
        InitializeComponent();
    }

    private void printPos( UIElement el)
    {
        int x = (int)Canvas.GetLeft( el );
        int y = (int)Canvas.GetTop( el );
        textPos.Text = string.Format("x:{0} y:{1}", x, y);
    }

    private bool _isDrag = false;
    private Point _dragOffset;

    /// <summary>
    /// ドラッグ開始
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void mark0_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        UIElement el = sender as UIElement;
        if (el != null)
        {
            _isDrag = true;
            _dragOffset = e.GetPosition(el);
            el.CaptureMouse();
        }
    }

    /// <summary>
    /// ドラッグ終了
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void mark0_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
    {
        if (_isDrag == true)
        {
            UIElement el = sender as UIElement;
            el.ReleaseMouseCapture();
            _isDrag = false;
        }
    }

    /// <summary>
    /// ドラック中
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void mark0_MouseMove(object sender, MouseEventArgs e)
    {
        if (_isDrag == true)
        {
            Point pt = Mouse.GetPosition(board);
            UIElement el = sender as UIElement;
            Canvas.SetLeft(el, pt.X - _dragOffset.X);
            Canvas.SetTop(el, pt.Y - _dragOffset.Y);
            printPos(el);
        }
    }
}

ざっと説明すると、

1.移動したい XAML のコントロールに3つのイベントを付けます。

 MouseLeftButtonDown="mark0_MouseLeftButtonDown"
 MouseLeftButtonUp="mark0_MouseLeftButtonUp"
 MouseMove="mark0_MouseMove"

マウスの左ボタンを押した時と離した時、そして移動しているときです。

このそれぞれのイベントに、ドラッグの処理を入れていきます。

private void mark0_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    UIElement el = sender as UIElement;
    if (el != null)
    {
        _isDrag = true;
        _dragOffset = e.GetPosition(el);
        el.CaptureMouse();
    }
}

左ボタンを押した時にドラッグを開始させます。
このとき、ドラッグ中のフラグ(_isDrag)を設定することと、マウスの相対位置(ドラッグするコントロールの左上からの相対位置)を e.GetPosition で取得して _dragOffset に保存しておきます。
これらはドラッグ中の時の、コントロールの移動に使います。

el.CaptureMouse() は、マウスコントロールをキャプチャするためのメソッドです。これがないと、マウスを素早く動かしたときに追随できません。

private void mark0_MouseMove(object sender, MouseEventArgs e)
{
    if (_isDrag == true)
    {
        Point pt = Mouse.GetPosition(board);
        UIElement el = sender as UIElement;
        Canvas.SetLeft(el, pt.X - _dragOffset.X);
        Canvas.SetTop(el, pt.Y - _dragOffset.Y);
        printPos(el);
    }
}

マウスを動かしているときのイベントでは、ドラック中かどうかを調べます。これは、コントロール上でマウスを動かしたときでも、このイベントが発生するためです。

移動するコントロールの位置は、Mouse.GetPosition でボタンの位置を取得して、先ほど保存しておいた _dragOffset だけシフトさせます。board は、ドラッグするコントロールが乗っている canvas コントロールの名前です。

canvas に対する位置の設定は、Canvas.SetLeft と Canvas.SetTop を使います。
このあたり、本来は拡大率を計算する必要があるのですが、通常ドラッグは等倍で行われるので、簡単のためそのまま計算します。

private void mark0_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
    if (_isDrag == true)
    {
        UIElement el = sender as UIElement;
        el.ReleaseMouseCapture();
        _isDrag = false;
    }
}

そして、マウスを離したときは、ドラッグ中のフラグ _isDrag を false にして、マウスのキャプチャを ReleaseMouseCapture メソッドで解放します。

これを実行すると

<001>
20100325_01

から

<002>
20100325_02

のようにマウスでドラッグできます。

Silverlight版は
http://moonmile.net/sl/SampleDrag/ 
で実行できます。
さて、コントロールを移動することができました。
しかし、このままでは xaml にいちいちイベントを追加しないと駄目ですよね。複数のコントロールを動かしたい場合は、大変なことになりそうだし、新しいコントロールを動的に追加したときなんか、どうやるんだろう? ってこといなりそうですね。

というわけで、今回はイベントを xaml にイベントを直に書きましたが、次回はこれを動的に追加できるようにします。

カテゴリー: 開発 パーマリンク

WPFでコントロールをドラッグ(1) への4件のフィードバック

  1. CoMo のコメント:

    エントリで否定されたThumbを使った場合のサンプルを書いてみました。自由度とのトレードオフでしょうが、Templateである程度カスタマイズできるのと、イベント処理も簡単になるので私はけっこうThumbも使い回しが効きそうに思いました。

    • masuda のコメント:

      そうそう、この記事を書いた後に Thumb を使ってテンプレートを使えば十分じゃん、ということに気づきました。で、追加しようと思って、そのままになっているのであります。。。申し訳ない。

      ただ、この時に調査している目的が、ドラッグ可能なコントロールを動的に追加」だったので、Thumb コントロールを使って、「動的に追加できるか?」が問題なんですよね。
      というところで止まっていたりします。

  2. elektrownie wiatrowe のコメント:

    Whats Going down i’m new to this, I stumbled upon this I have found It absolutely helpful and it has helped me out loads. I hope to give a contribution & assist other users like its aided me. Great job.

コメントは停止中です。