Pausing the C++ Example Output

A number of readers have written to ask about running the example code in C++ All-In-One Desk Reference For Dummies. The book shows that the examples pause so you can see the output, yet a few people experience problems getting the example to pause as shown in the book. Let’s take the first book example. When you run the example, you should see a command window similar to the one shown here open.

ExamplePause01

This is how the command window looks in Windows, but if you use some other operating system, you should see something similar. Notice that the output is paused. Pressing any key will cause the window to disappear and the example to end. The purpose of pausing the output is so that you can see the result.

There are two common reasons that people aren’t seeing the output pause. The most common reason is that the example is run in debug mode. Make sure you click Run or choose Build | Run to execute the example. If you click Debug/Continue or choose Debug | Start/Continue instead, the example will execute without pausing and you won’t see the output. Of course, you can always set a breakpoint to get the example to pause, but most people simply want to see the example output which means running the example, rather than debugging the example.

A less common cause is that the project environment is configured incorrectly. Normally the Code::Blocks environment automatically pauses when you run the example. However, it’s possible to set Code::Blocks not to pause. Some readers inadvertently change this setting while exploring the environment. In order to check this setting, choose Project | Properties to display the Project Targets/Options dialog box. Select the Build Targets tab and you see the dialog box shown here.

ExamplePause02

Notice the Pause When Execution Ends option. If this option is cleared, the example won’t pause when you run it. Make sure this option is checked and click OK. The projects that come with the downloadable code all have this setting set up correctly.

Of course, there could always be other reasons why the examples aren’t pausing. Please let me know if you have any problems setting the example output. It’s essential that you be able to see the example output as you follow along in the book to understand how C++ works. Send your queries about this (or any other book-specific) topic to John@JohnMuellerBooks.com. I always want to ensure you have the best possible experience when using my books.

 

Beta Readers Needed for a C++ Book Update

A lot of people (892 so far) have sent me e-mail about C++ All-In-One Desk Reference For Dummies. A few more have commented online and some of you have written reviews as well. In fact, it’s one of the more popular books I’ve written to date. I keep statistics on every message I receive so that I can better understand how a book is succeeding and how it has failed. For example, I know that most readers like CodeBlocks, but they wish my book used a newer version of the product. Don’t worry, the updated book will use the latest CodeBlocks release. I’ve also discovered that none of you apparently uses the Microsoft-specific materials in Book VII—this information will be replaced with something you do want in this next edition. All of the examples will also be tested on the Mac, Windows, and Linux platforms to ensure it works well no matter what platform you use.

Naturally, everything that requires an update in the book will get updated. I’ll also look at the latest specification and determine what sorts of new topics might interest you. However, I really do need your input. Only you actually know what you’d like to see in an updated book and I’m determined to find out what that is. You can help me shape the updated book by writing me at John@JohnMuellerBooks.com with your ideas and suggestions. Don’t wait until later, write me today!

Even better, why don’t you become a beta reader? You’re under no obligation to read the entire book. I’ll send you the chapters as I write them and it’s entirely up to you to send them back with comments. I’ll never bug you for comments. Any comments you do send will receive careful consideration and I’ll modify everything I can as I go through the Author Review (AR) process where I incorporate editor comments as well. By being a beta reader, you get to shape the book content and get precisely the book you’d like. Make sure you sign up to be a beta reader today.

As part of being a beta reader, your name will appear in the book Acknowledgements (unless you specially ask that I don’t provide it). However, one of the bigger benefits to you is that you get to read the book free of charge and gain the skills that it can provide for you. Imagine what learning a new programming language can do for your career. Even if you don’t need C++ for work, you can use what you gain to create applications for your own needs and to obtain a better understanding of how computers work.

Many people are concerned that they don’t qualify to be a beta reader, but that’s not the case at all. You don’t need to know anything about C++ or programming for that matter to be a beta reader for this book. All you need is a desire to learn how to use C++ to develop simple applications. It really is that simple.

 

Resetting Your CodeBlocks Configuration

