LoginSignup
1
2

More than 1 year has passed since last update.

【VB.NET】DataGridViewでセルコピー&ペースト

Last updated at Posted at 2022-12-12

DataGridViewでセルのコピー&ペースト。
Excelの動きに近づくように四苦八苦・・・
・コピー元から複数行へのコピー
・複数行から複数行へのコピー
が出来るようになっています

●フォームロード

Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    ''テーブルを作成
    Dim dataSet1 As DataSet = New DataSet("商品マスター")
    Dim dataTable1 As DataTable = dataSet1.Tables.Add("商品テーブル")
    Dim dataClumn1 As DataColumn = dataTable1.Columns.Add("ID")
    Dim dataClumn2 As DataColumn = dataTable1.Columns.Add("商品")
    Dim dataClumn3 As DataColumn = dataTable1.Columns.Add("値段")
    Dim dataClumn4 As DataColumn = dataTable1.Columns.Add("個数")

    ''テーブルにデータを追加
    dataTable1.Rows.Add(New Object() {1, "みかん", 100, "3個"})
    dataTable1.Rows.Add(New Object() {2, "パイナップル", 300, "3個"})
    dataTable1.Rows.Add(New Object() {3, "バナナ", 120, "1房"})
    dataTable1.Rows.Add(New Object() {"", "", "", ""})
    dataTable1.Rows.Add(New Object() {"", "", "", ""})
    dataTable1.Rows.Add(New Object() {"", "", "", ""})

    ''データグリッドの行の追加と削除、データ編集を不許可にする
    dataTable1.DefaultView.AllowNew = False
    dataTable1.DefaultView.AllowDelete = False
    dataTable1.DefaultView.AllowEdit = True

    ''データグリッドにテーブルを表示する
    ''(データソースにDataViewを使う)

    'DataGridView1.SelectionMode = DataGridViewSelectionMode.FullRowSelect
    DataGridView1.DataSource = dataTable1

End Sub

●各キー動作

''' <summary>
''' 選択行コピー・削除・切り取り
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
Private Sub DataGridView1_KeyDown1(sender As Object, e As KeyEventArgs) Handles DataGridView1.KeyDown

    If e.Control And e.KeyCode = Keys.C Then
        ''*** Ctrl+C 選択行コピー***
        copyRow()

    ElseIf e.KeyCode = Keys.Delete Then
        ''*** Delete 選択行削除***
        deleteRow()

    ElseIf e.Control And e.KeyCode = Keys.X Then
        ''*** Ctrl+X 選択行切り取り***
        copyRow()
        deleteRow()

    ElseIf e.Control And e.KeyCode = Keys.V Then
        ''*** Ctrl+V 選択行ペースト***
        CopyPaste(sender, e)
    End If

End Sub

●コピー

''' <summary>
''' コピー
''' </summary>
Private Sub copyRow()
    DataGridView1.ClipboardCopyMode = DataGridViewClipboardCopyMode.EnableWithoutHeaderText

    If DataGridView1.GetClipboardContent() IsNot Nothing Then
        Clipboard.SetDataObject(DataGridView1.GetClipboardContent())
    Else
        ''何もコピーしない
        Return
    End If

End Sub

●削除

''' <summary>
''' 削除
''' </summary>
Private Sub deleteRow()
    For Each c As DataGridViewCell In DataGridView1.SelectedCells

        Dim iR As Integer = c.RowIndex
        Dim iC As Integer = c.ColumnIndex

        If 4 <= c.ColumnIndex Then
            ''ヘッダ列以外
            DataGridView1.Rows(iR).Cells(iC).Value = ""
        End If

    Next
End Sub

●ペースト
DataGridViewSelectionMode.FullRowSelectでないとDataGridView.SelectedRowsが取得できないようなので、苦肉の策でSelectedCellsから選択行のインデックスを取得しています(あんまりイケてないかも・・・)

