DataGridView Rows Copy

這一篇談一個小小的技巧,最近也常在MSDN看過類似的問題,許多同好都會提出如何把一個DataGridView的Row複製到另一個DataGridView,所以就想寫一個小小的範例讓需要的人參考。

       這一篇談一個小小的技巧,最近也常在MSDN看過類似的問題,許多同好都會提出如何把一個DataGridView的Row複製到另一個DataGridView,所以就想寫一個小小的範例讓需要的人參考。

       不論是DataRow還是DataGridViewRow,都有一個很重要的特點,它不會同時屬於兩個上層物件的執行個體;當一個DataRow屬於一個DataTable1的執行個體時,不可以直接把它指派給另一個DataTable執行個體,例如把它指派給另一個叫DataTable2的執行個體。例如說像以下這樣的寫法,你會得到一個「這個資料列已經屬於其他資料表」的錯誤訊息:       

        Dim myRow As DataRow
        myRow = myDatatable1.Rows(0)
        myDatatable2.Rows.Add(myRow)

       在DataGridView的狀況也是一樣,這樣的問題來自於當myRow = myDatatable1.Rows(0)時,它並不是By Value而是By Reference,所以基本上myRow和myDatatable1.Rows(0)此時是指向同一個參考,所以當我們想將myRow新增到myDatatable2時就會出錯。因此,正確的方法是將myDatatable1.Rows(0)中所有Item的值取出,再將值設定給myDatatable2的NewRow。可能有人會反應說:「為何不直接從DataGridViewRow取值,而要從DataRow取值?」因為以下程式的介紹會用到DataRow一個很有用的屬性:DataRow.ItemArray

        現在進入正題,開始來複製DataGridView吧。第一個步驟先在畫面上加入兩個dataGridView控制項,DataGridView1與DataGridView2,並且在主程式中設定其資料來源:  

Dim myDatatable1 As New DataTable
   Dim myDatatable2 As New DataTable
   Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
       DataGridView1.SelectionMode = DataGridViewSelectionMode.FullRowSelect
       DataGridView2.SelectionMode = DataGridViewSelectionMode.FullRowSelect
       myDatatable1.Columns.Add("Col1")
       myDatatable1.Columns.Add("Col2")
      myDatatable2 = myDatatable1.Clone

       Dim i As Integer
       For i = 0 To 9
           Dim myNewRow As DataRow = myDatatable1.NewRow
           myNewRow.Item(0) = i
           myNewRow.Item(1) = Chr(65 + i)
           myDatatable1.Rows.Add(myNewRow)
       Next
       DataGridView1.DataSource = myDatatable1
       DataGridView2.DataSource = myDatatable2
   End Sub

       粗體字的部份代表myDatatable2的欄位結構由myDatatable1複製而來,關於DataTable.Clone方法詳見MSDN文件庫。

       第一個實驗 ,完整的將DataGridView1複製到DataGridView2,最簡單的方法就是直接複製DataTable了,因此我們在畫面上新增一個Button,命名為BTN_CopyAllx,並在其Click事件寫入以下程式:
 

myDatatable2 = myDatatable1.Copy
DataGridView2.DataSource = myDatatable2

   

      這個簡單的兩行程式成功的將DataGridView1的資料複製到DataGridView2上了,不過有一個問題,不論我們按BTN_AllCopyx幾次,兩邊的資料永遠都是相同的,如果我想每按一次就會累加的複製呢?接下來為這個方案加入一個新的類別CSRowCopy,並且建立一個靜態的方法RowCopyAll:
 

Public Class CSRowCopy
    Public Shared Sub RowCopyAll(ByRef DGVFrom As DataGridView, ByRef DGVto As DataGridView)
        For Each DGVRow As DataGridViewRow In DGVFrom.Rows
            Dim myNewRow As DataRow = CType(DGVto.DataSource, DataTable).NewRow
           myNewRow.ItemArray = CType(DGVRow.DataBoundItem, System.Data.DataRowView).Row.ItemArray
            CType(DGVto.DataSource, DataTable).Rows.Add(myNewRow)
        Next
    End Sub
End Class

 

      粗體字的部份用到了兩個大有用處的屬性,DataRow.ItemArray 屬性DataGridViewRow.DataBoundItem 屬性 。DataRow.ItemArray提供了我們一次就可以取得或設定單一DataRow所有資料欄位內容值的功能,如此一來就可以不需要用迴圈一個個取出;而DataGridViewRow.DataBoundItem 屬性讓我們取得與某一DataGridViewRow繫結的資料來源。

     然後在主畫面中新增一個Button,命名為BTN_CopyAll,並在其Click事件寫入以下程式:

     CSRowCopy.RowCopyAll(DataGridView1, DataGridView2)

     當多次按下BTN_CopyAll時會發現,DataGridView2的資料量會一路累加,這就是目前我們所要的結果。 DGVCopy1

     再來就是要複製DataGridview1上所被選擇的列到dataGridView2上,在CSRowCopy中增加一個靜態方法:
 

Public Shared Sub RowCopy01(ByRef DGVFrom As DataGridView, ByRef DGVto As DataGridView)
        For Each DGVRow As DataGridViewRow In DGVFrom.SelectedRows
            Dim myNewRow As DataRow = CType(DGVto.DataSource, DataTable).NewRow
            myNewRow.ItemArray = CType(DGVRow.DataBoundItem, System.Data.DataRowView).Row.ItemArray
            CType(DGVto.DataSource, DataTable).Rows.Add(myNewRow)
        Next
End Sub

 

    然後在主畫面中新增一個Button,命名為BTN_Copy01,並在其Click事件寫入以下程式:

   CSRowCopy.RowCopy01(DataGridView1, DataGridView2)

   來執行一下,先選擇幾行,然後按下Copy01:

   DGVCopy2

 

 

       的確是將DataGridView1所選的列都複製到DataGridView2去了,可是有點不太對勁,順序好像反了?這件事情也令我百思不得其解,我猜測當使用DataGridView.SelectedRows屬性取得DataGridViewSelectedRowCollection時或許是使用堆疊的方式儲存位址參照,所以順序上會相反,如果哪位前輩瞭解真正個原因也請麻煩告訴我一下。

       既然它反過來了,我們就再反過來一次好了,利用堆疊先進後出的特性,在CSRowCopy中再增加一個靜態方法:
 

Public Shared Sub RowCopy02(ByRef DGVFrom As DataGridView, ByRef DGVto As DataGridView)
      Dim myStack As New Stack(Of Object())(DGVFrom.SelectedRows.Count - 1)
       For Each DGVRow As DataGridViewRow In DGVFrom.SelectedRows
          myStack.Push(CType(DGVRow.DataBoundItem, System.Data.DataRowView).Row.ItemArray)
       Next
      For Each OBJs As Object() In myStack
           Dim myNewRow As DataRow = CType(DGVto.DataSource, DataTable).NewRow
           myNewRow.ItemArray = OBJs
           CType(DGVto.DataSource, DataTable).Rows.Add(myNewRow)
       Next
   End Sub

 

    上面我們就來宣告一個Stack集合類別,先將DataGridView1.SelectRows的資料push進Stack中,然後再從Stack中取回再加入到DataGridView2的DataSource。並在主畫面中新增一個Button,命名為BTN_Copy01,並在其Click事件寫入以下程式:

   CSRowCopy.RowCopy02(DataGridView1, DataGridView2)

   執行結果如下,還挺符合需求的:

   DGVCopy3

    這一篇的範例程式可以在後面的連結下載:RowCopyTest.rar