Quite a few people have written to me about issues they have with C++ All-In-One Desk Reference For Dummies that involve getting CodeBlocks up and running. The posts in the C++ All-in-One for Dummies archive normally provide everything needed to get the compiler up and running. I even provide posts on using the 10.05 version of the product, should you wish to upgrade. However, there are rare times when no matter how much you try, you simply can’t get the compiler to work.

One technique I haven’t really covered until now is to reset the CodeBlocks configuration. The problem with this approach is that it resets all of your settings, not just those that could be in error. This is the reason that I’ve taken a more measured approach to helping readers through problems until now. My concern is that resetting everything will actually cause more problems and end up confusing some readers, so you really do want to try those other posts first. That said, there are situations where resetting CodeBlocks is the only course of action that will work.

To reset your settings, open your copy of CodeBlocks. Choose Settings | Compiler and Debugger. You see the Compiler and Debugger Settings dialog box shown here.

ResettingYourCodeBlocksConfiguration01

Click Reset Defaults. This action will reset all of the defaults so that they match the initial installation configuration unless you have created a default of your own. Make absolutely certain that the Selected Compiler field shows GNU GCC Compiler as shown in the figure and then click OK. Close and then reopen CodeBlocks before you test your configuration.

Let me know if you have any questions about this procedure at John@JohnMuellerBooks.com. It’s always my goal to make my books as useful to you as possible.

CodeBlocks 12.11 Update (Final, for Now)

I had promised in my CodeBlocks 12.11 Update (Reminder) post to let you know about update information for the CodeBlocks 12.11 version. Because I received only a few messages from interested readers and all but one of them have said that the 10.05 posts are working fine for them, I’ve decided not to provide a new series of posts at this time. Of course, it may be that other people using C++ All-In-One Desk Reference For Dummies will experience problems and I’ll need to provide those updates after all. Nothing is ever final here—only final until it needs to change.

My main goal is to ensure that readers have everything needed to use my book successfully. At the moment, it appears that they do, but please don’t feel your input isn’t valuable to me. Always feel free to write me about issues you have with my books. Whenever possible, I’ll provide the information you need to use my books successfully right here on this blog so you can access the information whenever you need it. You should always check my blog as your first resource for answering questions.

If there is any additional input on this particular issue, please feel free to write me at John@JohnMuellerBooks.com. If I later find that a series of 12.11 posts is necessary, I’ll be more than happy to write them. In the meantime, keep those e-mails coming! I really enjoy hearing from you .

 

CodeBlocks 12.11 Update (Reminder)

Last week I wrote a post entitled, “CodeBlocks 12.11 Update.” So far, I’ve received only a few e-mails about the update. However, I’m still receiving e-mail on the topic and I want to be sure everyone is heard. If you haven’t had a chance to write me yet about the CodeBlocks 12.11 update and whether it affects you when working with my book, please contact me at John@JohnMuellerBooks.com.

Part of the reason I ask about these updates is that I like to track how many people are using a particular update with my books. It helps me build a picture of my readers so that I can serve their needs better. The information also helps me understand how quickly people update particular products so that I have a better idea of when to ask publishers about a book update.

Often, I’m not successful in obtaining an update from the publisher unless there is a compelling reason to write one. That’s where you come in. Readers have provided me with more good reasons to provide an update than any other source I use for determining when one is needed. Your input really does matter, so please keep those e-mails coming. I plan to provide a post that will let you know whether I’ll write update posts for CodeBlocks 12.11 for C++ All-In-One Desk Reference For Dummies next week Monday.

CodeBlocks 12.11 Update

A number of readers have recently begun writing me about issues with CodeBlocks 12.11 with the example code for my book. I originally wrote C++ All-In-One Desk Reference For Dummies around the 8.02 release of CodeBlocks, which is still available at http://www.codeblocks.org/downloads/5. Just look halfway down the page and you’ll find the download you need. I still highly recommend that you use the 8.02 release with the book, simply because the newer product versions introduce compatibility issues that make it necessary for you to modify the example code in certain ways.

