Monday, September 17, 2012

Windows 8: Windows Store apps - RoamingSettings and the DataChanged event

In a previous article (Windows 8: Windows Store apps and the WebView control) we discussed how to display a Settings flyout and focused mostly around making the WebView control play nice with the flyout.

We created a Settings flyout because we want to store the URI that is used by the WebView control.

Now that we have the Settings flyout working, how do we store settings in a Windows Store app?

ApplicationData and RoamingSettings

It turns out that Windows Store apps have access to a class called ApplicationData which allows the storage of session state, user preferences, and other settings.

You have access to the following types of storage with the ApplicationData class:
  • local - persistent data but only on the current device
  • roaming - data that is available to all devices the user has your app installed on
  • temporary - data that can be removed by the system at any point after your app is closed

Local would do the trick but roaming caught my attention.

Being able to set a setting on one device and have all devices, that your app is installed on, automatically know about the new setting sounds pretty neat and would make things a lot easier for the user.

Setting a RoamingSettings key/value pair can be done as follows:
using Windows.Storage;

ApplicationData.Current.RoamingSettings.Values["URI"] = sURI;

Pulling a RoamingSettings value can be done as follows:
string sURI = ApplicationData.Current.RoamingSettings.Values["URI"] as string;
if (!string.IsNullOrWhiteSpace(sURI))
// do something with the setting

The DataChanged event

Another neat feature of the roaming settings is that you can subscribe to a DataChanged event so that your app can know when a setting was changed on another device.

One thing to be aware of here is that the DataChanged event might not fire at the instant that you set a RoamingSettings value. The synchronization of the roaming app data is controlled by Windows itself.

In our case passing the roaming settings data to other devices instantly is not a concern but what would be nice is if the Settings flyout changes would trigger the DataChanged event in the app where it was changed. As it turns out this is possible...

You can simulate a DataChanged event by calling the SignalDataChanged method after you apply the new settings and it will send a DataChanged event to all registered event handlers:

Handling the DataChanged event

Initially, I let Visual Studio wire up my DataChanged event handler (I typed in the += characters and pressed Tab) which gave me the following:
// Don't use this
ApplicationData.Current.DataChanged += DataChangedHandler;

When I ran the application, I discovered an issue where the event is being triggered, because my breakpoint was being hit, but the WebView control was not being updated with the new URI.

After some digging, I discovered that the MSDN documentation for implementing a DataChanged event handler uses the following method:
// Use this
ApplicationData.Current.DataChanged += new TypeEventHandler(DataChangedHandler);

Changing how the DataChanged event handler is registered didn't solve my issue of the WebView control not updating when the URI changes.

Background Threads and the UI Thread

As I was thinking about the issue a thought occurred to me...

Updating the WebView control would be the UI thread's responsibility.

What if the DataChanged event is being received on a background thread?

I've seen the code to call a UI thread in a developer training session at some point in the past (I think it was in regards to Silverlight but I'm not sure).

Either way, I thought I knew what the issue was but I couldn't remember how to get around it so I started searching the internet and I ran across the following MSDN sample code that had just the information I needed:

The following is the DataChanged event handler you need if you wish to update (directly or indirectly) the UI of your app since the DataChanged event might be triggered on a background thread which has no access to the UI:
async void Current_DataChanged(ApplicationData sender, object args)
// DataChangeHandler may be invoked on a background thread, so
// use the Dispatcher to invoke the UI-related code on the UI
// thread.
await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
// Cause the web browser to reload in its content

In Conclusion

Overall, RoamingSettings in Windows Store apps are pretty straightforward.

One thing to remember with the DataChanged event, if you need to update the UI (directly or indirectly), is that the event might arrive on a background thread and you will need to dispatch the call to the UI thread.

Additional Resources

The following are some additional resources with regards to ApplicationData and RoamingSettings:
A download of the project (C# and built using Visual Studio Express 2012 for Windows 8) can be found in the following location:

1 comment: