Persisting Application Settings in the .NET Framework

 

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)

Contents

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

Introduction

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.

Prerequisites to Saving Application Settings

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:

' Visual Basic
Imports System
Imports System.Windows.Forms
Imports System.Reflection

' Set the assembly attributes.
<Assembly: AssemblyCompany("Microsoft")>
<Assembly: AssemblyProduct("MyApplication")>
<Assembly: AssemblyVersion("1.0.1")>
Public Class AboutDialogBox
   Inherits Form
   
   Public Sub New()
      ' Display the assembly information in a Label.
      Dim label1 As New Label()
      label1.Text = _
        Me.CompanyName + " " + _
        Me.ProductName + " Version: " + _
        Me.ProductVersion
      label1.AutoSize = True
      Me.Controls.Add(label1)
   End Sub
End Class 

//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());
   }
}

Using the Registry to Store Data

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:

  • The UserAppDataRegistry property returns the registry key that is used to store roaming data (settings that are specific to a user but machine independent) for each user; the registry key takes the form HKEY_CURRENT_USER\Software\[Control.CompanyName]\[Control.ProductName]\[Control.ProductVersion].
  • The CommonAppDataRegistry property returns the registry key that is used to store non-user specific and non-roaming data for the machine; the registry key takes the form HKEY_LOCAL_MACHINE\Software\[Control.CompanyName]\[Control.ProductName]\[Control.ProductVersion].

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.

Saving Data to the Registry

The following example saves the connection string to the registry when the form is closed, if it has changed

' Visual Basic
Private appSettingsChanged As Boolean
Private connectionString As String

Private Sub Form1_Closing(sender As Object, 
  e As CancelEventArgs) Handles MyBase.Closing
   If appSettingsChanged Then
      Try
         ' Save the connection string to the registry if it has changed.
         Application.UserAppDataRegistry.SetValue("ConnString", _
           connectionString)
      Catch ex As Exception
         MessageBox.Show(ex.Message )
      End Try
   End If
End Sub

// 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 );
      }
   }
}

Retrieving Data From the Registry

The following example retrieves a connection string from the registry when the form loads.

' Visual Basic
Private appSettingsChanged As Boolean
Private connectionString As String

Private Sub Form1_Load(sender As Object, 
  e As EventArgs) Handles MyBase.Load
   Try
      ' Get the connection string from the registry.
      If Not (Application.UserAppDataRegistry.GetValue("ConnString") _
        Is Nothing) Then
         connectionString = _
           Application.UserAppDataRegistry.GetValue( _
           "ConnString").ToString()
         statusBar1.Text = "Connection String: " + connectionString
      End If
   Catch ex As Exception
      MessageBox.Show(ex.Message)
   End Try
End Sub

// 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);
   }
}

