Adding settings class to a UWP app

  1. Create a new class inheriting from ObservableSettings.
  2. Call to the base class constructor indicating if you want to store the settings in LocalSettings or in RoamingSettings.
  3. Add all your properties calling the base class members
    Set and Get in the getter and in the setter. No need to pass the name of the property or use nameof() operator!
  4. Optionally you can set a default value decorating the property with DefaultSettingValue attribute.

Here an example of a settings class:

namespace Test
{
    public class Settings : ObservableSettings
    {
        private static Settings settings = new Settings();
        public static Settings Default
        {
            get { return settings; }
        }

        public Settings()
            : base(ApplicationData.Current.LocalSettings)
        {
        }

        [DefaultSettingValue(Value = 115200)]
        public int Bauds
        {
            get { return Get<int>(); }
            set { Set(value); }
        }

        [DefaultSettingValue(Value = "Jose")]
        public string Name
        {
            get { return Get<string>(); }
            set { Set(value); }
        }

    }
}

and here how to add an instance in your app.xaml:

<Application
    x:Class="Test.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:Test"
    RequestedTheme="Light">
    <Application.Resources>
        <local:Settings x:Key="settings"/>
    </Application.Resources>
</Application>

Access and modify the values in a MVVM fashion:

<Page
    x:Class="Test.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:Test"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    DataContext="{StaticResource settings}">

    <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <TextBlock Text="Bauds"/>
        <TextBox Text="{Binding Default.Bauds, Mode=TwoWay}"/>
        <TextBlock Text="Name"/>
        <TextBox Text="{Binding Default.Name, Mode=TwoWay}"/>
    </StackPanel>
</Page>

Everything will be stored properly in your settings repository.

Here you have the implementation of DefaultSettingValue and ObservableSettings:

using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Reflection;
using System.ComponentModel;
using Windows.Storage;


[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public sealed class DefaultSettingValueAttribute : Attribute
{
    public DefaultSettingValueAttribute()
    {
    }

    public DefaultSettingValueAttribute(object value)
    {
        Value = value;
    }

    public object Value { get; set; }
}

public class ObservableSettings : INotifyPropertyChanged
{
    private readonly ApplicationDataContainer settings;

    public ObservableSettings(ApplicationDataContainer settings)
    {
        this.settings = settings;
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected bool Set<T>(T value, [CallerMemberName] string propertyName = null)
    {
        if (settings.Values.ContainsKey(propertyName))
        {
            var currentValue = (T)settings.Values[propertyName];
            if (EqualityComparer<T>.Default.Equals(currentValue, value))
                return false;
        }

        settings.Values[propertyName] = value;
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        return true;
    }

    protected T Get<T>([CallerMemberName] string propertyName = null)
    {
        if (settings.Values.ContainsKey(propertyName))
            return (T)settings.Values[propertyName];

        var attributes = GetType().GetTypeInfo().GetDeclaredProperty(propertyName).CustomAttributes.Where(ca => ca.AttributeType == typeof(DefaultSettingValueAttribute)).ToList();
        if (attributes.Count == 1)
            return (T)attributes[0].NamedArguments[0].TypedValue.Value;

        return default(T);
    }

You can download a solution with a functional example from the repository I created in GitHub.

Leave a Comment