如何为每个记录的DataGridViewComboBox分配不同的数据源? [英] How do you assign different data sources for a DataGridViewComboBox for each record?

查看:246
本文介绍了如何为每个记录的DataGridViewComboBox分配不同的数据源?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个winforms DataGridView,我希望有一个列包含组合框的每个记录。每个组合框对于每一行都将有完全不同的值,并且需要在DataGridView的数据绑定期间分配一个数据源。我可以为所有(这很容易)分配1数据源,但是每个组合框有不同的值看起来不可能。)

I have a winforms DataGridView that I wish to have a column containing comboboxes for each record. Each combobox will have completely different values for each row and need to be assigned a datasource during the databinding of the DataGridView. I can assign 1 datasource to all (that's easy), but having each combobox have different values looks impossible).

这是我的工作 - 注意:数据源DataGridView是通过从调用表单中设置属性以编程方式定义的。

Here's what I'm working with - note: The datasource of the DataGridView is defined programmatically by setting a property from the calling form.

Public Class frmSendToQuickbooksPopup

    Public Property CurrentOrder As OrderICT
    Public Property lineitems As List(Of OrderLineItemICT)

    Private Sub frmSendToQuickbooksPopup_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        'define the combobox (datasource can't be assigned here as each will be different for each row)
        Dim dgvcboMatch As New DataGridViewComboBoxColumn
        dgvcboMatch.DisplayMember = "Name"
        dgvcboMatch.ValueMember = "ListID"
        dgvcboMatch.HeaderText = "Matches"
        dgvcboMatch.Name = "Match"
        dgvcboMatch.Width = 150
        dgvLineItems.Columns.Add(dgvcboMatch)

        Me.dgvLineItems.DataSource = lineitems
    End Sub

    Private Sub dgvLineItems_DataSourceChanged(sender As Object, e As EventArgs) Handles dgvLineItems.DataSourceChanged
        Dim L As New QBI.OrderLineItemICT
        With L
            dgvLineItems.Columns(.col_LineItemBvin).Visible = False
            dgvLineItems.Columns(.col_ProductId).Visible = False
            dgvLineItems.Columns(.col_ProductShortDescription).Visible = False
            dgvLineItems.Columns(.col_ShippingBoxCount).Visible = False
            dgvLineItems.Columns(.col_CustomProperties).Visible = False
            dgvLineItems.Columns(.col_ShippingLength).Visible = False
            dgvLineItems.Columns(.col_ShippingWidth).Visible = False
            dgvLineItems.Columns(.col_ShippingHeight).Visible = False
            dgvLineItems.Columns(.col_ProductName).DisplayIndex = 2
            dgvLineItems.Columns(.col_ProductName).HeaderText = "Product Name"
            dgvLineItems.Columns(.col_ProductName).Width = 170
            dgvLineItems.Columns(.col_ProductSku).HeaderText = "SKU"
            dgvLineItems.Columns(.col_ProductSku).Width = 160
            dgvLineItems.Columns(.col_Quantity).DefaultCellStyle.Format = "n0"
            dgvLineItems.Columns(.col_Quantity).DisplayIndex = 7
            dgvLineItems.Columns(.col_Quantity).DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter
            dgvLineItems.Columns(.col_Quantity).Width = 65
            dgvLineItems.Columns(.col_AdjustedPrice).DefaultCellStyle.Format = "c2"
            dgvLineItems.Columns(.col_AdjustedPrice).HeaderText = "Adj Price"
            dgvLineItems.Columns(.col_AdjustedPrice).DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight
            dgvLineItems.Columns(.col_AdjustedPrice).Width = 80
            dgvLineItems.Columns(.col_BasePrice).DefaultCellStyle.Format = "c2"
            dgvLineItems.Columns(.col_BasePrice).HeaderText = "Base Price"
            dgvLineItems.Columns(.col_BasePrice).DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight
            dgvLineItems.Columns(.col_BasePrice).Width = 85
            dgvLineItems.Columns(.col_Discounts).DefaultCellStyle.Format = "c2"
            dgvLineItems.Columns(.col_Discounts).HeaderText = "Discounts"
            dgvLineItems.Columns(.col_Discounts).DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight
            dgvLineItems.Columns(.col_Discounts).Width = 80
            dgvLineItems.Columns(.col_LineTotal).DefaultCellStyle.Format = "c2"
            dgvLineItems.Columns(.col_LineTotal).HeaderText = "Line Total"
            dgvLineItems.Columns(.col_LineTotal).DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight
            dgvLineItems.Columns(.col_LineTotal).Width = 80
            dgvLineItems.Columns(.col_UOM).DisplayIndex = 9
            dgvLineItems.Columns(.col_UOM).DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter
            dgvLineItems.Columns(.col_UOM).HeaderText = "Units"
            dgvLineItems.Columns(.col_UOM).Width = 55
            dgvLineItems.Columns(.col_ShippingWeight).DisplayIndex = 10
            dgvLineItems.Columns(.col_ShippingWeight).HeaderText = "Unit Wt"
            dgvLineItems.Columns(.col_ShippingWeight).DefaultCellStyle.Format = "N1"
            dgvLineItems.Columns(.col_ShippingWeight).DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight
            dgvLineItems.Columns(.col_ShippingWeight).Width = 70
        End With
    End Sub

