Dial clock control for WPF

In any major project inevitably the need for some specific feature arises that is not covered by the base library. Then you look for any solution over at some control library outfits on the net and at the end you bake your own solution. Our need was for an interface similar to the Windows time and date control:

While the date portion was easily accomplished with the Calendar control from WPFToolkit library, I ended up making my own clock custom control. Our requirements called for the user to be able to choose time for an event schedule, so the control supports setting the time with mouse and keyboard. But you can easily only display a clock and control it from your code as the sample project shows.

You can get the sample project here: WPFClock.zip

The Solution

The clock is implemented by following the Microsoft guidance for custom control development. The presentation is fully separate from the control logic allowing you to template the clock as you wish. For this sample project and our own application we opted for a template very similar to the Windows own design shown above. One noticeable difference is the presence of an AM/PM indicator as the conventional clock dial has 12 hour ticks.

The mechanics of the ClockControl are very similar to an actual dial clock implementation, so it has three dependency properties that denote the angle of the clock arms:

  • HourAngle - determine the angle of the hour arm
  • MinuteAngle - determine the angle of the minutes arm
  • SecondAngle - determine the angle of the seconds arm

One more property is required - IsPostMeridiem - that determines if the time shown is Ante or Post Meridiem.

Once any of these properties are changed the corresponding changed event is raised so that you can handle these changes in your code.

The user can interact with the clock either by clicking on it with the mouse or through the keyboard arrows, Page Up and Page Down. Doing our usability tests it proved that trying to click the arm and drag it around proved too difficult, so the arm position can be set by a single mouse click on the area where you want the arm to go. In order to set the hour arm you need to click closer to the centre of the dial, while clicking closer to the periphery will set the minute dial. Our requirements did not call for setting the seconds as well, so we actually removed the seconds arm but you can set the seconds as well with the keyboard as explained later on.

In order to interact with the clock with your mouse, the control template you use will need to have these three parts:

  • PART_Dial - A solid area that contains the clock dial in order to catch mouse events
  • PART_HourDial - A solid area that represents the area used to control the hour arm
  • PART_PM - A solid area used to switch the clock from Ante to Post Meridiem

The first part will contain the whole clock dial and is required to mouse-control the minute arm. The second part is a smaller transparent circle with the radius of the hour dial, required to mouse-control the hour arm. The third part is the AM/PM switch area. In my sample template it is the actual indicator that switches when clicked with the mouse.

The clock supports keyboard control with the following keys:

  • Page Up - increase the hour
  • Page Down - decrease the hour
  • Right Arrow - increase the minutes
  • Left Arrow - decrease the minutes
  • Up Arrow - increase the seconds
  • Down Arrow - decrease the seconds

To easily set and get the time using the System.DateTime structure two extension functions are provided in the ClockFunctions class:

  • UpdateClockFromTime - set the clock arms from the provided time argument
  • GetClockTime - returns the time the clock points to as an System.DateTime

And that's it, you have a neat dial clock for your everyday needs. In the sample application you can try setting the big clock on the left with your mouse or keyboard. You may need to click the clock first so that it has keyboard focus. The small clock on the right is an read-only clock that will show the current time. You can easily protect the clock from the users fiddling with it by setting its IsEnabled property to false.

Hope you really enjoyed this one :)