【问题标题】:Copy selected rows from a DataGridView to another, including Image Column将选定的行从 DataGridView 复制到另一个,包括图像列
【发布时间】:2019-01-22 16:22:53
【问题描述】:

我目前正在尝试将选定的行从一个 DataGridView 复制到另一个。
我正在尝试捕获 CheckBox 的值,如果选中,则将整行复制到另一个 DataGridView。

例如,添加到购物车然后查看购物车。我参考了以下帖子:
Copy selected datagridrow to new datagridview on different form

但它似乎没有帮助。
我尝试过使用如下所示的For 循环,但我不完全确定该怎么做。

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

我在这里做的没有用,我不知道该怎么做。
任何帮助将不胜感激,谢谢。

每当我运行此代码时,我都会收到错误消息:

System.NullReferenceException:对象引用未设置为实例 一个对象

我不知道如何解决它。

这是 DataGridView 的样子:

【问题讨论】:

  • "DataGridView2.Rows.Add(NewRow)" 应该跟在第一个 Next 语句之后。您是否遇到任何错误,或者只是没有看到任何事情发生?
  • 另外,newRow 应该由 DataTable 构造。例如。 Dim newRow = dt.NewRow.
  • @JerryM 我收到错误System.NullReferenceException: 'Object reference not set to an instance of an object.'。当一个值显然没有被分配给它时,我正在使用数据行。
  • 您应该将 dt 传递给 AppendColumnsToDGV2 例程。示例 `Private Sub AppendColumnsToDGV2 (dt As DataTable)'。您目前正在使用 2 个单独的数据表。
  • 您还在使用previous question 中显示的 JSON 生成的类吗?如果是这样,解决方案真的很简单。

标签: .net vb.net image winforms datagridview


【解决方案1】:

本题与上一题严格相关:
Display images in a DataGridView column using JSON objects as DataSource

您正在使用 RootObject 的子类 (Result) 来填充第一个 DataGridView。

修改Result类如下:

  • 添加一个新属性 Selected As Boolean,用 <JsonIgnore> 属性装饰。
  • 在此处添加一个名为 SelectionResult 的新子类,选择您认为在第二个 DataGridView 中需要的 Result 类的属性显示所选产品。
  • Result 类添加一个复制方法,该类将自身的子部分作为 SelectionResult 对象返回。

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) 作为字段。在上一个问题中,主类被称为 ProductsQuery,所以我重新使用了那里已经定义的名称:

Private CurrentProducts As List(Of ProductsQuery.Result) = New List(Of ProductsQuery.Result)()
Private SelectedProducts As List(Of ProductsQuery.SelectionResult) = New List(Of ProductsQuery.SelectionResult)()
  • 在填充第一个DataGridView的方法中,初始化CurrentProducts列表:

    CurrentProducts = New List(Of ProductsQuery.Result)()
    
  • 在 JSON 被反序列化后,用 JSON 结果填充列表:

    CurrentProducts.AddRange(JsonPost.uk.ghs.Products.Results)
    

在将所选产品添加到第二个 DataGridView 的 Button 的事件处理程序中,插入以下代码:

编辑
SelectedProducts 列表保留在第一个 DataGridView 中选择的项目:仅将 CurrentProducts 列表中尚未包含的项目添加到选择中。

btnRemoveSelection 按钮从SelectedProducts 列表中删除第二个DataGridView 中的选定项。 DataGridView Row 选择有点麻烦,因此可能需要添加一个 CheckBox Column 来简化要删除的项目的选择。

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

这会用第一个 DataGridView 的选定元素填充 List(Of SelectedProducs),并将第二个 DataGridView 的 DataSource 设置为此 List。

请注意,DataGridView 的第一个 Column 设置为 Visible = False,因为该 Column 对应于所选元素的 ID 属性

Result 类的 GetSelectionResult() 返回已在 SelectionResult 类中定义的属性值。您当然可以重新定义此类以包含您认为合适的任何属性。


这是这些修改的结果:

【讨论】:

  • 感谢@Jimi,您知道我将如何实际获取第二个 DGV 中的数据并留在那里。我删除了DataGridView2.DataSource = NothingSelectedProducts.Clear() 行,但是没有区别。每当我再次搜索时,它们就消失了。
  • 你的意思是想让第二个DGV保留在不同时刻做出的不同选择的结果吗?作为选择存款还是购物车?
  • 没错,就像购物车一样,所有选定的结果都保留在其中,供最终用户随时查看。
  • 好吧,这是有道理的。我会尽快进行编辑。
  • 代码已更新。我添加了第二个按钮,允许从选择列表中删除项目。阅读笔记。看看合不合适。
【解决方案2】:

如果您的数据绑定正确,则您的基础数据将在单击复选框时更新。然后你可以使用一些 LINQ。您应该尽可能避免迭代您的 DataGridViewRows(这里是可能的),因为它们不应该保存数据,而是显示它。

这个简单的例子在一个带有两个 DataGridViews 和一个具有默认名称的按钮的表单中工作,在 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

【讨论】:

  • @SchmellerMeller 这不需要扩展,也不需要超出 WinForms 应用程序中的默认值的其他引用。你具体指的是什么?
  • @SchmellerMeller 在您的问题中,您试图对表单而不是基础数据进行操作,我只是指出您应该对数据进行操作。这就是解决问题的方法。实际上,我的解决方案与 Jimi 的解决方案相同(看起来很棒,因为他知道您之前的问题)。一般来说,我的解决方案解决了您的问题中提出的一般问题。 Jimi's 解决了您的具体问题,因为它是针对您之前的问题量身定制的。
  • 我的错。我刚刚意识到它基本上完全一样。哎呀。
【解决方案3】:

您当前正在使用 2 个单独的数据表。此外,您每次设置列值时都尝试添加行。这可能对你有用。

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

【讨论】:

  • 感谢@JerryM,但现在我收到错误'Rows cannot be programmatically added to the DataGridView's rows collection when the control is data-bound.',所以我将其更改为datagridview2.datasource.rows.add(newrow),现在我收到错误System.ArgumentException: 'This row already belongs to this table.'
  • 糟糕。是的。该行应添加到数据表中。我已经更新了答案。
  • 另外当使用dt.rows.add(newrow)我也得到this row already belongs to this table
  • 我忘了按照自己的建议将 Add 方法调用移到下面然后 Next 语句。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-09-14
  • 1970-01-01
  • 2021-08-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多