RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


Exploring Secrets of Persistent Application Settings : Page 4

The .NET framework makes it easier than ever to create application settings and bind them to controls, but you need to know a few secrets to go beyond basic string settings and avoid problems.

Fecund Configuration Files
You potentially have multiple sets of configuration files to be aware of. If you run with debugging (F5) Visual Studio creates your configuration file in one location, but if you run without debugging (Ctrl-F5) it creates it in a different location! If you are not aware of this, you might think your program is behaving quite strangely if you innocuously switch from F5 to Ctrl-F5. Say, for example, you invoke the sample application above with F5. You make a change to a setting, exit, restart with F5 again, and you will find your change has persisted across invocations. But if you exit, then restart with Ctrl-F5 your change is apparently gone. You may see the program defaults again if this is the first time you executed with Ctrl-F5, or a different change if you had previously run with Ctrl-F5.

Earlier, I indicated that the user-scoped configuration file is stored in a path following this format:

   <Profile Directory>\<Company Name>\<App Name>_<Evidence Type>_
   <Evidence Hash>\<Version>\user.config
For the sample application running without debugging my actual file path is:

   C:\Documents and Settings\msorens\Local Settings\Application Data\
It is important to understand the derivation of the file naming components. Open the Properties window, then the Application pane, then select "Assembly Information" to get to the assembly information dialog. The <Company Name> and the <Version> come from the fields indicated in Figure 15.

Figure 15. Filename Component Derivation: The user.config path consists of several components that depend on the values in this dialog, your user profile—and the method you use to deploy your application.
The <Profile Directory> refers to your user profile, which in my case is C:\Documents and Settings\msorens\Local Settings\Application Data\.

The advertised format is not quite complete though. Running with debugging and storing settings creates a user.config for me in this path:

   C:\Documents and Settings\msorens\Local Settings\Application Data\
The only difference in the path name compared to the previous path is the added vshost portion (highlighted above). That is why there are at least two different user.config files. There may be even more, because the way you deploy your project affects the location of the user.config file as well. Whether or not you deploy with ClickOnce (i.e. the Publish tab of your project settings) affects the location. Quoting from ClickOnce and Application Settings:

"In Windows Forms applications not deployed using ClickOnce, an application's app.exe.config file is stored in the application directory, while the user.config file is stored in the user's Documents and Settings folder. In a ClickOnce application, app.exe.config lives in the application directory inside of the ClickOnce application cache, and user.config lives in the ClickOnce data directory for that application."
So you need to consider debug vs. non-debug mode, ClickOnce vs. non-ClickOnce, and possibly other factors I haven't managed to identify yet. Still, the multiple-config-file problem is significant primarily during development. It is typically not an issue after deployment because a user will not be able to make environmental changes that you as the developer can in Visual Studio.

Cleaning Up Settings Files
Figure 16. Cleaning Up Extraneous Settings Files: The numbered items show the procedure to "synchronize" (clean up) the various settings files that VS and the .NET framework create.
Visual Studio provides a convenient mechanism for cleaning up all the different manifestations of user.config. The easiest way to clean up any such differences is to open the Settings pane of your project properties and click the "Synchronize" button at the top. VS prompts you that it will erase any copies of the user.config file it finds (see Figure 16).

In Figure 16, there are just two extraneous files: the first is from a debugging execution, the second from a non-debugging execution, as discussed. However, I've seen up to 15 files show up in this list. In fact, one time I knew there was a stray copy out there though VS said there was not. I tried re-opening my project, re-opening VS, even rebooting, but it never did find the file. So I manually searched for the file and deleted it to clean it up.

Resetting User Settings
The previous section discussed cleaning up variations of the user-scoped configuration file user.config. But recall that there is also an application-scoped configuration file (app.exe.config) and re-synchronizing the two is useful at development time as well as after deployment. Visual Studio provides, as just mentioned, a button to throw away all versions of the user.config during development, so settings again all come from the app.exe.config. You could give the user of the deployed application the same flexibility and more. Here is how.

