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

Versatile Application Settings

, 21 Mar 2009 CPOL
Rate this:
Please Sign up or sign in to vote.
Shows how to use your types and generic lists in application settings.

Introduction

Application settings are a way to persist settings. They are a convenient replacement for INI files or using the Registry. There are a couple of problems, however. It is not easy to persist your own types or collections (generic or otherwise). This article will explore why, and offer a few solutions.

Application settings have two possible scopes, Application and User. I will focus exclusively on user scoped settings here. Application scoped settings are much the same, except they cannot be altered by the user at runtime.

A quick overview

If you create a new WinForms app (I've called mine VersatileApplicationSettings), go to Solution Explorer, and in the Properties folder, you will see a file Settings.settings (referred to as the settings UI from now on) and a 'sub file' called Settings.Designer.cs (referred to as the designer file from now on). The designer file is where the actual code goes. Double clicking the settings UI gives you a pretty UI to create and edit your settings. Every time you make a change through the settings UI, the entire designer file gets rewritten (just the same as a Form's designer file). This is the root of the major problem. Any customization we do has a tendency to be removed every time we use the settings UI - more later!

Normal use

Before we make any changes, let's have a quick look at the designer file's contents. You'll notice that everything is fully qualified right from the global namespace. I've added a using here in the interests of legibility, and ignored the compiler attributes.

using System.Configuration;
namespace VersatileApplicationSettings.Properties
{
    internal sealed partial class Settings : ApplicationSettingsBase
    {
        private static Settings defaultInstance = (
            (Settings)(ApplicationSettingsBase.Synchronized(new Settings()))
            );
        public static Settings Default
        {
            get
            {
                return defaultInstance;
            }
        }
    }
}

First notice that the class derives from the abstract class ApplicationSettingBase. This class gives us the functionality needed to save and synchronize our settings; in other words, the hard work is already done for us! There is also the public static Default property that is the Settings instance we actually work with - almost a Singleton.

Double click the settings UI file and create a new setting StringSetting, leaving the type as string, and save. Now, check the designer file and you'll see a new property (again edited for legibility and to show the important parts).

[UserScopedSettingAttribute()]
[DefaultSettingValueAttribute("")]
public string StringSetting
{
    get { return ((string)(this["StringSetting"])); }
    set { this["StringSetting"] = value; }
}

Pretty straightforward, and it should be clear how we could add our own settings without needing the settings UI. Now that we have this setting, it can be accessed from anywhere in our application, changed, saved, reloaded etc.

Your own types

When selecting types through the UI, there is no way to select our own classes as the type. We can, of course, add our own properties to the designer file, but with two problems: they won't be visible in the settings UI, and they will get overwritten. Ignoring the first problem (who needs the UI anyway!), there are three methods that I know of of working around this. In my least preferred order:

  1. Edit the source of the Settings.settings file (this will allow you to continue to use the settings UI).
  2. Create a new partial file for the Settings class.
  3. Create a new class that derives from ApplicationSettingBase.

Settings.settings

Open the Settings.settings file with any text editor, and you will see it's actually just an XML file that looks something like this:

<?xml version='1.0' encoding='utf-8'?>
<SettingsFile 
     xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" 
     CurrentProfile="(Default)" 
     GeneratedClassNamespace="VersatileApplicationSettings.Properties" 
     GeneratedClassName="Settings">
  <Profiles />
  <Settings>
    <Setting Name="StringSetting" Type="System.String" Scope="User">
      <Value Profile="(Default)" />
    </Setting>
  </Settings>
</SettingsFile>

Let's imagine you have a class like this:

using System;
namespace VersatileApplicationSettings
{
    [Serializable]
    public class MyClass
    {
        public MyClass() { }
        public string Name
        {
            get;
            set;
        }
    }
}

You can edit the XML and add something like this:

<Setting Name="MyClassSetting" Type="VersatileApplicationSettings.MyClass" Scope="User">
  <Value Profile="(Default)" />
</Setting>

