views:

87

answers:

1

I'm attempting to do what I considered simple data binding between a BindingSource and a ComboBox. I run into issues when the class I am using as the DataSource of the BindingSource has a property that is an instance of a generic class.

I have the following generic class:

public class GenericClass<T>
{
    public T Code { get; set; }
    public string Description { get; set; }

    public override string ToString()
    {
        return Description;
    }
}

I have a class that has an integer Code:

public class IntegerClass : GenericClass<int>
{
    // Nothing unique here, for simple test.
}

I also have the class that is set to the BindingSource's DataSource:

public class ClassBindingClass : INotifyProperty Changed
{
    private int _id;
    private IntegerClass _choice;
    private string _name;

    public int Id
    {
        get { return _id; }
        set
        {
            _id = value;
            OnPropertyChanged("Id");
        }
    }

    public IntegerClass Choice
    {
        get { return _choice; }
        set
        {
            _choice = value;
            OnPropertyChanged("Choice");
        }
    }

    public string Name
    {
        get { return _name; }
        set
        {
            _name = value;
            OnPropertyChanged("Name");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged(string propertName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertName));
    }
}

On my form I create a collection of IntegerClass and set my combobox's datasource as that collection. (This part works fine, the combo box displays the values appropriately.) Then I set the combobox's SelectedValue Binding to the BindingSource's Choice property updating on OnPropertyChanged.

If I replace IntegerClass with a non-generic class when you select a value in the combo box the BindingSource's Choice property changes the NotifyPropertyChanged event is fired and on my form I can update a label saying "Choice has changed!".

When the IntegerClass is part of the ClassBindingClass this no longer works and instead I cannot navigate out of the combo box and instead get a FormatException.

Is what I want to do possible? Can databinding handle generics?

+1  A: 

You mention SelectedValue... but your source (and the bound property) are both IntegerClass - so it isn't a value you want to bind, but the item itself. Unfortunately, there is no ComboBox.SelectedItemChanged so you might need to hack it a bit to get 2-way binding...

static class Program {
    [STAThread]
    static void Main() {
        Application.EnableVisualStyles();
        IntegerClass[] choices = new[] {
            new IntegerClass { Code = 123, Description = "a b c"},
            new IntegerClass { Code = 456, Description = "d e f"},
            new IntegerClass { Code = 789, Description = "g h i"},
        };
        ComboBox cbo = new TwoWayComboBox();
        cbo.DropDownStyle = ComboBoxStyle.DropDownList;
        cbo.DataSource = choices;
        Form form = new Form();
        ClassBindingClass obj = new ClassBindingClass();
        cbo.DataBindings.Add("SelectedItem", obj, "Choice", true, DataSourceUpdateMode.OnPropertyChanged);
        form.DataBindings.Add("Text", obj, "Choice", true, DataSourceUpdateMode.OnPropertyChanged); // show it
        form.Controls.Add(cbo);
        Application.Run(form);


    }

}

class TwoWayComboBox : ComboBox {
    public new object SelectedItem
    {
        get { return base.SelectedItem; }
        set { base.SelectedItem = value; }
    }
    private static readonly object SelectedItemChangedKey = new object();
    public event EventHandler SelectedItemChanged {
        add { Events.AddHandler(SelectedItemChangedKey, value);}
        remove { Events.RemoveHandler(SelectedItemChangedKey, value);}
    }
    protected override void OnSelectedIndexChanged(EventArgs e)
    {
        EventHandler handler = (EventHandler)Events[SelectedItemChangedKey];
        if (handler != null) { handler(this, EventArgs.Empty); }
        base.OnSelectedIndexChanged(e);
    }
}
Marc Gravell
Two questions:If I want to use `SelectedItem` instead of `SelectedValue` why does this work with a class without a generic subclass? I can change IntegerClass to just be a normal class and the code works, this is very confusing.Does your code above fix the issue of why it works with `SelectedItem` but only when the control loses focus?
robber.baron
The "when the control loses focus" is a combination of exposing the notification event and property (together) *and* `DataSourceUpdateMode`. For the rest - can you supply an example for comparison?
Marc Gravell