One thing leads to another doesn't it? :)
Well, the bubbling of events caused problems that are effectively unsolvable, so the bubbling can't occur like it did. That probably means a change to a child won't cause a CurrentItemChanged - in fact I think that was ultimately the root of the problem solved by not bubbling the event.
I can see two answers, though there are probably others.
One is to do as you suggest - handle the ChildChanged event to trigger your UI logic.
Another is to create an ObjectStatus control similar to the one in WPF, only for Windows Forms. Probably not a control though, since there's no control-to-control binding in Windows Forms - but perhaps just an object that elevates the Is___ properties to a bindable level in the UI.
Of course now that you bring this up, I bet the ObjectStatus control is broken now too, because it relied on the same mechanism :-\
RockfordLhotka:
One thing leads to another doesn't it? :)
RockfordLhotka:
Well, the bubbling of events caused problems that are effectively unsolvable, so the bubbling can't occur like it did. That probably means a change to a child won't cause a CurrentItemChanged - in fact I think that was ultimately the root of the problem solved by not bubbling the event.
What were the issues? Not that I'm advocating going back to the broken behavior, I think I just missed that discussion. My post was more along the lines of "what's best practice now?"
RockfordLhotka:
I can see two answers, though there are probably others.One is to do as you suggest - handle the ChildChanged event to trigger your UI logic.
Good, at least my solution made the list.. so I can't be too far off.
So this would likely be a component? I'm not sure I'll go this route, simply because I don't think there's a way to use data binding for Tools in an UltraToolBarManager, which is the only object that actually exists. But perhaps I could make a component similar to the BindingSourceRefresh that raises an event when anything in the object graph changes. I'll look into that.RockfordLhotka:
Another is to create an ObjectStatus control similar to the one in WPF, only for Windows Forms. Probably not a control though, since there's no control-to-control binding in Windows Forms - but perhaps just an object that elevates the Is___ properties to a bindable level in the UI.
The issue with bubbling PropertyChanged is that when a new item
was added to a grid it was instantly “committed” in memory.
Pressing ESC wouldn’t get rid of it. It appears this is because the
parent’s bindingsource did a quick currency change based on PropertyChanged
being handled, thus effectively causing an EndEdit() on the child (which is not
good).
Yes, I would think the ObjectStatus concept would be a
Component. I haven’t given it any serious thought in terms of design, but
I expect it would be similar to the ObjectStatus concept in Csla.Wpf.
Rocky
This change also broke my code. My UI now handles both PropertyChanged and ChildChanged off the root object for activating the Save button.
However, I have problems with grandchildren. I think the problem is that ChildChanged event itself won't bubble up the hierarchy, so I'm receiving events from root and child BO's but not from grandchildren.
Rocky, couldn't the childevent bubble up like 3.5.0, but inside another childevent instead of a propertychanged event? I hope that makes sense.
Regards
This is really unfortunate, I’m sorry for not catching
this…
Obviously some thought needs to go into this beyond the
knee-jerk reactions I’ve done thus far.
Yes, I think you are right that the ChildChanged needs to bubble
up. The question is how?
A child object’s Propertychanged bubbles through a parent
list as a ListChanged. If the list has a parent that cascades as a
ChildChanged. If that parent is also a child of a list we’re now in
trouble.
So it seems that BLB may need to raise ChildChanged as well.
In other words, it seems like a total parallel eventing system is
required along-side the PropertyChanged/ListChanged system.
And this can be done.
But then the question is how to best do this? Do lists and
objects need different events? Should these events work like exceptions, so
they have “inner events” all the way up the chain? That seems like
it would be pretty cool – but useful?
Basically, if I’m going to do this (and I think I must), I’d
rather do it in a well thought out manner.
Rocky
From: amselem
[mailto:cslanet@lhotka.net]
Sent: Wednesday, August 06, 2008 10:02 PM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] Enabling / disabling save button
This change also broke my code. My UI now handles both PropertyChanged and
ChildChanged off the root object for activating the Save button.
However, I have problems with grandchildren. I think the problem is that
ChildChanged event itself won't bubble up the hierarchy, so I'm receiving
events from root and child BO's but not from grandchildren.
Rocky, couldn't the childevent bubble up like 3.5.0, but inside another
childevent instead of a propertychanged event? I hope that makes sense.
Regards
I don’t think it works as-is for you though. Because it
won’t cascade up if a grandchild changes. In that case the grandchild’s
PC becomes a LC from its parent list, which becomes a CC from the child object.
The child’s parent list does nothing, and so neither does the parent. So
you get no notification that a grandchild changed.
Rocky
From: ajj3085
[mailto:cslanet@lhotka.net]
Sent: Thursday, August 07, 2008 7:23 AM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] RE: Enabling / disabling save button
Well, ChildChanged is useful as it is for me.. sometimes I
do need to know which property changed on a child BO not in a list.
Maybe the answer is to create another new event, StatusChanged. It could
be raised whenever IsDirty, IsNew, etc. are raised.. since this is ultimately
what tetranz and I are after. A reliable way to find out when IsSavable
has changed, to enable the buttons.
Andy
Ahh, yes.
It should be fine for that purpose right now.
But to address the from-bottom-to-top event bubbling requirement
I suspect I’ll have to do a ChildChanged event on BLB as well.
And I suspect having some sort of nested eventargs scheme would
be good, but that requires more thought.
My idea there, though, is that each level that bubbles a ChildChanged
would nest the original inside – so you could use some InnerArgs property
to walk from the top level back through the stack of args to fine the original
that was raised.
Or I could follow the current model, where the originator is in
the args, and the sender is whoever raised the event last, and you have no
access to the intermediate objects between the originator and the final sender.
Rocky
From: ajj3085
[mailto:cslanet@lhotka.net]
Sent: Thursday, August 07, 2008 12:10 PM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] RE: RE: Enabling / disabling save button
Well I didn't mean working in regards to enabling /
disabling buttons.. the other use I have for ChildChanged is running rules when
a certain property on the child BO changes. That is the part I think is
fine.
My vote would be to keep the entire context chain. It can make pinpointing your original object easier.
Having said that, I know that is not a trivial implementation. I've written one before, and I've seen the code some commercial products that use it have implemented. The question is how much code you want to write around this "context chain". In one case, there are a number of methods implemented on the base classes that allow you to retrieve information from anywhere in the chain. Basically, the methods ask for a target type, and they automatically walk the chain until they encounter the type requested or the top/bottom of the hierarchy. I'm not suggesting you have to go to this level of work in your implementation, but it's certainly something to think about.
The thing is that the biggest arguments I can see for keeping the context chain are (a) being able to insert yourself anywhere in the event-bubbling process, and (b) providing an easier way to find the original object. (a) can be useful, but probably in somewhat specialized situations - one that comes to mind now is the ability to provide more information to "tag along for the ride". It could also be useful if you want to stop the bubbling for some reason, but I'm not sure of the value of that either. (b) is probably only useful if you have a child object that can exist in more than one child list - not a completely uncommon situation, but probably not all that common either.
Lastly, consider that whatever you do for your child events probably should be duplicated in the PropertyChanged stack as well. After all, why limit your flexibility to one set of events?
So - do you want to write a lot of code for potentally not a lot of benefit?
HTH
- Scott
This is good thinking, I appreciate the analysis.
One thing though – is that PropertyChanged is what it is.
That event and its behavior is defined by the expectations of data binding. We’re
having this discussion because I didn’t follow their strictures quite
right – so the idea of doing more complex bubbling/enhancing of that
model is right out :)
Rocky
We have to remember performance too. There are a lot of these
events raised in a typical app, and so generating them can’t be expensive
or none of us will be happy. The more stuff we cram into the eventargs the more
time it will take to create the object – and that’ll happen at each
object in the parent-childlist-child-grandchildlist-grandchild level, so the
overhead will multiply.
But more importantly, it is easier to add things later than to
take things away. So I tend to only put things in because they fulfill a specific
scenario that is commonly useful.
I’m not sure what the scenario would be for including the
edit level of each object as the event bubbles up?
I can see the value in providing a reference to the originator
of the event, and to the PropertyChanged or ListChanged event args of the
underlying changed event on that object.
I’m still struggling a little with the value of having
references to the intermediate objects up the stack. It “feels”
useful, but I honestly don’t see where I’m going to use that
information.
Rocky
I can think of one possible use for having the whole object tree available, and it relates to being able to "insert" yourself into the call chain. Admittedly, this is a somewhat contrived example, but it's possible.
(Mind you, "inserting yourself into the call chain" means that your events are triggered by overridable methods. Not sure whether that was clear...)
Being able to manage the event chain means you can run validation rules pretty much anytime you want. If this ChildChanged event stack (which maybe should get a different name) bubbles through the whole hierarchy, you can pick an arbitrary level in the chain to explicitly call CheckRules() - say, if a change to one child object requires a change to another child object, along with a new check of the business rules. That kind of logic should probably reside in the parent collection. Some of this is currently available, and some of it isn't. You can even choose to run CheckRules() based on what object below the current level was changed.
So why would the intermediate objects be useful? If you have validation rules on a collection, then presumably those rules are designed to work on the entire collection. It may be useful to ignore the object(s) that are part of the update chain for purposes of the validation routine.
One other possible use goes back to stopping the bubbling. I honestly can't think of a good scenario for this, but I could see some value in being able to evaluate changes as they move up the stack, and possibly killing the bubble at some point. In killing the bubble, you may want to undo the changes currently moving through the stack. You would need the appropriate objects available to do that.
- Scott
Rocky any updates on this one?
I haven't had time to dig into the code, no.
I can (and might) do the simplest of the implementations in relatively short order. The more sophisticated options are...well...more sophisticated and would take a lot more time - time I don't have.
Thanks for your response Rocky, I understand that you're very busy with all the new stuff like the upcoming book and the Csla light fw.
As for the implementation I agree that the simplest implementation would be enough, it's not clear (at least for me) that the more complex option would be very useful.
Regards
Michael:
Just to be sure, for those of us not yet using the CslaActionExtender, to enable/disable save buttons we need to ditch the BindingSource CurrentItemChanged event in favour of PropertyChanged and ChildChanged. And because these events don't fire until something changes, we need to call SetButtonsEnabled() or something similar in BindUI(). Is this the recommended approach
This is a old post but i think i'm facing the same problem what is mentioned here.
I'm using CSLA 3.8.3 with winforms. I have a generic code which enables / disables save button by subscribing to each bindingsource's CurrentItemChanged.
The problem is that for first record it works fine but when user clicks on Add Record for the second record and enters new record info then currentitemchanged is never triggerred.
This was previously working fine but seems like it has stopped working now.. Any ideas, any suggestionso n how to fix this.
How are you adding the new record? Is it a child BusinessBase object in a BusinessListBase, or a standalone BusinessBase? Are you changing the BindingSource.DataSource or using the binding source to AddNew()?
this is happenning only in standalone businessbase.. this is the code which i'm using in my add records..
_SrcBLInfo =
SrcBLInfo.NewSrcBLInfo(); // Creates a new Object
e.NewObject = _SrcBLInfo;
// setting new object to Newly created BO
_SrcBLCtrl.srcBLInfoBindingSource.DataSource = _SrcBLInfo;
// Setting Binding source to the New Object
SetBindingSources();
The method SetBindingSource at the end reassings all the child binding sources.. this code is given in this method
TempBindingSource =
new BindingSource();
// Save the Existing BindingSource in a Temp Variable and Reassign it to Rebind UI
TempBindingSource = (
BindingSource)((BindingSource)ChildBindingSources[BindCount]).DataSource;
((
BindingSource)ChildBindingSources[BindCount]).DataSource = TempBindingSource;
I don't think you should be creating a new BindingSource, you should just set the DataSource on the existing binding source. If you create a new BindingSource object, you need to subscribe to the CurrentItemChanged event again, because it's the old binding source object which was subscribed. This also means that old binding source object will still be hanging around and won't get cleaned up by the garbage collector.
You might like to have another read through the CSLA samples to make sure you're following the correct way to bind and rebind your UI.
I just checked the project tracker sample but it is applying endedit etc. on the bo itself.. whereas i have read before that calling these methods can call problem..
is it possible for you to share portion of your code specially the part where u unbind and rebind the datasources..
i just tried one more thing.. i have temporairly disabled the child binding source code..
even now it is not working.. i'm just doing changes in the root object but currentitemchanged is not firing on 2nd record.
// Hope this helps.
protected FormMxsProject() { InitializeComponent(); // In the constructor we hook up the CurrentItemChanged event // to the BO's child collections bindServiceTypes.CurrentItemChanged += bindObject_CurrentItemChanged; bindTemplates .CurrentItemChanged += bindObject_CurrentItemChanged; }
private void BindUI() { // Sometimes we have binding sources for children which are bound to // another DataSource using the DataMember property. For this form, // that wasn't necessary, so we just explicitly set the DataSource // to the child collection.
bindObject .DataSource = BusinessObject = m_mxsProject; bindServiceTypes.DataSource = m_mxsProject.ServiceTypes; bindTemplates .DataSource = m_mxsProject.FlexTemplates; } protected override bool RebindUI(bool saveObject, bool rebind) { bool completed = false; bindObject .RaiseListChangedEvents = false; bindServiceTypes.RaiseListChangedEvents = false; bindTemplates .RaiseListChangedEvents = false; try { UnbindBindingSource(bindObject, saveObject); UnbindBindingSource(bindServiceTypes, saveObject); UnbindBindingSource(bindServiceTypes, saveObject); if (saveObject) { // We call ApplyEdit() directly on the BO // This is not the same as calling EndEdit() on a binding source // which you must do when working with grandchild binding sources m_mxsProject.ApplyEdit(); try { m_mxsProject = m_mxsProject.Save(); MxsProject.MxsProjectSaved(m_mxsProject); completed = true; } catch (Exception ex) { FormMessage.ShowError(ex); } } else { if (m_mxsProject != null) m_mxsProject.CancelEdit(); completed = true; } } finally { if (rebind && completed) BindUI(); bindObject .RaiseListChangedEvents = true; bindServiceTypes.RaiseListChangedEvents = true; bindTemplates .RaiseListChangedEvents = true; if (rebind && completed) { bindObject .ResetBindings(false); bindServiceTypes.ResetBindings(false); bindTemplates .ResetBindings(false); } } return completed; }
thanks for the reply.. kindly note the snapshot shows a call to method unbindbindingsource but code of that is not visible in the snapshot, pls advs..
public static void UnbindBindingSource(BindingSource source, bool apply) { IEditableObject current = source.Current as IEditableObject; if (!(source.DataSource is BindingSource)) source.DataSource = null; if (current != null) { if (apply) current.EndEdit(); else current.CancelEdit(); } }
i tried your code as well as tried the cslaactionextender but none of that is working..
this is the line, i'm giving for using csla action extender (taken from the sample)..
_SrcBLCtrl.cslaActionExtender1.ResetActionBehaviors(_SrcBLInfo);
there are two specific points for my forms which i also want to write here.
a. these are user controls not forms.
b. i'm using bindingnavigator
i don't know if this makes any difference.. i'm now out of options.. pls help..
We use a lot of user controls, too, so that shouldn't be a problem.
Are you using the binding navigator to bind to a child collection inside your main BusinessBase business object?
Perhaps you should create a new test class inheriting from BusinessBase, with just a couple of CSLA properties. You don't need to implement the data access. Then create a new form or user control and try to get the binding to work correctly, following the ProjectTracker sample. Once you've got that working, you can add a collection of children and try to get the binding to work for that.
It's not meant to be difficult, but you do need to follow the patterns. If you haven't bought the book, you should consider doing so. It's 800 pages—a wealth of information.
Kind regards
Michael
i have the book..
i will test it out today to see where the problem lies n get back.
Copyright (c) Marimer LLC