|
-
Oct 11th, 2021, 06:18 PM
#5
Hyperactive Member
Re: Copying and Pasting in a DGV
This is one That is virtually but not quite finished yet (and some bits have been copied form various sources and altered), Copy paste works ok, the only issue is in deleting rows, can only delete one row at a time, even if more are selected, I am currently working to fix this. However the rest of it works well and the cut / copy paste has the same rules as excel. You can also cut / copy paste from an external source to the DGV.
There is a known bug, that if you actually select the cell and are editing the actual contents and then right click, two pop up windows appear, the one I defined and another one which looks to be a pre defined one from somewhere.
If it is defined as read only then you can only copy
Any suggested improvements or bug fixes welcome
Code:
Public Class DataGridViewCutPaste
Inherits DataGridView
Private WithEvents contextMenuDataGrid As New ContextMenuStrip
Public Sub New()
End Sub
Public Property BindingSource() As BindingSource ' this now acts as get & set to the same name prefixed with underscore _
Public Property CutPasteReadWrite() As Boolean = False ' allows alteration to the datagridview
Private Function CheckBindingsource(ByRef aBindingsource As BindingSource)
If aBindingsource Is Nothing Then
MsgBox("bindingsource not defined")
CheckBindingsource = False
Exit Function
End If
CheckBindingsource = True
End Function
Private Sub ValidateData_Click(ByVal sender As System.Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles Me.KeyDown
If _CutPasteReadWrite = True Then
Select Case e.KeyCode 'read write options
Case 86 'ctrl V
PasteClipboardValue()
Case 88 'ctrl X
cut()
End Select
End If
Select Case e.KeyCode ' read options
Case 67 ' ctrl c
CopyToClipboard()
End Select
'MsgBox(e.KeyCode)
End Sub
Private Sub me_CellMouseDown(sender As Object, e As System.Windows.Forms.DataGridViewCellMouseEventArgs) Handles Me.CellMouseDown
If e.Button = MouseButtons.Right And e.RowIndex > -1 Then
Me.CurrentCell = Me(e.ColumnIndex, e.RowIndex)
If contextMenuDataGrid.Items.Count = 0 Then
If _CutPasteReadWrite = True Then
contextMenuDataGrid.Items.Add("Add Row")
contextMenuDataGrid.Items.Add("Add 10 Rows")
contextMenuDataGrid.Items.Add("Insert Row at Cursor")
contextMenuDataGrid.Items.Add("Delete Row")
contextMenuDataGrid.Items.Add("Paste")
contextMenuDataGrid.Items.Add("Cut")
End If
contextMenuDataGrid.Items.Add("Copy")
End If
contextMenuDataGrid.Show(Me, e.Location)
End If
End Sub
Private Sub contextMenuDataGrid_Click(sender As Object, ByVal e As System.Windows.Forms.ToolStripItemClickedEventArgs) Handles contextMenuDataGrid.ItemClicked
contextMenuDataGrid.Hide()
If CheckBindingsource(_BindingSource) = False Then Exit Sub
Select Case e.ClickedItem.Text
Case "Copy"
CopyToClipboard()
Case "Cut"
cut()
Case "Paste"
PasteClipboardValue()
Case "Insert Row at Cursor"
insertRowAtCursor()
Case "Add Row"
_BindingSource.AddNew()
Case "Add 10 Rows"
Add10Rows()
Case "Delete Row"
DeleteRow()
End Select
End Sub
Public Sub CopyToClipboard() ' public so it can be called off the toolstrip on the master form
Dim dataObj As DataObject = Me.GetClipboardContent
If Not IsNothing(dataObj) Then
Clipboard.SetDataObject(dataObj)
End If
End Sub
Public Sub cut() ' public so it can be called off the toolstrip on the master form
CopyToClipboard()
For counter As Integer = 0 To Me.SelectedCells.Count - 1
Me.SelectedCells(counter).Value = String.Empty
Next
End Sub
Sub DeleteRow()
Dim SelectedRowCount As Integer = Me.SelectedCells.OfType(Of DataGridViewCell)().Select(Function(x) x.RowIndex).Distinct().Count()
If CheckBindingsource(_BindingSource) = False Then Exit Sub
_BindingSource.RemoveCurrent()
End Sub
Sub Add10Rows()
Dim startingCell As Integer = Me.CurrentCell.ColumnIndex
If CheckBindingsource(_BindingSource) = False Then Exit Sub
For i = 1 To 10
_BindingSource.AddNew()
Next
Me.CurrentCell = Me.Rows(0).Cells(startingCell)
End Sub
Sub insertRowAtCursor()
If CheckBindingsource(_BindingSource) = False Then Exit Sub
Dim DataGridViewCursorCurrentRow As Integer = Me.CurrentRow.Index
Dim startingCell As Integer = Me.CurrentCell.ColumnIndex
_BindingSource.AddNew()
Dim dataGridROwCount As Integer = Me.RowCount
Dim j As Integer
For j = 0 To Me.Columns.Count - 1
For i = dataGridROwCount - 1 To DataGridViewCursorCurrentRow + 1 Step -1
Me.Rows(i).Cells(j).Value = Me.Rows(i - 1).Cells(j).Value
Me.Rows(i - 1).Cells(j).Value = ""
Next
Next
Me.CurrentCell = Me.Rows(0).Cells(startingCell)
End Sub
Private Sub PasteClipboardValue()
If Me.SelectedCells.Count = 0 Then
MessageBox.Show("No Cell selected", "Paste")
Exit Sub
End If
Dim StartingCell As DataGridViewCell = GetStartingCell(Me)
Dim SelectedrowCount = Me.SelectedCells.OfType(Of DataGridViewCell)().Select(Function(x) x.RowIndex).Distinct().Count()
Dim SelectedColumnCount = Me.SelectedCells.OfType(Of DataGridViewCell)().Select(Function(x) x.ColumnIndex).Distinct().Count()
Dim startingCellRow = StartingCell.RowIndex
Dim StartingCellColumn = StartingCell.ColumnIndex
Dim clipBoardValue As Dictionary(Of Integer, Dictionary(Of Integer, String)) = ClipboardValues(Clipboard.GetText.TrimEnd(vbCr, vbLf)) 'trim end needed due to excel
Dim clipboardRowCount As Integer = clipBoardValue.Keys.Count
Dim clipboardArray(1000, 30) As String
Dim i As Integer = 0
Dim j As Integer = 0
Dim maxArrayCol As Integer = 0
Dim maxarrayrow As Integer = 0
For Each rowkey As Integer In clipBoardValue.Keys
Dim startingCellColumnIndex As Integer = StartingCell.ColumnIndex
For Each cellkey As Integer In clipBoardValue(rowkey).Keys
Dim cell As DataGridViewCell = Me(startingCellColumnIndex, startingCellRow)
cell.Value = clipBoardValue(rowkey)(cellkey)
'If cell.Value Is Nothing Then
clipboardArray(i, j) = vbNull
' Else
clipboardArray(i, j) = cell.Value.trim(vbCr, vbLf)
' End If
j += 1 'j = j + 1
Next
If j > maxArrayCol Then maxArrayCol = j
j = 0
i += 1 'i = i + 1
Next
maxarrayrow = i
Dim maxClipboardCol As Integer = maxArrayCol
'clipboadarray Contains the clipboard with maxcolrow & col giving the number of rows & colums subtract 1 to get array
Dim cj As Integer = 0
Dim cc As Integer = 0
If SelectedrowCount = 1 And SelectedColumnCount = 1 Then ' this is if only a starting postion has been specified
Dim rowsLefttotheBottom = Me.RowCount - startingCellRow
Dim columnsLefttotheRight = Me.ColumnCount - StartingCellColumn
If rowsLefttotheBottom >= maxarrayrow Then SelectedrowCount = maxarrayrow Else SelectedrowCount = SelectedrowCount + rowsLefttotheBottom - 1
If columnsLefttotheRight >= maxArrayCol Then SelectedColumnCount = maxArrayCol Else SelectedColumnCount = SelectedColumnCount + columnsLefttotheRight - 1
End If
Dim rowsFilled As Integer
Dim rowsFilledMultiple = False
Dim columnsFilledMultiple = False
If SelectedrowCount Mod maxarrayrow = 0 Then rowsFilledMultiple = True Else rowsFilledMultiple = False
If SelectedColumnCount Mod maxArrayCol = 0 Then columnsFilledMultiple = True Else columnsFilledMultiple = False
For i = startingCellRow To startingCellRow + SelectedrowCount - 1
For j = StartingCellColumn To StartingCellColumn + SelectedColumnCount - 1
Dim cell As DataGridViewCell = Me(j, i)
cell.Value = clipboardArray(cj, cc)
Application.DoEvents()
cc += 1 'cc = cc + 1
Dim numberOfColsLeft = StartingCellColumn + SelectedColumnCount - 1
If cc >= maxClipboardCol Then
If cc >= maxClipboardCol Then
cc = 0
If columnsFilledMultiple = False Then Exit For
End If
End If
Next
rowsFilled += 1
cc = 0
cj += 1 'cj = cj + 1
If cj >= clipboardRowCount Then
cj = 0
If rowsFilledMultiple = False Then Exit For
End If
Next
End Sub
Private Function GetStartingCell(dgView As DataGridView) As DataGridViewCell
If dgView.SelectedCells.Count = 0 Then Return Nothing
Dim rowIndex As Integer = dgView.Rows.Count - 1
Dim ColIndex As Integer = dgView.Columns.Count - 1
For Each dgvcell As DataGridViewCell In dgView.SelectedCells
If dgvcell.RowIndex < rowIndex Then rowIndex = dgvcell.RowIndex
If dgvcell.ColumnIndex < ColIndex Then ColIndex = dgvcell.ColumnIndex
Next
Return dgView(ColIndex, rowIndex)
End Function
Private Function ClipboardValues(clipboardvalue As String) As Dictionary(Of Integer, Dictionary(Of Integer, String))
Dim lines() As String = clipboardvalue.Split(CChar(Environment.NewLine))
Dim copyValues As Dictionary(Of Integer, Dictionary(Of Integer, String)) = New Dictionary(Of Integer, Dictionary(Of Integer, String))
For i As Integer = 0 To lines.Length - 1
copyValues.Item(i) = New Dictionary(Of Integer, String)
Dim linecontent() As String = lines(i).Split(ChrW(Keys.Tab))
If linecontent.Length = 0 Then
copyValues(i)(0) = String.Empty
Else
For j As Integer = 0 To linecontent.Length - 1
copyValues(i)(j) = linecontent(j)
Next
End If
Next
Return copyValues
End Function
End Class
Last edited by Signalman; Oct 11th, 2021 at 06:35 PM.
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|