Storing data on Windows Phone
Mood: excited
Posted on 2013-03-28 22:06:00
Tags: windowsphone wpdev
Words: 925
This post is adapted from a presentation I gave at the University of Texas IEEE Computer Society on March 6, 2013
Storing data on a device is one of those things that is different on every platform, but it's crucial to most apps. (especially if you want to make them work offline) Windows Phone has a lot of good options for storing data - let's look at four of them:
- Resource packaged with the app - an ideal way to ship data with your app
- Isolated Settings - best for lightweight and small data, such as app settings
- Isolated Storage File - best for storing full files
Serializing to/from JSON - a brief aside; useful when packaging a resource with the app or storing data in an Isolated Storage File
- Local database - best for fully structured data that needs to be high performance
Resource packaged with the app
This is a great way to ship static data with an app. For example, in my Marriage Map app, I ship a static version of the marriage data so that if the phone is offline during first launch of the app, we can still show some data. (if the phone is online, it downloads the current data and saves it to Isolated Storage)
Anyway, this is pretty straightforward. Add the file to your project, select the file, and in the Properties window set the Build Action to Content. After this, you can read the file with:
If you're going to be storing data in this file, I'd recommend using JSON format - see Parsing JSON for details.
var resource = System.Windows.Application.GetResourceStream(
new Uri(@"Data\stateData.js", UriKind.Relative));
using (StreamReader sr = new StreamReader(resource.Stream))
{
string allDataString = sr.ReadToEnd();
}
Isolated Settings
Isolated Settings are great for storing very small bits of data, like user preferences. If you want to make a settings page and have that data automatically stored in Isolated Settings, see my previous posts on adding settings to your app and adding enum settings to your app.
The class we'll be using is System.IO.IsolatedStorage.IsolatedStorageSettings
- it's implements a simple Dictionary<TKey,TValue>
interface to read and write. To write data, use
and to read it, use
IsolatedStorageSettings.ApplicationSettings["NumQuizzes"] = 3;
Isolated Settings are backed by an Isolated Storage File, which we'll talk about next!
int nQ = (int)IsolatedStorageSettings.ApplicationSettings["NumQuizzes"];
Isolated Storage File
For more complex data, you can move up to using the full Isolated Storage API to store data in files. There's a full filesystem you have total control over. (which is only accessible to your app, of course!) I use this in FlightPredictor to store the user's flights.
The main class we'll use is System.IO.IsolatedStorage.IsolatedStorageFile
. To write to a file, use
and to read from it, use
using (var store = IsolatedStorageFile.GetUserStoreForApplication())
{
using (var stream = store.OpenFile("flights.json", System.IO.FileMode.Create))
{
using (var streamWriter = new StreamWriter(stream))
{
streamWriter.Write("{\"flights\": []}");
}
}
}
using (var store = IsolatedStorageFile.GetUserStoreForApplication())
{
using (var stream = store.OpenFile("flights.json", FileMode.Open))
{
using (var streamReader = new StreamReader(stream));
{
string flightsString = streamReader.ReadToEnd();
}
}
}
There are other methods on IsolatedStorageFile
like CreateDirectory()
and GetFileNames()
if you want to really use isolated storage as a filesystem.
Serializing to/from JSON
See this post for comparing parsing time for different file formats, including JSON
Note that these APIs let you read and write text to files. Usually, you'll want to store more structured data, and I'd recommend using the JSON format because Json.NET makes it very easy. Here's how!
First, you can use the DataContract
and DataMember
attributes on an existing class. For example, here are the first few lines of my Flight class:
Then, to turn a list of Flights into a string, use:
[DataContract]
public class Flight : INotifyPropertyChanged, IComparable<Flight>
{
[DataMember]
public string AirlineName;
[DataMember]
public int Number;
[DataMember]
public DateTime DepartureTime;
and to read a list of Flights, use:
List<Flight> flights = new List<Flight>();
flights.Add(flight);
string flightsString = JsonConvert.SerializeObject(flights);
If you'd rather not create a whole class, you can also deserialize "raw" JSON, which is very handy when you're getting results from a webservice. For example:
List<Flight> newFlights =
JsonConvert.DeserializeObject<List<Flight>>(flightsString);
JObject o = JObject.Parse(responseString);
JObject flightsJson = (JObject)o["flights"];
int numFlights = (int)flightsJson["total_entries"];
JObject flightJson = (JObject)((JArray)flightsJson["array"])[0];
Local database
You can also store data in a full database. This takes a bit more coding, but is useful if you need to do queries, etc. I use this in PhotoNotes - each photo gets a row in the database with a caption and audio filename. (which are stored in Isolated Storage Files!) Here's the topic on MSDN about a local database, but briefly, the steps you need are:
First, declare your DataContext
:
Then on your table class, you need the
public class PicturesDataContext : DataContext
{
// Specify the connection string as a static, used in main page and app.xaml
public static string DBConnectionString = "Data Source=isostore:/PictureNotes.sdf";
// Pass the connection string to the base class.
public PicturesDataContext(string connectionString)
: base(connectionString) { }
// Specify a single table
public Table
}
Table
attribute:
and then member fields with the
[Table]
public class PictureNote : INotifyPropertyChanged, INotifyPropertyChanging
Column
attribute are columns in the table:
Now, to insert rows, you can create new instances of the
[Column(CanBeNull=true)]
public string FileName
{
PictureNote
class and call InsertOnSubmit()
on the table:
And to query the table, you can use the totally cool LINQ to SQL. For example:
_noteLensDB.PictureNotes.InsertOnSubmit(new PictureNote()
{ FileName = shortFileName, NoteText = noteText,
NoteAudioFileName = _lastSavedAudioFileName });
_noteLensDB.SubmitChanges();
var query = from n in _noteLensDB.PictureNotes
where ((n.NoteText != "" && n.NoteText != null) || n.NoteAudioFileName != null)
select n;
var numPictures = query.Count();
foreach (var note in query)
{
string name = note.FileName;
}
--
See all my Windows Phone development posts.
I'm planning on writing more posts about Windows Phone development - what would you like to hear about? Reply here, on twitter at @gregstoll, or by email at ext-greg.stoll@nokia.com.
--
Interested in developing for Windows Phone? I'm the Nokia Developer Ambassador for Austin - drop me a line at ext-greg.stoll@nokia.com!
This backup was done by LJBackup.