title: How do you assign different data sources for a DataGridViewComboBox for each record?
body: <p>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).</p>
  
  <p>Here's what I'm working with - note: The datasource of the DataGridView is defined programmatically by setting a property from the calling form.</p>
  
  <pre><code>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
  </code></pre>
  
  <p>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.</p>
  
  <hr>
  
  <p>Another thing I've tried, but does not work (the combobox datasource just disappears!)</p>
  
  <pre><code>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
  </code></pre>
  
  <hr>
  
  <p>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.</p>
  
  <pre><code>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
  </code></pre>