這一篇談一個小小的技巧,最近也常在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的資料量會一路累加,這就是目前我們所要的結果。
再來就是要複製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:
的確是將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)
執行結果如下,還挺符合需求的:
這一篇的範例程式可以在後面的連結下載:RowCopyTest.rar