New Year's Task Manager

Description

  This article implements an application to keep track of your tasks you set out to complete
Arian Kulp's Blog

Difficulty: Easy
Time Required: 1-3 hours
Cost: Free
Software: Visual Basic or Visual C# Express Editions
Hardware:
Download:

 

Quit smoking. Exercise more. Spend more time with your family. These are all goals people might have for a new year. New Year's resolutions are easy to make, but notoriously difficult to stick to. Maybe what you need for the coming year is a nice little application to keep track of your tasks you set out to complete. Set a deadline to help prioritize the items, and to stimulate you to action. With the proper tool (and realistic goals!), you can have a successful year. In this In the Box installment, we take a look at a basic task list manager application to keep track of New Year's resolutions, or any other things you need to get done.

Downloadable code for this article is available from a link at the top of the page, for both C# and Visual Basic. You will need either the C# or Visual Basic version (or both) of Visual Studio 2005 Express Edition. These were released in early November, and are free for a year! Whether you are a student, hobbyist, or a seasoned professional, the Express editions are great tools to jumpstart your .NET code development.

This application has very little user interface associated with it. Its primary purpose is to alert you that a deadline has been reached for one or more tasks. A system tray icon (known as a notification icon) is used to indicate that the application is running, and to provide access to the minimalist main dialog. Double-clicking the icon brings up the dialog to manage your task list (add, modify, or remove them). Right-clicking the icon provides you with the option to exit.

At its core, the application is simple. The user interface dialog employs a CheckedListBox control to display the tasks. This control provides a list of items as in a traditional ListBox control, but also gives you a CheckBox control alongside each item. This makes it easy to offer an enable/disable, or select/unselect feature to items in your application. For a task list, the check box allows you to mark an item as complete. The control exposes the typical ListIndexChanged and other various mouse events, but also offers an ItemCheck event. This makes it easy to respond to an item being selected as a separate action from an item being checked or unchecked. Tasks can be added at any time using the Add button, and removed or modified using the Remove or Edit button respectively, assuming an item is selected.

Figure 1: The Main Task dialog

When a task is added or modified, the Task Details dialog appears. This form contains controls for the task name, due date, and completion status. The dialog is instantiated from the main form, and then displayed using the ShowDialog method. This blocks the main dialog until the child dialog is closed, thus simplifying the logic flow in the main dialog:

Visual Basic
Dim rowId As Integer = tasksCheckedListBox.SelectedIndex
Dim row As TasksDataSet.TasksRow = ds.Tasks(rowId)
 
details.CurrentRow = row
 
If details.ShowDialog() = Windows.Forms.DialogResult.OK Then
    UpdateDisplayWithRow(rowId, row)
End If
Visual C#
int rowId = tasksCheckedListBox.SelectedIndex;
TasksDataSet.TasksRow row = ds.Tasks[rowId];

details.CurrentRow = row;

if (details.ShowDialog() == DialogResult.OK)
{
       UpdateDisplayWithRow(rowId, row);
}

A call to the ShowDialog method not only blocks the main flow until the dialog is closed, but also returns a member of the DialogResult enumeration as well. When you create buttons in this dialog, you can set the DialogResult property to values such as OK, Yes, or Cancel. The Form Details dialog has OK and Cancel buttons with the DialogResult properties set accordingly. You still have the option of implementing the Click event handler in order to take an action before the dialog is dismissed. There is no need to explicitly close the form in the event handler with this property set.

Visual Basic
Private Sub dialogOKButton_Click(ByVal sender As Object, ByVal e As EventArgs) Handles dialogOKButton.Click
    Dim newRow As Boolean = False
    If row Is Nothing Then
        row = table.NewTasksRow()
        newRow = True
    End If
 
    row.TaskTitle = titleTextBox.Text
    row.TaskDueDate = taskDateTimePicker.Value.Date
    row.TaskComplete = taskCheckBox.Checked
    If newRow Then
        table.AddTasksRow(row)
    End If
End Sub

Visual C#

private void dialogOKButton_Click(object sender, EventArgs e)
{
   bool newRow = false;

    if (row == null)
    {
         row = table.NewTasksRow();
         newRow = true;
    }

    row.TaskTitle = titleTextBox.Text;
    row.TaskDueDate = taskDateTimePicker.Value.Date;
    row.TaskComplete = taskCheckBox.Checked;

    if (newRow)
    {
         table.AddTasksRow(row);
    }
}

As with the main form, the ControlBox property is set to False, thereby removing the minimize/maximize/close buttons, along with the icon/menu in the upper left corner.

Figure 2: The Task Details dialog

Once tasks are entered in the application, the data, encapsulated in a typed DataSet, is saved to a file using XML serialization. The final part is to alert you when a due date is reached. A Timer control with the Interval property set to 60000 is present on the main form. If the number looks big, don't worry: it's measured in milliseconds. This really means that a Tick event is sent from the control once each minute. Since the tasks are granular only to the day level, this seems more than sufficient.

Each time a Tick event is raised, the event handler iterates through the table of tasks searching for entries with a date prior to the current date, and in an incomplete state. Any such entries need to be displayed to the user. The title and due date are added to a string to be shown in a balloon tip. Because there may be more than one such task, a simple string isn't sufficient to hold the data. To be clear, you can append to a string as much as you need to; however, this isn't as efficient, as it triggers multiple memory allocations. The StringBuilder object allocates more space up-front and manages re-allocations more intelligently.

