Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Creating a custom TimePicker control in WPF 4.0

0.00/5 (No votes)
9 Aug 2011 1  
Steps in creating a custom TimePicker control in WPF 4.

Introduction

I was working on an application, Time Watcher 5.0, and I needed to use a TimePicker control which was not provided along with controls in WPF 4.0. There are several third party TimePicker controls but I was not satisfied with their "mechanism of action and behaviour". Below are the steps I took to create my own custom TimePicker control knowing that it will benefit a lot of people by sharing the process.

Please note this!

Please note that this is an introductory article on how to create custom controls for your application from a designer's point of view. This article will give an insight on how to use existing controls to design your own custom controls, and recommendations on how to create reusable custom controls (with registered properties) are provided at the end of this article.

In this demonstration, I'll be using Expression Blend 4. Now let's get to business!

Step 1 - Designing the control

In Expression Blend 4, do the following:

Layout

  1. Add a Grid to the LayoutRoot (This is the parent container)
  2. Set the background colour of the Grid to "#000000"
  3. Add a StackPanel to the Grid you just added
  4. Set the orientation to "horizontal"
  5. Set the vertical alignment and horizontal alignment to "stretch"
  6. Add a ToggleButton to the StackPanel

Your layout should look similar to this:

Layout.jpg

Figure 1

ToggleButton - Edit template

  1. Right click on the ToggleButton and click Edit Template -> Edit a copy.
  2. Select ContentPresenter and cut, select the Chrome, and delete.
  3. Select Template in the Timeline and add a Grid to it from Asset.
  4. Select the Grid and paste the ContentPresenter inside it (your Object and Timeline panel should look like figure 2(a)).
  5. Make sure the Template node is selected in the Timeline.
  6. Go to the Trigger tab, add a new property (see Figure 2b).
  7. Change the property to IsChecked and set the value to "true" (see Figure 2b).
  8. Now select the Grid inside the Template and change the background colour to "#FF919191".
  9. Return scope to Window.

Figure_2a.jpgFigure_2b.png

Figure 2

Layout - A finishing touch

  1. Rename the ToggleButton to 'HrBtn'.
  2. Select the ToggleButton and add a TextBlock to it.
  3. Rename the TextBlock to HrTxt and set the foreground colour to "#FFFFFFFF".
  4. Select the ToggleButton and copy.
  5. Paste three copies of the ToggleButton inside the StackPanel.
  6. Rename the new copies of the ToggleButton as MinBtn, SecBtn, and AmPmBtn.
  7. Rename the TextBlock inside each ToggleButton as MinTxt, SecTxt, and AmPmTxt.
  8. Add two other TextBlocks to the StackPanel and set their Text property as ':'.
  9. Add two Button controls to the StackPanel and rename them as upBtn and downBtn.
  10. Set the Text property of these Button controls to 'Up' and 'Down', respectively.
  11. Rearrange your timeline to resemble the one shown below:

Timeline.jpg

Your control should look similar to this:

Control.jpg

Step 2 - Coding your control

Under the upBtn_Click Event Handler, add the following code:

/*The code below check to see which node is selected in the control, 
get the current value of the node, increase the value by one 
and display back to the user. This get fired every time the user clicks the 'Up' Button*/

if (HrBtn.IsChecked == true) //If the hour node is selected in the control
{
    int hour = int.Parse(HrTxt.Text); //Converts the Text to an Integer
    
    if (hour == 12)
        hour = hour - hour;
        // This ensures that hour does not exceed
        // 12 since hour ranges from 1 to 12
    
    hour++; //Increase the hour by 1
    
    HrTxt.Text = hour.ToString();
    //Convert the resulting hour back to string format.
}
else if (MinBtn.IsChecked == true) 
{
    int min = int.Parse(MinTxt.Text); //Converts the Text to an Integer
    
    if (min == 59)
        min = -1; //This ensures that minute does not exceed
                  //60 since minute ranges from 0 to 59
    
    min++;
    
    if (min.ToString().Length == 1)
    // This ensures that the minute text maintain a standard length of 2
    {
        MinTxt.Text = "0" + min.ToString();
    }
    else
    {
        MinTxt.Text = min.ToString();
    }
}
else if (SecBtn.IsChecked == true)
{
    int sec = int.Parse(SecTxt.Text);
    
    if (sec == 59)
        sec = -1;
    
    sec++;
    
    if (sec.ToString().Length == 1)
    {
        SecTxt.Text = "0" + sec.ToString();
    }
    else
    {
        SecTxt.Text = sec.ToString();
    }
}
else if (AmPmBtn.IsChecked == true)
{
    //If the Am/Pm node is selected,
    //the code below alternates between Am and Pm.
    if(AmPmTxt.Text == "AM")
    {
        AmPmTxt.Text = "PM";
    }
    else
    {
        AmPmTxt.Text = "AM";
    }
}

Under the downBtn_Click Event Handler, add the following code:

/*The code below check to see which node is selected in the control, 
get the current value of the node, decrease the value by one 
and display back to the user. This get fired every time 
the user clicks the 'Down' Button*/

if (HrBtn.IsChecked == true)
{
    int hour = int.Parse(HrTxt.Text);
    
    if (hour == 1)
        hour = 13;
    
    hour--;
    
    HrTxt.Text = hour.ToString();
}
else if (MinBtn.IsChecked == true)
{
    int min = int.Parse(MinTxt.Text);
    
    if (min == 0)
        min = 60;
    
    min--;
    
    
    if (min.ToString().Length == 1)
    {
        MinTxt.Text = "0" + min.ToString();
    }
    else
    {
        MinTxt.Text = min.ToString();
    }
}
else if (SecBtn.IsChecked == true)
{
    int sec = int.Parse(SecTxt.Text);
    
    if (sec == 0)
        sec = 60;
    
    sec--;
    
    
    if (sec.ToString().Length == 1)
    {
        SecTxt.Text = "0" + sec.ToString();
    }
    else
    {
        SecTxt.Text = sec.ToString();
    }
}
else if (AmPmBtn.IsChecked == true)
{
    if(AmPmTxt.Text == "AM")
    {
        AmPmTxt.Text = "PM";
    }
    else
    {
        AmPmTxt.Text = "AM";
    }
}

If you have good hands in design especially in Expression Blend, you can skin your control as it suits you. The final control of Time Watcher 5.0 application looks like this:

Final.jpg

On a final note

When creating a reusable ontrol, it must be "skinable"; the use of the WPF commanding model (see Commanding Overview) is also better than raising clicked events if you are creating a control for reuse. A bindable DateTime or TimeSpan property will also enable users of this control to bind to it. Thanks to Keith Barrow for this contribution.

Recommendations

For further readings on how to create a custom control and add dependency properties, I recommend Create a WPF Custom Control, Part 1 and Create a WPF Custom Control, Part 2 by David Veeneman.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here