DataGridViewのドラッグ&ドロップでデータ(行)を移動させたい
質問
2007年12月25日火曜日 6:28
のですがどうしたらいいでしょうか。
具体的に3つの事がしたいです。
1.DataGridViewからDataGridViewに行ごとドラッグ&ドロップ。
2.DataGridView内でドラッグ&ドロップで並び替え。
3.ドロップしたいロケーションを赤線で表示。
DataGridViewのSelectionModeはFullRowSelectです。
いろいろなサイト等で調べていて、特に英語のサイトでは多くのこれに関する
サンプルがあるのですが、実現できていません。(サンプルは並び替えです)
private Rectangle dragBoxFromMouseDown;
private int rowIndexFromMouseDown;
private int rowIndexOfItemUnderMouseToDrop;
private void dataGridView1_MouseMove(object sender, MouseEventArgs e)
{
if ((e.Button & MouseButtons.Left) == MouseButtons.Left)
{
if (dragBoxFromMouseDown != Rectangle.Empty && !dragBoxFromMouseDown.Contains(e.X, e.Y))
{
DragDropEffects dropEffect = dataGridView1.DoDragDrop(dataGridView1.Rows[rowIndexFromMouseDown], DragDropEffects.Move);
}
}
}
private void dataGridView1_MouseDown(object sender, MouseEventArgs e)
{
rowIndexFromMouseDown = dataGridView1.HitTest(e.X, e.Y).RowIndex;
Size dragsize = SystemInformation.DragSize;
dragBoxFromMouseDown = new Rectangle(new Point(e.X - (dragsize.Width / 2),
e.Y - (dragsize.Height / 2)), dragsize);
}
private void dataGridView1_DragOver(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Move;
}
private void dataGridView1_DragDrop(object sender, DragEventArgs e)
{
Point clientPoint = dataGridView1.PointToClient(new Point(e.X, e.Y));
rowIndexOfItemUnderMouseToDrop = dataGridView1.HitTest(clientPoint.X, clientPoint.Y).RowIndex;
if (e.Effect == DragDropEffects.Move)
{
DataGridViewRow rowToMove = e.Data.GetData(typeof(DataGridViewRow)) as DataGridViewRow;
dataGridView1.Rows.RemoveAt(rowIndexFromMouseDown);
dataGridView1.Rows.Insert(rowIndexOfItemUnderMouseToDrop, rowToMove);
}
}
ただコピーしただけなので、うまくいかなかった時に修正する方法がまだわかっていません。
1,2,3を実現しようとするとかなりのコードになると思うのですが、よろしくお願いします!
すべての返信 (6)
2007年12月25日火曜日 10:04 ✅回答済み
部分的ですが、以下が参考になると思います。
DataGridView上でレコードの順番をマウスで変更したい・・・
http://forums.microsoft.com/MSDN-JA/ShowPost.aspx?PostID=403655&SiteID=7
2008年1月10日木曜日 5:21 ✅回答済み
古い話ですが、もう一つのスレッドと関連してるので返信します。
KentaroM さんからの引用 | |
|
1 の件は、ドロップが完了したときに dragdropcurrentIndex を -1 に戻して再描画すればいいです。
2 の件は、マーカー位置の変更が必要な時だけ、再描画させるようにすればいいです。
ドラッグ&ドロップはしたことがなかったので、独自バージョンを試しに作ってみました。
試してみてください。
上記の問題は下のコードにおいて、
1 の件は DragDrop イベントハンドラ内で _DropDestinationIsValid = false(と、Invalidate)、
2 の件は DragOver イベントハンドラ内での needRedraw の決定、
がそれぞれ該当します。
それと、グリッド全体を Invalidate するのではなく、InvalidateRow にて必要な Row のみを再描画させてます。
コード ブロック
private void Form1_Load(object sender, EventArgs e)
{
// テストの準備(Form1 には DataGridView が一つ必要)
DataTable table = new DataTable();
table.Columns.Add("Col1", typeof(string));
table.Columns.Add("Col2", typeof(string));
for (int i = 1; i <= 5; i;;)
table.Rows.Add(i.ToString() ; "-1", i.ToString() ; "-2");
dataGridView1.DataSource = table;
foreach (DataGridViewColumn col in dataGridView1.Columns)
col.SortMode = DataGridViewColumnSortMode.NotSortable;
dataGridView1.AllowDrop = true;
}
// Cell 上で Drag を始めたのか、
// 列幅変更時の Drag で Cell 領域に入ったのかを区別するためのフラグ
private int _OwnBeginGrabRowIndex = -1;
private void dataGridView1_MouseDown(object sender, MouseEventArgs e)
{
_OwnBeginGrabRowIndex = -1;
if ((e.Button & MouseButtons.Left) != MouseButtons.Left) return;
DataGridView.HitTestInfo hit = dataGridView1.HitTest(e.X, e.Y);
if (hit.Type != DataGridViewHitTestType.Cell) return;
// クリック時などは -1 に戻らないが問題なし
_OwnBeginGrabRowIndex = hit.RowIndex;
}
private void dataGridView1_MouseMove(object sender, MouseEventArgs e)
{
if ((e.Button & MouseButtons.Left) != MouseButtons.Left) return;
if (_OwnBeginGrabRowIndex == -1) return;
// ドラッグ&ドロップの開始
dataGridView1.DoDragDrop(_OwnBeginGrabRowIndex, DragDropEffects.Move);
}
private bool _DropDestinationIsValid;
private int _DropDestinationRowIndex;
private bool _DropDestinationIsNextRow;
private void dataGridView1_DragOver(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Move;
int from, to; bool next;
bool valid = DecideDropDestinationRowIndex(
dataGridView1, e, out from, out to, out next);
// ドロップ先マーカーの表示・非表示の制御
bool needRedraw = (valid != _DropDestinationIsValid);
if (valid)
{
needRedraw = needRedraw
|| (to != _DropDestinationRowIndex)
|| (next != _DropDestinationIsNextRow);
}
if (needRedraw)
{
if (_DropDestinationIsValid)
dataGridView1.InvalidateRow(_DropDestinationRowIndex);
if (valid)
dataGridView1.InvalidateRow(to);
}
_DropDestinationIsValid = valid;
_DropDestinationRowIndex = to;
_DropDestinationIsNextRow = next;
}
private void dataGridView1_DragLeave(object sender, EventArgs e)
{
if (_DropDestinationIsValid)
{
_DropDestinationIsValid = false;
dataGridView1.InvalidateRow(_DropDestinationRowIndex);
}
}
private void dataGridView1_DragDrop(object sender, DragEventArgs e)
{
int from, to; bool next;
if (!DecideDropDestinationRowIndex(
dataGridView1, e, out from, out to, out next))
return;
_DropDestinationIsValid = false;
// データの移動
to = MoveDataValue(from, to, next);
dataGridView1.CurrentCell =
dataGridView1[dataGridView1.CurrentCell.ColumnIndex, to];
dataGridView1.Invalidate();
}
private void dataGridView1_RowPostPaint(
object sender, DataGridViewRowPostPaintEventArgs e)
{
// ドロップ先のマーカーを描画
if (_DropDestinationIsValid
&& e.RowIndex == _DropDestinationRowIndex)
{
using (Pen pen = new Pen(Color.Red, 4))
{
int y =
!_DropDestinationIsNextRow
? e.RowBounds.Y ; 2 : e.RowBounds.Bottom - 2;
e.Graphics.DrawLine(
pen, e.RowBounds.X, y, e.RowBounds.X ; 50, y);
}
}
}
// ドロップ先の行の決定
private bool DecideDropDestinationRowIndex(
DataGridView grid, DragEventArgs e,
out int from, out int to, out bool next)
{
from = (int)e.Data.GetData(typeof(int));
// 元の行が追加用の行であれば、常に false
if (grid.NewRowIndex != -1 && grid.NewRowIndex == from)
{
to = 0; next = false;
return false;
}
Point clientPoint = grid.PointToClient(new Point(e.X, e.Y));
// 上下のみに着目するため、横方向は無視する
clientPoint.X = 1;
DataGridView.HitTestInfo hit =
grid.HitTest(clientPoint.X, clientPoint.Y);
to = hit.RowIndex;
if (to == -1)
{
int top = grid.ColumnHeadersVisible ? grid.ColumnHeadersHeight : 0;
top ;= 1; // ...
if (top > clientPoint.Y)
// ヘッダへのドロップ時は表示中の先頭行とする
to = grid.FirstDisplayedCell.RowIndex;
else
// 最終行へ
to = grid.Rows.Count - 1;
}
// 追加用の行は無視
if (to == grid.NewRowIndex) to--;
next = (to > from);
return (from != to);
}
// データの移動
private int MoveDataValue(int from, int to, bool next)
{
DataTable table = (DataTable)dataGridView1.DataSource;
// 移動するデータの退避(計算列があればたぶんダメ)
object[] rowData = table.Rows[from].ItemArray;
DataRow row = table.NewRow();
row.ItemArray = rowData;
// 移動元から削除
table.Rows.RemoveAt(from);
if (to > from) to--;
// 移動先へ追加
if (next) to;;;
if (to <= table.Rows.Count)
table.Rows.InsertAt(row, to);
else
table.Rows.Add(row);
return table.Rows.IndexOf(row);
}
2007年12月25日火曜日 9:05
その後の調査で、以下のリンクのサンプルを完全にコピーしました。
が、ドラッグまでは出来たのですが、最後目的地にドロップが出来ず、選択したアイテムが一行ずつ消えていって
しまいます。
dataGridView1.Rows.Insert(rowIndexOfItemUnderMouseToDrop, rowToMove);
がうまくいっていないみたいです。。。
2007年12月26日水曜日 10:03
trapemiyaさんありがとうございました。
お蔭様で、DataGridView内の並び替えは見事にできました。コードは以下になります。
private Rectangle dragBoxFromMouseDown;
private int rowIndexFromMouseDown;
private int rowIndexOfItemUnderMouseToDrop;
private void dataGridView1_MouseMove(object sender, MouseEventArgs e)
{
if ((e.Button & MouseButtons.Left) == MouseButtons.Left)
{
// If the mouse moves outside the rectangle, start the drag.
if (dragBoxFromMouseDown != Rectangle.Empty &&
!dragBoxFromMouseDown.Contains(e.X, e.Y))
{
// Proceed with the drag and drop, passing in the list item.
DragDropEffects dropEffect = dataGridView1.DoDragDrop(
dataGridView1.Rows[rowIndexFromMouseDown],
DragDropEffects.Move);
}
}
}
private void dataGridView1_MouseDown(object sender, MouseEventArgs e)
{
// Get the index of the item the mouse is below.
rowIndexFromMouseDown = dataGridView1.HitTest(e.X, e.Y).RowIndex;
if (rowIndexFromMouseDown != -1)
{
// Remember the point where the mouse down occurred.
// The DragSize indicates the size that the mouse can move
// before a drag event should be started.
Size dragSize = SystemInformation.DragSize;
// Create a rectangle using the DragSize, with the mouse position being
// at the center of the rectangle.
dragBoxFromMouseDown = new Rectangle(new Point(e.X - (dragSize.Width / 2),
e.Y - (dragSize.Height / 2)),
dragSize);
}
else
// Reset the rectangle if the mouse is not over an item in the ListBox.
dragBoxFromMouseDown = Rectangle.Empty;
}
private void dataGridView1_DragOver(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Move;
}
private void dataGridView1_DragDrop(object sender, DragEventArgs e)
{
// The mouse locations are relative to the screen, so they must be
// converted to client coordinates.
Point clientPoint = dataGridView1.PointToClient(new Point(e.X, e.Y));
// Get the row index of the item the mouse is below.
rowIndexOfItemUnderMouseToDrop =
dataGridView1.HitTest(clientPoint.X, clientPoint.Y).RowIndex;
// If the drag operation was a move then remove and insert the row.
if (e.Effect == DragDropEffects.Move)
{
object[] rowArray = dataSet1.player_t.Rows[rowIndexFromMouseDown].ItemArray;
DataRow row = dataSet1.player_t.NewRow();
row.ItemArray = rowArray;
dataSet1.player_t.Rows.RemoveAt(rowIndexFromMouseDown);
dataSet1.player_t.Rows.InsertAt(row, rowIndexOfItemUnderMouseToDrop);
}
}
DragDrop内のイベントを緑色の部分に変えて無事ドロップ出来ました。
次に、目的地点に赤線を表示してドロップしやすいようにしたいのですが、
サンプルコードをやったのですが、うまく表示されません。コードは以下です。
private void dataGridView1_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
{
if (e.RowIndex == rowIndexFromMouseDown && rowIndexFromMouseDown < dataGridView1.RowCount -1)
{
Pen p = new Pen(Color.Red, 2);
e.Graphics.DrawLine(p, e.CellBounds.Left, e.CellBounds.Top -1,
e.CellBounds.Right, e.CellBounds.Top -1);
}
}
赤線は出るのですが、目的地ではなく、出発地点に線が引かれ、ドロップした後も線は消えないので、
失敗している様です。このあたりも、e.Graphics.DrawLineメソッドを理解していないので、柔軟に修正が
出来ていないようです。
2007年12月27日木曜日 7:06
その後、USのフォーラムでアドバイスをいただき、dataGridView1.Invalidate()を追加する
という事がわかりました。
その人が言うにはDragDropイベントに追加とあったのですが、自分のケースではうまく
動作せず、DragOverイベントでドラッグしてマウスを移動するとそのドロップする候補の
地点が赤線になるというコードです。
private Rectangle dragBoxFromMouseDown;
private int rowIndexFromMouseDown;
private int rowIndexOfItemUnderMouseToDrop;
private void dataGridView1_MouseMove(object sender, MouseEventArgs e)
{
if ((e.Button & MouseButtons.Left) == MouseButtons.Left)
{
// If the mouse moves outside the rectangle, start the drag.
if (dragBoxFromMouseDown != Rectangle.Empty &&
!dragBoxFromMouseDown.Contains(e.X, e.Y))
{
// Proceed with the drag and drop, passing in the list item.
DragDropEffects dropEffect = dataGridView1.DoDragDrop(
dataGridView1.Rows[rowIndexFromMouseDown],
DragDropEffects.Move);
}
}
}
private void dataGridView1_MouseDown(object sender, MouseEventArgs e)
{
// Get the index of the item the mouse is below.
rowIndexFromMouseDown = dataGridView1.HitTest(e.X, e.Y).RowIndex;
if (rowIndexFromMouseDown != -1)
{
// Remember the point where the mouse down occurred.
// The DragSize indicates the size that the mouse can move
// before a drag event should be started.
Size dragSize = SystemInformation.DragSize;
// Create a rectangle using the DragSize, with the mouse position being
// at the center of the rectangle.
dragBoxFromMouseDown = new Rectangle(new Point(e.X - (dragSize.Width / 2),
e.Y - (dragSize.Height / 2)),
dragSize);
}
else
// Reset the rectangle if the mouse is not over an item in the ListBox.
dragBoxFromMouseDown = Rectangle.Empty;
}
int dragdropcurrentIndex = -1;
private void dataGridView1_DragOver(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Move;
dragdropcurrentIndex = dataGridView1.HitTest(dataGridView1.PointToClient(new Point(e.X, e.Y)).X,
dataGridView1.PointToClient(new Point(e.X, e.Y)).Y).RowIndex;
dataGridView1.Invalidate();
}
private void dataGridView1_DragDrop(object sender, DragEventArgs e)
{
// The mouse locations are relative to the screen, so they must be
// converted to client coordinates.
Point clientPoint = dataGridView1.PointToClient(new Point(e.X, e.Y));
// Get the row index of the item the mouse is below.
rowIndexOfItemUnderMouseToDrop =
dataGridView1.HitTest(clientPoint.X, clientPoint.Y).RowIndex;
// If the drag operation was a move then remove and insert the row.
if (e.Effect == DragDropEffects.Move)
{
object[] rowArray = dataSet1.player_t.Rows[rowIndexFromMouseDown].ItemArray;
DataRow row = dataSet1.player_t.NewRow();
row.ItemArray = rowArray;
dataSet1.player_t.Rows.RemoveAt(rowIndexFromMouseDown);
dataSet1.player_t.Rows.InsertAt(row, rowIndexOfItemUnderMouseToDrop);
}
}
private void dataGridView1_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
{
if (e.RowIndex == dragdropcurrentIndex && dragdropcurrentIndex < dataGridView1.RowCount -1)
{
Pen p = new Pen(Color.Red, 2);
e.Graphics.DrawLine(p, e.CellBounds.Left, e.CellBounds.Top -1,
e.CellBounds.Right, e.CellBounds.Top -1);
}
}
ここまで来ました。あと1つの問題と、新たな問題が1つできました。
1.ドロップした後も赤線が消えずに残ったままである。
2.(新しい問題点)ドラッグ中にDataGridViewがちらつく。(その時に確かではないが、HDがうなっている
気がする・・・)
2008年1月16日水曜日 7:34
こんにちは。中川俊輔 です。
trapemiyaさん、TH01さん、大変参考になる回答ありがとうございます。
KentaroMさんへ
問題が解決されたようなのでtrapemiyaさん、TH01さんの回答へ回答済みチェックをつけさせていただきました。
回答済みチェックが付くことにより、有用な情報を探している方が情報を見つけやすくなります。
問題解決につながる回答があった場合は、なるべく回答済みボタンを押してチェックを付けてください。
KentaroMさんはチェックを解除することもできますので、ご確認ください。
それでは!