However, I also understand your desire to work with the latest version of the product. That’s why I wrote a series of posts about the 10.05 release of CodeBlocks and what you would need to do to make it work with the examples. Unfortunately, another group of readers has complained that the instructions are too hard to follow. The fix, of course, is to use the 8.02 release if you find that the 10.05 release isn’t working well for you. There isn’t much else I can do at this point to make things easier (although, I’m always open to your ideas).

What I’d like to know is how many readers are actually using the 12.11 release and whether the instructions for the 10.05 release also work in this case. My initial take is that there are at least a few readers who have found they need version 12.11-specific instructions, which would mean another series of blog posts to address changes to the examples. I’d start with the original code and wouldn’t force you to make multiple updates. As you might imagine, testing every example in the book and writing an update procedure for it (if necessary) will take time, so I want to be sure that there is interest in this series of posts.

If you have an opinion on this topic, please let me know. I’d like to determine how much interest there is in a series of posts that describe code changes needed to make my book work with the 12.11 update. Please contact me at John@JohnMuellerBooks.com with your ideas and questions.

 

Exploring the TimeCheck Application (Part 14)

Today we’ll wrap up the discussion about frmConfigure. If you remember from the Exploring the TimeCheck Application (Part 13) post, we’ve finished up the major configuration controls, but there are three areas that you still need to know about:

 

  • Constructors
  • Form Loading
  • Form Closing


All three of these elements appear in this post. Let’s begin with the constructors. The application can either create frmConfigure by defining a blank form or by passing information needed to populate the form with data. The following code shows both of the constructors needed to fulfill these tasks:

// Create a variable to hold the user data.
public UserSettings UserData;
 
// Create a variable to hold the group data.
public GroupSettings GroupData;
 
public frmConfigure()
{
   // Perform the default configuration.
   InitializeComponent();
 
   // Check for the user data.
   if (UserData == null)
 
      // Initialize the user data.
      UserData = new UserSettings();
 
   // Check for the group data.
   if (GroupData == null)
 
      // Initialize the group data.
      GroupData = new GroupSettings();
 
   // Fill the administrator provided task and project lists.
   FillLists();
}
 
public frmConfigure(UserSettings UserData, GroupSettings GroupData)
{
   // Perform the default configuration.
   InitializeComponent();
 
   // Check for the user data.
   if (UserData == null)
 
      // Initialize the user data.
      this.UserData = new UserSettings();
 
   // Otherwise, use the supplied settings.
   else
      this.UserData = UserData;
 
   // Check for the group data.
   if (GroupData == null)
 
      // Initialize the group data.
      this.GroupData = new GroupSettings();
 
   // Otherwise, use the supplied settings.
   else
      this.GroupData = GroupData;
 
   // Fill the administrator provided task and project lists.
   FillLists();
}

In both cases, the constructor must create the UserData and GroupData local variables that you’ve seen used for so many purposes in the application. When the caller supplies the required data, frmConfigure uses it. Otherwise, frmConfigure creates new, blank variables that the application can fill with data. No matter how the data are supplied, the constructors call FillLists() to fill the user interface with data so that the user can see the settings (if any).

The form isn’t completely populated with data, nor is it stable at this point. The application raises the FormLoad() event at some point, which calls this event handler:

private void frmConfigure_Load(object sender, EventArgs e)
{
   // When there is user data to use, display it.
   if (UserData != null)
   {
      // Configure the form fields.
      cbProjectName.SelectedText = UserData.DefaultProject;
      cbWorkType.SelectedText = UserData.DefaultTask;
      txtNetLocation.Text = UserData.NetworkPath;
      chkCustomProject.Checked = GroupData.CustomProject;
      chkCustomWork.Checked = GroupData.CustomTask;
   }
}

At this point, the form is ready for the user to interact with. We’ll be looking at some additional code later to handle the requirements for working with administrators, versus standard users. For now, just consider that the form is ready to use. The user does whatever he/she needs to do, and then clicks either Cancel or OK to close the form. Now, some actions automatically save data because you simply don’t want to take a chance that the user will close the form in some other way and lose settings. However, it’s important to save the data one last time when the user clicks OK to ensure absolutely every change appears on disk. That’s the purpose of the two event handlers shown here.

