In continuation of the previous posts –
WPF – Editing Mode with Save and Cancel Capability
In this post I will demonstrate an elegant and simple solution that you can use regarding the second option presented in the first post series (cloning via serialization).
Often, in more advanced scenarios, you need to duplicate your object for a different view. (read the first post for more detail)
A commonly used technique is using serialization to clone the object. Unfortunately, in many times, our view models aren’t cloneable easily in their nature by using serialization, especially when we use PRISM within our view models. When using PRISM, your view models may contain references to all sorts of CAL services for example, which are usually non-serializable.
The common approach for dealing with this is using serialization events. You can set the fields not be serialized (or simply set them to null before serializing) and set their values after deserialization is complete.
You need to design your object to fit such cloning technique. For example, where would you register/instantiate your commands? constructor alone is no good due to the fact the we need to support duplication via serialization.
What I have to offer is..
- ViewModelBase – My ViewModelBase provides a method that you can override – InitializeCore(bool deserializing)
- This method occurs only once!
- If the object is being instantiated normally – part of the constructor
- If the object is being serialized – after deserialization completes
- This method provides easy access for implementing things needed either in cases of instantiation or serialization
One more thing..
I mentioned this above – in PRISM (and other cases), your view models are usually not easy to duplicate, look at the following example –
Note: The code here and in my samples are using the BinaryFormatter.
Currently, MyViewModel is not serialiable.
In order to make it serializable, I need to decorate my class with the Serializable attribute, but that is not enough.
The serialization will fail because the fields _eventAggregator and _container are not serializable too.
In order to make the serialization work, here is what I need to do –
2 things have been made – 1) I set my class to be serializable 2) Marked the fields to not participate in the serialization process.
Is that all? Well, no.
The serialization and deserialization back will work fine and I will have a duplicate object. However, the state of the duplicate object would be incomplete – the _eventAggregator and _container would be nulls and no processing had been done.
Now what? well, in order to make all things work properly, I would have to encapsulate the duplication in one method (be that in my view model or somewhere else) to do the following –
Serialize the view model, call an exposed method to complete operation, return the duplicate.
For example –
This is quite fine and gives us a neat solution.
My goal was to make such cases simpler though, since it is quite common in PRISM-based business applications.
Here is the solution
I built a nice wrapping on top of the binary serialization that you can use for such cases. (you can switch the actual serialization provider as you may need)
You can instruct it to serialize fields by ref.
Furthermore, it supports the entire object graph. If your view model contains other view models which have such decorated fields, it will process them as well.
This means, that upon serialization, it will set null to these fields and set their value back both in the duplicate and the original object.
So, using that, my view model looks as follows – (note the use of the InitializeCore I mentioned in the beginning of the post)
As you can see, I no longer need to worry about state preserving. I simply tag the fields I need to be set as references and do what I need in the InitializeCore implementation which occurs at the constructor or after deserialization when the fields already have their by-ref values.
While in such a simple case it is hard to see the benefit, when it comes to more complicated classes with deeper object graphs, it becomes much easier using this approach.
So.. how do you get the actual duplicate?
You need to call my wrapper in order to generate the duplicate. the internal implementation uses the BinaryFormatter but you can change that to NetDataContractSerializer/DataContractSerializer/XmlSerializer if you need.
Important Note – this serialization technique goes through the entire object graph, so it is advisable to use that in scenarios where you need to serialize a single object at a certain given time. (in this example, when the user edits an employee, the performance hit isn’t an issue for the benefit of using that)
You can download the source here – Zuker.WpfSamples.zip. (Edit State Preserve – EditWithCloneSerialize)
The sample shows a case of a really simple view model, but you can go ahead and try it with the example shown here, with the use of ‘SerializeByRef’.