Introduction
This work is inspired by a pricey watch which I spotted while perusing one of those magazines filled with other shockingly pricey items. This is a more affordable offering and minus bulletproof glass.
Design and Layout
I designed the Belt Clock in Expression Blend. The belts are made up of Grid
s, containing TextBlock
s, stacked in a layout container of type Canvas
.
HourCanvas
, plus some other elements that make up the visual components of the belt, are placed in another layout container whose ClipToBounds
property is set to True.
Here's how it actually looks like when the ClipToBounds
property of all the concerned layout containers are set to False.
The Code
The logic for ensuring that the correct time is 'displayed' is contained in a class called TickTock
. When the application is loaded the method SetTimeOnLoad()
calls several methods to ensure that the correct time is highlighted.
Public Sub SetTimeOnLoad()
GetTime(currentHour, currentMinute_1, currentMinute_2)
DisplayTimeOnLoad(hourCanvas, currentHour, hourStartGrid, _
hourGridUpperLimit, hourGridLowerLimit)
DisplayTimeOnLoad(min1Canvas, currentMinute_1, min1StartGrid, _
min1GridUpperLimit, min1GridLowerLimit)
DisplayTimeOnLoad(min2Canvas, currentMinute_2, min2StartGrid, _
min2GridUpperLimit, min2GridLowerLimit)
End Sub
The GetTime()
method sets the value of several fields with the current time.
Private Sub GetTime(ByRef hour As Integer, ByRef min1 As Integer, ByRef min2 As Integer)
hour = DateTime.Now.Hour
If (hour > 12) Then
hour -= 12
End If
If (DateTime.Now.Minute.ToString.Length = 1) Then
min1 = 0
min2 = CInt(DateTime.Now.Minute.ToString.Substring(0, 1))
Else
min1 = CInt(DateTime.Now.Minute.ToString.Substring(0, 1))
min2 = CInt(DateTime.Now.Minute.ToString.Substring(1, 1))
End If
End Sub
The DisplayTimeOnLoad()
method ensures that the correct time is highlighted.
Private Sub DisplayTimeOnLoad(ByRef cnv As Canvas, ByVal currTime As Integer, _
ByVal startGrid As Integer, ByVal upperLimit As Integer, _
ByVal lowerLimit As Integer)
If (currTime < startGrid) Then
shift = (startGrid - currTime) * gridHeight
While (shift > 0)
For Each grd In cnv.Children
initY = Canvas.GetTop(grd)
newY = initY + 1
Canvas.SetTop(grd, newY)
PlaceGridInLimitPosition(grd, upperLimit, lowerLimit)
Next
shift -= 1
End While
ElseIf (currTime > startGrid) Then
shift = (currTime - startGrid) * gridHeight
While (shift > 0)
For Each grd In cnv.Children
initY = Canvas.GetTop(grd)
newY = initY - 1
Canvas.SetTop(grd, newY)
PlaceGridInLimitPosition(grd, upperLimit, lowerLimit)
Next
shift -= 1
End While
End If
End Sub
In the method above the Grid
s of interest are moved either upwards or downwards so that the correct time is highlighted. PlaceGridInLimitPosition()
ensures that a Grid
that moves beyond a certain limit is placed accordingly.
Private Sub PlaceGridInLimitPosition(ByRef grd As Grid, ByVal upperLimit As Integer, _
ByVal lowerLimit As Integer)
If (upperLimit = hourGridUpperLimit) AndAlso (lowerLimit = hourGridLowerLimit) Then
If (newY >= 280) Then
Canvas.SetTop(grd, hourGridUpperLimit)
ElseIf (newY <= -240) Then
Canvas.SetTop(grd, hourGridLowerLimit)
End If
ElseIf (upperLimit = min1GridUpperLimit) AndAlso (lowerLimit = min1GridLowerLimit) Then
If (newY >= 120) Then
Canvas.SetTop(grd, min1GridUpperLimit)
ElseIf (newY <= -160) Then
Canvas.SetTop(grd, min1GridLowerLimit)
End If
ElseIf (upperLimit = min2GridUpperLimit) AndAlso (lowerLimit = min2GridLowerLimit) Then
If (newY >= 200) Then
Canvas.SetTop(grd, min2GridUpperLimit)
ElseIf (newY <= -240) Then
Canvas.SetTop(grd, min2GridLowerLimit)
End If
End If
End Sub
The method TimeKeeper_Tick()
handles the Tick
event of a DispatcherTimer
object, whose Interval
property is set to 1 sec, and ensures that the correct time is highlighted when time changes.
Private Sub TimeKeeper_Tick(ByVal sender As Object, ByVal e As EventArgs)
GetTime(currHour, currMin1, currMin2)
If (currentHour <> currHour) Then
DisplayNewTime(hourCanvas, hourGridLowerLimit, hourGridUpperLimit)
currentHour = currHour
End If
If (currentMinute_1 <> currMin1) Then
DisplayNewTime(min1Canvas, min1GridLowerLimit, min1GridUpperLimit)
currentMinute_1 = currMin1
End If
If (currentMinute_2 <> currMin2) Then
DisplayNewTime(min2Canvas, min2GridLowerLimit, min2GridUpperLimit)
currentMinute_2 = currMin2
End If
End Sub
DisplayNewTime()
causes the execution of the animation that creates the scrolling effect.
Private Sub DisplayNewTime(ByRef cnv As Canvas, ByVal lowerLimit As Integer, _
ByVal upperLimit As Integer)
For Each grd As Grid In cnv.Children
initY = Canvas.GetTop(grd)
If (initY <= upperLimit) Then
dblGridAnim.Duration = TimeSpan.FromMilliseconds(1)
dblGridAnim.To = lowerLimit
dblGridAnim.FillBehavior = FillBehavior.HoldEnd
grd.BeginAnimation(Canvas.TopProperty, dblGridAnim)
Else
newY = initY - gridHeight
dblGridAnim.Duration = TimeSpan.FromMilliseconds(800)
dblGridAnim.To = newY
dblGridAnim.FillBehavior = FillBehavior.HoldEnd
grd.BeginAnimation(Canvas.TopProperty, dblGridAnim)
End If
Next
End Sub
Conclusion
That's it. I hope you picked up something useful from this article. If there are any bugs kindly let me know.
History
- 16th May, 2012: Initial post