何となく Blog by Jitta
Microsoft .NET 考

目次

Blog 利用状況
  • 投稿数 - 591
  • 記事 - 18
  • コメント - 2182
  • トラックバック - 183
ニュース
  • 検索エンジンで来られた方へ:
    お望みの情報は見つかりましたか? よろしければ、コメント欄にどのような情報を探していたのか、ご記入ください。
It's ME!
  • はなおか じった
  • 世界遺産の近くに住んでます。
  • Microsoft MVP for Visual Developer ASP/ASP.NET 10, 2004 - 9, 2009
サイト内検索
広告

記事カテゴリ

書庫

日記カテゴリ

ギャラリ

その他

わんくま同盟

同郷

 

次のアプリケーションの提案がてら、興味本位で。

ドラッグ アンド ドロップについては、Control.DoDragDrop の説明などに書いてあるので、詳しいことは割愛。私が参考にしたのは、Drag&Drop(ドラッグ&ドロップ)を行う(DOBON.NET)

MSDN ライブラリや DOBON.NET には、ListBox に表示された項目の D&D について、書かれています。ここで行いたいのは、コントロールです。なので、違いを中心に書き留めます。


まず、準備。


// マウス ボタンを押し込んだところ。
private Point mouseDownPoint = Point.Empty;
// ドラッグするコントロールの親コントロール座標での、コントロールの配置座標とマウス座標の差。
private Point pickedPoint = Point.Empty;
// ドラッグするコントロールの大きさ。
private Size controlSize = Size.Empty;

pickedPoint と、controlSize が、追加したもの。コントロールは、描画されるオブジェクトの左上の座標を指定します。しかし、押し込むことができるのは、コントロール全体です。この、左上座標とマウス ボタンが押し込まれた座標との差を、保存します。controlSize は後述。

次、MouseDown イベント ハンドラ。


private void button1_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e) {
    Button btn = sender as Button;
    if (btn == null) { return; }
    if (e.Button == MouseButtons.Left) {
        mouseDownPoint = new Point(e.X, e.Y);
        Point sp = this.PointToClient(btn.PointToScreen(new Point(e.X, e.Y)));
        pickedPoint = new Point(btn.Left - sp.X, btn.Top - sp.Y);
    } else {
        mouseDownPoint = Point.Empty;
        pickedPoint = Point.Empty;
        controlSize = Size.Empty;
    }
}

とりあえず、ここでは Button を動かすことにしています。まぁ、この辺は適宜アレンジが必要ということで。

mouseDownPoint に、マウス ボタンを押し込んだ座標を格納するところは同じですが、その後に、ボタン上のクライアント座標からスクリーン座標に、そこからフォーム(ドラッグしたいコントロールの親コントロール)のクライアント座標に変換したものを、sp に入れています。その座標を、コントロールの左上座標からの差を計算し、pickedPoint に格納しています。あれ?e.X, e.Y をそのまま持っていて、後で補正すればいいのでは?

次、MouseMove イベント ハンドラ。


private void button1_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e) {
    if (mouseDownPoint == Point.Empty) { return; }
    Rectangle moveRect = new Rectangle(
        mouseDownPoint.X - SystemInformation.DragSize.Width / 2,
        mouseDownPoint.Y - SystemInformation.DragSize.Height / 2,
        SystemInformation.DragSize.Width,
        SystemInformation.DragSize.Height);
    if (moveRect.Contains(e.X, e.Y)) { return; }
    mouseDownPoint = Point.Empty;
    Button btn = sender as Button;
    controlSize = new Size(btn.Width, btn.Height);
    DragDropEffects dde = btn.DoDragDrop(btn, DragDropEffects.Move);
    if (beforeRect != Rectangle.Empty) {
        // フォームの外にドロップされたときも、トラッカーを消す
        ControlPaint.DrawReversibleFrame(beforeRect, Color.Black, FrameStyle.Dashed);
        beforeRect = Rectangle.Empty;
    }
    btn.Refresh();
}

同じく前半の説明は省略。btn 以降。

controlSize に、ドラッグ対象のコントロールの Width, Height を保存します。これを、トラッカーに使います。「トラッカー」は、ここでは「ドラッグ中に移動対象の大きさを示す枠」の意味で使います。「トラックレクト」とか、「ラバーバンド」とか、そんな言葉が使われるかもしれません。

