[ Custom Row and Column Drag and Drop Reordering Operations with the DataGridView ]

By: Daniel S. Soper


The source code below demonstrates how to extend the DataGridView class to allow for custom drag and drop reordering operations on both columns and rows. In this implementation, a red line is drawn on the DataGridView to indicate the target of the drag and drop operation.

Here are a few screenshots of this class in action:

Figure 1. A column drag and drop reordering in progress. The red line indicates the target of the drag and drop operation. Figure 2. The result of the column drag and drop operation within the DataGridView.
   
Figure 3. A row drag and drop reordering in progress. The red line indicates the target of the drag and drop operation. Figure 4. The result of the row drag and drop operation within the DataGridView.

Here's the C# source code... Only 385 lines (including comments)!

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Drawing;

namespace MyDataGridView
{
    /// 
    /// This class extends the DataGridView to allow for custom
    /// drag and drop reordering operations on both columns and rows.  
    /// The sequence of user interaction events occurs as follows: 
    /// 1. The user clicks (and releases) the left mouse button 
    /// on a row or column header cell to select the row or column. 
    /// 2. The user then clicks (and holds down) the left mouse 
    /// button to initiate a drag and drop operation which will allow 
    /// him/her to reorder the selected row or column within the 
    /// DataGridView. 
    /// 3. As the drag and drop operation begins, a horizontal (for 
    /// rows) or vertical (for columns) red line is displayed on the 
    /// DataGridView to indicate the target of the drag and drop operation 
    /// (i.e., to indicate where on the grid the selected row or column 
    /// will be dropped). 
    /// 4. When the user has selected the new target location for the 
    /// selected row/column, he/she releases the left mouse button, and 
    /// the appropriate reordering of columns or rows is carried out.
    /// ******************************************************************
    ///  AUTHOR: Daniel S. Soper
    ///     URL: http://www.danielsoper.com
    ///    DATE: 02 February 2007
    /// LICENSE: Public Domain. Enjoy!   :-)
    /// ******************************************************************
    /// 
    class MyDGV : DataGridView
    {
        //vars for custom column/row drag/drop operations
        private Rectangle DragDropRectangle;
        private int DragDropSourceIndex;
        private int DragDropTargetIndex;
        private int DragDropCurrentIndex = -1;
        private int DragDropType; //0=column, 1=row
        private DataGridViewColumn DragDropColumn;
        private object[] DragDropColumnCellValue;

        public MyDGV()
        {
            this.AllowUserToResizeRows = false;
            this.SelectionMode = DataGridViewSelectionMode.CellSelect;
            this.AllowUserToOrderColumns = false;
            this.AllowDrop = true;
            this.ColumnCount = 20;
            this.RowCount = 40;
            this.Size = new Size(600, 400);
        } //end default constructor

        protected override void OnColumnAdded(DataGridViewColumnEventArgs e)
        {
            //runs when a new column is added to the DGV
            e.Column.SortMode = DataGridViewColumnSortMode.Programmatic;
            e.Column.HeaderText = "column " + e.Column.Index;
            base.OnColumnAdded(e);
        } //end OnColumnAdded

        protected override void OnColumnHeaderMouseClick(DataGridViewCellMouseEventArgs e)
        {
            //runs when the mouse is clicked over a column header cell
            if (e.ColumnIndex > -1)
            {
                if (e.Button == MouseButtons.Left)
                {
                    //single-click left mouse button
                    if (this.SelectionMode != DataGridViewSelectionMode.ColumnHeaderSelect)
                    {
                        this.SelectionMode = DataGridViewSelectionMode.ColumnHeaderSelect;
                        this.Columns[e.ColumnIndex].Selected = true;
                    } //end if
                }
                else if (e.Button == MouseButtons.Right)
                {
                    //single-click right mouse button
                    if (this.SelectionMode != DataGridViewSelectionMode.ColumnHeaderSelect)
                    {
                        this.SelectionMode = DataGridViewSelectionMode.ColumnHeaderSelect;
                    } //end if
                    if (this.SelectedColumns.Count <= 1)
                    {
                        this.ClearSelection();
                        this.Columns[e.ColumnIndex].Selected = true;
                        //show column right-click menu here
                        MessageBox.Show("show column right-click menu");
                    }
                    else //more than one column is selected
                    {
                        //show column right-click menu here
                        MessageBox.Show("show column right-click menu");
                    } //end if
                } //end if
            } //end if
            base.OnColumnHeaderMouseClick(e);
        } //end OnColumnHeaderMouseClick

