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
};
}
}
}
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