Imports System
Imports System.Collections.Generic
Imports System.Text
Imports System.Windows.Forms
Imports 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
Inherits DataGridView
'vars for custom column/row drag/drop operations
Private DragDropRectangle As Rectangle
Private DragDropSourceIndex As Integer
Private DragDropTargetIndex As Integer
Private DragDropCurrentIndex As Integer = -1
Private DragDropType As Integer
'0=column, 1=row
Private DragDropColumn As DataGridViewColumn
Private DragDropColumnCellValue As Object()
Public Sub New()
Me.AllowUserToResizeRows = False
Me.SelectionMode = DataGridViewSelectionMode.CellSelect
Me.AllowUserToOrderColumns = False
Me.AllowDrop = True
Me.ColumnCount = 20
Me.RowCount = 40
Me.Size = New Size(600, 400)
End Sub
'end default constructor
Protected Overloads Overrides Sub OnColumnAdded(ByVal e As DataGridViewColumnEventArgs)
'runs when a new column is added to the DGV
e.Column.SortMode = DataGridViewColumnSortMode.Programmatic
e.Column.HeaderText = "column " + e.Column.Index
MyBase.OnColumnAdded(e)
End Sub
'end OnColumnAdded
Protected Overloads Overrides Sub OnColumnHeaderMouseClick(ByVal e As DataGridViewCellMouseEventArgs)
'runs when the mouse is clicked over a column header cell
If e.ColumnIndex > -1 Then
If e.Button = MouseButtons.Left Then
'single-click left mouse button
If Me.SelectionMode <> DataGridViewSelectionMode.ColumnHeaderSelect Then
Me.SelectionMode = DataGridViewSelectionMode.ColumnHeaderSelect
Me.Columns(e.ColumnIndex).Selected = True
'end if
End If
ElseIf e.Button = MouseButtons.Right Then
'single-click right mouse button
If Me.SelectionMode <> DataGridViewSelectionMode.ColumnHeaderSelect Then
Me.SelectionMode = DataGridViewSelectionMode.ColumnHeaderSelect
End If
'end if
If Me.SelectedColumns.Count <= 1 Then
Me.ClearSelection()
Me.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
End If
End If
'end if
MyBase.OnColumnHeaderMouseClick(e)
End Sub
'end OnColumnHeaderMouseClick
Protected Overloads Overrides Sub OnRowHeaderMouseClick(ByVal e As DataGridViewCellMouseEventArgs)
'runs when the mouse is clicked over a row header cell
If e.RowIndex > -1 Then
If e.Button = MouseButtons.Left Then
'single-click left mouse button
If Me.SelectionMode <> DataGridViewSelectionMode.RowHeaderSelect Then
Me.SelectionMode = DataGridViewSelectionMode.RowHeaderSelect
Me.Rows(e.RowIndex).Selected = True
Me.CurrentCell = Me(0, e.RowIndex)
'end if
End If
ElseIf e.Button = MouseButtons.Right Then
'single-click right mouse button
If Me.SelectionMode <> DataGridViewSelectionMode.RowHeaderSelect Then
Me.SelectionMode = DataGridViewSelectionMode.RowHeaderSelect
End If
'end if
If Me.SelectedRows.Count <= 1 Then
Me.ClearSelection()
Me.Rows(e.RowIndex).Selected = True
Me.CurrentCell = Me(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
End If
End If
'end if
MyBase.OnRowHeaderMouseClick(e)
End Sub
'end OnRowHeaderMouseClick
Protected Overloads Overrides Sub OnCellMouseClick(ByVal e As DataGridViewCellMouseEventArgs)
'runs when the mouse is clicked over a cell
If e.RowIndex > -1 AndAlso e.ColumnIndex > -1 Then
If e.Button = MouseButtons.Left Then
'single-click left mouse button
If Me.SelectionMode <> DataGridViewSelectionMode.CellSelect Then
Me.SelectionMode = DataGridViewSelectionMode.CellSelect
'end if
End If
ElseIf e.Button = MouseButtons.Right Then
'single-click right mouse button
If Me.SelectionMode <> DataGridViewSelectionMode.CellSelect Then
Me.SelectionMode = DataGridViewSelectionMode.CellSelect
End If
'end if
If Me.SelectedCells.Count <= 1 Then
Me.ClearSelection()
Me(e.ColumnIndex, e.RowIndex).Selected = True
Me.CurrentCell = Me(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
End If
End If
'end if
MyBase.OnCellMouseClick(e)
End Sub
'end OnCellMouseClick
Protected Overloads Overrides Sub OnMouseDown(ByVal e As MouseEventArgs)
'stores values for drag/drop operations if necessary
If Me.AllowDrop Then
If Me.HitTest(e.X, e.Y).ColumnIndex = -1 AndAlso Me.HitTest(e.X, e.Y).RowIndex > -1 Then
'if this is a row header cell
If Me.Rows(Me.HitTest(e.X, e.Y).RowIndex).Selected Then
'if this row is selected
DragDropType = 1
Dim DragSize As Size = SystemInformation.DragSize
DragDropRectangle = New Rectangle(New Point(e.X - (DragSize.Width / 2), e.Y - (DragSize.Height / 2)), DragSize)
DragDropSourceIndex = Me.HitTest(e.X, e.Y).RowIndex
Else
DragDropRectangle = Rectangle.Empty
'end if
End If
ElseIf Me.HitTest(e.X, e.Y).ColumnIndex > -1 AndAlso Me.HitTest(e.X, e.Y).RowIndex = -1 Then
'if this is a column header cell
If Me.Columns(Me.HitTest(e.X, e.Y).ColumnIndex).Selected Then
DragDropType = 0
DragDropSourceIndex = Me.HitTest(e.X, e.Y).ColumnIndex
Dim DragSize As Size = SystemInformation.DragSize
DragDropRectangle = New Rectangle(New Point(e.X - (DragSize.Width / 2), e.Y - (DragSize.Height / 2)), DragSize)
Else
DragDropRectangle = Rectangle.Empty
'end if
End If
Else
DragDropRectangle = Rectangle.Empty
'end if
End If
Else
DragDropRectangle = Rectangle.Empty
End If
'end if
MyBase.OnMouseDown(e)
End Sub
'end OnMouseDown
Protected Overloads Overrides Sub OnMouseMove(ByVal e As MouseEventArgs)
'handles drag/drop operations if necessary
If Me.AllowDrop Then
If (e.Button And MouseButtons.Left) = MouseButtons.Left Then
If DragDropRectangle <> Rectangle.Empty AndAlso Not DragDropRectangle.Contains(e.X, e.Y) Then
If DragDropType = 0 Then
'column drag/drop
Dim DropEffect As DragDropEffects = Me.DoDragDrop(Me.Columns(DragDropSourceIndex), DragDropEffects.Move)
ElseIf DragDropType = 1 Then
'row drag/drop
Dim DropEffect As DragDropEffects = Me.DoDragDrop(Me.Rows(DragDropSourceIndex), DragDropEffects.Move)
'end if
End If
'end if
End If
'end if
End If
End If
'end if
MyBase.OnMouseMove(e)
End Sub
'end OnMouseMove
Protected Overloads Overrides Sub OnDragOver(ByVal e As DragEventArgs)
'runs while the drag/drop is in progress
If Me.AllowDrop Then
e.Effect = DragDropEffects.Move
If DragDropType = 0 Then
'column drag/drop
Dim CurCol As Integer = Me.HitTest(Me.PointToClient(New Point(e.X, e.Y)).X, Me.PointToClient(New Point(e.X, e.Y)).Y).ColumnIndex
If DragDropCurrentIndex <> CurCol Then
DragDropCurrentIndex = CurCol
'repaint
Me.Invalidate()
'end if
End If
ElseIf DragDropType = 1 Then
'row drag/drop
Dim CurRow As Integer = Me.HitTest(Me.PointToClient(New Point(e.X, e.Y)).X, Me.PointToClient(New Point(e.X, e.Y)).Y).RowIndex
If DragDropCurrentIndex <> CurRow Then
DragDropCurrentIndex = CurRow
'repaint
Me.Invalidate()
'end if
End If
'end if
End If
End If
'end if
MyBase.OnDragOver(e)
End Sub
'end OnDragOver
Protected Overloads Overrides Sub OnDragDrop(ByVal drgevent As DragEventArgs)
'runs after a drag/drop operation for column/row has completed
If Me.AllowDrop Then
If drgevent.Effect = DragDropEffects.Move Then
Dim ClientPoint As Point = Me.PointToClient(New Point(drgevent.X, drgevent.Y))
If DragDropType = 0 Then
'if this is a column drag/drop operation
DragDropTargetIndex = Me.HitTest(ClientPoint.X, ClientPoint.Y).ColumnIndex
If DragDropTargetIndex > -1 AndAlso DragDropCurrentIndex < Me.ColumnCount - 1 Then
DragDropCurrentIndex = -1
'holds the appearance of the source column
DragDropColumn = Me.Columns(DragDropSourceIndex)
'holds the values of the cells in the source column
DragDropColumnCellValue = New Object(Me.RowCount - 2) {}
For i As Integer = 0 To Me.RowCount - 1
'for each cell in the source column
If Me.Rows(i).Cells(DragDropSourceIndex).Value IsNot Nothing Then
'if this cell has a value, store it in the object array
DragDropColumnCellValue(i) = Me.Rows(i).Cells(DragDropSourceIndex).Value
'end if
End If
Next
'next i
'remove the source column
Me.Columns.RemoveAt(DragDropSourceIndex)
'insert a new column at the target index using the source column as a template
Me.Columns.Insert(DragDropTargetIndex, New DataGridViewColumn(DragDropColumn.CellTemplate))
'copy the source column's header cell to the new column
Me.Columns(DragDropTargetIndex).HeaderCell = DragDropColumn.HeaderCell
'select the newly-inserted column
Me.Columns(DragDropTargetIndex).Selected = True
'update the position of the cuurent cell in the DGV
Me.CurrentCell = Me(DragDropTargetIndex, 0)
For i As Integer = 0 To Me.RowCount - 1
'for each cell in the new column
If DragDropColumnCellValue(i) IsNot Nothing Then
'set the cell's value equal to that of the corresponding cell in the source column
Me.Rows(i).Cells(DragDropTargetIndex).Value = DragDropColumnCellValue(i)
'end if
End If
Next
'next i
'release resources
DragDropColumnCellValue = Nothing
DragDropColumn = Nothing
'end if
End If
ElseIf DragDropType = 1 Then
'if this is a row drag/drop operation
DragDropTargetIndex = Me.HitTest(ClientPoint.X, ClientPoint.Y).RowIndex
If DragDropTargetIndex > -1 AndAlso DragDropCurrentIndex < Me.RowCount - 1 Then
DragDropCurrentIndex = -1
Dim SourceRow As DataGridViewRow = TryCast(drgevent.Data.GetData(GetType(DataGridViewRow)), DataGridViewRow)
Me.Rows.RemoveAt(DragDropSourceIndex)
Me.Rows.Insert(DragDropTargetIndex, SourceRow)
Me.Rows(DragDropTargetIndex).Selected = True
Me.CurrentCell = Me(0, DragDropTargetIndex)
'end if
End If
'end if
End If
'end if
End If
End If
'end if
MyBase.OnDragDrop(drgevent)
End Sub
'end OnDragDrop
Protected Overloads Overrides Sub OnCellPainting(ByVal e As DataGridViewCellPaintingEventArgs)
'draws red drag/drop target indicator lines if necessary
If DragDropCurrentIndex > -1 Then
If DragDropType = 0 Then
'column drag/drop
If e.ColumnIndex = DragDropCurrentIndex AndAlso DragDropCurrentIndex < Me.ColumnCount - 1 Then
'if this cell is in the same column as the mouse cursor
Dim p As 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
End If
ElseIf DragDropType = 1 Then
'row drag/drop
If e.RowIndex = DragDropCurrentIndex AndAlso DragDropCurrentIndex < Me.RowCount - 1 Then
'if this cell is in the same row as the mouse cursor
Dim p As 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
End If
End If
'end if
MyBase.OnCellPainting(e)
End Sub
'end OnCellPainting
End Class
'end class
End Namespace
'end namespace