        protected override void OnRowHeaderMouseClick(DataGridViewCellMouseEventArgs e)
        {
            //runs when the mouse is clicked over a row header cell
            if (e.RowIndex > -1)
            {
                if (e.Button == MouseButtons.Left)
                {
                    //single-click left mouse button
                    if (this.SelectionMode != DataGridViewSelectionMode.RowHeaderSelect)
                    {
                        this.SelectionMode = DataGridViewSelectionMode.RowHeaderSelect;
                        this.Rows[e.RowIndex].Selected = true;
                        this.CurrentCell = this[0, e.RowIndex];
                    } //end if
                }
                else if (e.Button == MouseButtons.Right)
                {
                    //single-click right mouse button
                    if (this.SelectionMode != DataGridViewSelectionMode.RowHeaderSelect)
                    {
                        this.SelectionMode = DataGridViewSelectionMode.RowHeaderSelect;
                    } //end if 
                    if (this.SelectedRows.Count <= 1)
                    {
                        this.ClearSelection();
                        this.Rows[e.RowIndex].Selected = true;
                        this.CurrentCell = this[0, e.RowIndex];
                        //show row right-click menu here
                        MessageBox.Show("show row right-click menu");
                    }
                    else //more than one row is selected
                    {
                        //show row right-click menu here
                        MessageBox.Show("show row right-click menu");
                    } //end if
                } //end if
            } //end if
            base.OnRowHeaderMouseClick(e);
        } //end OnRowHeaderMouseClick

        protected override void OnCellMouseClick(DataGridViewCellMouseEventArgs e)
        {
            //runs when the mouse is clicked over a cell
            if (e.RowIndex > -1 && e.ColumnIndex > -1)
            {
                if (e.Button == MouseButtons.Left)
                {
                    //single-click left mouse button
                    if (this.SelectionMode != DataGridViewSelectionMode.CellSelect)
                    {
                        this.SelectionMode = DataGridViewSelectionMode.CellSelect;
                    } //end if
                }
                else if (e.Button == MouseButtons.Right)
                {
                    //single-click right mouse button
                    if (this.SelectionMode != DataGridViewSelectionMode.CellSelect)
                    {
                        this.SelectionMode = DataGridViewSelectionMode.CellSelect;
                    } //end if
                    if (this.SelectedCells.Count <= 1)
                    {
                        this.ClearSelection();
                        this[e.ColumnIndex, e.RowIndex].Selected = true;
                        this.CurrentCell = this[e.ColumnIndex, e.RowIndex];
                        //show cell right-click menu here
                        MessageBox.Show("show cell right-click menu");
                    }
                    else //more than one cell is selected
                    {
                        //show cell right-click menu here
                        MessageBox.Show("show cell right-click menu");
                    } //end if
                } //end if

            } //end if
            base.OnCellMouseClick(e);
        } //end OnCellMouseClick

