Saving Ink to IsolatedStorage with Silverlight 2

One of the questions I have seen quite a few times in the Silverlight.net forums is how to persists (and de-persists) ink in Silverlight 2. Unlike WPF, Silverlight does not support a binary persistence format (ISF), but you can write some code to save and load ink to and from XAML. To demonstrate how to do this in a real world example, I am using another very cool feature in Silverlight 2: IsolatedStorage

IsolatedStorage is a per application, per computer, per user storage that offers a powerful, easy to use way for your web applications to store data locally on the user's and retrieve it later when the user visits your sites again on the same computer. You can use it like a file system, or as in the case of this example, simply use it to store application settings in key/value pairs. Learn more about how to do the latter here.

The goal of my application is to store the ink as XAML in IsolatedStorage when the user navigates away from the site. Upon return, the app will retrieve the persisted data from the storage, depersist it and present the ink to the user. Here are the relevant pieces of code (full code project is attached to this post).

Serializing and de-serializing ink strokes using XAML:

public string InkXAML

{

    get

    {

        // persists ink strokes to XAML

        StringBuilder builder = new StringBuilder();

        XmlWriter writer = XmlWriter.Create(builder);

        writer.WriteStartElement("StrokeCollection",

            "https://schemas.microsoft.com/winfx/2006/xaml/presentation");

        foreach (Stroke stroke in _presenter.Strokes)

        {

            writer.WriteStartElement("Stroke");

            writer.WriteStartElement("Stroke.DrawingAttributes");

            writer.WriteStartElement("DrawingAttributes");

            writer.WriteAttributeString("Width",

                stroke.DrawingAttributes.Width.ToString(invCult));

            writer.WriteAttributeString("Height",

                stroke.DrawingAttributes.Height.ToString(invCult));

            writer.WriteAttributeString("Color",

                string.Format(invCult, "#{0:X2}{1:X2}{2:X2}{3:X2}",

                stroke.DrawingAttributes.Color.A,

                stroke.DrawingAttributes.Color.R,

                stroke.DrawingAttributes.Color.G,

                stroke.DrawingAttributes.Color.B));

            writer.WriteAttributeString("OutlineColor",

                string.Format(invCult, "#{0:X2}{1:X2}{2:X2}{3:X2}",

  stroke.DrawingAttributes.OutlineColor.A,

                stroke.DrawingAttributes.OutlineColor.R,

                stroke.DrawingAttributes.OutlineColor.G,

                stroke.DrawingAttributes.OutlineColor.B));

            writer.WriteEndElement();

            writer.WriteEndElement();

            writer.WriteStartElement("Stroke.StylusPoints");

            writer.WriteStartElement("StylusPointCollection");

            foreach (StylusPoint sp in stroke.StylusPoints)

            {

        writer.WriteStartElement("StylusPoint");

                writer.WriteAttributeString("X", sp.X.ToString(invCult));

                writer.WriteAttributeString("Y", sp.Y.ToString(invCult));

                writer.WriteEndElement();

            }

   writer.WriteEndElement();

            writer.WriteEndElement();

            writer.WriteEndElement();

        }

        writer.WriteEndElement();

        writer.Flush();

        return builder.ToString();

    }

    set

    {

        // depersist ink strokes from XAML

        try

        {

            _presenter.Strokes = XamlReader.Load(value) as StrokeCollection;

        }

        catch (Exception exc) { Debug.WriteLine(exc.ToString()); }

    }

}

Persisting data on Application exit:

void Application_Exit(object sender, EventArgs e)

{

IsolatedStorageSettings settings = IsolatedStorageSettings.ApplicationSettings;

string inkXAML = _inkCollector.InkXAML;

if (settings.Contains("Ink"))

{

settings["Ink"] = inkXAML;

}

else

{

settings.Add("Ink", inkXAML);

}

}

Loading data on Application startup:

public Page()

{

InitializeComponent();

_inkCollector = new InkCollector(inkNote);

IsolatedStorageSettings settings =

IsolatedStorageSettings.ApplicationSettings;

string inkXAML = "";

if (settings.Contains("Ink"))

{

inkXAML = settings["Ink"] as string;

_inkCollector.InkXAML = inkXAML;

}

}

If you have Silverlight 2 installed (who hasn't? :-)), you can try it out live below. Draw some ink, navigate away, close the browser, clear the cache, etc. Then return to my blog and see that your ink is back, due to the power of IsolatedStorage.

InkStorage.zip