MarimerLLC / cslaforum

Discussion forum for CSLA .NET
https://cslanet.com
Other
31 stars 6 forks source link

Wonderful World of Windows Forms Databinding - Root --> Child --> Grandchild forms #125

Open Cymro opened 8 years ago

Cymro commented 8 years ago

Explaining the issues with windows forms databinding can be as taxing as the issues themselves, but here we go...

We employ an 'N' level undo methodology throughout our application and a Root --> Child --> Grandchild relationship is not uncommon. Each level is presented on a Windows Form Dialog. The Root form is responsible for saving, with Save and Cancel command buttons. The Child forms have OK and Cancel command buttons for committing or cancelling changes at each level.

It is with Child Forms that I have an issue and am trying to find a pattern that can be applied to a ChildForm base class. These should increase the Edit Level for the current Child item from the form above. The changes will either be committed or rolled back on closing this form.

Where there are only two levels (Root --> Child) I can control the "Undo" / "Apply" by using databindings "CancelEdit" / "EndEdit" and this seems to work fine, whilst leaving everything bound. When a third level is introduced however, I need true "N level undo" and (I think) need to unbind. The problem is that the object hierarchy could be bound 1,2 or more levels (forms) above.

So do I need to unbind the whole object hierarchy from all forms, or just the level above? Is there a consistent model for unbinding the level(s) above? I have tried using the BindingSourceHelper / BindingSourceNode (at each level, within each form), but am getting the dreaded "Edit level mismatch on AcceptChanges".

Has anybody employed such a pattern successfully? I am looking to get this implemented for the whole development department, and to make the task of binding in this environment as slick as possible.

jonnybee commented 8 years ago

There is one very important thing to keep in mind when using Windows Forms Databinding:

DataBinding will call BedinEdit/EndEdit/CancelEdit many times - maybe even on a per-field basis (in Simple Databinding) or per Row in ComplexDatabinding. And you cannot/should not make manual calls to BeginEdit on an object that is active in DataBinding as per spec - The EndEdit/CancelEdit is related to the first call to BeginEdit (ie: only the first call to IEditableObject.BeginEdit will do any work - subsequent calls is ignored).

See: https://msdn.microsoft.com/en-us/library/system.componentmodel.ieditableobject.beginedit(v=vs.110).aspx

This method is typically used to capture the BeginEdit semantics of a DataRowView. If BeginEdit is called on an object that is already being edited, the second and subsequent calls are ignored.

CSLA detects whether the call to BeginEdit is called directly or from the Interface.

So you might be able to make it work by applying [NotUndoable] attribute to the child/grandchild properties. and make sure to call BeginEdit in the modal form before you hand the object to the BindingSource. and call EndEdit/CancelEdit after you have unbinded the object from the DataSource.

I was part of a team that created a large windows forms app but we chose to not use Modal Forms - we only had non-modal forms (pretty much like in Outlook) and used the default DataBinding mechanisms in our forms.

Cymro commented 8 years ago

Thank you Jonny. Yes I learned early on the binding semantics of CSLA with Win Forms. I don't have access to the code right now, but I will give the [NotUndoable] attribute a go. However, won't that be controlling the edit level at the wrong level in the hierarchy. When adding an item to a child / grandchild, isn't it the parent that needs to be undone on a cancel?

The non modal route is definitely attractive, but we are kind of stuck with the UI paradigm we have...at least for now.

jonnybee commented 8 years ago

Well, that depends on whether you add the new item before you show the modal form or after the user done all the editing and then said OK.

That said - if the user selects Cancel on a new item your code always remove that item from the parent too.

I do fear that you will still fight the "Edit level mismatch" errors.

Cymro commented 8 years ago

I may have been closer than I thought. I think the issue I was experiencing was down to the use of the BindingSourceNode and BindingSourceHelper in the ChildForm. These are assuming (correctly for their intended use), that the "Root" binding source is not currently bound to anything. But in the case of the ChildForm the item to be hooked up to the "Root" BindingSource is still bound in the RootForm.

E.g.:

RootForm:

ChildForm

BindingSourceNode does not seem compatible to be used in this way on the ChildForm, although I am not sure that the changes would be too big to make it so. I may be able to add a set of BindChild / ApplyChild / CancelChild methods, or even check if the object you are binding derives from BindingBase and check "BindingEdit" to see if it is already bound. If so, do not increase the EditLevel.