Saving User Settings in .Net

By Peter Aitken  |  Posted 2005-07-27 Email Print this article Print

WEBINAR: Event Date: Tues, December 5, 2017 at 1:00 p.m. ET/10:00 a.m. PT

How Real-World Numbers Make the Case for SSDs in the Data Center REGISTER >

Saving user settings can be an important part of creating a user-friendly application. Saving them the right way is part of good programming practice. Once you know the factors that are involved, it is really rather easy.

Almost every application program has to save some information between sessions. No, I am not talking about a program's data files, although that's essential too. Rather, in this article, I am referring to application settings that affect the way the application runs.

The term application settings really encompasses two different kinds of settings:

  • Application settings, per se, are those that ship with the application, affect all users, and are generally meant to be changed only by an administrator.
  • User settings are specific to each user and include information such as the most recent file list, program window size, and toolbar customization. User settings need to be saved before the application quits and retrieved when the application starts.

Some programmers are not clear on the distinction between these types of settings. It's important that they be treated and stored separately. Let's see why.

Application Settings

The .Net Framework provides excellent support for application settings. By selecting Project, Add New Item and then selecting Application Configuration File, you create a file named App.Config. This is an XML file where you place your settings using the following format:

<?xml version="1.0" encoding="utf-8" ?>
<configuration> <appSettings> <add key="Database Server" value="NewYorkSQLServer" /> <add key="Help URL" value="" /> </appSettings> </configuration>

The first, second, and last lines of this file are created for you — then you add the required information as shown here using XML tags. When the application is compiled, this file is automatically renamed xxxx.exe.config (where xxxx is the application name) and placed in the output folder. When the application is installed, this file goes along, and it is automatically loaded when the application runs.

Your code can access the settings in the file using the classes in the System.Configuration namespace. For example, to retrieve the "Help URL" value you would write:

Dim HelpURL As String
HelpURL = ConfigurationSettings.AppSettings("Help URL")

These tools for creating and saving application settings are well designed and easy to use. So many programmers think, "Aha! I can use this to save user settings also." But then, after a fruitless search through the .Net documentation, they discover that the System.Configuration namespace does not provide any way to change or add to the App.Config file. There's a good reason for this. App.Config is not, I repeat not, intended for saving user settings.

Why not? I thought you would never ask.

One reason is user isolation, or the lack thereof. The information in App.Config is common to all users of the system. This defeats an important purpose of saving user settings, which is to allow each user to save his own settings separately from all other users.

Another reason is permissions. In a proper installation, App.Config is placed in the application folder under Program Files. If the system security is set up properly, normal users do not have write permission for this folder; only administrators do.

So, where do you keep user settings? Rumor has it that the next release of .Net will include support for user settings. Until then, you have to roll your own. Fortunately it is not difficult.

User Settings

Creating an object to hold user settings is a trivial matter. Just define properties or public variables to hold all the information that is needed. The difficulties arise when you consider that you have to (1) Save the information to disk, and (2) Save it in a manner that is specific for each user. Let's look at these in turn.

For saving the user settings to disk, we use serialization, a fancy term for converting an object into a series of bytes that can be sent over a network or saved to disk. This data can then be deserialized, giving you an exact copy of the original object. The plan, then, is to:

  1. Create an object that contains the user settings data.
  2. When the program quits, serialize it to disk.
  3. When the program starts, deserialize the disk data to retrieve the object and the user settings it contains. Of course, the first time the program runs, there will be no serialized data so the program will use default settings.

When you create a class that will be serialized, you must mark it with the <Serializable()> attribute. Here is a simple UserSettings class that stores the position and size of the program window:

<Serializable()> Public Class UserSettings
  Public WindowTop As Integer
  Public WindowLeft As Integer
  Public WindowWidth As Integer
  Public WindowHeight As Integer
End Class

Your program will create an instance of this class and store the required data in it. Then, when the program is closing, the object must be serialized as shown here. You might put this code in the form's Closing event procedure, although that is not the only option. Assume that us is the reference to the object and the variable filename contains the target file name:

Dim fs As New FileStream(filename, FileMode.Create)
Dim f As New BinaryFormatter
f.Serialize(fs, us)

Your program does, of course, need the following Imports to work:

Imports System.IO
Imports System.Runtime.Serialization.Formatters.Binary
Imports System.Runtime.Serialization

And In The Other Direction...

Serializing an object to disk is quite simple. Deserializing it is equally simple.

You need to make sure that the file exists. If it does not, which is the case the first time the program is run (and also if the file is deleted for whatever reason), the program skips the deserialization and uses default values for whatever settings would have been in the file. These steps are typically done in the form's Load event procedure, as shown here.

' The variable us has been declared elsewhere.
us = New UserSettings 
If File.Exists(filename) Then
  Dim fs As Stream = File.OpenRead(filename)
  Dim ds As New BinaryFormatter
  us = CType(ds.Deserialize(fs), UserSettings)
  ' Use default settings.
End If

After this code executes, the object us contains the same data that it did when the program last terminated.

Now let's look at the second problem: how to serialize the data to a file that is unique and associated with a specific user? The main element of this is selecting a folder that "belongs" to the specific user. There are two choices that I am aware of. The first is the Application.UserAppDataPath property which returns a path as follows:

C:\Documents and Settings\userid\Application Data\company\product\version

This is typical, although the drive letter and first level folder may vary on some systems. In this path, userid is the ID of the current user, and company\product\version are information associated with the application's assembly. This location would be ideal for storage of user settings except for one thing: if the application is updated to a new version, the stored user information will be lost. The other option is to use System.Environment.GetFolderPath, which returns a path like this:

C:\Documents and Settings\userid\Application Data

This location is safe against version changes, but is common to multiple applications, so there is the theoretical possibility that another application could overwrite your user data file. However, by assigning a file name that is related to the application name, you should be safe against this possibility.

Saving user settings can be an important part of creating a user-friendly application. Saving them the right way is part of good programming practice. Once you know the factors that are involved it is really rather easy.



Submit a Comment

Loading Comments...

Thanks for your registration, follow us on our social networks to keep up-to-date