Creating a Custom Application Configuration Class

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:

  • The UserAppDataPath property is used to store roaming data (settings that are specific to a user but machine independent) for each user; the path takes the form [UserName]\Application Data\[Control.CompanyName]\[Control.ProductName]\[Control.ProductVersion].
  • The LocalUserAppDataPath property is used to store non-roaming data for each user; the path takes the form [UserName]\Local Settings\Application Data\[Control.CompanyName]\[Control.ProductName]\[Control.ProductVersion].
  • The CommonAppDataPath property is used to store non-user specific, non-roaming data for the machine; the path takes the form All Users\Application Data\[Control.CompanyName]\[Control.ProductName]\[Control.ProductVersion].

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.

  1. Create the class and add a Boolean variable that specifies whether the application settings have changed. Be sure to add a reference to the System.Xml.Serialization and System.IO namespaces, as well as references to any other namespaces you might need to use to store your data.

    ' Visual Basic
    Imports System
    Imports System.Xml.Serialization
    Imports System.IO
    Imports System.Drawing
    
    Namespace Samples
       Public Class ApplicationSettings
          Private appSettingsChanged As Boolean
    
       End Class
    End Namespace
    
    // C#
    using System;
    using System.Xml.Serialization;
    using System.IO;
    using System.Drawing;
    
    namespace Samples
    {
       public class ApplicationSettings
       {
          private bool appSettingsChanged;
       }
    }
    
  2. Add private variables to store the application settings and create public properties to access them.

    ' Visual Basic
          ' Variables used to store the application settings.
          Private m_defaultDirectory As String
          Private m_backColor As Integer
          Private m_formLocation As Point
    
          ' Properties used to acces the application settings variables.
          Public Property DefaultDirectory() As String
             Get
                Return m_defaultDirectory
             End Get
             Set
                If value <> m_defaultDirectory Then
                   m_defaultDirectory = value
                   appSettingsChanged = True
                End If
             End Set
          End Property
    
          Public Property BackColor() As Integer
             Get
                Return m_backColor
             End Get
             Set
                If value <> m_backColor Then
                   m_backColor = value
                   appSettingsChanged = True
                End If
             End Set
          End Property
    
          Public Property FormLocation() As Point
             Get
                Return m_formLocation
             End Get
             Set
                If Not value.Equals(m_formLocation) Then
                   m_formLocation = value
                   appSettingsChanged = True
                End If
             End Set
          End Property
    
    // 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;
                }
             }
          }
    
  3. Add a method to serialize the application settings and save them to the configuration file.

    ' Visual Basic
          ' Serializes the class to the config file 
          ' if any of the settings have changed. 
          Public Function SaveAppSettings() As Boolean
             If Me.appSettingsChanged Then
                Dim myWriter As StreamWriter = Nothing
                Dim mySerializer As XmlSerializer = Nothing
                Try
                   ' Create an XmlSerializer for the 
                   ' ApplicationSettings type.
                   mySerializer = New XmlSerializer( _
                     GetType(ApplicationSettings))
                   myWriter = New StreamWriter( _
                     Application.LocalUserAppDataPath + _
                     "\myApplication.config", False)
                   ' Serialize this instance of the ApplicationSettings 
                   ' class to the config file. 
                   mySerializer.Serialize(myWriter, Me)
                Catch ex As Exception
                   MessageBox.Show(ex.Message)
                Finally
                   ' If the FileStream is open, close it.
                   If Not (myWriter Is Nothing) Then
                      myWriter.Close()
                   End If
                End Try
             End If
             Return appSettingsChanged
          End Function 
    
    // 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;
          }
    
  4. Add a method to deserialize the application settings from the configuration file.

    ' Visual Basic
          ' Deserializes the class from the config file.
          Public Function LoadAppSettings() As Boolean
             Dim mySerializer As XmlSerializer = Nothing
             Dim myFileStream As FileStream = Nothing
             Dim fileExists As Boolean = False
    
             Try
                ' Create an XmlSerializer for the ApplicationSettings type.
                mySerializer = New XmlSerializer(GetType(ApplicationSettings))
                Dim fi As New FileInfo(Application.LocalUserAppDataPath + _
                  "\myApplication.config")
                ' If the config file exists, open it.
                If fi.Exists Then
                   myFileStream = fi.OpenRead()
                   ' Create a new instance of the ApplicationSettings by
                   ' deserializing the config file. 
                   Dim myAppSettings As ApplicationSettings = CType( _
                     mySerializer.Deserialize(myFileStream), _
                       ApplicationSettings)
                   ' Assign the property values to this instance of 
                   ' the ApplicationSettings class. 
                   Me.m_backColor = myAppSettings.BackColor
                   Me.m_formLocation = myAppSettings.FormLocation
                   Me.m_defaultDirectory = myAppSettings.DefaultDirectory
                   fileExists = True
                End If
             Catch ex As Exception
                MessageBox.Show(ex.Message)
             Finally
                ' If the FileStream is open, close it.
                If Not (myFileStream Is Nothing) Then
                   myFileStream.Close()
                End If
             End Try
    
             ' If myDirectory is not set, default
             ' to the user's "My Documents" directory.
             If m_defaultDirectory Is Nothing Then
                m_defaultDirectory = Environment.GetFolderPath( _
                  System.Environment.SpecialFolder.Personal)
                Me.appSettingsChanged = True
             End If
             Return fileExists
          End Function
    
    // 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;
          }
    

Using Your Application Settings Class with Your Application

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.

Loading Saved Application Settings

You must have a variable of the type defined to hold your application setting; in this example it is an ApplicationSettingsclass namedapplicationSettings. 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.

' Visual Basic
' If the LoadAppSettings method is successful, assign the 
' ApplicationSettings properties to their corresponding form properties.
Private Sub Form1_Load(sender As Object, _
  e As EventArgs) Handles MyBase.Load
   If applicationSettings.LoadAppSettings() Then
      ' Create a Color from the integer (ARGB) value.
      Me.BackColor = Color.FromArgb(applicationSettings.BackColor)
      Me.Location = applicationSettings.FormLocation
      Me.defaultDirectory = applicationSettings.DefaultDirectory
   End If
End Sub

// 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;
   }
}

Saving Application Settings

You must have a variable of the type defined to hold your application setting; in this example it is an ApplicationSettingsclass namedapplicationSettings. 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.

' Visual Basic
' Save the new location to the ApplicationSettings 
' object when it is changed.
Private Sub Form1_LocationChanged(sender As Object, _
  e As EventArgs) Handles MyBase.LocationChanged
   applicationSettings.FormLocation = Me.Location
End Sub

' Save the new BackColor to the ApplicationSettings 
' object when it is changed.
Private Sub Form1_BackColorChanged(sender As Object, _
  e As EventArgs) Handles MyBase.BackColorChanged
   applicationSettings.BackColor = Me.BackColor.ToArgb()
End Sub 

' Save the new default directory to the ApplicationSettings object 
' before serializing the class settings and closing the form.
Private Sub Form1_Closing(sender As Object, _
  e As CancelEventArgs) Handles MyBase.Closing
   applicationSettings.DefaultDirectory = Me.defaultDirectory
   applicationSettings.SaveAppSettings()
End Sub

// 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();
}

Conclusion

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.