This is my second article for The Code Project community. This article will explain about making an analog clock control that mimics Windows Vista clock sidebar gadget. It mimics the loading and tick effect, has
timezone property which can be used to maintain
timezone for displaying in the clock, and has a nice designer support feature as well.
TheClock is an analog clock control that mimics Windows Vista sidebar gadget especially in its behaviour like loading, ticks, and even timezones. Take a note that since
timezone features will have a complex method, I will separate the article which you can find after I have finished with this article.
With the release of Windows Vista, most people would know that one new thing that comes with it is the sidebar with gadgets. In all those gadgets, there's a control that catches my attention, the analog clock. It has nice features and interface. After observing how it works, I decided to make my own VB analog clock based on that gadget. Thus, I created this article to share with CodeProject members, especially most of members who already contributed their good articles to this site that helped me a lot with my work.
Using the Code
The control has a main component named
TheClock. The main objective that this component must have is its ability to tick in a fancy way, just like Windows Vista gadget. The second feature that it should have is
Timezone feature. This article will explain about the main component itself. I will discuss
Timezone feature separately in another article.
Initialize the Component
The first thing we need to do is to make a component.
TheClock will inherit
control object and will implement
ISupportInitialize because we will add some initial values after placing the control.
#Region "The Clock - Copyright (C) 2007 Michael Rawi"
Description("A modern analog clock."), _
ToolboxBitmap(GetType(TheClock), "TheClock.png")> _
Public Class TheClock
Setting up Variables and Properties
Before making a control, you should have known what variable you will need for developing the control. You don't have to know completely since you can have additional variables later, but you should at least define what you will need the most. For this control, we will also need enumerations.
Public Enum Styles
Analog = 0
Public Enum SecondTicks
[Default] = 0
Rapid = 1
Original = 2
Private Enum FadeStatus
[In] = 0 'Slowly disappeared - 1.0F~0.5F in 0.5 second by 5 Frame
Out = 1 'Slowly reappeared
#Region "Component Variables"
#Region "Core Variables"
Private WithEvents _tmrTime As Timer
Private WithEvents _tmrTick As Timer
Private WithEvents _tmrLoad As Timer
Private _imgS As Image
Private _imgM As Image
Private _imgH As Image
Private _angleS As Single
Private _angleM As Single
Private _angleH As Single
Private _ComInfo As New Devices.ComputerInfo
#Region "Private Properties"
Private _secTick As SecondTicks = SecondTicks.Default
Private _style As Styles
#Region "Load Animation"
Private _loadTimeframe As Single
Private _loadHAngle As Single
Private _loadMAngle As Single
Private _loadSAngle As Single
Private _loadMod As Integer
Private _ShowSecond As Boolean = True
Private _AdvancedMode As Boolean = False
Private _FadeFocus As Boolean = True
Private _FadeStatus As FadeStatus = FadeStatus.Out
Private _FadeAlpha As Single = 0.5F
Private WithEvents _tmrFade As Timer
Private _span As TimeSpan = New TimeSpan(0, 0, 0)
Private _State As String
In the next step, we will create some property value for making design time easier.
''' Get or set the style of the clock.
''' <returns>Current Style</returns>
<Description("Get or set the style of the Clock"), Category("Features"), _
DefaultValue(GetType(Styles), "Analog")> _
Public Property Style() As Styles
Set(ByVal value As Styles)
_style = value
''' Get or set the second tick's style.
''' <returns>Current SecondTicks</returns>
<Description("Get or set the second tick's style"), Category("Features"), _
DefaultValue(GetType(SecondTicks), "Default")> _
Public Property SecondTick() As SecondTicks
Set(ByVal value As SecondTicks)
_secTick = value
Select Case value
_tmrTime.Interval = 100
_tmrTime.Interval = 1000
''' Get or set the second visibility.
''' <returns>Second visibility</returns>
<Description("Get or set the second visibility"), Category("Features"), _
Public Property ShowSecond() As Boolean
Set(ByVal value As Boolean)
_ShowSecond = value
''' Enable advanced mode. This feature is not ready yet
Public Property AdvancedMode() As Boolean
Set(ByVal value As Boolean)
_AdvancedMode = value
''' Enable fade focus effect.
''' <returns>Current fadefocus effect</returns>
<Description("Enable fade focus effect"), Category("Features"), _
Public Property EnableFadeFocus() As Boolean
Set(ByVal value As Boolean)
_FadeFocus = value
''' Set clock time span against current time.
''' <returns>Current timespan</returns>
<Description("Set clock time span against current time"), Category("Features"), _
DefaultValue(GetType(TimeSpan), "00:00:00")> _
Public Property ClockSpan() As TimeSpan
Set(ByVal value As TimeSpan)
_span = value
The one important thing to do is to lock the size of the control, since we will have fixed size from the background image. This can be done with override
Making the Stuff Work
After the initialize step, now we will continue with the main feature. Region
Draw Surface Area contains a method that is needed for drawing Hour, Minute and Second hand. To create all the effects, we will have 3 timers for this control. The first timer will act as time indicator. It will tick each second and tell the control to update the second hand's position. The second timer will do the "bounce" effect. Now, if you observe the Windows gadget correctly, you will notice that after the second hand tick, it will bounce back a little, just like the real clock. In order to create that effect, we will need a timer to bounce the second hand after it ticks. The last timer will be used for the initialize process. After the control loads, it needs to find the position of the current time and place the hands in the right place in an elegant way. That's why we need to implement
ISupportInitialize to do the work.
Public Sub EndInit() Implements System.ComponentModel.ISupportInitialize.EndInit
'Activate the clock
If Not Me.DesignMode Then
If ((Now.Hour + _span.Hours) Mod 12) * _
30 > (Now.Minute + _span.Minutes) * 6 Then
_loadMod = 1
_loadMod = 3
_loadTimeframe = (Now.Minute + _span.Minutes) * 6 + (Now.Second \ 10)
_loadHAngle = (((Now.Hour + _span.Hours) Mod 12) * _
30 + ((Now.Minute + _span.Minutes) * 6 \ 12)) / (_loadTimeframe / 6)
_loadMAngle = 6
_tmrLoad = New Timer
_tmrLoad.Interval = 50
_angleH = 0
_angleM = 0
_angleS = 0
Adding Designer Support
A good component should have some features that make development time easier. One of the features is designer support. This will making our component easier to use.
(System.Security.Permissions.SecurityAction.Demand, Name:="FullTrust")> _
Public Class TheClockDesigner
Private lists As DesignerActionListCollection
Public Overrides ReadOnly Property ActionLists() _
If lists Is Nothing Then
lists = New DesignerActionListCollection()
For the last feature, we will add
Timezone support for the component. By adding this feature, the user can choose what time she/he needs for the control instead of just following Windows standard control. This feature can also be integrated with designer support to make it even easier to use. Moreover, it will have some nice "Use My Own" feature that can adjust the clock time difference with input from the user.
Points of Interest
There's one problem still bothering me while creating this control. For a strange reason, I must adjust the second hand position determined by XP or Vista OS. I currently tested my project on those OSs, and find that I need to move the second hand 1 pixel to the left if I use XP OS. This code can be found in the
DrawSecondHand method. Until now, it is still confusing for me to know that XP coordinate is slightly different from Vista coordinates.
Private Sub DrawSecondHand(ByVal g As Graphics, ByVal Angle As Single)
Dim container As Rectangle
If Angle <> 180 Then
container = New Rectangle(-6, -64, 13, 129)
If _ComInfo.OSVersion.StartsWith("5.1") Then
container = New Rectangle(-7, -65, 13, 129) 'This works on XP
container = New Rectangle(-6, -64, 13, 129) 'This works on Vista
If _FadeFocus Then
Using ia As New Imaging.ImageAttributes
Dim cm As New Imaging.ColorMatrix
cm.Matrix33 = _FadeAlpha
g.DrawImage(_imgS, container, 0, 0, 13, 129, _
- Version 1.0 - Initial release