Using My.Settings in Visual Basic 2005
Optimal Solutions Integration, Inc.
Summary: This article discusses how the My.Settings class has made storing application and user settings much simpler in Visual Basic 2005. (11 printed pages)
The Old Way
The New and Better Way
Windows Forms applications require data that is needed for running the application. Most of the time you don't want to include this data in the code or you will have to recompile your application each time you make a change. If your application uses a Web service or a database, you probably want to store the URL or the connection string in a separate file so that you can change them easily. If your application stores the window layout and other UI customization, then you want to store this information separately for each user.
In the past, this data was stored in many different locations, such as INI files, the registry, or config files. The Microsoft .NET Framework 1.0 introduced the concept of app.config files, which are XML files that are used to store such settings. Each of these methods has some issues:
- Settings are not type-safe.
- Requires a considerable amount of coding (to read and write files, for example).
- The registry method requires that your application have sufficient security permissions to access the registry.
- It is hard to maintain user-scope settings for the application.
The new Application Settings architecture addresses these needs by providing an easy way to store both application-scoped and user-scoped settings on the client. Using Visual Studio or a code editor, you define a setting for a given property by specifying its name, data type, default value, and scope (application or user).
Application Settings works by persisting data as XML to different configuration (.config) files, depending on whether the setting is application-scoped or user-scoped. Application-scoped settings are read-only. Because the application-scoped settings are program information, you will typically not need to overwrite them. By contrast, user-scoped settings can be read and written safely at run time, even if your application runs under partial trust.
The Old Way
The .NET Framework 1.0 introduced the concept of config files with the app.config file. The application uses that file to store many of the application settings specific to your application. This file is a simple XML file that you can easily edit in Notepad to change application settings or add new ones. For example, this is a great place to store the address to your SMTP server, a database connection string, and so on. The .NET Framework 1.0 implementation had a few shortcomings. One shortcoming is that there was no easy way to write back to the file. If you wanted to change the settings through a page or a form in your application, you had to load the file as an XML file, change it, then write it back. Another shortcoming was that accessing properties in the config file was not type-safe. For example, the following line of code compiles fine but could cause a run-time error if the PageSize setting is not an integer (no type-safety):
Dim PageSize As Integer = Configuration.ConfigurationSettings.AppSettings("PageSize")
There is nothing preventing a user or the developer from putting a non-numeric value into this application setting, as shown below, resulting in an error at run time.
<add key="PageSize" value="ten"/>
The New and Better Way
The .NET Framework 2.0 addresses most of these shortcomings and more. The first thing you will notice is the ease with which you can create a new setting. You don't have to open the config file and type your new setting. Instead, you go to the Settings designer in the project properties form and add new settings. There you can select the name, the type, and the scope (application or user) of each setting.
Application-scoped settings are read-only and are shared between all users of that application. These settings are stored in the app.config file in the <applicationSettings> section.
**Note **This is different from the <appSettings> section mentioned above and used in .NET 1.0.
At run time, the app.config file will be in your bin folder and will be named with your application's name (MySettingsDemo.exe.config). An example of an application-scope setting would be a database connection string, a Web service URL, IP address of a server, and so forth.
<applicationSettings> <MySettingsDemo.MySettings> <setting name="SMTPServerIP" serializeAs="String"> <value>127.0.0.1</value> </setting> <setting name="MyServicesURL" serializeAs="String"> <value>https://localhost/myservices.asmx</value> </setting> </MySettingsDemo.MySettings> </applicationSettings>
User-scope settings are specific for each user. They can be read and set safely by the application code at run time. These settings are stored in a user.config file. To be technically accurate, there are two user.configs per user per application—one for non-roaming and one for roaming. Although the Visual Basic 2005 documentation states that the user.config file will be named according to the user's name (joe.config), this is not the case. The user.config file is created in the
<c:\Documents and Settings>\<username>\[Local Settings\]Application Data\<companyname>\<appdomainname>_<eid>_<hash>\<verison>. Where:
<c:\Documents and Settings>is the user data directory, either non-roaming (Local Settings above) or roaming.
<username>is the user name.
<companyname>is the CompanyNameAttribute value, if available. Otherwise, ignore this element.
<appdomainname>is the AppDomain.CurrentDomain.FriendlyName. This usually defaults to the .exe name.
<eid>is the URL, StrongName, or Path, based on the evidence available to hash.
<hash>is a SHA1 hash of evidence gathered from the CurrentDomain, in the following order of preference:
If neither of these is available, use the .exe path.
<version>is the AssemblyInfo's AssemblyVersionAttribute setting.
An example path would look like the following:
C:\Documents and Settings\Emad.BROKENOAK\Local Settings\Application Data\MySettingsDemo\MySettingsDemo_9cfe5ef1\126.96.36.199
The example path would open similar to Figure 1.
Figure 1. Example path
The user.config file is created automatically at run time the first time the application is run by a new user and a non-default value is written to a user-scoped setting. The settings are defined in the app.config file under the <userSettings> section. The default/initial values of settings are also defined in the app.config file. Here is how it looks in the app.config file:
<userSettings> <MySettingsDemo.MySettings> <setting name="LastSearchedItem" serializeAs="String"> <value /> </setting> <setting name="FormSize" serializeAs="String"> <value>400, 400</value> </setting> <setting name="FormLocation" serializeAs="String"> <value>0, 0</value> </setting> </MySettingsDemo.MySettings> </userSettings>
User-scope settings are great for storing your application preferences, which are usually different for every user. Examples of user-scope settings are display settings, such as font size, window location, MRU lists, and so on.
This architecture is very flexible because it allows your application to store settings and preferences for each user even if your application is running in a partial-trust scenario.
Creating a Setting
Let's assume you have a search box in your application and you want to store the value of the last item that was searched. First, let's create the UI for the application, as shown in Figure 2.
Figure 2. Creating the search box UI
Now, let's create the setting for the last searched item string. The completed search box will look like Figure 3.
In solution explorer, double-click My Project.
Click on the Settings tab to display the Settings Designer.
Enter a Name, Type, and Scope of the Setting ("LastSearchedItem", String, and User respectively).
In the Value column, enter the initial value for this setting (What would you like to search for?) or you can leave it blank.
Figure 3. Completed search box
Behind-the-Scenes of a Setting
When you add a new setting in the Settings Designer, several things occur in the background. First, an entry is added in the app.config file that defines the sections for defining the settings.
<configSections> <sectionGroup name="applicationSettings"type="System.Configuration.ApplicationSettingsGroup, System, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <section name="MySettingsDemo.MySettings" type="System.Configuration.ClientSettingsSection, System, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> </sectionGroup> </configSections>
Second, the setting definition itself is added to the app.config file (shown below). Depending on the type of settings you define, you might see an applicationSettings, a userSettings and/or a connectionStrings section(s) depending on what settings you define.
<userSettings> <MySettingsDemo.MySettings> <setting name="LastSearchedItem" serializeAs="String"> <value /> </setting> <setting name="FormSize" serializeAs="String"> <value>400, 400</value> </setting> <setting name="FormLocation" serializeAs="String"> <value>0, 0</value> </setting> </MySettingsDemo.MySettings> </userSettings> <connectionStrings> <add name="MySettingsDemo.MySettings.ConnectionString" connectionString="Server=TABLET; User ID=sa; Password=1234; Database=Northwind; Persist Security Info=True" providerName="System.Data.SqlClient" /> </connectionStrings> <applicationSettings> <MySettingsDemo.MySettings> <setting name="SMTPServerIP" serializeAs="String"> <value>192.168.2.11</value> </setting> <setting name="MyServicesURL" serializeAs="String"> <value>https://localhost/MyServices/Service1.asmx</value> </setting> </MySettingsDemo.MySettings> </applicationSettings>
Now that we have added a setting, we need to retrieve it and update it. Retrieving a setting is extremely easy. You can retrieve it like so:
Figure 4 shows how your application's setting fields are accessible through IntelliSense, making it very easy to find the exact setting value that you need to use.
Figure 4. IntelliSense feature in action
Another way to use a setting is to bind it to a property. This requires almost no code. For example, you can bind the FormLocation setting to the form's location property.
- In the properties window of the form, expand Data | (ApplicationSettings) | (PropertyBinding).
- Find the Location property and select the FormLocation setting as shown in Figure 5.
Figure 5. Selecting the FormLocation setting
This will create the code in the designer code-behind class of your form:
Me.DataBindings.Add(New System.Windows.Forms.Binding("Location", MySettingsDemo.MySettings.Value, "FormLocation", True, MySettingsDemo.MySettings.Value.LastSearchedItem, Nothing, System.Windows.Forms.BindingUpdateMode.OnPropertyChanged))
Notice the last parameter in the Binding class construction. This defines the update mode of your setting. You can update it on change, on validation, or never. The default code generated in the designer updates it on change.
When you bind settings to properties, you don't have to worry about reading or writing of the setting. The property will automatically be set when it is needed, and if its value is changed, it will be automatically saved. Notice how all this is done without writing a single line of code.
**Note **The settings are automatically saved only in certain project types, such as Visual Basic Windows Forms applications. In other cases, such as in Class Library projects, you will need to explicitly call the Save method of the My.Settings class.
If the property is not listed in the property-binding window, you can still achieve the same thing using code. For some reason, the Size property is not available as a property that can be bound. This is a design decision in this version, but the final version will make all properties available for binding. This is done by adding a new Binding to your form's DataBindings collection. The Binding class is used to define the property name, the setting name, the data store and the update mode. So, to bind the size property to the FormSize setting, you can manually create the binding in your form load event:
Me.DataBindings.Add(New System.Windows.Forms.Binding("Size", _ MySettingsDemo.MySettings.Value, _ "FormSize"))
If you want more control over your settings, you can define your own class that derives from ApplicationSettingsBase, which is the parent class of all settings classes. You define each setting as a property with both a get and a set defined. You decorate that property with the ApplicationScopedSettingAttribute if it is an application-scoped setting, or with the UserScopedSettingAttribute if it is a user-scoped setting. Actually, that's exactly what the settings designer does. Here is a fragment from the MySettings.vb file generated by the settings designer:
Partial NotInheritable Class MySettings Inherits System.Configuration.ApplicationSettingsBase <System.Diagnostics.DebuggerNonUserCode(), _ System.Configuration.UserScopedSettingAttribute(), _ System.Configuration.DefaultSettingValueAttribute("400, 400")> _ Public Property FormSize() As System.Drawing.Size Get Return CType(Me("FormSize"),System.Drawing.Size) End Get Set Me("FormSize") = value End Set End Property <System.Diagnostics.DebuggerNonUserCode(), _ System.Configuration.SpecialSetting(System.Configuration.SpecialSetting.ConnectionString), _ System.Configuration.ApplicationScopedSettingAttribute(), _ System.Configuration.DefaultSettingValueAttribute("Server=TABLET; User ID=sa; Password=1234; Database=Northwind; Persist Security In"& _ "fo=True")> _ Public ReadOnly Property ConnectionString() As String Get Return CType(Me("ConnectionString"),String) End Get End Property <System.Diagnostics.DebuggerNonUserCode(), _ System.Configuration.ApplicationScopedSettingAttribute(), _ System.Configuration.DefaultSettingValueAttribute("192.168.2.11")> _ Public ReadOnly Property SMTPServerIP() As String Get Return CType(Me("SMTPServerIP"),String) End Get End Property ...
**Note **Connection strings are decorated by the SpecialSetting attribute. They are also created in their own section in the config file.
Also, notice that this is a Partial class, which means you can create another file in your project and create another part to this class. Using this technique, you can fine-tune your settings without touching the designer-generated file. You can add event handling in this class to give you more precise control over your settings. The three events you can handle are:
It is important to note that the settings features sits atop a pluggable provider infrastructure. Currently Visual Studio 2005 includes only one provider—LocalFileSettingsProvider—that can access .config files. Alternate providers can be plugged-in and used instead of or in addition to the Local File Settings Provider. A good example of this is the settings in a cloud scenario, where a Web service is used to store settings in a database, which are then fetched from wherever a user logs in.
Previous architectures for handling application settings had some limitation and the new Application Settings architecture in the .NET Framework 2.0 resolves many of these issues. The new architecture greatly simplifies the creation, binding, retrieving, and updating of settings, as shown in this article with the help of Visual Basic 2005. It also addresses application-scope, as well as user-scope settings. This new architecture will certainly improve productivity and let developers focus on solving the business problem rather than spending days or weeks figuring out all the intricacies of application settings.
Emad Ibrahim (MCSD, MCAD) is a technical architect for Optimal Solutions Integration, Inc.