private void btnCancel_Click(object sender, EventArgs e)
{
   // Close this dialog box.
   Close();
}
 
private void btnOK_Click(object sender, EventArgs e)
{
   // Save the changes to the user data.
   if (cbProjectName.SelectedText != "")
      UserData.DefaultProject = cbProjectName.SelectedText;
   if (cbWorkType.SelectedText != "")
      UserData.DefaultTask = cbWorkType.SelectedText;
   UserData.NetworkPath = txtNetLocation.Text;
   UserSettings.SetUserSettings(UserData);
 
   // Save the changes to the group data.
   GroupData.CustomProject = chkCustomProject.Checked;
   GroupData.CustomTask = chkCustomWork.Checked;
   GroupData.ProjectList.Clear();
   foreach (String Item in lstStandardProjects.Items)
      GroupData.ProjectList.Add(new Project(Item));
   GroupData.TaskList.Clear();
   foreach (String Item in lstStandardTasks.Items)
      GroupData.TaskList.Add(new Task(Item));
   GroupSettings.SaveSettings(GroupData);
 
   // Close the dialog box.
   Close();
}

As you can see, clicking Cancel simply closes the form. However, when you click OK, the application saves the data as it appears on screen. This is important because it’s possible that things could have gotten out of sync as the user interacted with the form. The result of the btnOK_Click() event handler is that the data on disk appears precisely the same as the user saw it on screen.

At this point, you have a basic frmConfigure completed. The next post will focus on the code used to make frmMain work. In the meantime, let me know if you have any questions about any of the frmConfigure code at John@JohnMuellerBooks.com.

 

Exploring the TimeCheck Application (Part 13)

The previous post (Exploring the TimeCheck Application (Part 12)) discussed how to manage the project and task entries that the user needs to sign into the system. In this post, you see the code used to manage the remaining configuration options on frmConfigure. The last administrator-specific configuration options are the Allow Custom Project and Allow Custom Task Entry check boxes. Checking these two boxes will allow the user to type non-standard entries for signing into the system. Otherwise, the user must sign in using the administrator-provided options. Providing separate configuration options makes sense because there are times when you want to control these features individually. Here is the simple code used for the check box CheckChanged event handlers.

private void chkCustomProject_CheckedChanged(object sender, EventArgs e)
{
   // Modify the status of the group data to match the
   // checked status of the checkbox.
   GroupData.CustomProject = chkCustomProject.Checked;
 
   // Save the data to disk.
   GroupSettings.SaveSettings(GroupData);
}
 
private void chkCustomWork_CheckedChanged(object sender, EventArgs e)
{
   // Modify the status of the group data to match the
   // checked status of the checkbox.
   GroupData.CustomTask = chkCustomWork.Checked;
 
   // Save the data to disk.
   GroupSettings.SaveSettings(GroupData);
}

As you can see, the event handlers simply make a change to the GroupData properties and then save GroupData to disk. Other parts of the application read the GroupData information and use it to configure the interface as appropriate. As with other administrator-level settings, you’ll see how to hide these options in a later post. For now, all you’re trying to do is create a working application.

One of the configuration options, Network Location, is administrator-specific, even though the application stores it as part of the user settings. Hiding the setting from view is helpful, but realistically, this is one option that the user can change without permission by editing the configuration file directly. Most users won’t have the required skills, but you need to be aware of the potential for problems with this one setting. Here is the code used to set the network location that is used to provide a pointer to group data on the server.

private void btnNetSelect_Click(object sender, EventArgs e)
{
   // Display the network location selector.
   if (NetworkSelect.ShowDialog(this) == DialogResult.OK)
   {
 
      // Place the selected location in the textbox.
      txtNetLocation.Text = NetworkSelect.SelectedPath;
 
      // Save the data to disk.
      UserData.NetworkPath = NetworkSelect.SelectedPath;
      UserSettings.SetUserSettings(UserData);
   }
}

Notice that the application shows the NetworkSelect dialog box to the user. If the user chooses a new network location and clicks OK, the application will change the network location in the txtNetLocation text box and also saves the data to disk.

