Data binding user settings in Windows Phone applications
March 25, 2010
Thanks to the rich data binding system built into Silverlight for Windows Phone, you can easily store rich user settings without using code behind files.
The canonical example of user settings is a “Show this welcome screen at startup” checkbox so that your application can offer a nice out-of-box experience.
By writing a simple type with some properties, adding a few helper files, and setting up a two-way data binding, you can store any settings without having to write special code.
Here’s what the completed project’s two-way data binding looks like for the checkbox shown to the right:
<CheckBox IsChecked="{Binding Source={StaticResource MySettings}, Path=IsFirstRun, Mode=TwoWay}" Content="Show this page at startup" />
You’ll see these components in the data binding:
- Source is a static resource, a strongly typed settings class you define
- Path points to the property name to bind to
- Binding is two-way so that the value is stored automatically
- Value and type converters can be applied as always in bindings if necessary
Let’s quickly implement this functionality in a quick app. We’ll also add a text box to the main application page, to let the value always be persisted between application runs, and even application updates.
Getting started
You can use the free Windows Phone development tools, or just Visual Studio 2010 if you have the phone tools CTP installed already.
Create a new ‘Silverlight for Windows Phone’ application project.
Adding some helper code
Either add the class library project I have for download here, or just add the 4 C# files that make up the helper code:
- SettingsHelperCode.zip (8 KB, zip)
Creating your configuration/settings type
Next, we need to create a strongly typed configuration class that derives from my SettingsProvider type. This class must:
- Implement INotifyPropertyChanged, so that bindings work well
- Have properties of the appropriate type for the settings you are interested in. Optionally, include the DefaultValue attribute to provide defaults.
Here is the MySettings.cs file I’ve created for this app. It defines two properties that I always want to have stored:
- HelloWorld is just some text I let the user edit in the user interface. A more realistic setting might be “best friend” or a list of people to show on the home screen of the app.
- IsFirstRun is a true/false value that indicates whether the welcome screen should be shown.
// (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.ComponentModel; using JeffWilcox.Settings; namespace Sample { /// <summary> /// My settings class for storing application data and setting specific to /// the user. /// </summary> public class MySettings : SettingsProvider { private bool _isFirstRun; private string _hello; public MySettings() : base("MySettings.xml") { } [DefaultValue("What's up?")] public string HelloWorld { get { return _hello; } set { _hello = value; NotifyPropertyChanged("HelloWorld"); } } [DefaultValue(true)] public bool IsFirstRun { get { return _isFirstRun; } set { bool old = _isFirstRun; _isFirstRun = value; if (value != old) { NotifyPropertyChanged("IsFirstRun"); } } } } }
Looking closer, inside the MySettings file:
- MySettings type derives from SettingsProvider, part of the helper code downloaded above.
- The constructor calls the base class’s constructor with the name of a settings file to use within the isolated storage area for the phone application
- Properties have backing fields and fire change notifications
- Default values
Add the settings object to your App.xaml
Now, open up App.xaml. Add an XMLNS prefix declaration for your project. I named my app project “Sample”, so it’s short:
xmlns:local="clr-namespace:Sample"
And then add your settings object to the resources (I’m omitting all the theme resources in App.xaml to make it clear where this goes only).
AlwaysSaveOnChange needs to be set to true for Windows Phone applications: if you’re building a regular Silverlight app, the Application.Exit event works well and can be used to store. But on the phone, since an app may be in a paused or unknown state, this property tells my helper code to save the settings whenever a binding has a change notification. For performance impact should be minimal.
<Application.Resources> <!-- Settings --> <local:MySettings x:Key="MySettings" AlwaysSaveOnChange="True" /> </Application.Resources>
A simple TextBox binding
Now on my MainPage.xaml, I am just going to bind to the Hello World property from the MySettings type. Any time you type characters into the text box, they will be saved, and always there when you run the app. No need to write code to save or read settings.
<TextBox Text="{Binding Source={StaticResource MySettings}, Path=HelloWorld, Mode=TwoWay}" />
Good to go
To pull it in, I’m going to add a page called FirstRun.xaml to the project. This will be the content that I want first-time users to see. Then, I need to add the code to call this from main page at startup:
I define a loaded event for this that checks whether the current instance has run the page yet (hacky, and not a permanent setting), and then checks whether the user wants to see the screen (which defaults to true in my settings file):
if (!((App)App.Current).HasFirstRunCheckHappenedYet) { ((App)App.Current).HasFirstRunCheckHappenedYet = true; MySettings settings = (MySettings)App.Current.Resources["MySettings"]; if (settings != null && settings.IsFirstRun) { NavigationService.Navigate(new Uri("/FirstRun.xaml", UriKind.Relative)); } }
It then uses the navigation service to navigate, if needed.
If you’d like to learn more about Silverlight data binding, do check out this MSDN resource: http://msdn.microsoft.com/en-us/library/cc278072(VS.95).aspx
Some people have experimented with directly binding to the isolated storage settings file, but this approach gives your some separation – by having your own class, you can provide default values, even upgrade logic, and do some neat work.
This is based on a classic WPF post by Patrick Danino which used the full .NET configuration classes. Enjoy!