Exploring the TimeCheck Application (Part 11)

The focus of this post is frmConfigure, which you created in Exploring the TimeCheck Application (Part 3). In Exploring the TimeCheck Application (Part 10) you saw that this form has several lists that need to be maintained. This post begins the process of making the controls on this form functional. To begin with, there are two distinct lists for this application. The first contains projects that the user works on, while the second contains tasks that the user can perform with those projects. Each list requires that the administrator be able to add, edit, and delete entries, so there is a total of six buttons. In addition, any changes to these two lists necessarily modifies the user’s default selections. We’ll talk about the underpinnings for these six buttons today.

There are three essential tasks to consider: deleting, adding, and editing. Deleting an entry is a matter of removing it from the list and then re-displaying the list on screen. Adding and editing entries requires user input, which means you need another form, frmProjectTask, to the application using the same techniques you used earlier. The new form looks like this:

TimeCheck1101

You use the following settings to create this dialog box:

Control

Property

Value

Form1

(Name)

frmProjectTask

AcceptButton

btnAdd

CancelButton

btnCancel

FormBorderStyle

FixedDialog

Size

290, 110

Text

Add a Project

ToolTip1

(Name)

toolTip1

Button1

(Name)

btnAdd

AccessibleDescription

Add the new project to the list.

DialogResult

OK

Location

197, 12

Size

75, 23

TabIndex

1

Text

&Add

ToolTip on toolTip1

Add the new project to the list.

Button2

(Name)

btnCancel

AccessibleDescription

Exit without making a change.

DialogResult

Cancel

Location

197, 41

Size

75, 23

TabIndex

2

Text

&Cancel

ToolTip on toolTip1

Exit without making a change.

Label1

(Name)

lblProject

Location

12, 9

Size

40, 13

TabIndex

3

Text

&Project

TextBox1

(Name)

txtProject

AccessibleDescription

Contains the project text.

Location

12, 25

Modifiers

Public

Size

179, 20

TabIndex

0

ToolTip on toolTip1

Contains the project text.

In order to make frmProjectTask usable in the application, you need to define a number of constructors for it. These constructors make it possible to use the same basic form for all four needs:

  • Add a project
  • Edit a project
  • Add a task
  • Edit a task


In each case, you interact with the form in a slightly different way. The default setup lets you add a project to the list. In order to edit a project, you must supply the current project text, along with changing the dialog box title and the add button to appropriate values. The following code shows the four constructors used for the purpose.

public partial class frmProjectTask : Form
{
   // Add a project constructor.
   public frmProjectTask()
   {
      // Perform the default task.
      InitializeComponent();
   }
 
   // Edit a project constructor.
   public frmProjectTask(String Title, String Value, String ButtonText)
   {
      // Perform the default task.
      InitializeComponent();
 
      // Set the dialog box title.
      this.Text = Title;
 
      // Set the Accept button title.
      btnAdd.Text = ButtonText;
 
      // Provide a value for the textbox.
      txtProject.Text = Value;
  }
 
   // Add a task constructor.
   public frmProjectTask(String Title, String ValueName)
   {
      // Perform the default task.
      InitializeComponent();
 
      // Set the dialog box title.
      this.Text = Title;
 
      // Set the value label title.
      lblProject.Text = ValueName;
   }
    
   // Edit a task constructor.
   public frmProjectTask(String Title, String ValueName,
      String Value, String ButtonText)
   {
      // Perform the default task.
      InitializeComponent();
 
      // Set the dialog box title.
      this.Text = Title;
 
      // Set the value label title.
      lblProject.Text = ValueName;
 
      // Set the Accept button title.
      btnAdd.Text = ButtonText;
 
      // Provide a value for the textbox.
      txtProject.Text = Value;
   }
}

It’s actually possible to use the same constructor for all four tasks, but this approach makes it easier for the developer interacting with the form to create the form needed. Of course, it’s also nice to get by with a little less typing whenever possible.

Now that you have a form to use to get data from the user, it’s time to get back to frmConfigure. The previous post discussed a special method for filling the lists, FillLists(). It turns out that there is also a generic need for a method to clear the lists before filling them, so the application also has a ClearLists() method as shown here.

private void ClearLists()
{
   // Clear all of the lists on the dialog box.
   cbProjectName.Items.Clear();
   cbWorkType.Items.Clear();
   lstStandardProjects.Items.Clear();
   lstStandardTasks.Items.Clear();
 
   // Verify that the user's default selections are
   // still valid.
   if (!GroupData.ProjectList.Contains(new Project(UserData.DefaultProject)))
      UserData.DefaultProject = "None";
   if (!GroupData.TaskList.Contains(new Task(UserData.DefaultTask)))
      UserData.DefaultTask = "None";
 
   // Save the data to disk.
   UserSettings.SetUserSettings(UserData);
}

I’m sure some readers are wondering why I didn’t include this functionality with FillLists(), but there are genuinely times when you only need to fill an empty list. In addition, using two separate methods makes the purpose of each task clear and simple. Methods should focus on one task whenever possible to keep things simple. When you start making methods too complicated, debugging becomes a problem and it’s much harder for other developers to understand your code.

This part of the example starts out by clearing the lists. However, in clearing the lists you may actually end up clearing the user’s default selections as well. If the administrator has removed those default selections, the user will need a new default selection or the combo boxes will appear blank. The application sets the default value to “None” when the user’s original choice is no longer available.

Notice the use of the Contains() method to determine whether the default selection is still available. The Contains() method requires that you provide an object of the type that you want to use for comparison purposes. Our original version of the Project and Task classes doesn’t include a constructor that makes creating an object of this type easy, so this example adds it (more about this addition in a few moments).

The documentation doesn’t make it very clear that the Contains() method always returns false unless you implement it in the supporting class. However, you don’t implement it directly. What you do instead is provide a means for the List base class to make the determination based on an equality method, Equals(), that you provide. If you remember from Exploring the TimeCheck Application (Part 8) I created simple forms of the Project and Task classes for the sake of discussion. It wasn’t necessary to know about the Contains() method at the time and would have only confused matters. In order to make the Contains() method usable, you must add the IEquatable interface and the Equals() method. Here is the code used for the updated version of the Project class.

// Contains a single standard project name.
[Serializable()]
public class Project : IEquatable<Project>
{
   // Default a default constructor.
   public Project()
   {
   }
 
   // Define a constructor that accepts a project name.
   public Project(String ProjectName)
   {
      this.Name = ProjectName;
   }
 
   // Define a method for checking equality.
   public Boolean Equals(Project OtherProject)
   {
      // Verify the Name property values are equal.
      if (this.Name == OtherProject.Name)
         return true;
      else
         return false;
   }
 
   // Define the project property.
   public String Name { get; set; }
}

As you can see, the updated Project class contains a new constructor that accepts as project name as input. It then returns an object that includes the object name. The class also now includes an Equals() method that accepts a Project object as input and returns true or false based on the equality of the two project Name property values. However, the application won’t even use the Equals() method unless you also include the IEquatable<Project> entry. Even though this looks simple, a lot of developers encounter problems with it because the documentation doesn’t make the required implementation clear.

The Task class requires similar additions. You need to add the constructor, the interface reference, and the Equals() method as shown here.

// Contains a single standard task name.
[Serializable()]
public class Task : IEquatable<Task>
{
   // Create a default task constructor.
   public Task()
   {
   }
 
   // Create a task construtor that accepts a string.
   public Task(String TaskName)
   {
      this.Name = TaskName;
   }
 
   // Define a method for checking equality.
   public Boolean Equals(Task OtherTask)
   {
      // Verify the Name property values are equal.
      if (this.Name == OtherTask.Name)
         return true;
      else
         return false;
   }
 
   // Define the task property.
   public String Name { get; set; }
}

That’s it for this week. Next week we’ll start discussing the button event handlers. In the meantime, let me know if you have any questions about these important code additions or want to know why I covered them as I did in these posts (sometimes it’s important to know why something is done in a certain order). Let me know about your questions and concerns at John@JohnMuellerBooks.com. You can see the next post in this series at Exploring the TimeCheck Application (Part 12).

Author: John

John Mueller is a freelance author and technical editor. He has writing in his blood, having produced 99 books and over 600 articles to date. The topics range from networking to artificial intelligence and from database management to heads-down programming. Some of his current books include a Web security book, discussions of how to manage big data using data science, a Windows command -line reference, and a book that shows how to build your own custom PC. His technical editing skills have helped over more than 67 authors refine the content of their manuscripts. John has provided technical editing services to both Data Based Advisor and Coast Compute magazines. He has also contributed articles to magazines such as Software Quality Connection, DevSource, InformIT, SQL Server Professional, Visual C++ Developer, Hard Core Visual Basic, asp.netPRO, Software Test and Performance, and Visual Basic Developer. Be sure to read John’s blog at http://blog.johnmuellerbooks.com/. When John isn’t working at the computer, you can find him outside in the garden, cutting wood, or generally enjoying nature. John also likes making wine and knitting. When not occupied with anything else, he makes glycerin soap and candles, which comes in handy for gift baskets. You can reach John on the Internet at John@JohnMuellerBooks.com. John is also setting up a website at http://www.johnmuellerbooks.com/. Feel free to take a look and make suggestions on how he can improve it.