Reputation: 685
I wonder how you do such thing. Assume, we have MVVM CRUD app which modifies a tree (menu structure, for example). We have a view model with the menu items and two views: the first with a TreeView
and the second with a DataForm
. Main problems are:
DataForm
can not handle
hierarchical data.TreeView
the DataForm
should display different set of
fields (for example for menu items
with children or without).I've ended up with the following. View model has 3 fields:
Items
— the collection of
MenuItem
objects which have their
own Children
collection for
building hierarchical data source.SelectedItem
— currently selected
MenuItem
in the TreeView
.EditedItem
— EditViewModel
object which basically has two
descendants: MenuItemEditViewModel
and LeafMenuItemEditViewModel
.
This property is set automatically
when SelectedItem
is changed. Its
actual type is inferred from the
SelectedItem.Children
emptiness.TreeView
is bound to Items
and SelectedItem
. DataForm
is not required to maintain currency in this case (instead current item is set by the TreeView
) nor it is responsible for creating and deleting items. That's why I decided to bind only its CurrentItem
to view model's EditedItem
(ItemsSource
is unbound). Its AutoCommit
is set to False
(when it is True
and ItemsSource
is unbound all current item changes get copied to newly selected item when you select different item in the TreeView
, which is not so nice). DataForm
fields are autogenerated.
Obviously, that now if we select an item in the TreeView
, then make some changes in the DataForm
and try to select different item in the TreeView
we'll get well-known
Cannot change currency when an item has validation errors or it is being edited and AutoCommit is false. Set ItemsSource to a ICollectionView to manage currency instead
In this case I want DataForm
to discard all changes implicitly. There is a workaround to call DataForm.CancelEdit()
before TreeView
selected item is changed (usually an event like PreviewSelectionChanged
or BeforeSelectionChanged
). But it is not the MVVM way since the TreeView
and the DataForm
are defined in completely different views (read: is not acceptable).
Is there something like AutoCancel which forces DataForm
to cancel changes when its CurrentItem
is changed? Maybe someone from dev team can answer? Or how would you deal with such problem?
Upvotes: 2
Views: 3455
Reputation: 53
I was surprised to find the Silverlight is severly lacking in this functionality, considering all the business oriented RIA functionality. AutoCommit is not acceptable to me because I want the user to explicitly acknowledge pending changes, rather than just commit something to the database that they may not want.
You can reliably track the edit mode of the DataForm using a private member variable and trapping the BeginningEdit and EditEnded events of the DataForm (naming inconsistency! Why one is called xxxEdit and the others are Editxxx is beyond me. Should it not be EditBeginning and EditEnded??). Inside the event handler for BeginningEdit, set the flag to true and set it to false in EditEnded.
In your SelectionChanged event, you can then check the flag. If it is true, you can call the CancelEdit on the DataForm.
private bool _editing = false;
public MainPage() {
DataForm1.BeinningEdit +=
new EventHandler<CancelEventArgs>(DataForm1_BeginningEdit);
DataForm1.EditEnded +=
new EventHandler<DataFormEditEndedEventArgs>(DataForm1_EditEnded);
}
protected void DataForm1_BeginningEdit(object sender,
System.ComponentModel.CancelEventArgs e) {
_editing = true;
}
protected void DataForm1_EditEnded(object sender,
DataFormEditEndedEventArgs e) {
_editing = false;
}
void TreeView1_SelectedItemChanged(object sender,
RoutedPropertyChangedEventArgs<object> e)
{
if (_editing) {
object previous = DataForm1.SelectedItem;
object current = TreeView1.SelectedItem;
if (MessageBox.Show("Are you sure you want to cancel the changes?",
"Confirm", MessageBoxbutton.OKCancel) == MessageBoxResult.OK) {
DataForm1.CancelEdit();
}
else {
TreeView1.SelectedItem = previous;
}
}
}
Upvotes: 4