Jul 21, 2008 at 3:48 PM
Edited Jul 22, 2008 at 3:10 PM
Hi. I tried running the following code:
static void Main()
var recs = study.Nodes.AsBindable().SelectMany(
x => x.Deviations.AsBindable().SelectMany(
y => y.Causes.AsBindable().SelectMany(
q => q.Consequences.AsBindable().SelectMany(
con => con.Recommendations.AsBindable()))));
recs.CollectionChanged += recommendations_CollectionChanged;
var node1 = new Node(study);
var dev1 = new Deviation(study, node1);
var cause1 = new Cause(study, dev1);
var con1 = new Consequence(study, cause1);
var rec1 = new Recommendation(study, con1);
var rec2 = new Recommendation(study, con1);
var dev2 = new Deviation(study, node1);
var cause2 = new Cause(study, dev2);
var con2 = new Consequence(study, cause2);
var rec3 = new Recommendation(study, con2);
var rec4 = new Recommendation(study, con2);
The goal is to be able to detect changes to the combination of all lists of Recommendations in the "study". "Nodes", "Deviations", "Causes", "Consequences" and "Recommendations" are all properties
that return modifiable BindingLists. The event handler assigned to recs.CollectionChanged never gets called. Any ideas?
After downloading July 27th's source, it seems that breaking up the recs assignment statement like this:
var Nodes = study.Nodes.AsBindable();
var Deviations = Nodes.SelectMany(x => x.Deviations.AsBindable());
var Causes = Deviations.SelectMany(x => x.Causes.AsBindable());
var Consequences = Causes.SelectMany(x => x.Consequences.AsBindable());
var Recommendations = Consequences.SelectMany(x => x.Recommendations.AsBindable());
...works. Recommendations.Count().Current will return 4 when the program breaks. So one other problem's solved, and hierarchical updates work now.
Good stuff here. Should save countless hours creating IBindingList wrappers for a lot of Windows Forms developers.
The event also fires under one condition: the Current property on an aggregator or a call to ToBindingList() had to be made first. Maybe the iterators area a little
too delayed in their execution.
Jul 28, 2008 at 11:41 PM
Glad this works. The changes I made on the 27th were for the Union iterator, which is used by SelectMany under the hood (it does a Select followed by a Union).
>> Maybe the iterators area a little
too delayed in their execution.
Yeah, it's an interesting one. LINQ queries use delayed execution because you can continue to change the query and it's not until you execute them that they're stitched together, optimised and dispatched. Bindable LINQ queries (and LINQ to Objects queries)
don't really have that requirement. It would probably make the implementation simpler if I got rid of delayed execution.
Out of interest, what makes this a problem? The way aggregates are designed is that you'd bind the "Current" property to a form/control, and it would read the values so you wouldn't have to execute it yourself. Are you using the Count().Current for
a different reason?
Yes, at least sort of. I make use of the CollectionChanged events fired by the IBindableCollection in order for a class representing extended info about the distinct recommendations. To avoid introducing custom methods in the Recommendation objects, I
find it more feasible to allow a central class to listen to the events of all the individual collections of recommendations while the class itself manages its own list of extended info. I think this strategy also makes it easier to do certain tasks, such as
the removal of the functionality by removing event handlers should the class become obsolete over time.
In addition to user interface development, this could also work well in a workflow-based application, i.e. in the case that a bunch of functions belonging to different event listeners should run when a certain collection of an object changes.
To answer the question, for some reason, you have to request that the IBindableCollection aggregate some value (or call ToBindingList on it) before it will work its magic and start generating CollectionChanged events. In other words, assigning your own event
handler to the IBindableCollection.CollectionChanged event doesn't immediately give you the expected functionality.