The last two configuration options are user-specific, so you always see them displayed on screen. The user needs to be able to select a default project and task. Doing so makes the task of logging into the system easier and faster. Anything that speeds user activities will only give the reader more reasons to use your application. Here is the code used for these last two options.

private void cbProjectName_SelectedIndexChanged(object sender, EventArgs e)
{
   // Modify the user data to reflect the change
   // in default project selection.
   UserData.DefaultProject = cbProjectName.SelectedItem.ToString();
 
   // Save the data to disk.
   UserSettings.SetUserSettings(UserData);
}
 
private void cbWorkType_SelectedIndexChanged(object sender, EventArgs e)
{
   // Modify the user data to reflect the change
   // in default task selection.
   UserData.DefaultTask = cbWorkType.SelectedItem.ToString();
 
   // Save the data to disk.
   UserSettings.SetUserSettings(UserData);
}

As in other cases in this post, the application stores the information in the appropriate place and then stores it on disk. At this point, you have all of the code required to configure the application for use. Next week, we’ll discuss the remaining code used to make frmConfigure work. Let me know if you have any questions about this segment of the code at John@JohnMuellerBooks.com.

 

Exploring the TimeCheck Application (Part 12)

Last week, in the Exploring the TimeCheck Application (Part 11) post, you discovered some of the requirements for making the Add, Edit, and Delete buttons for the Standard Project Entries and Standard Tasks lists work. This addition included creating a new dialog box so that the user could provide the information required by the lists. Now that you have the basics down, let’s look at the code for performing the task. This first bit of code performs tasks with projects.

private void btnProjAdd_Click(object sender, EventArgs e)
{
   // Create the project dialog box.
   frmProjectTask ThisProject = new frmProjectTask();
 
   // Display the project dialog box on screen.
   if (ThisProject.ShowDialog(this) == DialogResult.OK)
   {
      // Create a new project and add it to the list.
      Project NewProject = new Project();
      NewProject.Name = ThisProject.txtProject.Text;
      GroupData.ProjectList.Add(NewProject);
      GroupSettings.SaveSettings(GroupData);
   }
 
   // Update the list information.
   ClearLists();
   FillLists();
}
 
private void btnProjEdit_Click(object sender, EventArgs e)
{
   // Make sure the user has selected a value to edit.
   if (lstStandardProjects.SelectedIndex == -1)
   {
      // Display a message box telling the user to select an item.
      MessageBox.Show("You must select an item to edit.",
         "Selection Error", MessageBoxButtons.OK,
         MessageBoxIcon.Error);
 
      // Exit the event handler.
      return;
   }
 
   // Create the project dialog box.
   frmProjectTask ThisProject = new frmProjectTask("Edit a Project",
      lstStandardProjects.SelectedItem.ToString(), "&Edit");
 
   // Display the project dialog box on screen.
   if (ThisProject.ShowDialog(this) == DialogResult.OK)
   {
      // Edit the existing project name and save it to disk.
      GroupData.ProjectList[lstStandardProjects.SelectedIndex].Name
         = ThisProject.txtProject.Text;
      GroupSettings.SaveSettings(GroupData);
   }
 
   // Update the list information.
   ClearLists();
   FillLists();
}
 
private void btnProjDelete_Click(object sender, EventArgs e)
{
   // Make sure the user has selected a value to delete.
   if (lstStandardProjects.SelectedIndex == -1)
   {
      // Display a message box telling the user to select an item.
      MessageBox.Show("You must select an item to delete.",
         "Selection Error", MessageBoxButtons.OK,
         MessageBoxIcon.Error);
 
      // Exit the event handler.
      return;
   }
 
   // Remove the selected project from the list.
   GroupData.ProjectList.RemoveAt(lstStandardProjects.SelectedIndex);
   GroupSettings.SaveSettings(GroupData);
 
   // Update the list information.
   ClearLists();
   FillLists();
}

