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 the Grid
you just added
- Set the orientation to "horizontal"
- Set the vertical alignment and horizontal alignment to "stretch"
- Add a
ToggleButton
to the StackPanel
Your layout should look similar to this:
Figure 1
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 a Grid
to it from Asset.
- Select the
Grid
and paste the ContentPresenter
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.
Figure 2
Layout - A finishing touch
- Rename the
ToggleButton
to 'HrBtn
'.
- Select the
ToggleButton
and add a TextBlock
to it.
- Rename the
TextBlock
to HrTxt
and set the foreground colour to "#FFFFFFFF".
- Select the
ToggleButton
and copy.
- Paste three copies of the
ToggleButton
inside the StackPanel
.
- Rename the new copies of the
ToggleButton
as MinBtn
, SecBtn
, and AmPmBtn
.
- Rename the
TextBlock
inside each ToggleButton
as MinTxt
, SecTxt
, and AmPmTxt
.
- Add two other
TextBlock
s to the StackPanel
and set their Text
property as ':'.
- Add two
Button
controls to the StackPanel
and rename them as upBtn
and downBtn
.
- Set the
Text
property of these Button
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:
if (HrBtn.IsChecked == true) {
int hour = int.Parse(HrTxt.Text);
if (hour == 12)
hour = hour - hour;
hour++;
HrTxt.Text = hour.ToString();
}
else if (MinBtn.IsChecked == true)
{
int min = int.Parse(MinTxt.Text);
if (min == 59)
min = -1;
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 == 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(AmPmTxt.Text == "AM")
{
AmPmTxt.Text = "PM";
}
else
{
AmPmTxt.Text = "AM";
}
}
Under the downBtn_Click
Event Handler, add the following code:
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.