''' <summary>
''' ペースト
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
Private Sub CopyPaste(sender As Object, e As KeyEventArgs)

    Try

        If DataGridView1.CurrentCell Is Nothing Then
            Return
        End If

        Dim dt As DataTable = CType(DataGridView1.DataSource, DataTable)
        Dim RowIndexList(dt.Rows.Count * dt.Columns.Count) As String
        Dim ColIndexList(dt.Rows.Count * dt.Columns.Count) As String

        Dim insertRowIndexMin As Integer = dt.Rows.Count    ''選択行の最小インデックス
        Dim insertRowIndexMax As Integer = 0                ''選択行の最大インデックス
        Dim insertColIndexMin As Integer = dt.Columns.Count ''選択列の最小インデックス
        Dim insertColIndexMax As Integer = 0                ''選択列の最大インデックス

        For Each c As DataGridViewCell In DataGridView1.SelectedCells
            ''選択行列のインデックスに入れ替えていく
            If insertRowIndexMin > c.RowIndex Then insertRowIndexMin = c.RowIndex
            If insertRowIndexMax <= c.RowIndex Then insertRowIndexMax = c.RowIndex
            If insertColIndexMin > c.ColumnIndex Then insertColIndexMin = c.ColumnIndex
            If insertColIndexMax <= c.ColumnIndex Then insertColIndexMax = c.ColumnIndex
        Next

        Dim selectRowNum As Integer = (insertRowIndexMax - insertRowIndexMin) + 1 ''選択行数
        Dim selectColNum As Integer = (insertColIndexMax - insertColIndexMin) + 1 ''選択列数

        'クリップボードの内容を取得して、行で分ける
        Dim pasteText As String = Clipboard.GetText()
        If pasteText Is Nothing Then
            Return
        End If
        Dim lines As String() = getLine(pasteText)

        'タブで分割
        Dim ValColNum As Integer = lines(0).Split(ControlChars.Tab).Length ''コピー列数
        Dim ValRowNum As Integer = lines.Length ''コピー行数

        Dim isHeader As Boolean = True
        Dim num As Integer = 0
        Dim num2 As Integer = 0


        For i As Integer = insertRowIndexMin To insertRowIndexMax

            If num = 0 Or (num Mod ValRowNum = 0 And selectRowNum Mod ValRowNum = 0) Then

                If ValColNum < selectColNum And selectRowNum Mod ValRowNum = 0 Then
                    ''コピー列数より選択列数が多いときのみ
                    For j As Integer = insertColIndexMin To insertColIndexMax

                        If num2 = 0 Or (num2 Mod ValColNum = 0 And selectColNum Mod ValColNum = 0) Then
                            setRow(lines, i, j, dt)
                        End If
                        num2 = num2 + 1

                    Next j

                Else
                    setRow(lines, i, insertColIndexMin, dt)
                End If

            End If

            num = num + 1

        Next i

    Catch ex As System.IO.FileNotFoundException
        Debug.Print("コピーに失敗しました")

        Return
    End Try

End Sub

●1行ごとに各セルの値を設定

''' <summary>
''' 1行ごとに各セルの値を設定
''' </summary>
''' <param name="lines"></param>
''' <param name="insertRowIndex"></param>
''' <param name="insertColIndexMin"></param>
''' <param name="dt"></param>
Public Sub setRow(lines, insertRowIndex, insertColIndexMin, dt)

    Dim rowIndex As Integer = insertRowIndex

    For Each line As String In lines
        'タブで分割
        Dim vals As String() = line.Split(ControlChars.Tab)

        '各セルの値を設定
        setCol(rowIndex, insertColIndexMin, vals, dt)


        '次の行へ
        rowIndex += 1

        ''行が無ければ終了
        If rowIndex >= dt.Rows.Count Then
            Return
        End If

        'End If
    Next line
End Sub

●クリップボードの内容を取得して、行で分ける

''' <summary>
''' クリップボードの内容を取得して、行で分ける
''' </summary>
''' <returns></returns>
Public Function getLine(pasteText)

    Dim lines As String()

    pasteText = pasteText.Replace(vbCrLf, vbLf)
    pasteText = pasteText.Replace(vbCr, vbLf)
    pasteText = pasteText.TrimEnd(New Char() {vbLf})
    lines = pasteText.Split(vbLf)

    Return lines

End Function

●各セルの値を設定

''' <summary>
''' 各セルの値を設定
''' </summary>
''' <param name="rowIndex"></param>
''' <param name="insertColIndexMin"></param>
''' <param name="vals"></param>
''' <param name="dt"></param>
Public Sub setCol(rowIndex, insertColIndexMin, vals, dt)

    Dim row As DataGridViewRow = DataGridView1.Rows(rowIndex)
    Dim i As Integer
    Dim colIndex As Integer = insertColIndexMin

    For i = 0 To vals.Length - 1

        row.Cells(colIndex).Value = vals(i)

        colIndex = colIndex + 1

        ''列が無ければ終了
        If colIndex >= dt.Columns.Count Then
            Exit For
        End If

    Next i

End Sub

参考記事:
DataGridViewで選択されたセルをクリップボードにコピーできるようにする
https://dobon.net/vb/dotnet/datagridview/clipboardcopy.html

C# DataGridViewでの値のコピー&ペーストの実装
https://effect.hatenablog.com/entry/2018/10/02/030816

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2