GUI-Problem with BindingSourceControl - No chance to call CancelEdit()

GUI-Problem with BindingSourceControl - No chance to call CancelEdit()

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


stefan posted on Monday, March 26, 2007

Hi @ all!

My situation:
The objects:
I have an object that represents a userdefined query (UserQuery).
The UserQuery has a child-collection of 'Criteria'-children (CriteriaCol).
In my EditUserQuery form I have a couple BindingSource controls:
- UserqueryBindingSource, bound to the root object
- CriteriaBindingSource, bound to the Criteria-collection inside the root object
- CurrentCriteriaBindingSource, bound to the currently seleted criteria (read on...)

The GUI:
The entire list of criteria is displayed in a DataGridViewControl, which is bound to
CriteriaBindingSource. There I display the descriptions of each criteria item (= read only).
The user selects a specific criteria item for editing by clicking on the DataGridView.
Then the CurrentCriteriaBindingSource is bound to the selected item. On the same form,
there is a region for editing a single criteria item. The controls in this region are bound
to the currently selected criteria item through the CurrentCriteriaBindingSource.
There are two buttins in this area:
- btnUpdateCriteria, which calls CurrentCriteriaBindingSource.EndEdit()
- btnCancelEdit, which calls CurrentCriteriaBindingSource.CancelEdit()

The problem:
Clicking on the DataGridViev loads the selected criteria into the edit-area - fine.
After editing the current criteria object, I have no chance to Cancel the edit process,
because as soon as the focus gets to another control that's not bound to CurrentCriteriaBindingSource,
the DataGridView updates and shows the modified values. Calling CancelEdit on the CurrentCriteriaBindingSource has no effect.

I think there is a flaw in how I deal with the different BindingSource controls, but I have no idea.

Hope that someone can help me find a solution!

Stefan


Bayu replied on Monday, March 26, 2007

Hey,

You should break it down further into all elementary use cases. The key realization is that your single form supports multiple use cases simultaneously:
1 - show the details of a single UserQuery
2 - show the list of all criteria for this UserQuery
3 - edit a single Criteria

It is not clear to me how you would like your form to work:
a) editable criteria list with editable criterias:
    - users can add/remove/edit criteria's to the list
    - each criteria is editable by itself on which changes can be canceled AND users can cancel the WHOLE set of changes done to the list as a whole (i.e. undo all additions/removals and edits)

b) only changes to a single criteria can be undone

c) also the UpdateQuery has editable properties, and Cancel should cancel all changes to the list of criteria as mentioned under a) AND any changes to UpdateQuery itself


The key difference is the following:
- in a) the use cases 2 and 3 are actually the same use case
- in b) 2 and 3 are really distinct use cases
- in c) there is only one use case that involves all of 1, 2 and 3

Now comes the mantra: you should model after use cases, i.e. you need a top-level (root) object for each single use case.

For 1)
- if the UpdateQuery has editable properties it should be an editable root
- otherwise it can be a readonly object

For 2)
- if changes to the list (add/remove) should be cancellable, use an editable list, depending on whether the canceling should involve the UpdateQuery (as in c)) or not (as in a)) this is an EditableChildList or an EditableRootList
    - in case of EditableChildList, UpdateQuery would have a property for this purpose
    - in case of EditableRootList the UpdateQuery object could provide a GetCriterias() function that returns all relevant Criterias in a editable root list for this purpose
- otherwise it will be a ReadOnlyList (root) which can be fetched/refreshed independently (perhaps through a UpdateQuery.GetCriteriaList() function)

For 3)
- depending on your sitiation for 2) you would have an EditableRoot or an EditableChild object here
- in case of b) (editableroot) you can fetch the object based on the ID that corresponds to the selected row in your grid
- in case of a) or c) you already have an editable copy of your object, so you don't need to fetch anything anymore


Now about your Cancel and Update buttons:
- in case of a)
    - you would need to begin a new edit (invoke BeginEdit() on the Criteria object) when binding the Criteria, and later on you can then cancel or commit the change
    - independently you will also invoke BeginEdit on the list object which will be paired with en EndEdit upon Save
    - optionally you can provide a CancelAll button that routes to the list's CancelEdit

- in case of b)
    - the list is readonly, changes to Criteria objects are beyond the scope of the list, so there is only BeginEdit and EndEdit/CancelEdit to be invoked on the Criteria object
    - note that the readonly list will not synchronize automatically with changes to criteria objects, so this is why you need this list to be root-level so you can refetch it independently (and refresh your bindingsource)

- in case of c)
    - similar to a), but now you can begin, cancel and end edits on 3 levels (UpateQuery/EditableChildList and Criteria)

Note that in typicaly UI designs you only provide Cancel buttons for the top-level object(s), users typically do not have a clue what you mean by CancelAll vs Cancel.

Perhaps I helped you out a bit, or perhaps I just added to the fog. ;-)

I hope the former though. ;-p

Regards,
Bayu

Copyright (c) Marimer LLC