I have a requirement for which i have to implement a 3 Step save functionality
I have a MainView which is divided into two parts:
- Left Part has a
UserControl
which has aTreeView
inside it. - Right Part has a
ContentControl
which hostsUserControl
depending upon the Item selected in theTreeViewUserControl
.
To Explain the senario i give you an example.
开发者_如何学编程If i click a leaf Node the Data UserControl Will be populated and the Data of the respective selected TreeView Item is populated for the Model ->ViewModel-> View(UserControl). If the user changes the Data and then saves it it must be saved temporarily and if the user cancels all changes must be discarded. The same behavior should be demonstrated when adding new nodes to the TreeView.
Only when the user clicks Save in the File menu should the data be serialized and saved to disk.
In addition, if the user tries to navigate away, I wish to prevent them from doing so until they either save or cancel their changes.
I am facing problem in saving the temporary state of these Data objects in such senario. I tried using shallow copies but this is not working as its giving only the references of the main Entity (initialized when the file is read).
Is there any other approach?
Your question really doesn't have much to do with WPF or MVVM. You need to keep track of changes to your Model's states, and be able to query the states of all Models from your ViewModels to determine if you should allow users to leave.
Unfortunately, the framework doesn't natively support this pattern. You must write it yourself.
How I would (roughly) accomplish this:
Each Model would implement an interface, lets say IDirty
. This interface has the following properties/methods:
- IsDirty { get; }
- CancelEdit();
- AcceptEdit();
Upon creation, the Model is not dirty (IsDirty == false
). As its properties are changed, it becomes Dirty. The original state of all properties is also remembered. If the user cancels, then CancelEdit()
is called and the original state is restored. If AcceptEdit()
is called, then the original state is overwritten with the current state.
I would also create custom ObservableCollection
implementations that would allow me to query the state of all Models contained within to determine if any is dirty. Same for my ViewModels, so the View can bind to the ViewModel's IsDirty
property, which queries all collections of Models within the ViewModel. Likewise, calls to CancelEdit
and AcceptEdit
are chained as well, so you can (for example) call AcceptEdit()
on the ViewModel and all Models within will have AcceptEdit
called on them.
This can become very tedious to code. I'm sure there are some frameworks out there that are designed to provide this type of functionality. I just don't know of any offhand.
If you use the standard WPF Binding approach then it is very hard to achieve robust form handling.
This is because there are 2 types of IsDirty, ViewModel.IsDirty (your responsibility) and Binding.IsDirty (WPF responsibility).
Binding.IsDirty occurs when a TextBox Text value has changed, but not been sent to the Binding Source.
It is possible for ViewModel.IsDirty to be false, while Binding.IsDirty to be true.
This happens because TextBox default UpdateSourceTrigger is LostFocus, not PropertyChanged.
It has to be this way otherwise editing a DateTime (etc.) will fight the user (validation will fail during partial edits and there will be constant reformatting of the user input)
Unfortunately WPF Binding class does not implement IsDirty functionality, even thought it is in a perfect position to do so, it has access to both the Source (ViewModel) and the Target (TextBox) and is notified of all relevant events.
Furthermore the design of the Binding class does not lend itself to being extended with this functionality. IMO It is a "Black Box" and a great example of how not to design software.
My solution is to create my own Binding class and then I can be productive again.
精彩评论