Introduction
This article explains how to get a DataGridViewComboBoxColumn
with a contextual list of elements depending on the selected row. Even if I use a BindingSource
with a BindingList
, I realized that the DataSource
of a DataGridViewComboBoxColumn
can’t be filtered dynamically. My method consists in keeping the entire list of the DataSource
and just show filtered items in the DataGridViewComboBoxCell
.
Background
Using the Code
The first step is to create my own DataGridViewComboBoxColumn
: ContextualDataGridViewComboBoxColumn
.
public class ContextualDataGridViewComboBoxColumn : DataGridViewComboBoxColumn
{
public ContextualDataGridViewComboBoxColumn()
{
CellTemplate = new ContextualDataGridViewComboBoxCell();
}
public IEnumerable<object> FilteredRequest { get; set; }
}
I add to this class a property of IEnumarable<object>
where the contextual query can be stocked to get the list of authorized elements to show.
In the same way, I create my own DataGridViewComboBoxCell
(ContextualDataGridViewComboBoxCell
) and instance it into CellTemplate
in the constructor of my ContextualDataGridViewComboBoxColumn
.
public class ContextualDataGridViewComboBoxCell : DataGridViewComboBoxCell
{
public override Type EditType
{
get
{
return typeof(ContextualDataGridViewComboBoxEditingControl);
}
}
public IEnumerable<object> FilteredRequest
{
get
{
return (OwningColumn as ContextualDataGridViewComboBoxColumn).FilteredRequest;
}
}
}
The FilteredRequest
is obtained from its owning column. The EditType
property is overridden to apply my own DataGridViewComboBoxEditingControl
which represents the hosted combo box control in a DataGridViewComboBoxCell
.
Indeed, to prevent any changes in the DataSource
, I write a personal DataGridViewComboBoxEditingControl
to graphically show filtered elements only.
The FilteredRequest
is obtained through the CurrentCell
of the dataGridView
. To draw and resize elements of the dropdown list by yourself, set the DrawMode
to OwnerDrawVariable
. Then, we can override OnMeasureItem
and OnDrawItem
methods:
- The first one is adopted when describing the size of the
Item
. For each item that we don't want to be visible, we simply set the ItemHeight
to 0
. - The second one is the drawing element method. If an item is not in the filtered list, then we just don’t draw it.
public class ContextualDataGridViewComboBoxEditingControl : DataGridViewComboBoxEditingControl
{
public ContextualDataGridViewComboBoxEditingControl()
{
DrawMode = DrawMode.OwnerDrawVariable;
MouseWheel += ContextualDataGridViewComboBoxEditingControl_MouseWheel;
KeyDown += ContextualDataGridViewComboBoxEditingControl_KeyDown;
}
public IEnumerable<object> FilteredRequest
{
get
{
try
{
var currentCell =
(EditingControlDataGridView.CurrentCell as ContextualDataGridViewComboBoxCell);
if (currentCell != null)
return currentCell.FilteredRequest;
return null;
}
catch (Exception)
{
return null;
}
}
}
protected override void OnMeasureItem(MeasureItemEventArgs e)
{
if (e.Index < 0)
return;
if (FilteredRequest != null && !FilteredRequest.Contains(Items[e.Index]))
e.ItemHeight = 0;
else
base.OnMeasureItem(e);
}
protected override void OnDrawItem(DrawItemEventArgs e)
{
if (e.Index < 0)
return;
if (FilteredRequest != null && !FilteredRequest.Contains(Items[e.Index])) return;
e.DrawBackground();
e.Graphics.DrawString(GetItemText(Items[e.Index]), this.Font, Brushes.Black,
new RectangleF(e.Bounds.X, e.Bounds.Y, e.Bounds.Width, e.Bounds.Height));
}
}
It’s almost done! For now, you can display any elements you want in the dropdown
list of your combobox
. But there still is a problem when you change the selected item without opening the list (with the mouse wheel or up and down keys). For that purpose, you can just manage the selected item using MouseWheel
and KeyDown
events.
private void ContextualDataGridViewComboBoxEditingControl_KeyDown(object sender, KeyEventArgs e)
{
if (!DroppedDown && FilteredRequest != null)
{
if (e.KeyCode == Keys.Up)
changeSelectedItem(-1);
else if (e.KeyCode == Keys.Down)
changeSelectedItem(1);
e.Handled = true;
}
}
private void ContextualDataGridViewComboBoxEditingControl_MouseWheel(object sender, MouseEventArgs e)
{
if (!DroppedDown && FilteredRequest != null)
{
if (e.Delta < 0)
changeSelectedItem(1);
else if (e.Delta > 0)
changeSelectedItem(-1);
((HandledMouseEventArgs)e).Handled = true;
}
}
private void changeSelectedItem(int delta)
{
int tmpIndex = SelectedIndex;
tmpIndex += delta;
while (tmpIndex > -1 && tmpIndex < Items.Count)
{
if (FilteredRequest.Contains(Items[tmpIndex]))
{
SelectedIndex = tmpIndex;
break;
}
tmpIndex += delta;
}
}
History
- 27/02/2017: First publication