Windows 8 Consumer Preview and Visual Studio 11 Beta – Local and Roaming application data storage (Part 5/11)

February 29, 2012

In previous post (Settings – LINK) I explained how to create application’s settings screed and customize it with application specific UI and data. In this post I will explain how to preserve application settings and other application data.

Wind8 CP enables few locations to store application data. Application can use temporary directory to save any kind of information which was possible also before Windows 8. In addition WinRT provides native support saving data in application’s data store as collection of Key Value Pairs. This data can be stored locally or using roaming store. Roaming store will be synchronized by Windows if user permits data synchronization (and when such synchronization is permitted):

image

To take advantage of this mechanism application must use ApplicationData class from Windows.Storage namespace. This class provides access to both, LocalSettings and RemoteSettings. Both classes exposes sample APIs, which enables abstracting Save/Load functionality from actual data location.

In my sample application I am supporting both types of storages. As you probably remember from previous post, my settings screen supports switching between stores by enabling and disabling settings synchronization.

image

Let’s get started. Regardless to actual data location (local store or cloud), application subscribes to DataChanged event. This event occurs when roaming data is synchronized (in my scenario occurs when changed on some other device running same application).

Note: In my case application synchronizes only settings, then it reloads synchronized data once it was changed on other device. In other scenarios it might or might not make sense reloading synchronized data during application execution and depends exclusively on application.

protected override void OnNavigatedTo(NavigationEventArgs e)
{ 
    //Sunbscribe to DataChanged event to get notification if data changed in some other device
    ApplicationData.Current.DataChanged += (sender, args) =>
    {
        //Reload settings when they changes somewhere
        ReloadSettigns();
    };

    //Get settings
    ReloadSettigns();

    //...
}

Let’s see the ReloadSettings function:

private void ReloadSettigns()
{
    //Application logic prefers roaming storage over local one
    roamingSettings = ApplicationData.Current.RoamingSettings;
    if (roamingSettings.Values.Count == 0)
        roamingSettings = ApplicationData.Current.LocalSettings;

    //Application settings build from exactly 8 values. If something is missing use default values
    if (roamingSettings.Values.Count == 8)
    {
        //Value is object and must be cast to final type
        RemotePushEnabled = (bool)roamingSettings.Values["RemotePushEnabled"];
        LocationEnabled = (bool)roamingSettings.Values["LocationEnabled"];
        SensorsEnabled = (bool)roamingSettings.Values["SensorsEnabled"];
        LocalPushEnabled = (bool)roamingSettings.Values["LocalPushEnabled"];
        SharingEnabled = (bool)roamingSettings.Values["SharingEnabled"];
        ProximityEnabled = (bool)roamingSettings.Values["ProximityEnabled"];
        BackgroundAccess = (bool)roamingSettings.Values["BackgroundAccess"];
        SyncSettings = (bool)roamingSettings.Values["SyncSettings"];
    }
}

As you remember from previous post, when some value changed on Settings screen, the value assigned to corresponding variable and SaveSettings function being executed. Let’s see the function:

private void SaveSettings()
{
    //Clean previous values
    CleanSettings();

    //If user prefers to sync data in the cloud use RoamingSettings, otherwise use LocalSettings
    if (SyncSettings)
        roamingSettings = ApplicationData.Current.RoamingSettings;
    else
        roamingSettings = ApplicationData.Current.LocalSettings;

    //Add values to settings
    roamingSettings.Values.Add("RemotePushEnabled", RemotePushEnabled);
    roamingSettings.Values.Add("LocationEnabled", LocationEnabled);
    roamingSettings.Values.Add("SensorsEnabled", SensorsEnabled);
    roamingSettings.Values.Add("LocalPushEnabled", LocalPushEnabled);
    roamingSettings.Values.Add("SharingEnabled", SharingEnabled);
    roamingSettings.Values.Add("ProximityEnabled", ProximityEnabled);
    roamingSettings.Values.Add("BackgroundAccess", BackgroundAccess);
    roamingSettings.Values.Add("SyncSettings", SyncSettings);

    //Signal data changed which will trigger cloud sync process when system synchronizes data next time
    ApplicationData.Current.SignalDataChanged();
}

private void CleanSettings()
{
    if (null != roamingSettings)
        roamingSettings.Values.Clear();
}

Note: Cleaning all values instead of replacing them is probably not the best practice which can increase synchronization traffic usage, but in case of such simple example (and for code brevity) I preferred this approach.

In addition settings can be organized in Containers:

//Create new container
roamingSettings.CreateContainer("GeneralSettings", ApplicationDataCreateDisposition.Always);
//Setting named value in specific container
roamingSettings.Containers["GeneralSettings"].Values.Add("RegisteredUser", "AlexG");
//Getting named value form specific container
string user = (string)roamingSettings.Containers["GeneralSettings"].Values["RegisteredUser"];
//Delete container with all values
roamingSettings.DeleteContainer("GeneralSettings");

When using roaming settings important to know how much data being synced and how much could be synced. ApplicationData class provides information about roaming storage quota and usage:

ulong quota = ApplicationData.Current.RoamingStorageQuota;
ulong used = ApplicationData.Current.RoamingStorageUsage;

In addition, it is possible to create basic version management of data stored in any store by querying current version and setting new version:

//Get current version
uint version = ApplicationData.Current.Version;
//Set new version
await ApplicationData.Current.SetVersionAsync(123, new ApplicationDataSetVersionHandler((version) => {
    if (version.CurrentVersion == version.DesiredVersion)
    {
         //Worked
    }
    else
    {
         //Something went wrong...
    }
}));

Last, but not least, ApplicationData class provides access to Local/Roaming/Temporary folders. All classes exposes the sample API and enables managing files and folders using standard WinRT async patterns:

//Create file in roaming folder. Also possible to create file/folder in ApplicationData.Current.TemporaryFolder or ApplicationData.Current.LocalFolder
StorageFile file = await ApplicationData.Current.RoamingFolder.CreateFileAsync("settings.dat", CreationCollisionOption.ReplaceExisting);//Open file for writing and write some binary buffer...
Stream stream = await file.OpenStreamForWriteAsync();
stream.Write(buffer, offset, count);
await stream.FlushAsync();

That’s it for now.

Stay tuned for next part.

Alex

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

2 comments

  1. AlexJune 20, 2012 ב 2:24

    So, if I got you correctly, settings.dat will be synced?

    Reply
  2. Alex GoleshJuly 1, 2012 ב 17:43

    Yes

    Reply