Jeff Danner
Microsoft Corporation
June 2002
Summary: How to persist applications settings at run time in the Microsoft .NET Framework by saving them to the registry or serializing them to a configuration file. (20 printed pages)
Introduction
Prerequisites to Saving Application Settings
Using the Registry to Store Data
Saving Data to the Registry
Retrieving Data From the Registry
Creating a Custom Application Configuration Class
Using Your Application Settings Class with Your Application
Loading Saved Application Settings
Saving Application Settings
Conclusion
Saving application settings is a common task. In the past you would have saved your settings in an INI file or in the registry. With the Microsoft® .NET Framework, you have the additional option of serializing the application settings into an XML file and easily updating and retrieving those settings. Microsoft Visual Studio® .NET uses the System.Configuration.AppSettingsReader class to read its DynamicProperties, which are stored in a configuration file. However, the dynamic properties are read-only at run time, so you cannot persist the changes made by the user. This paper describes how to serialize your data and write it out to a file as well as how to read and deserialize the data. How and where you store your data depends on what you want to store, I will discuss how to save your data in the appropriate locations, based upon its type.
The Windows Forms Application class contains several properties that allow easy navigation to the appropriate part of the registry or user data folder. In order to use these properties correctly, you must set the AssemblyCompany, AssemblyProduct and AssemblyVersion attributes.
These attributes set values that are exposed by the Control class through the CompanyName, ProductName and ProductVersion properties.
The following is a simple Windows form that sets the assembly attributes and displays them in a Label:
//C#
using System;
using System.Windows.Forms;
using System.Reflection;
// Set the assembly attributes.
[assembly: AssemblyCompany("Microsoft")]
[assembly: AssemblyProduct("MyApplication")]
[assembly: AssemblyVersion("1.0.1")]
public class AboutDialogBox : Form
{
public AboutDialogBox()
{
// Display the assembly information in a Label.
Label label1 = new Label();
label1.Text = this.CompanyName + " "
+ this.ProductName + " Version: "
+ this.ProductVersion;
label1.AutoSize = true;
this.Controls.Add(label1);
}
[STAThread]
static void Main()
{
Application.Run(new AboutDialogBox());
}
}
You may not want to simply serialize data that is viewed as sensitive or vital to the application, since anyone can view or edit the data with a text editor; the registry makes the data less accessible. The registry provides a robust store for application and user settings. Most back-up programs automatically back up the registry settings, and when you put information in the right place in the registry, you automatically get user isolation when storing settings. Although users can edit the registry, they generally tend not to, and this makes your settings more stable. Overall, when Microsoft Windows® Logo guidelines for registry usage are followed, it is a reasonable place to store application settings.
Your application will require Create and Write registry permissions to write to the registry and Read permission to read the registry. For more information working with registry keys, see the documentation for the GetValue and SetValue methods of the Microsoft.Win32.RegistryKey class and the System.Security.Permissions.RegistryPermissionAccess enumeration documentation in the .NET Framework SDK documentation.
To save information to the registry, use the UserAppDataRegistry or CommonAppDataRegistry property of the Application class. These properties return a RegistryKey object that can be used to store application data, based on the user type:
These two properties are read-only; however, the RegistryKey object they return has several methods that enable you to read, update, or create registry keys and their values.
The following example saves the connection string to the registry when the form is closed, if it has changed
// C#
private bool appSettingsChanged;
private string connectionString;
private void Form1_Closing(object sender, CancelEventArgs e)
{
if(appSettingsChanged)
{
try
{
// Save the connection string to the registry, if it has changed.
Application.UserAppDataRegistry.SetValue("ConnString",
connectionString);
}
catch(Exception ex)
{
MessageBox.Show(ex.Message );
}
}
}
The following example retrieves a connection string from the registry when the form loads.
// C#
private bool appSettingsChanged;
private string connectionString;
private void Form1_Load(object sender, EventArgs e)
{
try
{
// Get the connection string from the registry.
if(Application.UserAppDataRegistry.GetValue("ConnString") != null)
{
connectionString =
Application.UserAppDataRegistry.GetValue(
"ConnString").ToString();
statusBar1.Text = "ConnectionString: " +
connectionString;
}
}
catch(Exception ex)
{
MessageBox.Show(ex.Message);
}
}
The application configuration class described in this paper serializes an instance of itself as XML. Serialization is the process of converting an object or a graph of objects into a linear sequence of bytes for either storage or transmission to another location. Deserialization is the process of taking in stored or transmitted information and recreating objects from it. You can serialize an object as text (XML being text with a hierarchical structure) or in a binary format. For more information on serialization, see Serializing Objects in the .NET Framework Developer's Guide.
Windows Forms classes that derive from Control cannot be easily serialized as XML since they contain objects that have dynamic states, such as a window handle (HWND). Since controls cannot easily be entirely serialized, and because you generally do not need to persist every property exposed by the control, you should create a small XML-serializable class to store the desired property values. For example, you might only need to store the form's BackColor, the last directory where the user saved a file, or the Location of the form when it was last closed. Your application will require Write permissions to write or create the XML file and Read permission to read the XML file. For more information, see the documentation for the StreamWriter constructor and the System.Security.Permissions.FileIOPermissionAccess enumeration documentation in the .NET Framework SDK. Some types cannot be serialized as XML without first being converted. For example, a System.Drawing.Color must be converted to an integer using its ToArgb method to convert it to a value that can be serialized as XML. Type converters can also be used to convert types to string representations. For more information on type converters, see the TypeConverter class documentation and Implementing a Type Converter in the .NET Framework SDK Documentation.
Several properties of the Application class provide the application storage paths that can be used to store application data:
Note The UserName value used in the data paths is retrieved from the UserName property of the System.Environment class.
Typically, the roaming store holds smaller data such as window settings while the non-roaming store holds larger data, for example a cache of large bitmaps.
The following example includes a class that stores settings for an application. The class provides the SaveAppSettings and LoadAppSettings methods to load (deserialize) and save (serialize) your application settings. The BackColor property value is stored as an integer so it can be serialized. The example saves the serialized data to a file named MyApplication.config. Note that this is not the same file as the App.config file used by Visual Studio .NET.
// C#
using System;
using System.Xml.Serialization;
using System.IO;
using System.Drawing;
namespace Samples
{
public class ApplicationSettings
{
private bool appSettingsChanged;
}
}
// C#
// Variables used to store the application settings.
private string m_defaultDirectory;
private int m_backColor;
private Point m_formLocation;
// Properties used to access the application settings variables.
public string DefaultDirectory
{
get{return m_defaultDirectory;}
set
{
if(value != m_defaultDirectory)
{
m_defaultDirectory = value;
appSettingsChanged = true;
}
}
}
public int BackColor
{
get{return m_backColor;}
set
{
if(value != m_backColor)
{
m_backColor = value;
appSettingsChanged = true;
}
}
}
public Point FormLocation
{
get{return m_formLocation;}
set
{
if(value != m_formLocation)
{
m_formLocation = value;
appSettingsChanged = true;
}
}
}
// C#
// Serializes the class to the config file
// if any of the settings have changed.
public bool SaveAppSettings()
{
if(this.appSettingsChanged)
{
StreamWriter myWriter = null;
XmlSerializer mySerializer = null;
try
{
// Create an XmlSerializer for the
// ApplicationSettings type.
mySerializer = new XmlSerializer(
typeof(ApplicationSettings));
myWriter =
new StreamWriter(Application.LocalUserAppDataPath
+ @"\myApplication.config",false);
// Serialize this instance of the ApplicationSettings
// class to the config file.
mySerializer.Serialize(myWriter, this);
}
catch(Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
// If the FileStream is open, close it.
if(myWriter != null)
{
myWriter.Close();
}
}
}
return appSettingsChanged;
}
// C#
// Deserializes the class from the config file.
public bool LoadAppSettings()
{
XmlSerializer mySerializer = null;
FileStream myFileStream = null;
bool fileExists = false;
try
{
// Create an XmlSerializer for the ApplicationSettings type.
mySerializer = new XmlSerializer(typeof(ApplicationSettings));
FileInfo fi = new FileInfo(Application.LocalUserAppDataPath
+ @"\myApplication.config");
// If the config file exists, open it.
if(fi.Exists)
{
myFileStream = fi.OpenRead();
// Create a new instance of the ApplicationSettings by
// deserializing the config file.
ApplicationSettings myAppSettings =
(ApplicationSettings)mySerializer.Deserialize(
myFileStream);
// Assign the property values to this instance of
// the ApplicationSettings class.
this.m_backColor = myAppSettings.BackColor;
this.m_formLocation = myAppSettings.FormLocation;
this.m_defaultDirectory = myAppSettings.DefaultDirectory;
fileExists = true;
}
}
catch(Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
// If the FileStream is open, close it.
if(myFileStream != null)
{
myFileStream.Close();
}
}
if(m_defaultDirectory == null)
{
// If myDirectory is not set, default
// to the user's "My Documents" directory.
m_defaultDirectory = Environment.GetFolderPath(
System.Environment.SpecialFolder.Personal);
this.appSettingsChanged = true;
}
return fileExists;
}
You only need to add a few lines of code to your application to use the application settings class to serialize and deserialize values you would like to persist.
You must have a variable of the type defined to hold your application setting; in this example it is an ApplicationSettings class named
applicationSettings. In the appropriate place in your code, call the method that deserializes the data; in this example, the method is named LoadAppSettings(), and it is called in the Load event of the form. You could call this method in the constructor after any other initialization code, or in the Load event handler. You then assign the stored values to the corresponding properties of your application.
The following example loads the application settings in the Load event of the form and assigns the values to their corresponding properties. This example assumes that your form has a variable named defaultDirectory.
// C#
// If the LoadAppSettings method is successful, assign the
// ApplicationSettings properties to their corresponding form properties.
private void Form1_Load(object sender, EventArgs e)
{
if(this.applicationSettings.LoadAppSettings() )
{
// Create a Color from the integer (ARGB) value.
this.BackColor = Color.FromArgb(applicationSettings.BackColor);
this.Location = applicationSettings.FormLocation;
this.defaultDirectory = applicationSettings.DefaultDirectory;
}
}
You must have a variable of the type defined to hold your application setting; in this example it is an ApplicationSettings class named
applicationSettings. In the appropriate place in your code, you assign the property values you want to persist to the corresponding properties in the application settings class. If the properties you are persisting have a changed event such as the BackColorChanged event, you can update the corresponding property in your application settings class in that event handler. This updates the instance of your applications settings class every time the user or your application changes the property.
Before closing the application, call the method that serializes the data; in this example the method is named SaveAppSettings(), and it is called in the Closing event of the form. You could call this method in the Closing event handler or overload the Close method to take a Boolean value to specify whether to save the application settings.
The following example updates the properties in the application settings class and saves the application settings in the Closing event of the form.
// C#
// Save the new location to the ApplicationSettings
// object when it is changed.
private void Form1_LocationChanged(object sender, EventArgs e)
{
applictionSettings.FormLocation = this.Location;
}
// Save the new BackColor to the ApplicationSettings
// object when it is changed.
private void Form1_BackColorChanged(object sender, EventArgs e)
{
applicationSettings.BackColor = this.BackColor.ToArgb();
}
// Save the new default directory to the ApplicationSettings object
// before serializing the class settings and closing the form.
private void Form1_Closing(object sender, CancelEventArgs e)
{
applicationSettings.DefaultDirectory = this.defaultDirectory;
applicationSettings.SaveAppSettings();
}
As in the past, you can read from and write to the registry with the appropriate permissions; however, you should try to limit the amount of data stored in the registry. With minimal implementation and customization, the .NET Framework gives you the ability to easily serialize your application settings and store the data as XML. Unlike the application configuration model that Visual Studio .NET provides, the serialized data can be read and updated.
The application data and registry data paths used in this document complies with the Windows logo guidelines. To ensure your application complies with all the Windows logo guidelines, see Designed for Microsoft Windows XP Application Specification section of the Designed for Windows Logo Program on the Microsoft Web site.