4.2 KiB
Serializing the model and ink data
Serializing
SaveModelAsync() serializes the Model
and saves the ink data.
The Model class is serialized by using the DataContractJsonSerializer class. The Model
is annotated with the System.Runtime.Serialization.DataContract attribute. This attribute indicates that the type can be serialized using a serializer such as the DataContractJsonSerializer
.
Not all of the members of the Model
class need to be serialized. Members to serialize are marked with the System.Runtime.Serialization.DataMember attribute. This defines the contract that the DataContractJsonSerializer
uses to serialize the object.
First, the Model
is serialized using the DataContractJsonSerializer
, but the ink can't be serialized by using one of the standard serializers. Although the InkStrokecontainer
has a SaveAsync() method, that method isn't suited for saving consecutive InkStrokeContainer
objects to a file because when we try to load an InkStrokeContainer
using LoadAsync, it is unable to distinguish between the multiple InkStrokeContainer
in the stream.
We didn't want to have individual backing files for every note to store the Ink Serialized Format (ISF) data. That would have meant writing code to manage the backing files for the notes. So, instead, the code extracts the ink stroke data from each Note's InkStrokecontainer
, combines it into one InkStrokecontainer
, and saves it to an ISF file.
In order to identify which ink strokes belong to which InkStrokecontainer
, the number of ink strokes per container are preserved. When the ink stroke data is loaded, because the notes are read in the same order they were written out, we can identify how many ink strokes in the container belong to each note and restore them.
Deserializing
LoadModelAsync() deserializes the Model
and ink data.
First, the Model
is deserialized using the DataContractJsonSerializer
. It is important to note that when a type is deserialized, the DataContractJsonSerializer
does not call the constructor or field initializers for the deserialized object. In the case of the Model
, this is a problem for the _family
and _stickyNotes
fields because they need to represent valid collection objects before the deserialization process tries to add deserialized content to those collections.
To initialize the fields before deserialization tries to access them, the System.Runtime.Serialization.OnDeserializing attribute is applied to the OnDeserializing method. This method is called when theModel
is being deserialized. It in turn calls Initialize(), which constructs the collections for the _family
and _stickyNotes
fields. Initialization code for the object is centralized in Initialize()
so that it can also be called from the Model
constructor.
After the Model
is deserialized, the code iterates through the restored notes. For each note, the code reads the number of ink strokes that belong to its InkCanvas
. Once the number of ink strokes per note is read, the combined ink stroke data is loaded. Then, each note extracts the number of ink strokes that belong to it from the combined InkStrokecontainer
.
It is important to call Windows.UI.Input.Inking.Clone to copy an InkStroke
from the combined InkStrokeContainer
to the individual note's InkStrokeContainer
because an InkStroke
can't belong to multiple InkStrokeContainers
at the same time.