        protected override void OnMouseDown(MouseEventArgs e)
        {
            //stores values for drag/drop operations if necessary
            if (this.AllowDrop)
            {
                if (this.HitTest(e.X, e.Y).ColumnIndex == -1 && this.HitTest(e.X, e.Y).RowIndex > -1)
                {
                    //if this is a row header cell
                    if (this.Rows[this.HitTest(e.X, e.Y).RowIndex].Selected)
                    {
                        //if this row is selected
                        DragDropType = 1;
                        Size DragSize = SystemInformation.DragSize;
                        DragDropRectangle = new Rectangle(new Point(e.X - (DragSize.Width / 2), e.Y - (DragSize.Height / 2)), DragSize);
                        DragDropSourceIndex = this.HitTest(e.X, e.Y).RowIndex;
                    }
                    else
                    {
                        DragDropRectangle = Rectangle.Empty;
                    } //end if
                }
                else if (this.HitTest(e.X, e.Y).ColumnIndex > -1 && this.HitTest(e.X, e.Y).RowIndex == -1)
                {
                    //if this is a column header cell
                    if (this.Columns[this.HitTest(e.X, e.Y).ColumnIndex].Selected)
                    {
                        DragDropType = 0;
                        DragDropSourceIndex = this.HitTest(e.X, e.Y).ColumnIndex;
                        Size DragSize = SystemInformation.DragSize;
                        DragDropRectangle = new Rectangle(new Point(e.X - (DragSize.Width / 2), e.Y - (DragSize.Height / 2)), DragSize);
                    }
                    else
                    {
                        DragDropRectangle = Rectangle.Empty;
                    } //end if
                }
                else
                {
                    DragDropRectangle = Rectangle.Empty;
                } //end if
            }
            else
            {
                DragDropRectangle = Rectangle.Empty;
            }//end if
            base.OnMouseDown(e);
        } //end OnMouseDown

        protected override void OnMouseMove(MouseEventArgs e)
        {
            //handles drag/drop operations if necessary
            if (this.AllowDrop)
            {
                if ((e.Button & MouseButtons.Left) == MouseButtons.Left)
                {
                    if (DragDropRectangle != Rectangle.Empty && !DragDropRectangle.Contains(e.X, e.Y))
                    {
                        if (DragDropType == 0)
                        {
                            //column drag/drop
                            DragDropEffects DropEffect = this.DoDragDrop(this.Columns[DragDropSourceIndex], DragDropEffects.Move);
                        }
                        else if (DragDropType == 1)
                        {
                            //row drag/drop
                            DragDropEffects DropEffect = this.DoDragDrop(this.Rows[DragDropSourceIndex], DragDropEffects.Move);
                        } //end if
                    } //end if
                } //end if
            } //end if
            base.OnMouseMove(e);
        } //end OnMouseMove

        protected override void OnDragOver(DragEventArgs e)
        {
            //runs while the drag/drop is in progress
            if (this.AllowDrop)
            {
                e.Effect = DragDropEffects.Move;
                if (DragDropType == 0)
                {
                    //column drag/drop
                    int CurCol = this.HitTest(this.PointToClient(new Point(e.X, e.Y)).X, this.PointToClient(new Point(e.X, e.Y)).Y).ColumnIndex;
                    if (DragDropCurrentIndex != CurCol)
                    {
                        DragDropCurrentIndex = CurCol;
                        this.Invalidate(); //repaint
                    } //end if
                }
                else if (DragDropType == 1)
                {
                    //row drag/drop
                    int CurRow = this.HitTest(this.PointToClient(new Point(e.X, e.Y)).X, this.PointToClient(new Point(e.X, e.Y)).Y).RowIndex;
                    if (DragDropCurrentIndex != CurRow)
                    {
                        DragDropCurrentIndex = CurRow;
                        this.Invalidate(); //repaint
                    } //end if
                } //end if
            } //end if
            base.OnDragOver(e);
        } //end OnDragOver

