The new settings system in Visual Basic 2005 is a multi-file, XML-based, strongly-typed, and easy-to-manage configuration approach. Its file-focused methodology includes these features and benefits.
But it's not all fun and games. As a developer, you have to do some of the heavy lifting, like coming up with meaningful names for each setting ("MainFormLocation," "DatabaseConnection," and so on), and altering the behavior of your program as needed based on the stored settings.
The actual settings appear in XML files scattered throughout the file system.
The settings system is a great place to store state, things that you want the program to remember from the last time it was run, but that shouldn't be hard-coded into the source code.
Adding Settings to a Project
The Project Properties window within Visual Studio 2005 provides centralized control of the settings for an application. The Settings panel of this window, shown in Figure 14-1, provides access to the application's custom settings.
Figure 14-1. The Settings panel with no defined settings
To add a setting, type in its Name, select its data Type from the drop-down list, choose the Scope (User or Application), and enter its Value using whatever value editor is available for the selected type. The Type list includes many default selections, including the basic Visual Basic data types, fonts, colors, and drawing-related sizes. Also included is a "(Connection string)" type that, when selected, enables a Connection Properties string builder in the Value column.
It's important that you select the correct type for each stored setting; otherwise, your workstation will explode. Actually, I think they fixed that in a later beta. It's really because all settings are strongly typed. If you assign the type to Integer, you won't be able to stuff the word "None" in there as a special flag as you could have done with an INI file. You can choose any valid .NET type for the data type, although complex types without their own custom editors will require that you set their value through code.
What happens when you add a new setting to your Visual Basic project? Let's find out. I'll add two settings to a new Windows Forms project: an Integer named WarningLimit, and a System.Drawing.Font named NoticeFont (see Figure 14-2).
Figure 14-2. The Settings panel with two new settings
As you already know, Visual Studio is just a user-friendly wrapper around .NET code, and the settings panel is no different. So the real changes occur somewhere in the code, or more correctly, in both code and the related Settings.settings file. If you "Show All Files" in the Solution Explorer panel, and expand My Project followed by Settings.settings, you will find that this XML file has its own Visual Basic source code file, Settings.Designer.vb.
If you open the Settings.Designer.vb file, you find the following partial code.
Namespace My Partial Friend NotInheritable Class MySettings Inherits Global.System.Configuration. _ ApplicationSettingsBase <Global.System.Configuration. _ UserScopedSettingAttribute(), _ Global.System.Diagnostics. _ DebuggerNonUserCodeAttribute(), _ Global.System.Configuration. _ DefaultSettingValueAttribute("25")> _ Public Property WarningLimit() As Integer Get Return CType(Me("WarningLimit"),Integer) End Get Set Me("WarningLimit") = value End Set End Property <Global.System.Configuration. _ UserScopedSettingAttribute(), _ Global.System.Diagnostics. _ DebuggerNonUserCodeAttribute(), _ Global.System.Configuration. _ DefaultSettingValueAttribute( _ "Arial, 14.25pt, style=Bold")> _ Public Property NoticeFont() _ As Global.System.Drawing.Font Get Return CType(Me("NoticeFont"), _ Global.System.Drawing.Font) End Get Set Me("NoticeFont") = value End Set End Property End Class End Namespace
I excluded a lot of the extra code. It's amazing how much code Microsoft loads up in prewritten attributes, and it's not really possible to know what goes on inside. I can guess what the DefaultSettingValueAttribute attribute does for each setting (assigns the initial default value of the setting), but some of the others are mysteries. Oh well. Even the ancients didn't have answers for everything.
But the code that remains is quite clear. Visual Studio generates two properties within the My.MySettings class, properties namedamazingly enoughWarningLimit and NoticeFont. Here's the property entry for NoticeFont.
Public Property NoticeFont() As Global.System.Drawing.Font Get Return CType(Me("NoticeFont"), _ Global.System.Drawing.Font) End Get Set Me("NoticeFont") = value End Set End Property
You won't find any private class members that store the hidden WarningLimit and NoticeFont values. Instead, somewhere else in this partial class is a default property (named Item) that gets and sets each defined property value, accessed through Me("something").
The settings available through this default property are loaded directly from the XML stored in the Settings.settings file. (This file is compiled into the application; you don't have to distribute Settings.settings with the application.) Here's the content from that file with our two new configuration values.
<?xml version='1.0' encoding='utf-8'?> <SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="My" GeneratedClassName="MySettings" UseMySettingsClassName="true"> <Profiles /> <Settings> <Setting Name="WarningLimit" Type="System.Int32" Scope="User"> <Value Profile="(Default)">25</Value> </Setting> <Setting Name="NoticeFont" Type="System.Drawing.Font" Scope="User"> <Value Profile="(Default)"> Arial, 14.25pt, style=Bold</Value> </Setting> </Settings> </SettingsFile>
Each setting contains distinct Name, Type, Scope, and Value attributes or entries, matching the four columns that appeared in the Visual Studio settings editor.
Visual Basic creates an instance of the My.MySettings class we just saw previously, and makes it available as My.Settings. As you add settings to your project, they become strongly-typed class members of My.Settings. To access one, simply reference it directly in your code.
MsgBox("The font for notices is: " & _ My.Settings.NoticeFont.ToString())
(The output for this code appears in Figure 14-3.) The My.Settings.NoticeFont is an actual instance of a System.Drawing.Font that you can use like any other Font instance.
Figure 14-3. Be sure to take "notice" of this font
You can modify the value of any setting scoped as "User," and have the new value preserved for your next use of the application (that is, for the current user's next use of the application).
My.Settings.WarningLimit = 30
All changes made to these settings are saved automatically to the user-specific setting files by default. If you don't want the updates saved automatically, set the My.Application.SaveMySettingsOnExit flag to False. Then, when you are ready to save the new settings, use the My.Settings.Save method.
Settings come in three delicious flavors: default, persisted, and current. Default settings are those values defined by the programmer through the Visual Studio settings editor. Persisted settings include the saved changes to specific settings, and the default settings for those that have never been altered by the user. Current settings include any changes made to the settings during the current session, but not yet saved. You can play with these states using members of the My.Settings object.
One of the strangest aspects of settings is that they are version-specific. If you release your application as version 22.214.171.124, and then later release version 126.96.36.199, each user will lose all of the previously persisted settings. Actually, they won't be lost, but they will be stuck in 188.8.131.52-land. If you always want to have the most up-to-date settings as modified by the user, you will have to make sure that older settings are "upgraded" when installing a new version. My.Settings includes an Upgrade method that does the work for you. But if the user installs a newer version and upgrades the settings, makes changes to those settings, and then calls Upgrade again, any changes made since the last upgrade will be lost.
To get around this problem, the code should only upgrade settings when a new version appears. The easiest way to do this is to include a setting called something like SettingsUpgraded and set it to False. Check this flag before calling Upgrade. If it is still False, then it is safe to call Upgrade. Once the code upgrades the settings, change SettingsUpgraded to True.
If (My.Settings.SettingsUpgraded = False) Then My.Settings.Upgrade() My.Setttings.SettingsUpgraded = True End If
This need to upgrade settings whenever even minor version number changes are made to an assembly seems a bit over the top. But it's necessary to support .NET's goal of side-by-side installation. The user should be able to install two different versions of your application on the same workstation, and use each one without interference from the other. Storing version-specific settings helps achieve this goal.
Although using and updating your own custom configuration values can be exciting, even more exciting is that the fields in your Windows Forms and related controls can interact with the persisted settings automatically. By binding form- and control-specific properties to the settings system, Visual Basic automatically saves and restores user-controlled preferences within the user interface.
A typical use for bound settings is to have the application remember where a particular form appeared on the screen when the program was last run. The form's Location property maintains its on-screen position. Recording this value to the settings requires two steps. First, create a setting of type System.Drawing.Point to hold the persisted location value. Second, indicate in the form's properties that its Location value should persist to the new settings entry.
Perform the first step by adding a new user-scoped System.Drawing.Point setting in the project properties' Settings panel. Let's name it "MainFormPosition," and leave the Value field blank for now.
Back in the form editor, select the form object itself, and then access the Properties Windows. Expand the (ApplicationSettings) property to locate the (PropertyBinding) sub-property. Clicking the "..." button for this entry displays the Application Settings dialog. This selection process appears in Figure 14-4.
Figure 14-4. Bringing up the application settings dialog for a form
Find the Location entry in the list, and choose "MainFormPosition" for its value. Now, each time you run the application containing this bound setting, the modified form will "remember" its previous location.