Results 1 to 5 of 5

Thread: Copying and Pasting in a DGV

  1. #1

    Thread Starter
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    40,109

    Copying and Pasting in a DGV

    I decided to break another thread I just created by doing a copy and paste action on a DGV. As it turns out, that's a bit fiddly, too. If I just click in a cell and press Ctrl+C, I get an internal exception about a method not being implemented. If I highlight JUST the text in the cell, the Ctrl+C works fine.

    This appears to be more or less understood. Copying the whole cell is problematic, but copying the value in the cell is not. However, this is not intuitive to the user, and it means that pasting into multiple cells is not even possible without doing a bit more work.

    I believe that I can handle the KeyDown event to check for Ctrl+C, then also check for Ctrl+V, and get the behavior people would expect, but people are weird, so this won't always work. People expect that a cell in a DGV is it's contents, so they expect to be able to select a cell and copy and paste it. Lots of people know Ctrl+C/Ctrl+V, but I'm always surprised at the number that right click and select Copy, then select Paste, which would certainly break the KeyDown approach. People also aren't going to understand the idea of selecting the contents of a cell rather than the whole cell, so that's kind of a non-starter to begin with.

    For these reasons, the official MS means for copying and pasting in a DGV seem to be out of consideration. Before I go build my own grid to get around this (I've done it before, I'll do it again if I have to), I was wondering whether anybody has a way to do copying and pasting in a DGV that works the way people would expect it to?
    My usual boring signature: Nothing

  2. #2
    Fanatic Member Delaney's Avatar
    Join Date
    Nov 2019
    Location
    Paris, France
    Posts
    846

    Re: Copying and Pasting in a DGV

    same application than for the other thread :

    for the copy :

    Code:
    copy_data_from_datagrid(DataGrid_Courbe.GetClipboardContent) ' I put this in a right click option (ContextMenuStrip)
    'copu sub
       Sub copy_data_from_datagrid(ByVal Data As Object)
          If Not IsNothing(Data) Then
             My.Computer.Clipboard.Clear()
             Clipboard.SetDataObject(Data)
          End If
    
       End Sub
    for the paste


    Code:
     'paste sub
       Sub paste_clipboard_to_datagrid()
          On Error Resume Next
          Dim lignes() As String
          Dim Cell() As String
          Dim row As Integer
          Dim nb_lignes As Integer
          Dim nb_lignes_restante As Integer
          Dim nom_courbe As String
    
          row = DataGrid_Courbe.SelectedCells(0).RowIndex 'get the index of the selected line
    
          lignes = Clipboard.GetData(DataFormats.Text).ToString.Split({ControlChars.Cr, ControlChars.Lf}, StringSplitOptions.RemoveEmptyEntries) 'get from clipboard an array of data to paste, one line of array for each line copied
    
          nb_lignes = lignes.Length
          nb_lignes_restante = DataGrid_Courbe.Rows.Count - row
    
          If nb_lignes > nb_lignes_restante Then 'check if you need to create new line to paste the data
    
             'create new lines if needed
             For i = 1 To nb_lignes - nb_lignes_restante + 1
                DataGrid_Courbe.Rows.Add()
             Next
    
          End If
          'fill the lines
          For i = 1 To nb_lignes
             Dim j As Integer
             Cell = lignes(i - 1).Split(vbTab)
             For j = 0 To 1
                DataGrid_Courbe.Rows(row + i - 1).Cells(j).Value = Cell(j)
             Next
          Next
    
    
          ' update the curve
      ....
       End Sub
    if you want I can send to you this part of the application but it will be in french.
    The best friend of any programmer is a search engine
    "Don't wish it was easier, wish you were better. Don't wish for less problems, wish for more skills. Don't wish for less challenges, wish for more wisdom" (J. Rohn)
    “They did not know it was impossible so they did it” (Mark Twain)

  3. #3

    Thread Starter
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    40,109

    Re: Copying and Pasting in a DGV

    Not yet. It's interesting to see. After thinking it over a bit, I'm not quite sure what I want to do here. I already did something that flood filled the columns, and there will always be an issue with copy/paste in this case, since each column has different constraints on it. What works in one cell won't work in another. One whole column can't be copy/pasted at all (every value had better be unique, and not just to the grid, or somebody has messed up), some columns are entwined with others, and so forth. For that reason, I'm not quite certain what I want to do with copying and pasting.

    Still, that's an interesting approach. It looks like it could be either right click or Ctrl+C without much difficulty, so it would work. I might also be able to add my peculiar paste constraints into copy/paste. For example, in the column that has to be unique, I could just disable the copying. I might fiddle around with that a bit.
    My usual boring signature: Nothing

  4. #4
    PowerPoster
    Join Date
    Sep 2005
    Location
    Modesto, Ca.
    Posts
    5,517

    Re: Copying and Pasting in a DGV

    The DGV has an EditMode option of "EditOnEnter" that would probably solve the error.

  5. #5
    Hyperactive Member
    Join Date
    Apr 2010
    Posts
    420

    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
  •  



Click Here to Expand Forum to Full Width