Winforms controls and "generic" events handlers. How can I do this?
- by Yanko Hernández Alvarez
In the demo of the ObjectListView control there is this code (in the "Complex Example" tab page) to allow for a custom editor (a ComboBox) (Adapted to my case and edited for clarity):
EventHandler CurrentEH;
private void ObjectListView_CellEditStarting(object sender,
CellEditEventArgs e)
{
if (e.Column == SomeCol)
{
ISomeInterface M = (e.RowObject as ObjectListView1Row).SomeObject; //(1)
ComboBox cb = new ComboBox();
cb.Bounds = e.CellBounds;
cb.DropDownStyle = ComboBoxStyle.DropDownList;
cb.DataSource = ISomeOtherObjectCollection;
cb.DisplayMember = "propertyName";
cb.DataBindings.Add("SelectedItem",
M, "ISomeOtherObject", false,
DataSourceUpdateMode.Never);
e.Control = cb;
cb.SelectedIndexChanged +=
CurrentEH = (object sender2, EventArgs e2) =>
M.ISomeOtherObject =
(ISomeOtherObject)((ComboBox)sender2).SelectedValue; //(2)
}
}
private void ObjectListView_CellEditFinishing(object sender,
CellEditEventArgs e)
{
if (e.Column == SomeCol)
{
// Stop listening for change events
((ComboBox)e.Control).SelectedIndexChanged -= CurrentEH;
// Any updating will have been down in the SelectedIndexChanged
// event handler.
// Here we simply make the list redraw the involved ListViewItem
((ObjectListView)sender).RefreshItem(e.ListViewItem);
// We have updated the model object, so we cancel the auto update
e.Cancel = true;
}
}
I have too many other columns with combo editors inside objectlistviews to use a copy& paste strategy (besides, copy&paste is a serious source of bugs), so I tried to parameterize the code to keep the code duplication to a minimum. ObjectListView_CellEditFinishing is a piece of cake:
HashSet<OLVColumn> cbColumns = new HashSet<OLVColumn> (new OLVColumn[] { SomeCol, SomeCol2, ...};
private void ObjectListView_CellEditFinishing(object sender,
CellEditEventArgs e)
{
if (cbColumns.Contains(e.Column)) ...
but ObjectListView_CellEditStarting is the problematic.
I guess in CellEditStarting I will have to discriminate each case separately:
private void ObjectListView_CellEditStarting(object sender,
CellEditEventArgs e)
{
if (e.Column == SomeCol)
// code to create the combo, put the correct list as the datasource, etc.
else if (e.Column == SomeOtherCol)
// code to create the combo, put the correct list as the datasource, etc.
And so on.
But how can I parameterize the "code to create the combo, put the correct list as the datasource, etc."? Problem lines are
(1) Get SomeObject. the property NAME varies.
(2) Set ISomeOtherObject, the property name varies too.
The types vary too, but I can cover those cases with a generic method combined with a not so "typesafe" API (for instance, the cb.DataBindings.Add and cb.DataSource both use an object)
Reflection? more lambdas? Any ideas? Any other way to do the same?
PS: I want to be able to do something like this:
private void ObjectListView_CellEditStarting(object sender,
CellEditEventArgs e)
{
if (e.Column == SomeCol)
SetUpCombo<ISomeInterface>(ISomeOtherObjectCollection,
"propertyName",
SomeObject,
ISomeOtherObject);
else if (e.Column == SomeOtherCol)
SetUpCombo<ISomeInterface2>(ISomeOtherObject2Collection,
"propertyName2",
SomeObject2
ISomeOtherObject2);
and so on. Or something like that.
I know, parameters SomeObject and ISomeOtherObject are not real parameters per see, but you get the idea of what I want. I want not to repeat the same code skeleton again and again and again.
One solution would be "preprocessor generics" like C's DEFINE, but I don't thing c# has something like that.
So, does anyone have some alternate ideas to solve this?