Writeable Application Scope Settings





5.00/5 (6 votes)
Create and use writeable Application Scope settings.
Introduction
I often use project settings and in particular project user scope settings. I also use application scope settings but don't find them as useful since they are read only. This article illustrates how to create and use custom writable application scope settings in a simple yet versatile way.
Using the code
I wanted to implement custom settings so they could be used much in the same way as existing project settings. Here is a small example of
setting a property with a custom settings class MySettings
and a property on the settings class
OptionA
:
CustomProperties<MySettings>.Settings.Default.OptionA = true;
The class diagram of CustomProperties<T>
where T
is the settings class:
The class CustomProperties
is created as a singleton through the
Settings
property. The property Default
references an instance
of the type T
(the settings class).
public class CustomProperties<T> where T : class
{
private static CustomProperties<T> settings;
/// <summary>
/// Gets the settings object
/// </summary>
public T Default { get; private set; }
/// <summary>
/// Gets the settings as singleton
/// </summary>
public static CustomProperties<T> Settings
{
get { return settings ?? (settings = new CustomProperties<T>()); }
}
...
Test application
To further illustrate the use of Application Scope settings, I've made a small test application.
The application uses a sample settings class MySettings
with two
Boolean
options and a List<string>
of selected options.
Loading settings in the test application:
/// <summary>
/// Initializes the settings.
/// </summary>
private void InitializeSettings()
{
collection = new ObservableCollection<string>(CustomProperties<MySettings>.Settings.Default.SelectedOptions);
listBoxSelectedOptions.ItemsSource = collection;
checkBoxOptionA.IsChecked = CustomProperties<MySettings>.Settings.Default.OptionA;
checkBoxOptionB.IsChecked = CustomProperties<MySettings>.Settings.Default.OptionB;
SettingsFile.NavigateUri = new Uri(CustomProperties<MySettings>.Settings.SettingsFile);
}
and saving the settings:
private void buttonSave_Click(object sender, RoutedEventArgs e)
{
// Save the settings on exit.
CustomProperties<MySettings>.Settings.Default.OptionA = checkBoxOptionA.IsChecked == true;
CustomProperties<MySettings>.Settings.Default.OptionB = checkBoxOptionB.IsChecked == true;
CustomProperties<MySettings>.Settings.Default.SelectedOptions = new List<string>(collection);
CustomProperties<MySettings>.Settings.Save();
}
The settings are stored using serialization to the following path: C:\[CommonAppData]\[ProductName]\[name of T].xml.
If the product name is empty or cannot be found, the name of the entry assembly or executing assembly is used.
For the test application, the path resolves to: C:\ProgramData\ApplicationScopeSettingsTest\MySettings.xml.
Load and save of the settings is performed using XmlSerializer
.
/// <summary>
/// Loads the specified xml file.
/// </summary>
/// <returns>Object T</returns>
private T Load()
{
// Check if settings file exist
if (!settingsFile.Exists) return default(T);
// Load settings
var serializer = new XmlSerializer(typeof(T));
using (XmlReader reader = new XmlTextReader(File.OpenRead(settingsFile.FullName)))
{
return (T)serializer.Deserialize(reader);
}
}
/// <summary>
/// Saves the settings.
/// </summary>
public void Save()
{
var serializer = new XmlSerializer(typeof(T));
using (XmlWriter writer = new XmlTextWriter(File.Create(settingsFile.FullName), Encoding.UTF8))
{
serializer.Serialize(writer, Default);
}
}
Default values
When CustomProperties
is loaded and no previous settings has been found, it will create an instance of the settings (type
T
) with its default values. Creating a
instance of the settings with default values:
Default = (T)Activator.CreateInstance(typeof(T));
If any values are assigned to lists or collections in the constructor of the settings class, it will cause extra items to load when the settings are
deserialized (loaded). To workaround the problem I have created another constructor on the MySettings
class that takes a Boolean
value
so I have two ways of creating the settings. Like this:
// Create the settings using initial values
const bool initializeObject = true;
Default = (T)Activator.CreateInstance(typeof(T), new object[] { initializeObject });
The modified settings class MySettings
:
/// <summary>
/// Initializes a new instance of the <see cref="MySettings"/> class.
/// </summary>
public MySettings() : this(false)
{
}
public MySettings(bool initializeValues)
{
// Initialize
OptionA = false;
OptionB = true;
SelectedOptions = new List<string>();
// Add values to lists here.
if (!initializeValues) return;
SelectedOptions.AddRange(new List<string> { "Red", "Blue" });
}
This will make sure that both deserializing the settings from file and creating an instance of the settings class with default values will work.
If you won't assign any values to lists or collections, you can safely delete the overload and modify the CreateInstance<T>
method to omit the value.
Using CustomProperties<T> for User Scope settings
CustomProperties<T>
can also be used to create custom user scope settings. This can simply be done by altering the placement of the settings
file to reference Environment.SpecialFolder.ApplicationData
. The paths ApplicationData and CommonAppData can be written to as a restricted user.
Upgrading the settings between versions
Since the settings have been serialized, it is reasonably easy to add or remove properties on the settings class. Additional properties will be assigned default values when loaded the first time. Removed properties will be ignored the first time the settings class loads.
History
This is version 1.0.0.0.