tags:

views:

40

answers:

3

This a data binding question in C#.

I have a simple Person class:

public class Person {
    public string Name {get; set;}
    public string Level {get; set;}
}

And I have a Group class, which contains a Persons property as a BindingList of Person instances:

public class Group {
    private BindingList<Person> persons_;
    public BindingList<Person> Persons {
            get { return persons_; }
            set { persons_ = value; }
    }
}

Finally, I use a BindingSource to connect a group instance to a DataGridView:

// Instantiate a Group instance.
Group p = new Group();
p.Persons = new BindingList<Person> {
    new Person {Name = "n1", Level = "l1"},
    new Person {Name = "n2", Level = "l2"},
    new Person {Name = "n3", Level = "l3"}
};

BindingSource bs = new BindingSource();
bs.DataSource = p;
bs.DataMember = "Persons";
DataGridView dgv = new DataGridView();
dgv.DataSource = bs;

After the above code, the DataGridView will display three Person instances. Everything works fine. Then I have a button, when the button is clicked, I create another Person BindingList and use it to replace p.Persons:

p.Persons = new BindingList<Person> {
    new Person {Name = "n10", Level = "l10"},
    new Person {Name = "n11", Level = "l11"},
    new Person {Name = "n12", Level = "l12"}
};

After the above code, the data binding stops working. Adding ResetBindings() calls from either the BindingSource or the DataGridView doesn't solve the break of the data binding.

Since my BindingSource is bound to the Persons property of the Group class, I think the change of the Persons property should not break the data binding but in fact it does. But this is what we normally do if binding a string property to a Label or TextBox. We just assign a new instance of string to the property and the Label or TextBox will update itself properly. Why this doesn't work for collections of instances? What is the correct code to achieve the same behavior? Thank you very much.

+1  A: 

That's because the Persons property doesn't trigger a notification when it changes. The Group class should implement INotifyPropertyChanged, and the setter of the Persons property should raise the PropertyChanged event.

Thomas Levesque
An alternative would be calling Clear() on the Persons property and re-populating it instead of creating a new BindingList.
Anna Lear
The implementation of the INotifyPropertyChanged doesn't help. Actually the call of ResetBinding() would do similar thing.
Steve
+1  A: 

Your data model is very wrong: Collection properties should be readonly.

Instead, you should remove the Persons setter, then clear the list and call Add repeatedly.
If you want to, you can even create an extension method

public static void  ReplaceWith<T>(this IList<T> list, params T[] newItems)
SLaks
Your suggestion is true. But if the BindingList has a lot of items and when I update it in the setter, I clear items and add items one by one in, the DataGridView UI will update itself every time an item is added, will this create too much extra work? It will be nice to tell the BindingSource to suspend at the beginning of adding items and resume after. But in the setter of Group.Persons property or within Group, there is no knowledge of the BindingSource.
Steve
@Steve: You can make your own class that inherits `BindingList<T>` and includes a `ReplaceWith` method that modifies the underlying list (thus not raising any events), then raises a `Reset`.
SLaks
@Steve: SuspendBinding() and ResumeBinding() methods might help you out there.
Anna Lear
@Anna Lear: SuspendBinding() and ResumeBinding() are methods for BindingSource, which is not reachable inside Group class. I found RaiseListChangedEvents property of BindingList that could do what I desire instead.
Steve
You're right; I forgot about that property. You could set it in an extension method.
SLaks
A: 

You can use

ObservableCollection of Person objects and bind to the grid, this collection is designed for the purpose you are looking.

When you add a new person object in the collection , it will automatically sends change notification to the UI, and your grid will response to this notification by adding one more row to the grid.

saurabh