Drag and Drop Rows of Datagrid View Winform C#

c#, datagridview

Solution

I much prefer using mouse events over actual drag&drop events for this when doing d&d within an application.

1 - Unbound example

Here is a simple example that uses the mouse events to drag rows around while diplaying one `Cell` value in a `Label`.

It inserts the row after the one you drop it on.

The `dragLabel` part is optional..

First we declare two helper variables at class level:

int dragRow = -1;
Label dragLabel = null;

Then we code the `CellMouseDown` to prepare the `Label`..:

private void dataGridView1_CellMouseDown(object sender,DataGridViewCellMouseEventArgs e)
{
    if (e.ColumnIndex<0 || e.RowIndex < 0) return;
    dragRow = e.RowIndex;
    if (dragLabel == null) dragLabel = new Label();
    dragLabel.Text = dataGridView1[e.ColumnIndex, e.RowIndex].Value.ToString();
    dragLabel.Parent = dataGridView1;
    dragLabel.Location = e.Location;
}

In the `MouseMove` we just move the Label. But as the `MouseDown` usually starts a cell selection we clear the selection..

private void dataGridView1_MouseMove(object sender, MouseEventArgs e)
{
    if (e.Button == MouseButtons.Left && dragLabel != null)
    {
        dragLabel.Location = e.Location;
        dataGridView1.ClearSelection();
    }
}

The real work is in the `MouseUp`. We need a `HitTest` to find out which, if any, row we are on. We also need to calculate the target row depending on the direction of the drag.

Finally we clean up the `Label`..

private void dataGridView1_MouseUp(object sender, MouseEventArgs e)
{
    var hit = dataGridView1.HitTest(e.X,e.Y);
    int dropRow = -1;
    if (hit.Type != DataGridViewHitTestType.None)
    {
        dropRow =   hit.RowIndex;
        if (dragRow >= 0 )
        {
            int tgtRow = dropRow + (dragRow > dropRow ? 1 : 0);
            if (tgtRow != dragRow)
            {
                DataGridViewRow row = dataGridView1.Rows[dragRow];
                dataGridView1.Rows.Remove(row);
                dataGridView1.Rows.Insert(tgtRow, row);

                dataGridView1.ClearSelection();
                row.Selected = true;
            }
        }
    }
    else  { dataGridView1.Rows[dragRow].Selected = true;}

    if (dragLabel != null)
    {
        dragLabel.Dispose();
        dragLabel = null;
    }
}

A few notes:

Make sure no row is new or modified or you can't move it. It took me awhile to realize that I had to switch off `AllowUserToAddRows` before adding the rows initially..

The treatment of selections is up to you. I chose to select the dragged row if the drop fails. And I chose to let it fail when dragging into the empty space.

Update:

2 - DataBound example

If the DGV is data-bound you can't move the rows directly. Instead you need to move the rows in the `DataSource`.

Let's assume you have a `DataTable DT`. Then you can remove rows from it and insert rows at any position. (Quite different from SQL DBMS tables!)

Note that a `DataRow` loses its values once removed from the `DataTable`. Therefore we need to clone the values before we remove the row. Other than that the code is pretty much the same. Simply replace the innermost condition with this:

if (tgtRow != dragRow)
{
    DataRow dtRow = DT.Rows[dragRow];
    DataRow newRow = DT.NewRow();
    newRow.ItemArray = DT.Rows[dragRow].ItemArray; // we need to clone the values

    DT.Rows.Remove(dtRow);
    DT.Rows.InsertAt(newRow, tgtRow);
    dataGridView1.Refresh();
    dataGridView1.Rows[tgtRow].Selected = true;
}

Problem

I want to Drag Rows from certain position to another position in same grid view. The Other rows should automatically adjust according to the Drag and Drop. Thank You

Original source