Click here to Skip to main content
Click here to Skip to main content

Writeable Application Scope Settings

, 18 Jun 2012 CPOL
Rate this:
Please Sign up or sign in to vote.
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 diagram

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.

A test application

The application uses a sample settings class MySettings with two Boolean options and a List<string> of selected options.

The settings class

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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Niel M.Thomas
Software Developer (Senior)
Denmark Denmark
Name: Niel Morgan Thomas
Born: 1970 in Denmark
Education:
Dataengineer from Odense Technical University.
More than 20 years in IT-business.
Current employment:
Working with application development in a major Danish company that produce medical equipment.

Comments and Discussions

 
QuestionGet Access Denied error when save xml from non-Administrative user PinmemberRich In Soquel26-Aug-14 13:32 
AnswerRe: Get Access Denied error when save xml from non-Administrative user PinprofessionalNiel M.Thomas26-Aug-14 19:09 
GeneralMy vote of 5 Pinmembercjb11018-Jun-12 21:22 
GeneralWorks on complex lists of classes too Pinmembersdfgd18-Jul-12 16:09 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.141022.1 | Last Updated 18 Jun 2012
Article Copyright 2012 by Niel M.Thomas
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid