The .Net framework provides two base classes for controls; one for Windows forms and the other for ASP.NET server controls.

We can extend the existing controls by adding specific functionality to them or develop our own controls from scratch. Such controls are called custom controls. We can also group controls together and create another control, such as an address box, that contains couple of textboxes and labels. Controls that are grouped together and are based on System.Winforms.UserControl are called user controls. This article explains the process of creating a user control and demonstrates how we can extend the functionality of the framework's DateTimePicker control.

The .Net framework introduces a lot of new classes that help us creating powerful windows and web applications. The framework allows us to create customized classes in the form of custom classes, components and controls.

So how do we differentiate between a class, component and a control? A class, as we all know, is a unit of reusable code. A component is a class that implements a specific interface called the IComponent or derives from a class that implements this interface directly or indirectly. Components provide control over external resources and provide design time support. A control is a component that provides user interface capabilities.

Background

The existing DateTimePicker control appears like a textbox with a drop down arrow that opens up a calendar. This control allows us to change its value by typing another date, through the use of arrow keys to increment or decrement the day, month or year and by selecting a specific date from the drop down calendar. Figure 1 displays an image of the DateTimePicker control.

Figure 1: The Dropdown DateTimePicker Control
Figure 1: The Dropdown DateTimePicker Control

We wish extend this functionality by adding some more features to this DateTimePicker control. We will make this control smarter by changing its value based on a specific key pressed by the user. An example would be incrementing the date by 1 day when a + is pressed or decrementing the date by 1 day when a - is pressed. Here is the list of proposed keys that we will capture in our custom DateTimePicker control and perform some action to update the current value:

Understanding the Keyboard events

To add this functionality into our control, we need to understand how Keyboard events work in the DateTimePicker control. The DateTimePicker control exposes three Keyboard events: KeyDown, KeyUp and KeyPress. When a user presses a key, the first event to occur is the KeyDown, followed by KeyUp event when the key is released. The KeyPress is the last event in this sequence and it notifies Windows that a key has been pressed and now needs to be handled. In our case, we will use the KeyPress event to capture the key that was pressed and check if this key is in our list of keys. If so, we will take some action based on the key that was pressed by the user.

The KeyPress event is passed two parameters: a sender object and a KeyPressEventArgs. The KeyPressEventArgs specifies the character that is composed when a KeyPress occurs and is defined in the System.WinForms namespace. It exposes two public properties: KeyChar and Handled. KeyChar is a read-only property that returns the character corresponding to the key that was pressed. If the user presses SHIFT + A, the KeyChar property returns a value of uppercase A. Handled, on the other hand is a read-write logical property that indicates whether the key was handled or not. By default, the value of this property is false and it means that the event will be forwarded to Windows for processing and displaying the appropriate character. When this property is set to true from the KeyPress event, it tells Windows that the event has been handled and no further work needs to be done.

Creating the User Control

Now that we know how the keyboard events work, let us look into creating our custom user control. This section explains a step-by-step process for creating a new user control. If you are already familiar with this process you may skip this section.

Right-click on the solution explorer and select new user control. This opens a working area where the control can be added. Select the DateTimePicker control from the toolbox and add it in the working area. Select the DateTimePicker control, right-click and select Properties. This will open the properties window. Make sure that the top-left of the DateTimePicker is set to 0 and the height-width of the container (design area) is set to the height-width of the DateTimePicker control. Set the DateFormat property of the DatePicker to short date and size the control appropriately. To add code in our DateTimePicker control, click on the icon that displays this (see below) image in the property window. This will display a list of all the events supported by the DateTimePicker control. Double-clicking on the KeyPress event opens the source view, where we will add code to handle the KeyPress event.

KeyPress() event

Listing 1 shows our new source code for the KeyPress event, written in C#. For Visual Basic .NET developers, there should be no problem at all translatinging this code into VB.NET syntax, as we are working with only .Net Framework components such as DateTimePicker, DateTime and TimeSpan.

Listing 1: The KeyPress() event code

//KeyPress() event
protected void txtDate_KeyPress (object sender, System.WinForms.KeyPressEventArgs e)
{
    //Get the key character
    char lcKey = e.KeyChar;
    
    //Get the current value of the date time control
    DateTime ldRetVal = txtDate.Value;
    
    //Detect the keypress and handle it based on the following
    switch (lcKey)
    {
        case 't':
        case 'T': 
            //In case of T or t, we update the date with today's date
            ldRetVal = DateTime.Now;
    
            //Notify that the event has been handled
            e.Handled = true;
            break;
        case '=':
        case '+':
            //In case of + we add one more day to the current value
            ldRetVal = ldRetVal.AddDays(1);
    
            //Notify that the event has been handled
            e.Handled = true;
            break;
        case '_':
        case '-':
            //In case of - we substract one day from the current value
            ldRetVal = ldRetVal.AddDays(-1);
    
            //Notify that the event has been handled
            e.Handled = true;
            break;
        case 'm':
        case 'M':
            //In case of M or m, we update the date with the first day of the month
            //In order to substract dates we create a timespan object
            TimeSpan ts = new TimeSpan(ldRetVal.Day - 1,0,0,0);
            
            //Substract these days from the current date
            ldRetVal = ldRetVal.Subtract(ts);
            
            //Notify that the event has been handled
            e.Handled = true;
            break;
        case 'h':
        case 'H':
            //In case of H or h, we update the date with the last day of the month
            //First we add one more month to the current date
            ldRetVal = ldRetVal.AddMonths(1);
    
            //In order to substract dates we create a timespan object
            TimeSpan ts = new TimeSpan(ldRetVal.Day,0,0,0);
    
            //Now we go back the number of days passed in this month
            //Substract these days from the current date
            ldRetVal = ldRetVal.Subtract(ts);
    
            //Notify that the event has been handled
            e.Handled = true;
            break;
        case '[':
        case '{':
            //In case we move to the previous month
            ldRetVal = ldRetVal.AddMonths(-1);
            e.Handled = true;
            break;
        case ']':
        case '}':
            //In case we move to the next month
            ldRetVal = ldRetVal.AddMonths(1);
            e.Handled = true;
            break;
        case 'y':
        case 'Y':
            //In case of H or h, we update the date with the first day of the year
            
            //1. We will first go to the first day of the current month
            //2. We will determine the number of months that have been passed
            //3. Move back in time for that many months
            
            //Go to the first day of the current month
            TimeSpan ts = new TimeSpan(ldRetVal.Day-1,0,0,0);
            ldRetVal = ldRetVal.Subtract(ts);
            
            //Get the number of months that have passed
            int nMonths = (ldRetVal.Month -1) * -1;
            
            //Go back in time for that many months
            ldRetVal = ldRetVal.AddMonths(nMonths );
            
            //Notify that the event has been handled
            e.Handled = true;
            break;
        case 'r':
        case 'R':
            //In case of R or r, we update the date with the last day of the year
            
            //1. We will first go to the last day of the current month
            //2. We will determine the number of months that have been passed
            //3. Move forward in time for 12 - that many months
            
            //Go to the last day of the current month
            ldRetVal = ldRetVal.AddMonths(1);
            TimeSpan ts = new TimeSpan(ldRetVal.Day,0,0,0);
            ldRetVal = ldRetVal.Subtract(ts);
            
            //Get the number of months that need to be added
            int nMonths = 12 - ldRetVal.Month ;
            
            //Go back in time for that many months
            ldRetVal = ldRetVal.AddMonths(nMonths);
            
            //Notify that the event has been handled
            e.Handled = true;
            break;
        default:
            //return back and as the event was not handled before this
            //point the default event will still trigger
            return;
    }
    
    //Update the contents of the date control
    txtDate.Value = ldRetVal;
}

Using this control

All the user controls created in a solution automatically show up in the toolbox toolbar. This makes it really simple to pick our user control and drop it in our form at design time. The Integrated Development Environment (IDE) also allows you to customize your toolbox and group controls together. Figure 2 shows a sample form created using the above user control.

Figure 2: Simply dropping the user control for DateTimePicker automatically provides the new features for updating the dates based on specific key codes.
Figure 2: Simply dropping the user control for DateTimePicker automatically provides the new features for updating the dates based on specific key codes.

This article explained how easily we could create a customized DateTimePicker user control with added features. We also learned how the keyboard events work and understood the workings of DateTime and TimeSpan Framework classes.

Finally

When you use this DateTimePicker user control in a form, you will notice that the properties of this control are different than a regular DateTimePicker control. This is because the user control contains the DateTimePicker control and you only see the properties and methods for the user control container. This also means that we cannot access or override the default methods or properties of the DateTimePicker contained in this user control. To achieve that functionality, our class needs to be a custom control instead of a user control. In a future article, we will take this control and learn how we can create a custom control and add properties, a description and methods. We will also learn how we can create a custom library of controls.

Key CharactersUpdates the current date with
T, tToday's date
+, =Next Day
-, _Previous Day
M, mFirst day of month
H, hLast day of month
Y, yFirst day of year
R, rLast day of year
[, {Previous Month
], }Next Month