Cancel creation of a child object

Cancel creation of a child object

Old forum URL: forums.lhotka.net/forums/t/3011.aspx


SonOfPirate posted on Friday, June 08, 2007

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:

  1. data-bind the form to the new child object
  2. no data-binding and manually populate the child object only when the user clicks the OK button.

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?

 

malloc1024 replied on Friday, June 08, 2007

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.

Brian Criswell replied on Friday, June 08, 2007

Make a factory method for the child object that allows you to pass the default values in to the new object.  The collection or parent object could then supply a method that called the factory method on the child object and returned it.  Then do what you are doing in option 1 with the databinding.

ajj3085 replied on Monday, June 11, 2007

Pirate, the third option seems to be the one that Csla intends you to use.  Yes, adding and removing the object incurs some overhead, but then again, so what?  Unless you're doing this in some tight loop, the overhead doesn't really matter at all.  The end user won't notice a thing.  The object ultimately will be disposed of when the parent is Saved or discarded.  

SonOfPirate replied on Monday, June 11, 2007

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.

 

Brian Criswell replied on Monday, June 11, 2007

The ObjectListView exposes a list of ObjectView which implements IEditableObject.  This will give you the same end result with the DataGridView (pressing ESC to cancel a new row).  It also means that the following works:
ObjectListView view = new ObjectListView(list);
object item = view.AddNew();

using (PopupForm form = new PopupForm())
{
    form.Object = item.Object;
    if (form.ShowDialog() == DialogResult.OK)
    {
       item.EndEdit();
    }
    else
    {
       item.CancelEdit();
    }
}

Bowman74 replied on Monday, June 11, 2007

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