In our application, we allow the users to create new child objects by clicking a button which displays a form for them to enter/edit the child object's initial properties. The form allows the user to commit or cancel the "creation" of the child object. We saw two paths we could take with the UI form:
We have gone down the path of option #1 but are now running into problems because of the actual implementation.
When the user clicks the button, the UI form calls the parent object's CreateChild() method which returns a new orphaned, empty, instance of our child object which is then bound to the form. When the user clicks the OK button, the bound child object is added into the parent object's ChildCollection. If they clicked Cancel, the orphaned object is simply disposed.
I mention the fact that the child object is orphaned because its Parent property is null until it is actually added into the collection. The reason this is causing problems is that some of the object's properties get their default values from their parent. So, we need to have the reference before we display the form.
This leads us to a third option which is to create the object, add it to the collection, bind to it and display the form. Then, if the user click OK, do nothing; otherwise, we remove the object from the collection. But, this Add/Remove seems like extra overhead and has raised some eyebrows.
What is the typical approach for this type of situation?
I have been doing option 3 for awhile now. It seems to work best with CSLA. I changed CSLA to handle the automatic removal
of the new object. I added an extra property in BusinessBase called
IsBrandNew. An object is brand new when
it is new and ApplyEdit has not been called.
Once ApplyEdit has been called on a new object, it is no longer brand
new. If CancelEdit is called on an
object that is brand new, it notifies the collection to remove it from the
collection. The overhead is so minimal
that you should never notice it.
If you don’t add it to the collection when you create it, you will need to pass the parent to the object when you create it. It might need some information form the parent (as in your case) or it might need to raise an event etc.
It can be beneficial to add the new object to the collection when you create it. For example, what if a user wants to see the total on an invoice before they press save on the current new line item? This is much easier if the new line item is already added to the collection. I find that adding a new object to the collection when it is created works best.
I actually "discovered" the System.ComponentModel.ICancelAddNew interface over the weekend and am looking at how to implement this into our classes as a way to possibly address this.
If my understanding is correct, when a new "row" is added to the collection by our UI grid (typically) via IBindingList.AddNew, calling ICancelAddNew.CancelNew as a result of the user pressing the Escape key (perhaps) instructs the collection to remove the item and discard it. If the user arrows off the row or presses the Enter key, the grid would call EndNew which would essentially commit the new row to the collection.
This interface is implemented by the System.ComponentModel.BindingList<T> class.
It's been a while since I created a UI of this type and it was with a (much) earlier version of CSLA. At that time this was why there were nested levels of client side transactions. That is when the user pressed the Add button you start a nested BeginEdit on the parent object and then created the child object and brought it up in the edit screen. If the user pressed OK, ApplyEdit was called to commit the sub-transaction and all is good. If the user pressed Cancel then CancelEdit was called and the object was reset to where it was before the Add button was pressed but still preserving all the transactions made up to that point.
Very important if you can have parents that create children in edit screens that can in turn create grandchildren. Once you start getting to the grandchild level manually adding and removing is going to cause you headaches when compared to just using the nested CSLA transactions IMHO.
I assume this will still work in current versions of CSLA. IIRC it was a prime factor on why nested transactions are supported by the CSLA framework in the first place.
Thanks,
Kevin
Copyright (c) Marimer LLC