This article is Day #8 in a series called 31 Days of Windows 8. Each of the articles in this series will be published for both HTML5/JS and XAML/C#. You can find additional resources, downloads, and source code on our website.
In several of the articles in this series, we have mentioned that storing data is not only incredibly important, but also that it’s super easy to do, both locally to a device, as well as roaming across the many different devices a user may use.
Microsoft offers us some specific guidance on when to use roaming vs. local storage, but I’ll give you a quick summary here so that you’ve had a chance to read it (because we both know you didn’t click that link). Again, these are guidelines, so you’re not going to get denied from the store for breaking these rules, but there are also limits to data transfer size and speed. Exceeding those will prevent your app from actually roaming data for a period of time.
DO
- Use roaming for preferences and customization. Any choice that a user is likely to make on each machine that they use should be roamed. These are basic settings, like color preferences, favorite actors, or whether or not to publish data to Twitter.
- Use roaming to let users continue a task. Having my browser favorites follow me around, or even my high scores is awesome. Allowing me to continue writing that email (or blog post) I never finished? Even better.
DON’T
- Use roaming for information that is clearly local-only. This includes things like file paths and other data that only makes sense to the local device.
- Don’t roam large datasets. There is a quota, which you can determine in code, that limits the size of your roaming dataset. It is best to only roam preferences and small data files, as we will show in this article.
- Don’t use roaming for instant synchronization or rapidly changing data. Windows controls when and how often your app data will be roamed, so don’t count on instant synchronization. Build a web service of your own if you need this kind of reliability. Also, don’t update the roaming data constantly. For example, you don’t need to roam the user’s current location at all times, instead update it every minute or so. You’ll still provide a rich experience without destroying your quota.
One last thing to remember: the way data is roamed across devices is managed by the user’s Microsoft account. If they log into two machines with the same account credentials, AND they install the same app in both places, THEN the roaming settings and files will travel. Until then, nothing happens.
Now that I’ve scared you into never using this, let’s take a look at how it’s done. There are two types of data that can be stored, and we will address each one of them both locally and roamed. First up is Settings, followed by Files.
Local and Roaming Settings
When you hear the word “settings” in Windows 8 (or even Windows Phone) development, “small, simple data” is what should come to mind. We’re really talking about storing name/value pairs.
Good examples of these are user preferences. Perhaps you stored the user’s first name (a string value) so that you could address them as such in your game. Maybe they decided to turn off notifications (a boolean value) from your app. Settings are also one of the easiest ways to store data, and I’ve found myself on more than one occasion storing a great number of settings values in my applications. Because these are invisible values that live in an invisible data store, I’ve chained a set of methods together that will create, then read, and then delete the settings values, both locally and roaming. You’ll see that it’s actually pretty simple to use.
ApplicationDataContainer settingsLocal;
ApplicationDataContainer settingsRoaming;
string currentBook;
int currentPage;
public MainPage()
{
this.InitializeComponent();
settingsLocal = ApplicationData.Current.LocalSettings;
settingsRoaming = ApplicationData.Current.RoamingSettings;
AddSettings();
}
private void AddSettings()
{
//There is no reason to set the same data to both local and roaming.
//This is here merely for illustration of HOW to do it.
//You should make the choice as to whether your data should be roamed.
settingsLocal.Values["currentBook"] = "Hitchhiker's Guide To The Galaxy";
settingsLocal.Values["currentPage"] = 42;
settingsRoaming.Values["currentBook"] = "Hitchhiker's Guide To The Galaxy";
settingsRoaming.Values["currentPage"] = 42;
ReadSettings();
}
private void ReadSettings()
{
//If you want typed data when you read it out of settings,
//you're going to need to know what it is, and cast it.
currentBook = (string)settingsLocal.Values["currentBook"];
currentPage = (int)settingsRoaming.Values["currentPage"];
DeleteSettings();
}
private void DeleteSettings()
{
settingsLocal.Values.Remove("currentBook");
settingsLocal.Values.Remove("currentPage");
settingsRoaming.Values.Remove("currentBook");
settingsRoaming.Values.Remove("currentPage");
}
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
In the sample project, you can set breakpoints at the beginning of each method, and then use Visual Studio to inspect our settings values in the Locals tab.
Here is what my data looks like after AddSettings() has executed (click to enlarge):
Now, after I’ve read the data from settings and stored it to values in my page:
Finally, after my DeleteSettings() method has executed:
As you’re building your apps, it’s important to remember that all of this data, both settings and files, are sandboxed to your application. What this means is that when your application is uninstalled, all of the values are gone with it. This also means that when you’re building an app that uses these values, and you want to start with a greenfield user experience, you might want to uninstall the app from your machine before testing it to get rid of any legacy values that may have been saved earlier.
In addition to saving these name/value pairs, you might also want to categorize them a bit. We can create categories of settings, which makes adding and removing groups of settings very simple to do. This can also obviously be done both locally and roaming. Here’s a look at creating a category, and adding a setting to it:
settingsLocal.CreateContainer("mediaSettings", ApplicationDataCreateDisposition.Always);
settingsLocal.Containers["mediaSettings"].Values["Volume"] = 11;
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
So we’ve taken a deep look at Settings, let’s move on to files now.
Local and Roaming Files
Files operate in a very similar way to settings, except that we are actually reading and writing the values to the hard drive, and I’ll demonstrate that in this example as well. I’ve set up an identical set of methods to the ones we used in the Settings section: an AddFile(), a ReadFile, and a DeleteFile() method. Here’s a look at the code:
StorageFolder folderLocal;
StorageFolder folderRoaming;
string fileName = "tacotext.txt";
string fileContents = "taco";
public MainPage()
{
this.InitializeComponent();
folderLocal = ApplicationData.Current.LocalFolder;
folderRoaming = ApplicationData.Current.RoamingFolder;
AddFile();
}
private async void AddFile()
{
StorageFile fileLocal = await folderLocal.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting);
await FileIO.WriteTextAsync(fileLocal, fileContents + "taco");
ReadFile();
}
private async void ReadFile()
{
StorageFile fileLocal = await folderLocal.GetFileAsync(fileName);
string textLocal = await FileIO.ReadTextAsync(fileLocal);
fileContents = textLocal;
DeleteFile();
}
private async void DeleteFile()
{
StorageFile fileLocal = await folderLocal.GetFileAsync(fileName);
await fileLocal.DeleteAsync();
}
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
As you can see, the only major difference is that we async/await in our methods that rely on reading the hard drive. We don’t have to specify folder locations, we don’t even have to define a folder structure if we don’t want to.
In addition, you can look at your files as they are saved. Each application stores its files locally on the machine, and if you use a breakpoint, you can determine that location on your device. For instance, the tacotext.txt file that I’ve created is stored at the Path property shown below:
Once you’ve created it, you can actually crack the folder open and see the contents, even open the files yourself:
Otherwise, that’s about it! Saving files, even large files, can be done this way. You only need to remember the file name that you gave them. The Windows 8 sandbox takes care of the rest. Please note that my example above actually only stores a local file, but that you use the EXACT same code (with a reference to ApplicationData.Current.RoamingFolder instead) for Roaming files.
As a reminder, roaming files will not transfer immediately, so don’t expect the type of performance you’ve seen with Skydrive or DropBox’s applications. Be mindful of the data quota, but otherwise, use this stuff extensively.
Summary
Settings and Files are a powerful tool in our Windows 8 development arsenal. It’s easy to do, and makes your application so much cooler when it lights up multiple machines at once. I still tell stories about the first time I played Jetpack Joyride on a second machine, and all of my purchases, jetpacks, equipment, and settings showed up immediately. It made what is an already awesome game that much better for me. So much so that I’m recommending you pick it up yourself. (It’s a free game, but there’s an in-app purchase option for $1.49 to double your coins from each level completed. Totally worth it.)
To download all of the sample code from this article, click the icon below:
Tomorrow, we are going to discuss Live Tiles, and how we create both primary and secondary tiles, as well as how we update them. See you then!
Leave a Reply