I have a tree in Grid view, bound to a list (which is a custom type derived from BindingList) of objects (which implement INotifyPropertyChanged). One of the columns is of type CheckState (set in code, since the Designer doesn't know about that type), and is bound to an object property which is declared as CheckState. All of these properties default to unchecked.
Here's the behavior: if I check the checkbox on one item, nothing happens (that is, the property on the object does not get set). If I now check the checkbox on a second item, the property on the *first* item is set, but not the property on the second item. If I check a third item, the second item will get set, and so forth. When the property is modified, I am firing the PropertyNotified event like I'm supposed to.
This is pretty clearly something wrong with my code. Any idea what I could be doing to cause this behavior?
Thanks, Aaron
Hm, maybe it's not something wrong with my code. I've done the same thing in the WinTree_BoundToList sample, and I get the same behavior. Here's the setup for the tree:
UltraTreeColumnSet customerCols = new UltraTreeColumnSet(); UltraTreeNodeColumn col = customerCols.Columns.Add("Checked"); col.DataType = typeof(CheckState); col.AllowCellEdit = Infragistics.Win.UltraWinTree.AllowCellEdit.Full; customerCols.Columns.Add("CustomerName"); customerCols.Columns.Add("CustomerAddress"); this.ultraTree1.NodeLevelOverrides[0].ColumnSet = customerCols; this.ultraTree1.Override.CellClickAction = CellClickAction.EditCell;
this.ultraTree1.DataSource = (my list of objects);
Here's the definition of the Customer object that the tree is bound to:
public class Customer : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; private void NotifyPropertyChanged(String propertyName) { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); }
public CheckState Checked { get { return mChecked; } set { mChecked = value; NotifyPropertyChanged("Checked"); } } public int CustomerID { get; set; } public string CustomerAddress { get; set; } public string CustomerName { get; set; }
CheckState mChecked = CheckState.Unchecked; }
Any idea what's going on?
FYI, setting the data type to bool instead of CheckState does not change the behavior.
Aaron
Thanks, but my understanding is that BindingList<T> will automatically subscribe to the PropertyChanged event of any objects added to it (as per the example at http://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged.aspx).
Could the problem be that I've actually got two levels of lists? In order to get the tree to show two levels, I have to actually provide it three levels of data, meaning that I have a list containing a list containing objects. If the BindingList behaves as I expect, it will catch the property-changed notifications from the Customer objects and translate them into events through its IBindingList interface, but maybe the outer list won't transfer those to the tree view?
I could probably make my BindingList subclass implement INotifyPropertyChanged as well, so that the events would bubble all the way to the top. Do you think this would work?
Hm, there's an issue with that approach: the PropertyChanged event wants to get the name of the property that's been modified. If I just bubble the event up through, the receiver is presumably going to look for a property with that name on the list object, and it won't be there.
Does the UltraTree pay attention to the name of the property that's been modified? If not, it won't matter what name I pass.
Another possibility, probably a better one in fact, is to add code to the collection object to check to see if the object added supports the IBindingList interface, and to subscribe to its events if so and trigger my own events from there. That way, the original PropertyChanged event would immediately become a ListChanged event as expected, and would get translated appropriately as it went up the tree.
OK, that didn't work (the ListChanged event never gets fired), and I'm not sure why. However, after a bit more thought, there's a problem with your suggestion of what's going on. If the problem were that the event was getting dropped, then the tree view would have no reason to query the property at all, ever, so the checkbox wouldn't get updated until something else occurred (like a selection in the tree). Right?
In fact, simply calling Invalidate on the tree causes it to update all of its checkboxes, as does just mousing over its area. This suggests to me that its data is already up to date, and the only thing that is missing is the repaint event.
Have I missed something?
Hi Aaron,
aaronsher said:Thanks, but my understanding is that BindingList<T> will automatically subscribe to the PropertyChanged event of any objects added to it (as per the example at http://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged.aspx).
I was not aware of that. But it's not happening here. You can hook the ListChanged event of the collection class yourself and see that it never gets called. There's nothing the tree can do about that.
aaronsher said:Could the problem be that I've actually got two levels of lists? In order to get the tree to show two levels, I have to actually provide it three levels of data, meaning that I have a list containing a list containing objects. If the BindingList behaves as I expect, it will catch the property-changed notifications from the Customer objects and translate them into events through its IBindingList interface, but maybe the outer list won't transfer those to the tree view?
Since it's not working on the root level, I doubt it has anything to do with the levels.
aaronsher said:However, after a bit more thought, there's a problem with your suggestion of what's going on. If the problem were that the event was getting dropped, then the tree view would have no reason to query the property at all, ever, so the checkbox wouldn't get updated until something else occurred (like a selection in the tree). Right?
No, my suggestion is completely consistent with that is happening here. The tree is not getting notified that anything changed so it's not updating it's display. When you mouse over the cell or you call Invalidate, then tree re-paints the cell and gets the values from the data source again, anyway.
After looking at the sample again, I'm a bit confused. I think the problem here is probably that you are using BindingList<Object> instead of BindingList<Customer>.
But I'm not sure why you are using Object or why you are adding a collection into the collection. In fact, I'm surprised that this code works at all - this data structure is really odd.
It seems to me that you should be definining your Customer object to return a CustomerCollection of child items. Then you root BindingList contains individual Customer items, each of this has a collection of child items. That way the CustomerCollection can derive from BindingList<Customer> and I bet that fixes the issue of the property change notifications.
The reason that it's a BindingList<Object> is so that I can have a multilevel tree of objects. In other words, I want to be able to have a CustomerCollection of CustomerCollections of Customers (this is analogous to something that I'm doing in my real code, I don't claim it makes sense in this context).
If I define CustomerCollection as BindingList<Customer>, then I can't have collections of collections. How would you suggest that I define a multilevel data structure to which I can bind the tree?
I'll investigate why the ListChanged event is not being fired.