Public Function GetComboboxData(ProductNameToMatch As String, ProductSKUToMatch As String) As List(Of WPM_Item)
    'this function returns a list of returned matches for a given row's ProductName or SKU
    'this function will be the datasource for a given combobox
    Dim itms As New List(Of WPM_Item)
    itms = WPM_Data.FindWPM_ItemMatch(ProductNameToMatch, ProductSKUToMatch)
    Return itms
End Function



End Class

如果有一些方法来拦截DataGridView的数据绑定,所以我可以从另一个单元格中获取一个值(调用另一个过程),然后使用返回数据填充每行的组合框。这是很容易的w / webforms,但我没有看到winforms版本的事件。

If there were some way to intercept the databinding of the DataGridView so I can get a value from another cell (to call another procedure), then use the return data to populate each row's combobox. This is easy w/webforms, but I don't see an event for the winforms version.

Private Sub dgvLineItems_DataBindingComplete(sender As Object, e As DataGridViewBindingCompleteEventArgs) Handles dgvLineItems.DataBindingComplete
    For Each row As DataGridViewRow In Me.dgvLineItems.Rows
        Dim dr = DirectCast(row.DataBoundItem, OrderLineItemICT)
        If dr Is Nothing Then
            Return
        End If

        Dim name As String = row.Cells(dr.col_ProductName).Value.ToString
        Dim SKU As String = row.Cells(dr.col_ProductSku).Value.ToString

        Dim cell As DataGridViewComboBoxCell = TryCast(row.Cells("Match"), DataGridViewComboBoxCell)
        cell.DisplayStyle = DataGridViewComboBoxDisplayStyle.ComboBox

        Dim wpm_items As List(Of WPM_Item) = GetComboboxData(name, SKU)
        If cell.DataSource Is Nothing Then
            cell.DataSource = wpm_items 'this forgets the datasource, why?
            cell.DisplayMember = "Name"
            cell.ValueMember = "ListID"
            cell.Value = wpm_items.Item(0).Name 'this always throws an error "DataGridViewComboBoxCell value is not valid."
        End If

    Next

End Sub


$ b b




这是同一个赋值的另一个版本,这次组合框用add方法填充。 (STILL不记得值)
我基本上遵循另一个论坛的想法 - 也许DotNet 4.5 DataGridView有一个错误。


Here's another version of that same assignment, this time the combobox is populated with the add method. (STILL does not remember the values) I basically followed another forum's idea - maybe DotNet 4.5 DataGridView has a bug in it.

Private Sub dgvLineItems_DataBindingComplete(sender As Object, e As DataGridViewBindingCompleteEventArgs) Handles dgvLineItems.DataBindingComplete
    For Each row As DataGridViewRow In Me.dgvLineItems.Rows
        Dim dr = DirectCast(row.DataBoundItem, OrderLineItemICT)
        If dr Is Nothing Then
            Return
        End If

        Dim name As String = row.Cells(dr.col_ProductName).Value.ToString
        Dim SKU As String = row.Cells(dr.col_ProductSku).Value.ToString

        Dim cell As DataGridViewComboBoxCell = DirectCast(row.Cells("Match"), DataGridViewComboBoxCell)
        cell.DisplayStyle = DataGridViewComboBoxDisplayStyle.ComboBox

        Dim wpm_items As List(Of WPM_Item) = GetComboboxData(name, SKU)

        'THIS DOES NOT WORK EITHER!! 
        cell.DisplayMember = "Name"
        cell.ValueMember = "ListID"
        For Each wItm As WPM_Item In wpm_items
            Dim c As New comboboxitem
            c.Text = wItm.Name
            c.Value = wItm.ListID
            cell.Items.Add(c)
        Next
    Next
End Sub
Public Class ComboboxItem
    Public Property Text() As String
    Public Property Value() As Object

    Public Overrides Function ToString() As String
        Return Text
    End Function
End Class


推荐答案

