13

Concerning data binding I have these classes:

public class Foo : List<Bar>
{
    public string FooName { get; set; }
}

public class Bar
{
    public string BarName { get; set; }
    public string BarDesc { get; set; }
}

And I have a List<Foo>

I would like to have Foo items in ComboBox, and Bar items in ListBox. When I change selected item in ComboBox, I want ListBox to change. When I change selected item in ListBox I would like to have TextBox filled with BarDesc.

Following works only for ListBox and ComboBox:

comboBox1.DataSource = foos;
comboBox1.DisplayMember = "FooName";
listBox1.DataBindings.Add("DataSource", foos, "");
listBox1.DisplayMember = "BarName";

I don't now how to bind selected Bar in ListBox to TextBox.Text property. Maybe added binding for listBox1 is not a good idea.

Maybe I should do something like that:

((CurrencyManager)listBox1.BindingContext[foos]).CurrentChanged += new EventHandler((o, a) =>
{
    textBox1.DataBindings.Clear();
    textBox1.DataBindings.Add("Text", listBox1.DataSource, "BarDesc");
});

How can I resolve my problem?

ΩmegaMan
  • 29,542
  • 12
  • 100
  • 122
prostynick
  • 6,129
  • 4
  • 37
  • 61
  • Microsoft never intended for classes to inherit from `List`. I think they recommend `Collection` as a base class. – John Alexiou May 08 '15 at 12:56
  • I didn't know about it, but even if I switched to Collection the problem would have stayed. Anyway, nowadays I would rather not extend `List`, but rather aggregate it in `Foo` as a property. – prostynick May 08 '15 at 13:08
  • Yes, this was just a remark, unrelated to the solution of the problem. See http://stackoverflow.com/q/21692193/380384 – John Alexiou May 08 '15 at 17:16

4 Answers4

18

To make all this work, I had to add the Items property to the Foo class. This is the "link/relationship" between the two binding sources.

public partial class Form1 : Form {
    public class Foo : List<Bar> {
        public string FooName { get; set; }
        public Foo(string name) { this.FooName = name; }
        public List<Bar> Items { get { return this; } }
    }
    public class Bar {
        public string BarName { get; set; }
        public string BarDesc { get; set; }
        public Bar(string name, string desc) {
            this.BarName = name;
            this.BarDesc = desc;
        }
    }
    public Form1() {

        InitializeComponent();

        List<Foo> foos = new List<Foo>();

        Foo a = new Foo("letters");
        a.Add(new Bar("a", "aaa"));
        a.Add(new Bar("b", "bbb"));
        foos.Add(a);

        Foo b = new Foo("digits");
        b.Add(new Bar("1", "111"));
        b.Add(new Bar("2", "222"));
        b.Add(new Bar("3", "333"));
        foos.Add(b);

        //Simple Related Object List Binding
        //http://blogs.msdn.com/bethmassi/archive/2007/04/21/simple-related-object-list-binding.aspx

        BindingSource comboBoxBindingSource = new BindingSource();
        BindingSource listBoxBindingSource = new BindingSource();

        comboBoxBindingSource.DataSource = foos;
        listBoxBindingSource.DataSource = comboBoxBindingSource;
        listBoxBindingSource.DataMember = "Items";

        comboBox1.DataSource = comboBoxBindingSource;
        comboBox1.DisplayMember = "FooName";

        listBox1.DataSource = listBoxBindingSource;
        listBox1.DisplayMember = "BarName";

        textBox1.DataBindings.Add("Text", listBoxBindingSource, "BarDesc");

    }
}
AMissico
  • 21,470
  • 7
  • 78
  • 106
  • 1
    Oh this is nice `public List Items { get { return this; } }` Why I didn't thought about it? :) – prostynick Mar 04 '10 at 14:13
  • 2
    Because it is not immediately obvious that you want/can return the object itself through one of its properties, unless you have run across it before. I bet you will remember this technique for years to come. :O) – AMissico Mar 04 '10 at 21:33
1

Use BindingSource for all your complex data binding needs:

// bind to List<Foo>
BindingSource comboBoxBindingSource = new BindingSource();
comboBoxBindingSource.DataSource = foos;
// use this binding source in comboBox1
// for display use FooName
comboBox1.DataSource = comboBoxBindingSource;
comboBox1.DisplayMember = "FooName";

// bind to comboBox1's SelectedValue
// it will point to the Foo selected in combobox
BindingSource listBoxBindingSource = new BindingSource();
listBoxBindingSource.DataSource = comboBox1;
listBoxBindingSource.DataMember = "SelectedValue";
// use this binding source in listBox1
// for display use BarName
listBox1.DataSource = listBoxBindingSource;
listBox1.DisplayMember = "BarName";

// bind to comboBox'1s SelectedValue (reusing listBoxBindingSource)
// and set Text to value of BarDesc
textBox1.DataBindings.Add("Text", listBoxBindingSource, "BarDesc");
Stefan
  • 4,166
  • 3
  • 33
  • 47
1

You should probably set the ValueMember on the combo and the list box. You might need to also handle bindingSource.CurrentChanged and rebind the listbox to the currently selected list.

Big Endian
  • 944
  • 1
  • 6
  • 23
1

Off topic regarding inheritance vs composition.

You are effectively saying "Foo is a list of bars that has a name", when it would be a lot simpler to say "Foo has a name and has a list of bars". But Why?

What if you wanted to make Foo have two lists? Or a List and a Dictionary? C# doesn't allow multiple inheritance (which itself is dirty!), so you would have to go and create another class that holds multiple data structures anyway.

Also because of inheritance you had to create an "Items" method that returns itself, unnecessary code.

Try this:

public class Foo
{
    public string FooName { get; set; }
    public List<Bar> Items;
}
Researcher
  • 1,006
  • 7
  • 14