Custom Settings Provider in .NET 2.0

Sun, 7/30/06

In .NET 2.0, WinForms (and other) applications got a big boost in ease of use for settings with the settings designer:

In addition to providing a quick and easy place to define app-wide application-scope and user-scope settings, WinForms makes it easy to bind to the settings in your forms:

Further, if you’d like to program against the settings directly, you can do so against the type-safe class that the settings designer generates:

Between the type-safe wrappers on the Settings class that you get by default in a WinForms project and the Default property, you get to write code like this:

MessageBox.Show(Properties.Settings.Default.TextSetting);

This is very nice. So nice, in fact, that’d you’d really love to take advantage of this support in a wide variety of applications. For some of these applications, the default settings provider (the object responsible for loading and storing your settings), the LocalFileSettingProvider (LFSP) will be just fine. It supports application, user and roaming user settings and settings groups.

Unfortunately, the LFSP stores all of an application’s settings in a file over which you have no control. If you’d prefer your settings to be stored in the Registry or at a centralized server, you need to build your own settings provider. Luckily, the .NET 2.0 settings machinery supports this fully. However, it doesn’t make it easy.

Building a Custom Settings Provider

The .NET 2.0 SDK comes with two SDK samples that demonstrate how to build a custom settings provider:

Both of these samples show how to derive from the base SettingsProvider class and implement the SetPropertyValues and GetPropertyValues methods. These methods are where you commit the settings to/from storage, e.g. a file, the Registry or a web service. The way things work out, a settings provider is never handed an instance of the class that exposes the settings. Instead, it’s dealing with a property bag and the settings machinery itself (ApplicationsSettingBase) does the mapping to/from, handles default values, etc.

Deriving from SettingsProvider is the bare minimum you need to plug a custom settings provider into the settings system and both samples show how to do this. However, neither sample shows how to implement the IApplicationSettingsProvider interface, which is required to support the settings client calling the Reset, Upgrade and GetPreviousVersion methods. Since these are handy methods to support, I read the documentation for the IApplicationSettingsProvider interface, but was disappointed that neither it nor the provided samples answered the following questions:

I used intuition (seriously, the model of which copy of the settings I’m updating, the in-memory settings object or the stored settings took me embarrassingly long to discern), the LFSB source (obtained with Reflector), experimentation with LFSB helped me answer these questions, which I used to update the RegistrySettingsProvider sample to implement IApplicationSettings and to fix some stuff I considered broken:

My updates still don’t support roaming settings (where do you write Registry keys that roam?!?) or settings groups, both of which shouldn’t be too hard if you’re motivated. I have worked hard to add a bunch of commentary to the implementation to document the undoc’d protocol of IApplicationSettings so that you can build your own settings provider.

Using a Custom Settings Provider in the Raw

As illustrated by the two SDK samples, you can absolutely write your own settings class, deriving it from the ApplicationSettingsBase and using attributes from the System.Configuration namespace, e.g. UserScopedSettingAttribute, ApplicationScopedSettingAttribute and DefaultSettingValueAttribute, to decorate your properties for serialization by your favorite settings provider and both samples show just how to do that. There are two ways to get ABS to use your custom settings provider in the case that you’re writing your own settings class. If you’d like to allow a settings provider to be designated as a per settings basis, you can use SettingsProviderAttribute on that property, e.g.

using System.Configuration;

[UserScopedSettingAttribute()]
[DefaultSettingValueAttribute("True")]
[SettingsProviderAttribute(typeof(MyNamespace.MySettingsProvider))]
public bool CheckSetting2 {
  get { return ((bool)(this["CheckSetting2"])); }
  set { this["CheckSetting2"] = value; }
}

You can also specify the settings provider with a string if you like:

[UserScopedSettingAttribute()]
[DefaultSettingValueAttribute("True")]
[SettingsProvider("MyNamespace, MySettingsProvider")]
public bool CheckSetting2 {
  get { return ((bool)(this["CheckSetting2"])); }
  set { this["CheckSetting2"] = value; }
}

For any property that ABS doesn’t find the SettingsProviderAttribute, it will assume the LFSP. Also, ABS is smart enough to use multiple settings providers for different settings so that you split them as you see fit.

If you’d prefer to set a customer settings provider on an entire class, you can set the SettingsProviderAttribute on the entire settings class:

[SettingsProviderAttribute(typeof(MyNamespace.MySettingsProvider))]
class MySettings : ApplicationSettingsBase {...}

However, if you’re using the settings designer-generated classes, neither of these techniques work in the face of regenerated code. Luckily, VS knows what you want and can help.

Using a Custom Settings Provider in VS

To set a provider on a per settings basis, select as many settings as you like in the settings provider and press F4 to set the Provider property in the settings property browser (F4 isn’t the most discoverable way to get to the settings properties, but I haven’t found another way):

This generates the per-property attributes that I showed above. If you want to do it on a per-class basis, press the View Code button in the settings design (also not very discoverable), and you’ll get a Settings.cs file containing a partial class:

using System.Configuration;

namespace MyNamespace {
  [SettingsProvider(typeof(MyNamespace.MySettingsProvider))]
  internal sealed partial class Settings {
    public Settings() {
      private void SettingChangingEventHandler(
        object sender, SettingChangingEventArgs e) {
          // Add code to handle the SettingChangingEvent event here.
      }
 
      void SettingsSavingEventHandler(
        object sender, CancelEventArgs e)
      {
        // Add code to handle the SettingsSaving event here.
      }
    }
  }
}

On this partial class, which will only be generated once into Settings.cs, you can add SettingsProviderAttribute and it will not be disturbed by the regenerated Settings class code (which lives in Settings.Designer.cs).

Where Are We?

To implement a custom settings provider, you’ll want to derive from SettingsProvider, implement the overrides and implement IApplicationSettings. To hook it up to the designer-generated Settings class, use the per-property settings or the Settings partial class you get by pressing the View Code button in the settings designer. You can see this in action with the updated RegistrySettingsProvider sample (which I’m working to get into the SDK, but am hosting on my web site ‘til that happens).

Discuss