NoWar
NoWar

Reputation: 37633

How to populate ComboBox DataGridView depending on Row ID

I use DataGridViewComboBoxColumn in DataGridView. What I need is to filter ComboBox values depending on each Row ID.

Si it should look like

-----------------------------
Row ID       ComboBox
              Values
            ID    Name
-----------------------------
1            1     A
             2     B
-----------------------------               
2            3     C
             4     D
-----------------------------

So what I do is creating DataGridViewComboBoxColumn with empty DataSource like it has been shown in code below

var d = new DataGridViewComboBoxColumn()
{
 HeaderText = columnHeader,
 Width = 50,
 DataPropertyName = "D" + i.ToString(),
 DataSource = new BindingSource(){ DataSource = new List<WorkTypeItem>() },
 ValueMember = "ID",
 DisplayMember = "Name",
 Name = "D" + i.ToString()
};
dgvCalendar.Columns.Add(d);

And I assume to populate Row ComboBox for each cell in the CellFormatting event.

 private void DgvCalendar_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{

  // Get object bounded with Row  
  CalendarDetailView row = dgvCalendar.Rows[e.RowIndex].DataBoundItem as CalendarDetailView;

  // By row ID  filter list for cell ComboBox
  var workItems = MyWorkItems.Where(x => x.ID == row.ID); 

  // So how we can to populate cell ComboBox now?
  dgvCalendar.Rows[e.RowIndex].Cells[1] ..... ??? = workItems ;


}

I do this because I cannot display all possible values in the cell ComboBox because they are not suite for the each row. So the idea is not display 1000 values in the ComboBox but only values referenced by Row ID.

My objective is the each cell has its own list of the values but from the same List().

Any clue how to do it?


UPDATE #1

I just get working the code thank to Marco Guignard! I am just update it for C# version.

private void DgvCalendar_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
   if (e.Control is ComboBox)
   {
        int currentKey = (int)dgvCalendar.CurrentRow.Cells[dgvCalendar.CurrentCell.ColumnIndex].Value;

        ComboBox editor = (ComboBox)e.Control;
        CalendarDetailView row = dgvCalendar.CurrentRow.DataBoundItem as CalendarDetailView;

       var filteredItems = WorkTypes.Where(x =>x.ID = row.ID).ToList();

       editor.DataSource = filteredItems;
        editor.SelectedValue = currentKey;
   }           
}

UPDATE #2

Acctually I've finished up it with a similar approach but using a different event.

So initially on column creation logic I use all possible records from the database's table to initialize DataGridView with values and when DataGridView appears we see it.

And when we have to edit combobox cell we just apply objects allowed value list.

private void DgvCalendar_CellBeginEdit(object sender, DataGridViewCellCancelEventArgs e)
    {
        if (dgvCalendar.Columns[e.ColumnIndex].Name.StartsWith("D"))
        {
           var isComboBox = dgvCalendar.CurrentCell is DataGridViewComboBoxCell;
           if (isComboBox)
           {
               DataGridViewComboBoxCell dgViewComboBoxCell = dgvCalendar.CurrentCell as DataGridViewComboBoxCell;

               CalendarDetailView row = dgvCalendar.Rows[e.RowIndex].DataBoundItem as CalendarDetailView;

               dgViewComboBoxCell.DataSource = new BindingSource()
               {
                 DataSource = row.WorkTypes
               };
          }
      }
    }

Upvotes: 0

Views: 2255

Answers (1)

Marco Guignard
Marco Guignard

Reputation: 653

You have to handle the EditingControlShowing Event.

First you have to check the CurrentCell Column of your datagridview is corresponding to the combobox column. Then you can change the combobox editor datasource as you wish.

Don't forget to keep a global list as datasource for the column, as it will still be used for the display of the cells !

Here is a sample in VB.NET (the FixupDropDownWidth is not needed, it's just a bonus for a better UI display).

        Private Sub DataGridView_EditingControlShowing(sender As System.Object, e As System.Windows.Forms.DataGridViewEditingControlShowingEventArgs) Handles DataGridView.EditingControlShowing
                'Route the event depending the cells being edited
                Try
                    If Me.DataGridView.CurrentCell.OwningColumn Is Me.DataGridView.Columns("comboboxcolumnname") Then
                        comboboxcolumnname_EditingControlShowing(sender, e)
                    End If
                Catch ex As Exception
    'Exception handling...
                End Try
            End Sub

    Private Sub comboboxcolumnname_EditingControlShowing(sender As System.Object, e As System.Windows.Forms.DataGridViewEditingControlShowingEventArgs)
        Dim editor As ComboBox = CType(e.Control, ComboBox)
        Dim currentKey As Short = Me.DataGridView.CurrentRow.Cells("comboboxcolumnname").Value

        'Filter editor datasource, remove inactives items except current
        Dim listObject As New List(Of Object)
        listObject.AddRange(GlobalList.Where(Function(c) c.FilterCondition = True OrElse c.ID = currentKey).OrderBy(Function(c) c.Name))
        editor.DataSource = listObject

        editor.SelectedValue = currentKey

        'Adapt the width of the DropDown
        CType(Me.DataGridView.Columns("comboboxcolumnname"), DataGridViewComboBoxColumn).FixupDropDownWidth(listObject)
    End Sub

 <Extension()>
    Public Sub FixupDropDownWidth(column As DataGridViewComboBoxColumn, items As IEnumerable)
        Dim width As Integer = column.DropDownWidth

        Dim vertScrollBarWidth As Integer = 0
        If column.Items.Count > column.MaxDropDownItems Then vertScrollBarWidth = SystemInformation.VerticalScrollBarWidth

        Dim g As Graphics = column.DataGridView.CreateGraphics()
        Dim font As Font

        font = column.DefaultCellStyle.Font
        If font Is Nothing Then font = column.DataGridView.Font

        Dim maxWidth As Integer = 0
        For Each item In items
            Dim stringValue As String
            If item.GetType.GetProperty(column.DisplayMember) IsNot Nothing Then
                stringValue = CStr(item.GetType().GetProperty(column.DisplayMember).GetValue(item, Nothing))
            Else
                stringValue = item.ToString
            End If
            maxWidth = g.MeasureString(CStr(stringValue), font).Width + vertScrollBarWidth
            If width < maxWidth Then width = maxWidth
        Next

        column.DropDownWidth = width
    End Sub

Upvotes: 1

Related Questions