Reputation: 37
I'm currently trying to copy selected rows from one DataGridView to another.
I'm trying to capture the value of the CheckBox, where if it's checked, then the entire row will be copied to another DataGridView.
For example, like add to cart then review cart. I've referred to the following post:
Copy selected datagridrow to new datagridview on different form
However it doesn't seem to help.
I've tried using a For
loop like the one below, but I'm not entirely sure how to go about this.
Private Sub btnAdd_Click(sender As Object, e As EventArgs) Handles btnAdd.Click
Dim dt As New DataTable()
AppendColumnsToDGV2()
For Each row As DataGridViewRow In DataGridView1.Rows
If row.Cells("SelectColumn").Value = True Then
Dim NewRow As DataRow
For i As Integer = 0 To row.Cells.Count - 1
NewRow(i) = row.Cells(i).Value
DataGridView2.Rows.Add(NewRow)
Next
End If
Next
AppendColumnsToDGV2
:
Private Sub AppendColumnsToDGV2()
Dim dt As New DataTable
'dt.Columns.Add(CreateDGVCheckBoxCol())
'dt.Columns.Add(CreateImageColumn())
dt.Columns.Add(DataGridView1.Columns(3).HeaderText)
dt.Columns.Add(DataGridView1.Columns(4).HeaderText)
dt.Columns.Add(DataGridView1.Columns(5).HeaderText)
dt.Columns.Add(DataGridView1.Columns(6).HeaderText)
DataGridView2.DataSource = dt
End Sub
What I'm doing here isn't working and I have no idea how to go about this.
Any help would be appreciated, thank you, kindly.
Whenever I run this code, I get the error:
System.NullReferenceException: Object reference not set to an instance of an object
I'm not sure how to fix it.
This is what the DataGridView looks like:
Upvotes: 2
Views: 1035
Reputation: 32288
This question is strictly related to the previous one:
Display images in a DataGridView column using JSON objects as DataSource
You're using a sub-class (Result
) of the RootObject
to fill the first DataGridView.
Modify the Result
class as follows:
Selected As Boolean
, decorated with a <JsonIgnore>
attribute. SelectionResult
here, a selection of properties of the Result
class that you think are needed in the second DataGridView to show the selected products.Result
class which returns a sub-section of itself as a SelectionResult
object. Public Class Result
<JsonIgnore>
Public Property Selected As Boolean
'(...)
Public Function GetSelectionResult() As SelectionResult
Return New SelectionResult With {
.ID = Me.id,
.Image = Me.Image,
.Name = Me.Name,
.ProductDescription = Me.ProductDescription,
.Department = Me.Department,
.Price = Me.Price,
.Unitprice = Me.Unitprice
}
End Function
End Class
Public Class SelectionResult
Public Property ID As Integer
Public Property Image As Bitmap
Public Property Name As String
Public Property ProductDescription As String
Public Property Department As String
Public Property Price As Decimal
Public Property Unitprice As Decimal
End Class
List(Of Class)
as Fields in the Form. The main class, in the previous question, was called ProductsQuery
, so I'm re-using the names already defined there: Private CurrentProducts As List(Of ProductsQuery.Result) = New List(Of ProductsQuery.Result)()
Private SelectedProducts As List(Of ProductsQuery.SelectionResult) = New List(Of ProductsQuery.SelectionResult)()
In the method that fills the first DataGridView, initialize the CurrentProducts
List:
CurrentProducts = New List(Of ProductsQuery.Result)()
After the JSON has beed deserialized, fill the List with the JSON results:
CurrentProducts.AddRange(JsonPost.uk.ghs.Products.Results)
In the event handler of the Button that adds the selected products to the second DataGridView, insert this code:
Edit:
The SelectedProducts
list preserves the items selected in the first DataGridView: only the items that are not already in the CurrentProducts
list are added to the selection.
The btnRemoveSelection
Button removes the selected items in the second DataGridView from the SelectedProducts
list. The DataGridView Row selection is somewhat cumbersome, so might want to add a CheckBox Column to ease the selection of the items to remove.
Private Sub btnAdd_Click(sender As Object, e As EventArgs) Handles btnAdd.Click
SelectedProducts.AddRange(CurrentProducts.
Where(Function(p) p.Selected = True AndAlso
(Not SelectedProducts.Any(Function(sp) sp.ID = p.id))).
Select(Function(p) p.GetSelectionResult()).ToArray())
ResetCart()
End Sub
Private Sub btnRemoveSelection_Click(sender As Object, e As EventArgs) Handles btnRemoveSelection.Click
If DataGridView2.SelectedRows.Count = 0 Then Return
Dim itemsRemoved As Boolean = False
Dim selectedItems() As Integer = DataGridView2.SelectedRows.
OfType(Of DataGridViewRow)().
Select(Function(r) CInt(r.Cells("ID").Value)).ToArray()
For Each ID As Integer In selectedItems
Dim currentIndex As Integer = SelectedProducts.FindIndex(Function(p) p.ID = ID)
If currentIndex >= 0 Then
SelectedProducts.RemoveAt(currentIndex)
itemsRemoved = True
End If
Next
If itemsRemoved Then
ResetCart()
End If
End Sub
Private Sub ResetCart()
DataGridView2.DataSource = Nothing
DataGridView2.DataSource = SelectedProducts
DataGridView2.Columns(0).Visible = False
DataGridView2.AutoResizeRows()
End Sub
This fills the List(Of SelectedProducs)
with the selected elements of the first DataGridView and sets the DataSource of the second DataGridView to this List.
Note that the first Column of the DataGridView is set to Visible = False
, because that Column corresponds to the ID
property of the element selected
The GetSelectionResult()
of the Result
class returns the properties values that have been defined in the SelectionResult
class. You can of course re-define this class to contain whatever properties you see fit.
This is the result of these modifications:
Upvotes: 5
Reputation: 15772
If you are databinding correctly, your underlying data will update when the checkbox is clicked. Then you can just use some LINQ. You should avoid iterating over your DataGridViewRows whenever possible (here it is possible) because they shouldn't hold the data, rather display it.
This simple example works in a form with two DataGridViews and one Button with default names, in vb.net.
Public Class Form1
Private allProducts As List(Of Product)
Private basketProducts As List(Of Product)
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
allProducts = New List(Of Product) From {
New Product() With {.Name = "Fairy Gel", .ID = 1},
New Product() With {.Name = "Fairy Caps", .ID = 2},
New Product() With {.Name = "Fairy Liquid", .ID = 3}}
DataGridView1.DataSource = allProducts
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
basketProducts = allProducts.Where(Function(p) p.Selected).ToList()
DataGridView2.DataSource = basketProducts
End Sub
' dummy class to emulate your data
Private Class Product
Public Property Selected As Boolean
Public Property Name As String
Public Property ID As Long
End Class
End Class
Upvotes: 2
Reputation: 885
You are currently using 2 separate DataTables. Also you are attempting to add row each time you set a column value. This might work for you.
Private Sub btnAdd_Click(sender As Object, e As EventArgs) Handles btnAdd.Click
Dim dt As New DataTable()
AppendColumnsToDGV2(dt)
For Each row As DataGridViewRow In DataGridView1.Rows
If row.Cells("SelectColumn").Value = True Then
Dim NewRow = dt.NewRow
For i As Integer = 0 To row.Cells.Count - 1
NewRow(i) = row.Cells(i).Value
Next
dt.Rows.Add(NewRow)
End If
Next
End Sub
Private Sub AppendColumnsToDGV2(dt As DataTable)
'dt.Columns.Add(CreateDGVCheckBoxCol())
'dt.Columns.Add(CreateImageColumn())
dt.Columns.Add(DataGridView1.Columns(3).HeaderText)
dt.Columns.Add(DataGridView1.Columns(4).HeaderText)
dt.Columns.Add(DataGridView1.Columns(5).HeaderText)
dt.Columns.Add(DataGridView1.Columns(6).HeaderText)
DataGridView2.DataSource = dt
End Sub
Upvotes: 0