For one of the apps I have been working on, I needed to persist a List<T> of T objects across sessions. They are simple objects with simple properties, so I decided to use XML. However, since WP7 is a new platform, there’s not a lot of information out there on what is a good best practice.

It turns out for simple situations like mine, serializing objects into XML and writing that to file is by far the easiest way to do it.

Let’s examine a scenario.

The App

This is not an app I am working on, but it is basically the same thing.

Let’s say that you want to track your jogs. Starting simple, you decide to track the following properties:

  • RouteID : int
  • Duration : TimeSpan (we will encounter an interesting serialization bug with TimeSpan as well)
  • Date : DateTime
  • Distance (miles) : float

Of course, we won’t attempt to build all of this here, but rather, focus on the storage aspect of it.

The “Jog” Object

You will persist a list of these objects to be able to view them historically. This is what a Jog object might look like. Note the extra property, DurationXml, with its special attributes – this exists because XmlSerializer does not properly serialize TimeSpan objects.

    public class Jog
    {
        private TimeSpan _duration = new TimeSpan();
        public DateTime Date { get; set; }
        [XmlIgnore]
        public TimeSpan Duration
        {
            get { return _duration; }
            set { _duration = value; }
        }
        // HACK: This property only exists for XML serialization.
        // It gets serialized as a <Duration> tag.
        [XmlElement("Duration", DataType="duration")]
        public string DurationXml
        {
            get
            {
                return XmlConvert.ToString(_duration);
            }
            set
            {
                if (value == null)
                {
                    _duration = TimeSpan.Zero;
                    return;
                }
                TimeSpan newDuration = XmlConvert.ToTimeSpan(value);
                if (_duration == newDuration)
                    return;
                _duration = newDuration;
            }
        }
        public int RouteID { get; set; }
        public float Distance { get; set; }
    }

Note the hackery around the Duration and DurationXml properties.

Jog List

For the purposes of this app, you’d have a List of Jog objects: List<Jog> which you keep as a static on the App class and maintain it there. You’d then create some methods in the App class that would help out with storage, and then call them from Application_Closing, Application_Launching, and Activated/Deactivated as you handle tombstoning and deactivation.

Storage Helper

Then, you might write a class like this which helps with storage, and keep it statically on the App class. All you have to do is call XmlSerializer.Serialize(object) and XmlHelper.Deserialize(xml) to manage the saving/loading of your object list. Put together, it looks like this:

    public class StorageHelper
    {
        private const string FILE_NAME = "Jogs.xml";
        public List<Jog> LoadJogs()
        {
            List<Jog> jogs = new List<Jog>();
            TextReader reader = null;
            try
            {
                IsolatedStorageFile isoStorage = IsolatedStorageFile.GetUserStoreForApplication();
                IsolatedStorageFileStream file = isoStorage.OpenFile(FILE_NAME, FileMode.OpenOrCreate);

reader = new StreamReader(file);

 

                XmlSerializer xs = new XmlSerializer(typeof(List<Jog>));
                jogs.AddRange((List<Jog>)xs.Deserialize(reader));
                reader.Close();                
            }
            catch
            {
                
            }
            finally
            {
                if (reader != null)
                    reader.Dispose();                
            }
            return jogs;
        }
        public void SaveJogs(List<Jog> jogs)
        {
            TextWriter writer = null;
            try
            {
                IsolatedStorageFile isoStorage = IsolatedStorageFile.GetUserStoreForApplication();
                IsolatedStorageFileStream file = isoStorage.OpenFile(FILE_NAME, FileMode.Create);
                writer = new StreamWriter(file);
                XmlSerializer xs = new XmlSerializer(typeof(List<Jog>));
                xs.Serialize(writer, jogs);
                writer.Close();
            }
            catch
            {
            }
            finally
            {
                if (writer != null)
                    writer.Dispose();
            }
        }
        public StorageHelper()
        {
        }
    }

Don’t Forget Your References

One thing to note here is that several of the Xml classes won’t magically work with IntelliSense. You need to add references to System.Xml and System.Xml.Serialization for all this good stuff to work.

I’ll be posting an example Jog Tracker application that uses isolated storage in the coming weeks, but for now, this should be enough to get you started.