次のアプリケーションの提案がてら、興味本位で。
ドラッグ アンド ドロップについては、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 に格納しています。
次、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