Exploring the TimeCheck Application (Part 7)

I’m sure a number of you were wondering when I was going to get back to the TimeCheck application (if ever). I always want to be sure that I provide good material for you and things have been hectic this summer. Of course, we were off on vacation for a while, then there was my little equipment failure, and the drought has brought problems of it’s own. Now we’re back to working on this example.

If you’ll remember from the last post, Exploring the TimeCheck Application (Part 6), this application actually manages several databases and they’re in different places. In fact, this example will eventually handle more data situations than any other example I’ve ever written for use as a teaching aid—books simply haven’t offered me enough space to explore this sort of application before. This post will discuss the user settings that were described in Part 6. To start this part of the example, you must add a class to the application using the following steps:

 

  1. Right click the TimeCheck entry in Solution Explorer and choose Add | New Item from the context menu. You’ll see the Add New Item – TimeCheck dialog box shown here.
    TimeCheck0701
  2. Highlight the Class template in the middle pane. You see a description of the purpose for this template in the right pane.
  3. Type UserSettings.cs in the Name field and click Add. Visual Studio adds the new class file for you and creates some basic code for it.


Let’s begin at the top of the new class file you’ve created. In order to support the code for this example, you must add some using statements to the top of the UserSettings class file as shown here.

// Add these statements to your project for XML support.
using System.Xml;
using System.Xml.Serialization;
 
// Add this statement for file system support.
using System.IO;

The user’s settings for this example reside on the user’s hard drive. The application must know where to find the network drive with the group settings. In addition, each user can select a default task and a default project. Consequently, the user settings consist of these three properties.

// Contains the user's project preference.
public String DefaultProject { get; set; }
 
// Contains the user's task preference.
public String DefaultTask { get; set; }
 
// Contains the location of the data on the network.
public String NetworkPath { get; set; }

You don’t need to perform any special checks in this case because the application will perform them automatically and the user won’t be able to enter anything that will crash the application. Using this shortcut method of creating properties works just fine for this part of the example.

The default constructor for any class doesn’t do anything. Yes, C# supplies a constructor because you absolutely must provide one, but it’s useless in this case because you need to initialize the properties. The next step is to create a new default constructor that performs the tasks needed as shown here.

// Create a default constructor.
public UserSettings()
{
   // Create default properties.
   this.DefaultProject = "None";
   this.DefaultTask = "None";
   this.NetworkPath = "None";
}

Now we get to the meat of the class. The class must provide static methods for getting and setting the user settings. These static methods help the application work with existing data more efficiently and reduce the need to create unnecessary objects that simply use memory. The following code shows the static methods used to get and set user settings for this application.

// Obtains the user settings.
public static UserSettings GetUserSettings()
{
   // Define a path to the user settings.
   String UserPath =
      Environment.GetFolderPath(
         Environment.SpecialFolder.ApplicationData) + @"\TimeCheck";
 
   // When the path doesn't exist, the user hasn't been set up to
   // use the application, so you need to exit.
   if (!Directory.Exists(UserPath))
      return null;
 
   // Check for the file.
   if (!File.Exists(UserPath + @"\AppData.Config"))
      return null;
 
   // Create an XML serializer.
   XmlSerializer DataReader = new XmlSerializer(typeof(UserSettings));
 
   // Define a data stream.
   StreamReader Input = new StreamReader(UserPath + @"\AppData.Config");
 
   // Load the settings.
   UserSettings Settings = new UserSettings();
   Settings = (UserSettings)DataReader.Deserialize(Input);
   Input.Close();
 
   // Return the user settings to the caller.
   return Settings;
}
 
public static void SetUserSettings(UserSettings Settings)
{
   // Define a path to the user settings.
   String UserPath =
      Environment.GetFolderPath(
         Environment.SpecialFolder.ApplicationData) + @"\TimeCheck";
 
   // When the path doesn't exist, the user hasn't been set up to
   // use the application, so you need to create the path.
   if (!Directory.Exists(UserPath))
      Directory.CreateDirectory(UserPath);
 
   // Check for the file. If it doesn't exist, create it.
   if (!File.Exists(UserPath + @"\AppData.Config"))
   {
      FileStream NewFile = File.Create(UserPath + @"\AppData.Config");
      NewFile.Close();
   }
 
   // Create an XML serializer.
   XmlSerializer DataWriter = new XmlSerializer(typeof(UserSettings));
 
   // Define a data stream.
   StreamWriter Output = new StreamWriter(UserPath + @"\AppData.Config");
 
   // Load the settings.
   DataWriter.Serialize(Output, Settings);
   Output.Close();
}

In both cases, the code begins by obtaining the location of the user’s data store on the local hard drive by calling Environment.GetFolderPath(). The Environment.SpecialFolder.ApplicationData enumeration contains the location of the root of the data store. This application stores its data in the \TimeCheck subdirectory.

The next step is to ensure that the \TimeCheck subdirectory actually exists and that it contains a file named AppData.Config. When the class is getting the settings, a failure to find either the directory or the file means that there is no data to obtain, so GetUserSettings() returns a value of null. However, when saving the user settings, the SetUserSettings() method must create the required directory and file when it doesn’t exist.

At this point, there is either data to get or data to save. The data is stored in XML format. In order to work with XML data, you create an XmlSerializer to either serialize (change the properties into XML) or deserialize (change the XML into properties) the data. A StreamReader or StreamWriter provides read or write access to the physical file. The code can now serialize the local data to the XML file or deserialize the data from the XML file and store it locally.

Notice that the example is careful to close files immediately after use. Even though it may seem that the application would perform this task automatically, it does so only after you exit the method and may not close it immediately. In the meantime, an application error could potentially cause damage to the data. In addition, if the application must create the AppData.Config file, the application will register an access error when you try to reopen the file for reading.

Well, that’s it for the UserSettings class. Next week will begin looking at the GroupSettings class (which is considerably more complicated than this class was). In the meantime, let me know if you have any questions about this part of the application at John@JohnMuellerBooks.com. You can see the next post in this series at Exploring the TimeCheck Application (Part 8).