Once saved, this will work fine. It'll even show in the settings UI, although you won't be able to set a default value (this may be possible using a TypeConverter - I haven't tried). The biggest problem with this approach is you can't use generic lists, so if you wanted, say, a List<MyClass>, it isn't possible (as far as I know).

Settings.cs partial

The second option is to forget about the settings UI altogether. If we add a new class file Settings.cs, change the namespace, and mark it as partial (note the original designer file is partial), we can just add our own properties to the code.

using System.Configuration;
namespace VersatileApplicationSettings.Properties
{
    partial class Settings
    {
        [UserScopedSettingAttribute()]
        [DefaultSettingValue("")]
        public MyClass MyClassSetting
        {
            get { return ((MyClass)(this["MyClassSetting"])); }
            set { this["MyClassSetting"] = value; }
        }
    }
}

This works well, and we can use generic lists too.

[UserScopedSettingAttribute()]
[DefaultSettingValue("")]
public List<MyClass> MyClassSettings
{
    get { return ((List<MyClass>)(this["MyClassSettings"])); }
    set { this["MyClassSettings"] = value; }
}

ExtendedSettings class

Although the solution above performs exactly how we want, I prefer the idea of separating out into a separate settings class, so when our custom settings don't appear in the settings UI, it's not a surprise. This is easy enough to do, we just need to follow the basic pattern that the auto generated code produces, i.e., static instance, and add our properties. To make sure that the static instance used is *always* the same instance, I prefer to use a stronger Singleton pattern than Microsoft's. The pattern is taken from here, and the class file is ExtendedSettings.cs.

This works exactly like the previous example, except the settings would be accessed through Properties.ExtendedSettings.Settings instead of Properties.Settings.Default.

using System.Collections.Generic;
using System.Configuration;
namespace VersatileApplicationSettings.Properties
{
    internal sealed class ExtendedSettings : ApplicationSettingsBase
    {
        private ExtendedSettings() { }
        public static ExtendedSettings Settings
        {
            get { return InstanceCreator.settingsInstance; }
        }
        private class InstanceCreator
        {
            static InstanceCreator() { }
            internal static readonly ExtendedSettings settingsInstance = 
                            new ExtendedSettings();
        }
        [UserScopedSetting()]
        [DefaultSettingValue("")]
        public MyClass MyClassSetting
        {
            get { return (MyClass)this["MyClassSetting"]; }
            set { this["MyClassSetting"] = value; }
        }
        [UserScopedSetting()]
        [DefaultSettingValue("")]
        public List<MyClass> MyClassSettings
        {
            get { return (List<MyClass>)this["MyClassSettings"]; }
            set { this["MyClassSettings"] = value; }
        }
    }
}

The demo

In the attached demo, I've used the last approach to remember the Location and Size of the forms. There are only two forms, but because the settings are stored in a generic list, it can grow as necessary to accommodate as many as you need - all in one setting.

History

  • 21 March 2009: Initial version.

License

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

Share

About the Author

DaveyM69
CEO Dave Meadowcroft
United Kingdom United Kingdom
No Biography provided

Comments and Discussions

 
GeneralGood Article - Very helpful Pinmemberrbc10252-Jun-10 9:48 
GeneralRe: Good Article - Very helpful PinmvpDaveyM692-Jun-10 10:25 
GeneralSmall note PinmemberTheShihan22-Apr-10 5:06 
The object/class you want to store needs to have the "[Serializable]" attribute and a parameterless constructor. Otherwise it won't be saved.
GeneralPerfect PinmemberDmitry E N15-Jul-09 10:58 
GeneralNice one again PinmemberXmen W.K.4-Apr-09 2:29 
GeneralRe: Nice one again PinmvpDaveyM695-Apr-09 2:06 
GeneralGood one PinmvpN a v a n e e t h21-Mar-09 18:00 
GeneralRe: Good one PinmvpDaveyM6922-Mar-09 0:35 

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 | Terms of Use | Mobile
Web02 | 2.8.141223.1 | Last Updated 21 Mar 2009
Article Copyright 2009 by DaveyM69
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid