DataGridView 级联/相关组合框列 [英] DataGridView Cascading/Dependent ComboBox Columns

查看:34
本文介绍了DataGridView 级联/相关组合框列的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

因此,我不时使用 Winforms 处理遗留应用程序,但并不熟悉绑定对象的最佳实践.基本上我有一个由三个部分组成的套装,其中有两个人,他们可能只有一个产品,但该产品可能会导致有不同的 SKU 集.有没有办法从第一个组合框的值触发组合框的事件和填充?我一直在环顾四周,我正在寻找有关如何绑定组合框的基本数据(我可以做到这一点),或者对如何绑定它做一些事情.触发依赖父项更改并更改数据集后不绑定.下面的例子:

So I work from time to time in Winforms on a legacy app and am not familiar with best practices at all times with binding objects. Basically I have a three part set where I have two people, they may have only one product, but that product could cause the possibility to have different sets of SKUs. Is there a way to trigger an event and population of a combobox from the values of a first combobox? I have been looking around and I am either finding basic data of just how to bind a combobox(I can do that fine) or do something with how you bind it. Not binding after a dependent parent change is triggered and changing the dataset. Example below:

POCOS:

Public Class Person
  Public Property PersonID As Integer
  Public Property FirstName As String
  Public Property LastName As String
  Public Property ProductId As Integer
  Public Property SkuId As Integer
End Class

Public Class Product
  Public Property ProductId As Integer
  Public Property Description As String
End Class

Public Class Sku
  Public Property SKUId As Integer
  Public Property ProductId As Integer
  Public Property Description As String
End Class

主要代码示例(基本 UI 实际上只有一个标记为ds"的数据集,该数据集与具有数据表的人员和产品 POCOS 几乎完全相同.一个数据网格视图dgv",其列绑定到人员中的数据,除了名为 SKU 的列之外没有绑定,因为我想在事后绑定它,这就是我失败的地方.

Main code example (basic UI really only has a Datset labeled 'ds' that matches nearly identically the Person and Product POCOS with datatables. A datagridview 'dgv' whose columns are bound to data in Person EXCEPT for a column called SKU that has no binding as I want to bind it after the fact and that is where I am failing miserably at.

2016 年 9 月 13 日更新我可以让下面的代码在某些大型解决方案中工作除外(我这样做的全部原因).它基本上不会执行将 cell() 转换为 datagridviewcomboboxcell 的行并忽略它并跳过该行.没有理由,它只是跳过它.我想知道在较大的类中数据网格视图是否会损坏或其他什么.

Update 9-13-2016 I can get the below code to work EXCEPT in certain large scale solutions(the whole reason I did this). It basically will NOT execute the line that casts the cell() to a datagridviewcomboboxcell and ignores it and jumps over the line. No reason why, it just jumps over it. I am wondering if in larger classes the datagrid views can become corrupt or something.

主要代码:

Private _people As List(Of Person) = New List(Of Person)
Private _products As List(Of Product) = New List(Of Product)
Private _SKUs As List(Of Sku) = New List(Of Sku)
Private _initialLoadDone = False
Private _currentRow As Integer? = Nothing

Private Sub DynamicComboBoxDoubleFill_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    _products = New List(Of Product)({
                                     New Product With {.ProductId = 1, .Description = "Offline"},
                                     New Product With {.ProductId = 2, .Description = "Online"}
                                     })

    Dim s = ""
    For Each o In _products
      Dim row As DataRow = ds.Tables("tProducts").NewRow
      row("ProductId") = o.ProductId
      row("Description") = o.Description
      ds.Tables("tProducts").Rows.Add(row)
    Next

    _SKUs = New List(Of Sku)({
     New Sku With {.SKUId = 1, .ProductId = 1, .Description = "Mail"},
     New Sku With {.SKUId = 2, .ProductId = 1, .Description = "Magazine"},
     New Sku With {.SKUId = 3, .ProductId = 2, .Description = "Email"},
     New Sku With {.SKUId = 4, .ProductId = 2, .Description = "APIRequest"}
    })

    Dim items = _SKUs

    _people = New List(Of Person)({
      New Person With {.PersonID = 1, .FirstName = "Emily", .LastName = "X", .ProductId = 1, .SkuId = 1},
      New Person With {.PersonID = 2, .FirstName = "Brett", .LastName = "X", .ProductId = 2, .SkuId = 3}
                                  })
    For Each p In _people
      Dim row As DataRow = ds.Tables("tPeople").NewRow
      row("PersonId") = p.PersonId
      row("FirstName") = p.FirstName
      row("LastName") = p.LastName
      row("ProductId") = p.ProductId
      row("SkuId") = p.SkuId
      ds.Tables("tPeople").Rows.Add(row)
    Next

    For Each row As DataGridViewRow In dgv.Rows
      ArrangeValuesForSKUComboBox(row)
    Next

    _initialLoadDone = True
  End Sub

  Private Sub ArrangeValuesForSKUComboBox(row As DataGridViewRow)
    Dim productId = CInt(row.Cells("ProductId")?.Value)
    Dim skus = _SKUs.Where(Function(x) x.ProductId = productId).ToList().Select(Function(x) New With {Key .SkuId = x.SKUId, .SkuDesc = x.Description}).ToList()

    Dim cell = row.Cells("SKU")
    'Yeah I don't always work.  In this example I do, in others I won't.
    'For this reason I just want more ideas.  I don't care if you completely blow up how the binding is done and do something else entirely.
    Dim combobox = CType(cell, DataGridViewComboBoxCell)
    combobox.DataSource = skus
    combobox.ValueMember = "SKUId"
    combobox.DisplayMember = "SkuDesc"
    combobox.Value = skus.FirstOrDefault()?.SkuId
  End Sub

  Private Sub dgv_CellValueChanged(sender As Object, e As DataGridViewCellEventArgs) Handles dgv.CellValueChanged
    If _initialLoadDone Then
      Dim headerText As String = TryCast(sender, DataGridView).Columns(e.ColumnIndex).HeaderText
      If headerText = "PRODUCT" Then
        ArrangeValuesForSKUComboBox(dgv?.CurrentRow)
      End If
    End If
  End Sub

推荐答案

要在 DataGridView 中有依赖(级联或主/从)ComboBox 列,可以按照这个步骤:

To have dependent (cascading or master/slave) ComboBox columns in DataGridView, you can follow this steps:

  1. 设置从列的 DataSource 为所有可用值.

目标:这里的目标是防止首次加载时出现渲染错误,因此所有从属组合框都可以正确显示值.

Goal: Here the goal is prevent rendering errors at first load, so all slave combo boxes can show value correctly.

Hanlde EditingControlShowing 网格事件并检查当前单元格是否为从属组合单元格,然后使用类型为 e.Control 获取编辑控件DataGridViewComboBoxEditingControl.然后检查主组合单元格的值,并根据主组合单元格的值将编辑控件的DataSource 属性设置为合适的值子集.如果主单元格的值为空,则将数据源设置为空.

Hanlde EditingControlShowing event of the grid and check if the current cell is slave combo cell, then get the editing control using e.Control which is of type DataGridViewComboBoxEditingControl. Then check the value of master combo cell and set the DataSource property of editing control to a suitable subset of values based on the value of master combo cell. If the value of master cell is null, set the data source to null.

目标:这里的目标是设置从组合的数据源,以便在从从组合中选择值时只显示合适的值.

Goal: Here the goal is setting data source of slave combo to show only suitable values when selecting values from slave combo.

处理CellValueChanged并检查当前单元格是否为主组合,然后将依赖单元格的值设置为空.
注意: 您可以根据主单元值将其设置为第一个可用的有效值,而不是将从单元的值设置为空.

Handle CellValueChanged and check if the current cell is master combo, then set the value for dependent cell to null.
Note: Instead of setting the value of slave cell to null, you can set it to first available valid value based on master cell value.

目标:这里的目标是防止主combo的值改变后slave的combo有无效的值,所以我们重置这个值.

Goal: Here the goal is prevent the slave combo to have invalid values after the value of master combo changed, so we reset the value.

遵循上述规则,您可以根据需要拥有任意数量的依赖组合框.

Following above rules you can have as many dependent combo boxes as you need.

示例

在下面的示例中,我有一个 Country (Id, Name) 表、一个 State(Id, Name, CountryId) 表和一个 Population(CountryId, StateId, Population) 表.我想使用国家和州的 2 个组合列和人口的文本列来执行人口表的数据输入.我知道这不是一个正常的数据库设计,但它只是例如在网格中有主/从(从属)组合框列:

In below example I have a Country (Id, Name) table, a State(Id, Name, CountryId) table and a Population(CountryId, StateId, Population) table. And I want to perform data entry for Population table using 2 combo columns for country and state and a text column for population. I know this is not a normal db design, but it's just for example of having master/slave (dependent) combo box columns in grid:

Private Sub EditingControlShowing(sender As Object, _
    e As DataGridViewEditingControlShowingEventArgs) _
    Handles PopulationDataGridView.EditingControlShowing

    Dim grid = DirectCast(sender, DataGridView)
    If (grid.CurrentCell.ColumnIndex = 1) Then 'State column
        Dim combo = DirectCast(e.Control, DataGridViewComboBoxEditingControl)
        If (grid.CurrentRow.Cells(0).Value IsNot DBNull.Value) Then
            Dim data = Me.DataSet1.State.AsDataView()
            data.RowFilter = "CountryId = " + grid.CurrentRow.Cells(0).Value.ToString()
            combo.DataSource = data
        Else
            combo.DataSource = Nothing
        End If
    End If
End Sub

Private Sub CellValueChanged(sender As Object, e As DataGridViewCellEventArgs) _
    Handles PopulationDataGridView.CellValueChanged
    Dim grid = DirectCast(sender, DataGridView)
    If (e.ColumnIndex = 0 And e.RowIndex >= 0) Then 'Country Column
        grid.Rows(e.RowIndex).Cells(1).Value = DBNull.Value 'State Column 
    End If
End Sub

这篇关于DataGridView 级联/相关组合框列的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