        protected override void OnDragDrop(DragEventArgs drgevent)
        {
            //runs after a drag/drop operation for column/row has completed
            if (this.AllowDrop)
            {
                if (drgevent.Effect == DragDropEffects.Move)
                {
                    Point ClientPoint = this.PointToClient(new Point(drgevent.X, drgevent.Y));
                    if (DragDropType == 0)
                    {
                        //if this is a column drag/drop operation
                        DragDropTargetIndex = this.HitTest(ClientPoint.X, ClientPoint.Y).ColumnIndex;
                        if (DragDropTargetIndex > -1 && DragDropCurrentIndex < this.ColumnCount - 1)
                        {
                            DragDropCurrentIndex = -1;
                            //holds the appearance of the source column
                            DragDropColumn = this.Columns[DragDropSourceIndex];
                            //holds the values of the cells in the source column
                            DragDropColumnCellValue = new object[this.RowCount - 1];
                            for (int i = 0; i < this.RowCount; i++)
                            {
                                //for each cell in the source column
                                if (this.Rows[i].Cells[DragDropSourceIndex].Value != null)
                                {
                                    //if this cell has a value, store it in the object array
                                    DragDropColumnCellValue[i] = this.Rows[i].Cells[DragDropSourceIndex].Value;
                                } //end if
                            } //next i
                            //remove the source column
                            this.Columns.RemoveAt(DragDropSourceIndex);
                            //insert a new column at the target index using the source column as a template
                            this.Columns.Insert(DragDropTargetIndex, new DataGridViewColumn(DragDropColumn.CellTemplate));
                            //copy the source column's header cell to the new column
                            this.Columns[DragDropTargetIndex].HeaderCell = DragDropColumn.HeaderCell;
                            //select the newly-inserted column
                            this.Columns[DragDropTargetIndex].Selected = true;
                            //update the position of the cuurent cell in the DGV
                            this.CurrentCell = this[DragDropTargetIndex, 0];
                            for (int i = 0; i < this.RowCount; i++)
                            {
                                //for each cell in the new column
                                if (DragDropColumnCellValue[i] != null)
                                {
                                    //set the cell's value equal to that of the corresponding cell in the source column
                                    this.Rows[i].Cells[DragDropTargetIndex].Value = DragDropColumnCellValue[i];
                                } //end if
                            } //next i
                            //release resources
                            DragDropColumnCellValue = null;
                            DragDropColumn = null;
                        } //end if
                    }
                    else if (DragDropType == 1)
                    {
                        //if this is a row drag/drop operation
                        DragDropTargetIndex = this.HitTest(ClientPoint.X, ClientPoint.Y).RowIndex;
                        if (DragDropTargetIndex > -1 && DragDropCurrentIndex < this.RowCount - 1)
                        {
                            DragDropCurrentIndex = -1;
                            DataGridViewRow SourceRow = drgevent.Data.GetData(typeof(DataGridViewRow)) as DataGridViewRow;
                            this.Rows.RemoveAt(DragDropSourceIndex);
                            this.Rows.Insert(DragDropTargetIndex, SourceRow);
                            this.Rows[DragDropTargetIndex].Selected = true;
                            this.CurrentCell = this[0, DragDropTargetIndex];
                        } //end if
                    } //end if
                } //end if
            } //end if
            base.OnDragDrop(drgevent);
        } //end OnDragDrop

        protected override void OnCellPainting(DataGridViewCellPaintingEventArgs e)
        {
            //draws red drag/drop target indicator lines if necessary
            if (DragDropCurrentIndex > -1)
            {
                if (DragDropType == 0)
                {
                    //column drag/drop
                    if (e.ColumnIndex == DragDropCurrentIndex && DragDropCurrentIndex < this.ColumnCount - 1)
                    {
                        //if this cell is in the same column as the mouse cursor
                        Pen p = new Pen(Color.Red, 1);
                        e.Graphics.DrawLine(p, e.CellBounds.Left - 1, e.CellBounds.Top, e.CellBounds.Left - 1, e.CellBounds.Bottom);
                    } //end if
                }
                else if (DragDropType == 1)
                {
                    //row drag/drop
                    if (e.RowIndex == DragDropCurrentIndex && DragDropCurrentIndex < this.RowCount - 1)
                    {
                        //if this cell is in the same row as the mouse cursor
                        Pen p = new Pen(Color.Red, 1);
                        e.Graphics.DrawLine(p, e.CellBounds.Left, e.CellBounds.Top - 1, e.CellBounds.Right, e.CellBounds.Top - 1);
                    } //end if
                } //end if
            } //end if
            base.OnCellPainting(e);
        } //end OnCellPainting

    } //end class
} //end namespace
			

Click here to return to the Free Programming Resources index

Click here to return to DanielSoper.com