Creating a custom TimePicker control in WPF 4.0






4.36/5 (9 votes)
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
- Add a
Grid
to the LayoutRoot (This is the parent container) - Set the background colour of the
Grid
to "#000000" - Add a
StackPanel
to theGrid
you just added - Set the orientation to "horizontal"
- Set the vertical alignment and horizontal alignment to "stretch"
- Add a
ToggleButton
to theStackPanel
Your layout should look similar to this:
ToggleButton - Edit template
- Right click on the
ToggleButton
and click Edit Template -> Edit a copy. - Select
ContentPresenter
and cut, select the Chrome, and delete. - Select
Template
in the Timeline and add aGrid
to it from Asset. - Select the
Grid
and paste theContentPresenter
inside it (your Object and Timeline panel should look like figure 2(a)). - Make sure the
Template
node is selected in the Timeline. - Go to the Trigger tab, add a new property (see Figure 2b).
- Change the property to
IsChecked
and set the value to "true" (see Figure 2b). - Now select the
Grid
inside the Template and change the background colour to "#FF919191". - Return scope to Window.
Layout - A finishing touch
- Rename the
ToggleButton
to 'HrBtn
'. - Select the
ToggleButton
and add aTextBlock
to it. - Rename the
TextBlock
toHrTxt
and set the foreground colour to "#FFFFFFFF". - Select the
ToggleButton
and copy. - Paste three copies of the
ToggleButton
inside theStackPanel
. - Rename the new copies of the
ToggleButton
asMinBtn
,SecBtn
, andAmPmBtn
. - Rename the
TextBlock
inside eachToggleButton
asMinTxt
,SecTxt
, andAmPmTxt
. - Add two other
TextBlock
s to theStackPanel
and set theirText
property as ':'. - Add two
Button
controls to theStackPanel
and rename them asupBtn
anddownBtn
. - Set the
Text
property of theseButton
controls to 'Up' and 'Down', respectively. - Rearrange your timeline to resemble the one shown below:
Your control should look similar to this:
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:
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.