The .NET framework provides two methods to handle this. The first, Reload discards changes to the current session, (since the beginning of the program's execution), so that even if your program saves the configuration upon exit, it will not have any changes to save. The second method, Reset, rewrites the user configuration file to match the application's configuration file, effectively wiping out any changes in the user's copy. You can call either with a single line, as shown below:

   // To discard changes during the current session:
   // To restore settings to "out-of-the-box":
Maintaining Settings Between Program Versions
The astute reader will have noticed that, because the program version number is part of the path to the user.config file, if you issue a new release to the program (i.e. increment the version number), then all previously stored user.config settings for each user will be lost. That is true, but Microsoft provided an elegant workaround for this. For any settings from the current version that match settings in the prior version, this routine will import them into the current version's user.config file:

   if (Properties.Settings.Default.UpgradeSettings)
       Properties.Settings.Default.UpgradeSettings = false;
You will also need to create the referenced setting (UpgradeSettings) and initialize it to True. Then the above code fragment will update the current user.config just the first time the new version is executed. In the process, it will change the persistent setting to false, so the condition in the if statement will never again evaluate to true. To test what this does, insert the above code in the sample program initialization (e.g. in the form constructor or load event handler). Run the application, saving some custom values for the various settings. Next, increment the program version number in Visual Studio to (for example), and re-run the program. You should find that your custom setting values are still present. Of course, that does not prove that this mechanism worked, because our custom values were there before! So for the skeptics among you, check your directory and you should now find a new directory with a new configuration file, as in:

   C:\Documents and Settings\...\\user.config
   C:\Documents and Settings\...\\user.config
Note that when you change version numbers you are again proliferating copies of the user.config file, but Visual Studio's synchronize function is clever enough to know this. Now that we have introduced a new version, three files show up when synchronizing: the earlier two from version and the new one from version (see Figure 17)

Figure 17. Synchronizing After an Upgrade: Visual Studio's Synchronize function can also clean up extraneous config file copies on your development machine after a version number change to your application.
Final Thoughts
Bound settings provide a mechanism for automatically updating persistent setting values across program invocations. They are ideally suited for simple bound properties such as splitter positions, control sizes, visibility, and other object properties that users manipulate visually. But you may not actually want to bind all of your settings. For example, consider an Options dialog, where users may be setting things such as the Text property of a text field, or the Checked property of a radio button, etc., just like in the sample application earlier. As in any conventional Windows program, you probably want to do two things in this Options dialog where binding could get in the way—provide a Cancel button and perform validation on values the user has entered. For both of these, you may need to discard any changes rather than saving them. That is more easily done, though, from the opposite perspective: save the changes if they warrant it; otherwise do nothing and they will be discarded, as in this code:

   private void OptionForm_Load(object sender, EventArgs e)
      uiAppTextBox.Text = Properties.Settings.Default.UIAppPath;
      logPathTextBox.Text = Properties.Settings.Default.LogFilePath;
   private void okButton_Click(object sender, EventArgs e)
      Properties.Settings.Default.UIAppPath = uiAppTextBox.Text;
      Properties.Settings.Default.LogFilePath = logPathTextBox.Text;
As the code fragment illustrates, you load the form values from settings during the Form_Load event. Store the form values into settings only when the user presses OK, which will presumably be enabled only when form validation succeeds, and do nothing if users press the Cancel button. For more details on form validation see my article Exploring Secrets of Form Validation ).

Whether bound or unbound, you now have the tools to create application settings that persist across invocations of a program, even across new versions of your program. This removes the tedium—as you have certainly encountered yourself in some programs—of having to re-establish your preferred settings each time you execute the program.

Michael Sorens is a freelance software engineer, spreading the seeds of good design wherever possible, including through his open-source web site, teaching (University of Phoenix plus community colleges), and writing (contributed to two books plus various articles). With BS and MS degrees in computer science and engineering from Case Western Reserve University, he has worked at Fortune 500 firms and at startups, using C#, SQL, XML, XSL, Java, Perl, C, Lisp, PostScript, and others. His favorite project: designing and implementing the world's smallest word processor, where the medium was silicon, the printer "head" was a laser, and the Declaration of Independence could literally fit on the head of a pin. You can discuss this or any other article by Michael Sorens here.
Email AuthorEmail Author
Close Icon
Thanks for your registration, follow us on our social networks to keep up-to-date