ここで、DoDragDrop メソッドにはまりました。これ、ドラッグ開始をマークして、すぐに抜けてくるんだと思っていました。しかし、ドラッグ中はこの中から他のイベント ハンドラが呼び出されています。そのため、このメソッドから抜けてきた後に、トラッカーを消す処理を入れています。また、対象のコントロール上にトラッカーの跡が残るので、リフレッシュさせています。

次、あまり説明するもののない、MouseUp, DragOver, DragEnter イベント ハンドラ。


private void button1_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e) {
    mouseDownPoint = Point.Empty;
    pickedPoint = Point.Empty;
    controlSize = Size.Empty;
}

private void Form1_DragEnter(object sender, System.Windows.Forms.DragEventArgs e) {
    if (e.Data.GetDataPresent(typeof(Button))) {
        e.Effect = DragDropEffects.Move;
    } else {
        e.Effect = DragDropEffects.None;
    }
}

private void Form1_DragOver(object sender, System.Windows.Forms.DragEventArgs e) {
    if (e.Data.GetDataPresent(typeof(Button))) {
        e.Effect = DragDropEffects.Move;
    } else {
        e.Effect = DragDropEffects.None;
    }
}

見てわかるように、MouseUp はドラッグ対象コントロールのイベントですが、DragOver, DragEnter は、ドロップが実行されるコントロールのイベントです。

次、ドロップが実行される、DragDrop イベント ハンドラ。


private void Form1_DragDrop(object sender, System.Windows.Forms.DragEventArgs e) {
    if (e.Data.GetDataPresent(typeof(Button))) {
        Button target = e.Data.GetData(typeof(Button)) as Button;
        if (target == null) {
            e.Effect = DragDropEffects.None;
            return;
        }
        ControlPaint.DrawReversibleFrame(beforeRect, Color.White, FrameStyle.Dashed);
        beforeRect = Rectangle.Empty;
        Point newPoint = this.PointToClient(new Point(e.X, e.Y));
        target.Left = newPoint.X + pickedPoint.X;
        target.Top = newPoint.Y + pickedPoint.Y;
        mouseDownPoint = Point.Empty;
        pickedPoint = Point.Empty;
        controlSize = Size.Empty;
    } else {
        e.Effect = DragDropEffects.None;
    }
}

ListBox の項目を移動させる時、項目を引いて足したのと同じようなことを行います。ここでは、コントロールの新しい場所を指定しています。また、トラッカーの表示などに使った変数をクリアします。

最後、トラッカーを表示するための QueryContinueDrag イベント ハンドラ。


private Rectangle beforeRect = Rectangle.Empty;
private void button1_QueryContinueDrag(object sender, System.Windows.Forms.QueryContinueDragEventArgs e) {
    if ((e.KeyState & 2) == 2) {
        e.Action = DragAction.Cancel;
    }
    // トラッカー表示
    if (controlSize == Size.Empty) { return; }
    Point loc = new Point(Control.MousePosition.X + pickedPoint.X, Control.MousePosition.Y + pickedPoint.Y);
    Rectangle rect = new Rectangle(loc, controlSize);
    if (beforeRect.Equals(rect)) { return; }
    if (beforeRect != Rectangle.Empty) {
        ControlPaint.DrawReversibleFrame(beforeRect, Color.Black, FrameStyle.Dashed);
    }
    ControlPaint.DrawReversibleFrame(rect, Color.White, FrameStyle.Dashed);
    beforeRect = new Rectangle(rect.Location, rect.Size);
}

rect には、今ドラッグを終了するとどこにコントロールが置かれるかを示す範囲情報が入ります。beforeRect は、描いたトラッカーを消すために、前回表示した範囲情報を入れます。loc は、マウス カーソルの現在位置から pickedPoint の補正を加えた、コントロールの Left, Top 情報が入ります。Rectangle は値型なので、メンバを変更することができません。そのため、毎回 new します。


わかっている問題

  • 時々、トラッカーが残る。

  • ドラッグ中、コントロール上にトラッカーが残る。

投稿日時 : 2008年2月20日 22:26
コメント
No comments posted yet.
タイトル  
名前  
Url
コメント