Enabling user preferences

The users of our applications are likely to be very different to each other, or at least have their individual preferences. One user may prefer to work in one way, while another may have different preferences. Providing the ability for them to customize the application to suit the way they work will increase the usability of the application for them.

This may relate to the View that they prefer to see when the application starts, or to which particular options in each View that they prefer to use, or even to the size and position of the application when it was last used. There are any number of preferences that we can offer each user.

Luckily, we can offer this customization functionality with minimal work, as the .NET Framework provides us with settings files for just this purpose. These settings can either have application or user scope and can be mixed and matched in each settings file.

Application settings are the same for each user and are suited to storing configuration settings, such as email server details or credentials. User settings can be different for each user and are suited to the kind of personal customizations just discussed.

Typically, the startup project will already have a settings file named Settings.settings. It can be found by opening the Properties folder in the Solution Explorer in Visual Studio, and opened by double-clicking on it. Alternatively, you can right-click on the project in the Solution Explorer, select the Properties option, and then select the Settings tab:

Settings files can also be added to other projects although they are not typically available by default. In order to add a settings file to another project, we first need to open the project properties by right clicking on the project in the Solution Explorer and selecting the Properties option.

In the project properties window, select the Settings tab and click the link that says This project does not contain a default settings file. Click here to create one. A settings file will be created within the project Properties folder in the Solution Explorer. We are then free to start adding our user preferences:

To add our custom settings, click a blank row in the settings file and enter the name, data type, scope, and default value of the setting. The name will be used in code, and so it cannot contain spaces. We can select our own custom data types, although whichever type we select must be serializable. The default value is the initial value that the setting will have before the user changes it.

Settings will usually be loaded upon application startup and saved just before application shutdown. As such, it is customary to attach event handlers to the Loaded and Closed events in the MainWindow.xaml.cs file, although we can also do it in the App.xaml.cs file if we have configured the application to use it. We can see a typical example here:

using System; 
using System.Windows; 
using CompanyName.ApplicationName.ViewModels; 
 
namespace CompanyName.ApplicationName 
{ 
  public partial class MainWindow : Window 
  { 
    public MainWindow() 
    { 
      InitializeComponent(); 
      Loaded += MainWindow_Loaded; 
      Closed += MainWindow_Closed; 
    } 
 
    private void MainWindow_Loaded(object sender, RoutedEventArgs e) 
    { 
      MainWindowViewModel viewModel = new MainWindowViewModel(); 
      viewModel.LoadSettings(); 
      DataContext = viewModel; 
    } 
 
    private void MainWindow_Closed(object sender, EventArgs e) 
    { 
      MainWindowViewModel viewModel = (MainWindowViewModel)DataContext; 
      viewModel.SaveSettings(); 
    } 
  } 
} 

We attach the two event handlers in the constructor, right after the components are initialized. In the MainWindow_Loaded method, we instantiate an instance of the MainWindowViewModel class, call its LoadSettings method, and set it as the window's DataContext property value.

In the MainWindow_Closed method, we access the instance of the MainWindowViewModel class from the DataContext property, but, this time, call its SaveSettings method. Now, let's see these methods in the MainWindowViewModel.cs file:

using CompanyName.ApplicationName.ViewModels.Properties;

...

public void LoadSettings() 
{ 
  Settings.Default.Reload(); 
  StateManager.AreAuditFieldsVisible =
    Settings.Default.AreAuditFieldsVisible; 
  StateManager.AreSearchTermsSaved = Settings.Default.AreSearchTermsSaved; 
} 
 
public void SaveSettings() 
{ 
  Settings.Default.AreAuditFieldsVisible = 
    StateManager.AreAuditFieldsVisible;
  Settings.Default.AreSearchTermsSaved = StateManager.AreSearchTermsSaved;
  Settings.Default.Save(); 
} 

The first thing that we need to do in the LoadSettings method is to call the Reload method on the default instance of the settings file. This loads the settings from the settings file into the Default object. From there, we set each settings property to its corresponding property that we created in our StateManager class, for use in the application.

Note that the values of each user's personal settings are not stored in the Settings.settings file. Instead, they are stored in their AppData folder, which is hidden by default. The exact file path can be found using the ConfigurationManager class, but to find it we'll need to add a reference to the System.Configuration DLL and use the following code:

using System.Configuration;

...

string filePath = ConfigurationManager.OpenExeConfiguration( 
  ConfigurationUserLevel.PerUserRoamingAndLocal).FilePath; 

In my case, that resolves to the following file path:

C:\Users\Sheridan\AppData\Local\CompanyName\  
  CompanyName.ApplicationNa_Url_0nu0qp14li5newll2223u0ytheisf2gh\
  1.0.0.0\user.config 

Note that the folder in the CompanyName folder is named using a particular identification number that relates to the current settings and application version. Over time and after making changes, new folders will appear here with new identification numbers, but this is all totally transparent to the users as their previous settings will be safely transferred.