Visual Basic
Dim buffer As StringBuilder = New StringBuilder
 
' Iterate over each task in the DataSet
For Each row As TasksDataSet.TasksRow In ds.Tasks
    ' Compare date and completed flag
    If (DateTime.Now.Date > row.TaskDueDate) _
        AndAlso Not row.TaskComplete Then
 
        buffer.Append(row.TaskTitle).Append(", Due: ")
        buffer.AppendFormat("{0:d}", row.TaskDueDate).Append(vbLf)
    End If
Next
Visual C#
// StringBuilder is more efficient than string concatentation
StringBuilder buffer = new StringBuilder();

// Iterate over each task in the DataSet
foreach (TasksDataSet.TasksRow row in ds.Tasks)
{
   // Compare date and completed flag
   if ((DateTime.Now.Date > row.TaskDueDate) && !row.TaskComplete)
   {
       buffer.Append(row.TaskTitle).Append(", Due: ");
       buffer.AppendFormat("{0:d}", row.TaskDueDate).Append('\n');
   }
}

Once all relevant tasks are added to the buffer, they can be displayed. Assuming the buffer has at least one character in it, there must have been tasks to display. The ShowBalloonTip method of the notify icon displays the familiar popup message as seen in Windows Update and many system utilities.

Visual Basic

If (buffer.Length > 0) Then
    ' Display an Info balloon tip for 8 seconds
    appNotifyIcon.ShowBalloonTip(8000, _
        "Coding 4 Fun - Task Manager", _
        buffer.ToString(), ToolTipIcon.Info)
End If

Visual C#

// If the buffer is not empty, there is at least one
// task to display
if (buffer.Length > 0)
{
     // Display an Info balloon tip for 8 seconds
     appNotifyIcon.ShowBalloonTip(8000, 
                              "Coding 4 Fun - Task Manager", 
                              buffer.ToString(), ToolTipIcon.Info);
}

The balloon tip consists of a title, message, and icon similar to a message box. The biggest differences compared to a message box, though, are that it doesn't obscure the center of the screen, and it doesn't demand to be noticed. This is perfect for non-critical events.

Figure 3: A notification alert for a task

Since this is an application that can go with never displaying its user interface, a typical Windows Form project template isn't suitable. In VB, if no application is showing, the application exits. This is also true in C#, though the work-around is a bit simpler. In both languages, I used a Program class file to contain the entry point to the program, the Main method. In this method, I create the main form object, and then start the application event loop by using the Run method of the Application object. I also added logic to ensure that the application can only be run once at a time. It would make no sense to run it twice.

The Mutex class in the System.Threading namespace allows you to create a named entity that is unique across the operating system. This may not sound particularly interesting, but because it is unique, you can request ownership of it, thereby preventing another application from owning it. This effect can be exploited to serialize access to a resource across application boundaries. In this case, you can use it to prevent another instance of the application from running. Just create the mutex, requesting ownership. If you are able to do so, then you must be the only running instance. Easy, and powerful!

Visual Basic

Dim onlyInstance As Boolean
Dim mtx As Mutex = New Mutex(True, "C4F:TaskManager", onlyInstance)
 
' If no other process owns the mutex, this is the
' only instance of the application.
If onlyInstance Then
    Dim mf As TaskManagerForm = New TaskManagerForm()
    Application.Run()
Else
    MessageBox.Show("An instance is already running", _
        "Coding 4 Fun - Task Manager ", _
        MessageBoxButtons.OK, MessageBoxIcon.Stop)
End If
Visual C#  
bool onlyInstance;
Mutex mtx = new Mutex(true, "C4F:TaskManager", out onlyInstance);

// If no other process owns the mutex, this is the
// only instance of the application.
if (onlyInstance)
{

   // Create the main form, then start the
   // application.  If the form is closed,
   // the application continues to run.
   TaskManagerForm mf = new TaskManagerForm();

   Application.Run();
}
else
{
   MessageBox.Show(
      "An instance is already running",
      "Coding 4 Fun - Task Manager ",
      MessageBoxButtons.OK, MessageBoxIcon.Stop);
}

Assigned to You

I'll close out this article with some items you can work on. You could really extend the application by adding additional properties to the tasks themselves. It would be nice if the application played a sound when an event is reached. The sound could be task-specific, maybe even with an image. The image and sound could be used when the due date is reached.

It would also be good to add some sort of snooze functionality. As it is, an alert will come up every minute until it is completed, or the due date is moved out. Of course, since it is using the balloon tip, only one alert will ever be on the screen.

Conclusion

I hope that this application helps you in learning some .NET coding skills, and maybe even makes it easier for you attain some of your goals for the New Year. Remember to keep your goals realistic, so that you can reach them. Resolutions can have a positive effect on your life—just don't let them add any more stress!

To get started with reviewing this article's sample code, or with any .NET project, download one or more of the Visual Studio Express Editions at http://msdn.microsoft.com/vstudio/express/default.aspx. Remember that they are free for a year. If you only make one resolution for 2006, make it be to write some great .NET code!

The Discussion

  • User profile image
    Oscar

    Question... Can it show with different colors when a task is completed or when a task is to due?

    and if so, how to do it (code)

    Thank you

Comments closed

Comments have been closed since this content was published more than 30 days ago, but if you'd like to send us feedback you can Contact Us.