Allrighty Then!这里有一个工作的解决方案。我发现的是,你必须以编程方式创建你的DataGridView(繁琐,是的!)和关闭自动生成列(绝对必须) - 这防止dataGridView事件的多个激发。它还防止丢失您的组合框的数据源。使用DataBindingComplete事件并解析其中的行是执行此操作的最佳方式(CellFormatting事件是overkill,并称为w /每个鼠标移动,单击,调整大小等)。
对于我们使用对象而不是数据表,我希望这个解决方案是可用的。

Allrighty Then! Here's a working solution. What I discovered is that you must create your DataGridView programmatically (tedious, yes!) and turn off auto-generation of columns (absolute must) - this prevents multiple firings of dataGridView events, for one. It also prevents losing your datasource for your combobox. Using the DataBindingComplete event and parsing the rows within is the best way to do this (CellFormatting event is overkill and is called w/every mouse move, click, resize, etc). For those of us working with objects instead of datatables, I hope this solution is usable.

代码:

Imports QBI
Imports QBI.QBI
Imports QBI.AppCore.Xutilities

Public Class TestDGV1

    Public dgv1 As DataGridView

    Private Sub TestDGV1_Load(sender As Object, e As EventArgs) Handles MyBase.Load

        Dim ordlineitems As List(Of OrderLineItemICT)
        ordlineitems = Order_DataICT.GetNewOrderLineItemsICT("26073bff-3a08-4bc2-8da9-79c75534bd6b")
        dgv1 = Me.CreateDGV(ordlineitems) 'create the DataGridView and save it as "dgv1" (which must be publically accessible)

        Me.SplitContainer1.Panel2.Controls.Add(dgv1) 'NOTE, the datagridview is inside a SplitContainer
    End Sub

    Public Function CreateDGV(dsItems As List(Of OrderLineItemICT)) As DataGridView
        Dim dgv As New DataGridView()
        dgv.Dock = DockStyle.Fill
        dgv.DataSource = dsItems
        dgv.EditMode = DataGridViewEditMode.EditOnEnter
        dgv.AutoGenerateColumns = False
        dgv.AllowUserToAddRows = False

        Dim L As New QBI.OrderLineItemICT 'only using this reference for my column names defined elsewhere

        Dim col0 As New DataGridViewComboBoxColumn With {.Name = "Match", .DataPropertyName = "NameMatch", .DisplayMember = "Name", .ValueMember = "ListID", .HeaderText = "Matches", .AutoComplete = True}
        col0.DisplayIndex = 0

        Dim col1 As New DataGridViewTextBoxColumn With {.Name = L.col_LineItemBvin, .DataPropertyName = L.col_LineItemBvin, .Visible = False, .ReadOnly = True, .HeaderText = L.col_LineItemBvin}
        col1.DisplayIndex = 1

        Dim col2 As New DataGridViewTextBoxColumn With {.Name = L.col_ProductId, .DataPropertyName = L.col_ProductId, .Visible = False, .ReadOnly = True, .HeaderText = L.col_ProductId}
        col2.DisplayIndex = 2

        Dim col3 As New DataGridViewTextBoxColumn With {.Name = L.col_ProductSku, .DataPropertyName = L.col_ProductSku, .Visible = True, .ReadOnly = True, .HeaderText = "SKU"}
        col3.DisplayIndex = 3
        col3.Width = 160
        Dim col4 As New DataGridViewTextBoxColumn With {.Name = L.col_Quantity, .DataPropertyName = L.col_Quantity, .Visible = True, .ReadOnly = True, .HeaderText = "QTY"}
        col4.DisplayIndex = 9
        col4.Width = 65
        col4.DefaultCellStyle.Format = "n0"
        col4.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter

        Dim col5 As New DataGridViewTextBoxColumn With {.Name = L.col_BasePrice, .DataPropertyName = L.col_BasePrice, .Visible = True, .ReadOnly = True, .HeaderText = "Base Price"}
        col5.DisplayIndex = 6
        col5.DefaultCellStyle.Format = "c2"
        col5.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight
        col5.Width = 85

        Dim col6 As New DataGridViewTextBoxColumn With {.Name = L.col_Discounts, .DataPropertyName = L.col_Discounts, .Visible = True, .ReadOnly = True, .HeaderText = "Discounts"}
        col6.DisplayIndex = 7
        col6.DefaultCellStyle.Format = "c2"
        col6.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight
        col6.Width = 80

        Dim col7 As New DataGridViewTextBoxColumn With {.Name = L.col_AdjustedPrice, .DataPropertyName = L.col_AdjustedPrice, .Visible = True, .ReadOnly = True, .HeaderText = "Adj Price"}
        col7.DisplayIndex = 8
        col7.DefaultCellStyle.Format = "c2"
        col7.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight
        col7.Width = 80

        Dim col8 As New DataGridViewTextBoxColumn With {.Name = L.col_LineTotal, .DataPropertyName = L.col_LineTotal, .Visible = True, .ReadOnly = True, .HeaderText = "Line Total"}
        col8.DisplayIndex = 11
        col8.DefaultCellStyle.Format = "c2"
        col8.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight
        col8.Width = 80

        Dim col9 As New DataGridViewTextBoxColumn With {.Name = L.col_ProductName, .DataPropertyName = L.col_ProductName, .Visible = True, .ReadOnly = True, .HeaderText = "Product Name"}
        col9.DisplayIndex = 4
        col9.Width = 170

        Dim col10 As New DataGridViewTextBoxColumn With {.Name = L.col_ProductShortDescription, .DataPropertyName = L.col_ProductShortDescription, .Visible = False, .ReadOnly = True, .HeaderText = L.col_ProductShortDescription}
        col10.DisplayIndex = 5

        Dim col11 As New DataGridViewTextBoxColumn With {.Name = L.col_ShippingWeight, .DataPropertyName = L.col_ShippingWeight, .Visible = True, .ReadOnly = True, .HeaderText = "Unit Wt"}
        col11.DisplayIndex = 12
        col11.DefaultCellStyle.Format = "N1"
        col11.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight
        col11.Width = 70
        Dim col12 As New DataGridViewTextBoxColumn With {.Name = L.col_ShippingWidth, .DataPropertyName = L.col_ShippingWidth, .Visible = False, .ReadOnly = True, .HeaderText = L.col_ShippingWidth}
        col12.DisplayIndex = 13

        Dim col13 As New DataGridViewTextBoxColumn With {.Name = L.col_ShippingHeight, .DataPropertyName = L.col_ShippingHeight, .Visible = False, .ReadOnly = True, .HeaderText = L.col_ShippingHeight}
        col13.DisplayIndex = 14

        Dim col14 As New DataGridViewTextBoxColumn With {.Name = L.col_ShippingBoxCount, .DataPropertyName = L.col_ShippingBoxCount, .Visible = False, .ReadOnly = True, .HeaderText = L.col_ShippingBoxCount}
        col14.DisplayIndex = 15

        Dim col15 As New DataGridViewTextBoxColumn With {.Name = L.col_CustomProperties, .DataPropertyName = L.col_CustomProperties, .Visible = False, .ReadOnly = True, .HeaderText = L.col_CustomProperties}
        col15.DisplayIndex = 16

        Dim col16 As New DataGridViewTextBoxColumn With {.Name = L.col_UOM, .DataPropertyName = L.col_UOM, .Visible = True, .ReadOnly = True, .HeaderText = "Units"}
        col16.DisplayIndex = 10
        col16.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter
        col16.Width = 55

        dgv.Columns.AddRange({col0, col1, col2, col3, col4, col5, col6, col7, col8, col9, col10, col11, col12, col13, col14, col15, col16})

        AddHandler dgv.DataBindingComplete, AddressOf dgvLineItems_DataBindingComplete

        Return dgv
    End Function

    Private Sub dgvLineItems_DataBindingComplete(sender As Object, e As DataGridViewBindingCompleteEventArgs)
        For Each row As DataGridViewRow In Me.dgv1.Rows
            Dim dr = DirectCast(row.DataBoundItem, OrderLineItemICT)
            If dr Is Nothing Then
                Return
            End If

            Dim name As String = row.Cells(dr.col_ProductName).Value.ToString
            Dim SKU As String = row.Cells(dr.col_ProductSku).Value.ToString
            Dim cell As DataGridViewComboBoxCell = DirectCast(row.Cells("Match"), DataGridViewComboBoxCell)
            Dim wpm_items As List(Of WPM_Item) = GetComboboxData(name, SKU) 'function inside
            If cell.DataSource Is Nothing Then
                cell.DataSource = wpm_items 
                cell.DisplayMember = "Name"
                cell.ValueMember = "ListID"
                cell.Value = cell.Items(0).ListID
            End If
        Next
    End Sub

    Public Function GetComboboxData(ProductNameToMatch As String, ProductSKUToMatch As String) As List(Of WPM_Item)
        'this function returns a list of returned matches for a given row's ProductName or SKU
        'this function will be the datasource for a given combobox
        Dim itms As New List(Of WPM_Item)
        itms = WPM_Data.FindWPM_ItemMatch(ProductNameToMatch, ProductSKUToMatch)
        Return itms
    End Function

End Class

这篇关于如何为每个记录的DataGridViewComboBox分配不同的数据源?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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