The btnProjAdd_Click() event handler begins by creating the default frmProjectTask object, ThisProject. It then displays the dialog box. When the user clicks OK, the code creates a new Project entry, NewProject, adds the project name to it from ThisProject.txtProject.Text, adds the entry to GroupData.ProjectList, and then saves GroupData. Now you know why ThisProject.txtProject.Text has to be public. There are other ways you could handle the situation, such as using a property, but this approach is faster and shouldn’t pose any security issues. You must save the changes by calling SaveSettings(). Because the list has changed, you must call ClearLists() to remove existing data from the lists and FillLists() to add the new data.

In most respects, the btnProjEdit_Click() event handler works like the btnProjAdd_Click() event handler. However, in this case, the user must choose an entry to edit, rather than create a new entry. When the user has failed to select an entry, the code displays an error message and exits. As before, the code displays the dialog box, but this time it changes the button and title bar text to reflect the difference in task. In addition, the code modifies the existing entry, rather than creating a new one. As before, you must call both ClearLists() and FillLists() to update the application.

Deleting an entry in the btnProjDelete_Click() event handler is the simplest of the three tasks. The code begins by verifying that the user has actually selected an entry and reacting if the user hasn’t. It then looks for the requested entry in the list and calls RemoveAt() to remove the entry from the list. A call to ClearLists() and FillLists() completes the process.

The three event handlers for tasks work much like those used for projects. Of course, the code makes additional changes to the frmProjectTask dialog box to ensure the user sees the right information. Here is the code for these three event handlers.

private void btnTaskAdd_Click(object sender, EventArgs e)
{
   // Create the project dialog box.
   frmProjectTask ThisTask = new frmProjectTask("Add a Task", "&Task");
 
   // Display the project dialog box on screen.
   if (ThisTask.ShowDialog(this) == DialogResult.OK)
   {
      // Create a new task and add it to the list.
      Task NewTask = new Task();
      NewTask.Name = ThisTask.txtProject.Text;
      GroupData.TaskList.Add(NewTask);
      GroupSettings.SaveSettings(GroupData);
   }
 
   // Update the list information.
   ClearLists();
   FillLists();
}
 
private void btnTaskEdit_Click(object sender, EventArgs e)
{
   // Make sure the user has selected a value to edit.
   if (lstStandardTasks.SelectedIndex == -1)
   {
      // Display a message box telling the user to select an item.
      MessageBox.Show("You must select an item to edit.",
         "Selection Error", MessageBoxButtons.OK,
         MessageBoxIcon.Error);
 
      // Exit the event handler.
      return;
   }
 
   // Create the task dialog box.
   frmProjectTask ThisTask = new frmProjectTask("Edit a Task",
      "&Task", lstStandardTasks.SelectedItem.ToString(), "&Edit");
 
   // Display the project dialog box on screen.
   if (ThisTask.ShowDialog(this) == DialogResult.OK)
   {
      // Edit the existing project name and save it to disk.
      GroupData.TaskList[lstStandardTasks.SelectedIndex].Name
         = ThisTask.txtProject.Text;
      GroupSettings.SaveSettings(GroupData);
   }
 
   // Update the list information.
   ClearLists();
   FillLists();
}
 
private void btnTaskDelete_Click(object sender, EventArgs e)
{
   // Make sure the user has selected a value to edit.
   if (lstStandardTasks.SelectedIndex == -1)
   {
      // Display a message box telling the user to select an item.
      MessageBox.Show("You must select an item to edit.",
         "Selection Error", MessageBoxButtons.OK,
         MessageBoxIcon.Error);
 
      // Exit the event handler.
      return;
   }
 
   // Remove the selected task from the list.
   GroupData.TaskList.RemoveAt(lstStandardTasks.SelectedIndex);
   GroupSettings.SaveSettings(GroupData);
 
   // Update the list information.
   ClearLists();
   FillLists();
}

As you can see, the differences aren’t all that much. Well, that’s it for this week. Please let me know if you have any questions at all about the code for these six event handlers. Next week we’ll begin looking at the code for some of the other configuration dialog box controls. In the meantime, contact me at John@JohnMuellerBooks.com with your questions and concerns. You can read the next segment in this series at Exploring the TimeCheck Application (Part 13).

 

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).