Exploring the TimeCheck Application (Part 9)

The Exploring the TimeCheck Application (Part 8) post helped you explore the first part of the GroupSettings class. As promised, this week’s post will help you complete the GroupSettings class. Of course, one of the most essential tasks you perform with this sort of class is creating a new instance of the class as needed. In many cases, you can use the default constructor, which requires no input and produces no output. However, when working with settings, you need to create a constructor that sets the settings to a known state as shown here.

// Define a constructor to initialize default values.
public GroupSettings()
{
   CustomProject = false;
   CustomTask = false;
   ProjectList = null;
   TaskList = null;
}

Setting CustomProject and CustomTask to false makes sense because you don’t want the user to be able to create custom projects or tasks unless the administrator wants to allow it. You may have heard the phrase principle of least privilege used in regard to application security and this is an example of such a setup. The application assumes that the user doesn’t have the right to perform some special task unless the administrator specifically allows it.

What may concern some people is the setting of ProjectList and TaskList to null. When an administrator uses the application for the first time, there aren’t any projects or tasks assigned for the application, so that’s one reason to set these List objects to null. However, the second reason is to signal an error to the application. When a user starts the application and the application can’t find the network drive containing the group settings, it’s important to signal the application that an error has happened. This error won’t prevent the application from continuing, but it will prevent the user from logging in using standardized projects and tasks. As a result, the application must have some way of signaling this problem and dealing with it. You’ll see how this all works in a later post.

In addition to creating new GroupSettings objects, the class must be able to read and write GroupSettings to the network drive as an XML file. This one file contains the settings that everyone will use. There are many permutations to discuss with regard to this sort of file. For example, you need to consider whether multiple administrators could possibly modify the file at one time. If so, you need to implement some sort of locking strategy to ensure that only one administrator can open the file for modification at a time. Of course, you don’t want regular users making modifications to the file. For the purposes of clarity, I chose to make things as simple as possible. I’m assuming that there is only one administrator. In addition, I’m assuming that because of the interface modifications you’ll learn about in a later post, that only the administrator will have access to the interface elements required to make the changes. With these restrictions in mind, here is the code the example uses to save data to the file.

// Create a method for saving the settings.
public static void SaveSettings(GroupSettings Settings)
{
   // Obtain the network path.
   String NetworkPath = UserSettings.GetUserSettings().NetworkPath;
 
   // Exit when the application hasn't been configured yet.
   if (NetworkPath == null)
      return;
 
   // When the path doesn't exist, the group hasn't been set up to
   // use the application, so you need to create the path.
   if (!Directory.Exists(NetworkPath + @"\GroupData"))
      Directory.CreateDirectory(NetworkPath + @"\GroupData");
 
   // Check for the file. If it doesn't exist, create it.
   if (!File.Exists(NetworkPath + @"\GroupData\AppData.Config"))
   {
      FileStream NewFile = File.Create(
         NetworkPath + @"\GroupData\AppData.Config");
      NewFile.Close();
   }
 
   // Create an XML serializer.
   XmlSerializer DataWriter = new XmlSerializer(typeof(GroupSettings));
 
   // Define a data stream.
   StreamWriter Output = new StreamWriter(
      NetworkPath + @"\GroupData\AppData.Config");
 
   // Perform the data write.
   DataWriter.Serialize(Output, Settings);
 
   // Close the stream.
   Output.Close();
}

The code begins by creating a path variable that holds the location of the network storage location. This location is unique for an individual machine and is stored as part of the UserSettings (see Exploring the TimeCheck Application (Part 7)). When NetworkPath is null, it means that the application hasn’t been properly configured and there is nothing for the application to do, so it returns a null value to the caller. The application must also ensure that the GroupData folder exists and that it contains the AppData.Config file. When the AppData.Config file is missing, the application must create it.

 

Remember that this task takes place across a network connection. Normally, I’d include some error trapping code here, but left it out for the sake of clarity. A production version of this application will include the required error trapping and error handling code.


Once the application establishes that there is a file to use to hold the data, it creates an XmlSerializer object, DataWriter, and defines its output as type GroupSettings. The application then creates a StreamWriter to actually output the data and then serializes the data as XML in the output. This entire process is precisely the same one that is used for the UserSettings class, except that it happens across a network connection (with all of the potential for problems that a network connection can cause). In fact, I encourage you to compare this method to the one used for the UserSettings class.

Everyone will use the method to obtain the data from the file. You don’t need to implement much in the way of special handling because the file is read and immediately closed. If there are a lot of users to accommodate, you may possibly need to add file locking features to the application. However, since there is no modification of data taking place, allowing shared access works just fine in most cases. Here is the code used to read data from the AppData.Config file.

// Create a method for obtaining the current settings.
public static GroupSettings GetSettings()
{
   // Obtain the network path.
   String NetworkPath = UserSettings.GetUserSettings().NetworkPath;
 
   // Exit when the application hasn't been configured yet.
   if (NetworkPath == null)
      return null;
 
   // Check for the existence of the group settings file. If it
   // doesn't exist, exit.
   if (!File.Exists(NetworkPath + @"\GroupData\AppData.Config"))
      return null;
 
   // Create an XML deserializer.
   XmlSerializer DataReader = new XmlSerializer(typeof(GroupSettings));
 
   // Define a data stream.
   StreamReader Input = new StreamReader(
      NetworkPath + @"\GroupData\AppData.Config");
 
   // Perform the data read.
   GroupSettings GroupData = (GroupSettings)DataReader.Deserialize(Input);
 
   // Close the stream.
   Input.Close();
 
   // Return the data.
   return GroupData;
}

It’s a good idea to compare this method to the one used with the UserSettings class to read the user data. The basic concept is the same. The only difference is that this class works across a network connection, so the production version of the application will likely contain additional error trapping and error handling code.

Well, that’s it for this week. Next week we’ll discuss any remaining internal elements or possibly move on to a few of the user interface elements depending on what sorts of questions I receive about the application. In the meantime, please let me know about any questions or concerns you have about the application at John@JohnMuellerBooks.com. You can find the next post in this series at Exploring the TimeCheck Application (Part 10).