This article shows how to store .NET application settings in the application's folder (or any other place) to allow them to be portable together with the app. This is achieved by creating a custom settings provider which handles loading and storing of settings.
An updated version of this implementation with some little additions is hosted on GitHub at https://github.com/bluegrams/SettingsProviders. Another settings provider which attempts to store settings in JSON format can also be found there.
.NET provides an integrated mechanism for storing application and user settings which makes it easy for developers to create and use these settings. To give a brief overview: Settings for Windows Forms (or WPF) generally are divided into two categories: Read- and writeable user-scoped settings stored for every user account and application-scoped which are typically read-only at runtime. Both types are accessed through a settings class derived from
ApplicationSettingsBase which provides the settings properties and basic methods for loading and saving.
The actual data however is provided by a class derived from
SettingsProvider which handles loading and storing the data. The default settings provider stores user-scoped settings in an XML-like format in the file user.config in the AppData folder of the user. This is quite sufficient for a typical application, but in some scenarios (e.g. a portable application that should not touch the AppData folder), an alternative storage location would be preferable. As the storage location of the default settings provider can't be changed, we must implement our own settings provider. This is not quite complicated as just a few methods have to be implemented. Nevertheless, there are a few tricky points to be considered.
Using the Code
The Standard Way
Applying the portable settings provider to your application settings can simply be done within Visual Studio. To do this, go to the settings designer, select a setting and open its properties page. It contains a
Provider field which is empty by default, set this to the name of the provider. Note that you must specify the full name of the class including namespaces, which is
Bluegrams.Application.PortableSettingsProvider in this case.
It is also noticeable that the settings provider has to be set individually for every settings property on this way. This allows flexibility but it may not be the easiest method especially for large settings files.
The Quick Way
Therefore, this implementation of a portable settings provider provides a simpler method. To make all settings in a settings class portable, just use its
ApplyProvider() method like this:
The Config File
When applied, the portable settings provider stores the user-scoped settings in a file named portable.config in the application folder. If this file is not existent, the default values are used and a new file is created. The data is serialized in an XML-style format similar to the default settings files:
Several points are noticeable. First, the file is divided in a
<Roaming> section and a section with the name of the current PC. Properties in the roaming section are the ones which are portable to another machine, whereas all other settings are specific to one machine (settings like window location or size should be found here). To place a setting in the roaming section, just set the
Roaming property to
true for that setting (see image above).
settings properties then are further divided into the settings classes they belong to. This allows different classes from different locations to share the same settings file.
Points of Interest
A custom settings provider must only implement three methods, where
SetPropertyValues() are the two important ones. These methods receive a collection of settings properties and perform loading or saving of the XML file. As the provider should only handle user-scoped settings and it must differentiate between roamed and non-roamed settings, the methods
IsUserScoped() iterate through a property's attributes to check this. The
IsRoaming() method searches for the
SettingsManageabilityAttribute whereas the
IsUserScoped() method indicates whether the
UserScopedSettingAttribute is present.
As a third method,
Initialize() must be overridden from the base class. Here, it is important to specify a value for the
name argument as this argument cannot be left empty.
Next, we make sure the
Reload() method works as expected. To achieve this, the settings provider does not cache any loaded XML content on its own but loads the settings from file every time the
GetPropertyValues() method is called. This sounds extremely inefficient but the settings class caches the properties itself, so the settings provider only comes into action if the settings really have to be reloaded because, for example, the
Reload() method is called.
As a last point of interest, have a look at the
Upgrade() methods available in every settings class. To provide support for this methods, the settings provider has to implement an
IApplicationSettingsProvider. In this implementation, the
Reset() method simply deletes the settings file to reset all settings to their default values.
- 2018-11-17: Fixed several bugs in the implementation
- 2018-04-08: Initial version