Click here to Skip to main content
15,891,633 members
Articles / Desktop Programming / WPF

WPF: Belt Clock

Rate me:
Please Sign up or sign in to vote.
4.88/5 (44 votes)
16 May 2012CPOL1 min read 57.9K   2.7K   43   38
A WPF belt clock

Image 1

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 Grids, containing TextBlocks, stacked in a layout container of type Canvas.

Image 2

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.

Image 3

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.

VB.NET
Public Sub SetTimeOnLoad()
    GetTime(currentHour, currentMinute_1, currentMinute_2)

    ' Highlight hour.     
    DisplayTimeOnLoad(hourCanvas, currentHour, hourStartGrid, _
                      hourGridUpperLimit, hourGridLowerLimit)

    ' Highlight first part of minutes.        
    DisplayTimeOnLoad(min1Canvas, currentMinute_1, min1StartGrid, _
                      min1GridUpperLimit, min1GridLowerLimit)

    ' Highlight second part of minutes.        
    DisplayTimeOnLoad(min2Canvas, currentMinute_2, min2StartGrid, _
                      min2GridUpperLimit, min2GridLowerLimit)
End Sub

The GetTime() method sets the value of several fields with the current time.

VB.NET
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.

VB.NET
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
        ' Move grids downwards.
        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
        ' Move grids upwards.
        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 Grids 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.

VB.NET
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.

VB.NET
Private Sub TimeKeeper_Tick(ByVal sender As Object, ByVal e As EventArgs)
    GetTime(currHour, currMin1, currMin2)

    ' Set new hour.
    If (currentHour <> currHour) Then
        DisplayNewTime(hourCanvas, hourGridLowerLimit, hourGridUpperLimit)
        currentHour = currHour
    End If

    ' Set new first part of minutes.
    If (currentMinute_1 <> currMin1) Then
        DisplayNewTime(min1Canvas, min1GridLowerLimit, min1GridUpperLimit)
        currentMinute_1 = currMin1
    End If

    ' Set new second part of minutes.   
    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.

VB.NET
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

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Kenya Kenya
Experienced C# software developer with a passion for WPF.

Awards,
  • CodeProject MVP 2013
  • CodeProject MVP 2012
  • CodeProject MVP 2021

Comments and Discussions

 
QuestionWow ! Pin
vincennes4-Mar-14 6:08
professionalvincennes4-Mar-14 6:08 
GeneralRe: Wow ! Pin
Meshack Musundi4-Mar-14 9:13
professionalMeshack Musundi4-Mar-14 9:13 
BugWrong display time Pin
Marco Bertschi15-Oct-13 3:14
protectorMarco Bertschi15-Oct-13 3:14 
GeneralMy vote of 5 Pin
atu_mboya6-Oct-12 10:56
atu_mboya6-Oct-12 10:56 
GeneralRe: My vote of 5 Pin
Meshack Musundi7-Oct-12 6:10
professionalMeshack Musundi7-Oct-12 6:10 
GeneralMy vote of 5 Pin
Christian Amado28-Aug-12 12:06
professionalChristian Amado28-Aug-12 12:06 
GeneralMy vote of 5 Pin
P.Salini29-May-12 0:58
P.Salini29-May-12 0:58 
GeneralRe: My vote of 5 Pin
Meshack Musundi29-May-12 19:48
professionalMeshack Musundi29-May-12 19:48 
GeneralMy vote of 5 Pin
Jαved28-May-12 21:05
professionalJαved28-May-12 21:05 
GeneralRe: My vote of 5 Pin
Meshack Musundi29-May-12 19:47
professionalMeshack Musundi29-May-12 19:47 
GeneralMy vote of 5 Pin
burndavid9327-May-12 13:13
burndavid9327-May-12 13:13 
GeneralRe: My vote of 5 Pin
Meshack Musundi27-May-12 20:59
professionalMeshack Musundi27-May-12 20:59 
Questiontnx dude Pin
Mahdi_kishislan24-May-12 9:26
Mahdi_kishislan24-May-12 9:26 
AnswerRe: tnx dude Pin
Meshack Musundi24-May-12 21:50
professionalMeshack Musundi24-May-12 21:50 
GeneralMy vote of 5 Pin
Carsten V2.024-May-12 9:23
Carsten V2.024-May-12 9:23 
GeneralRe: My vote of 5 Pin
Meshack Musundi24-May-12 21:49
professionalMeshack Musundi24-May-12 21:49 
GeneralMy vote of 5 Pin
Mohammad A Rahman23-May-12 11:25
Mohammad A Rahman23-May-12 11:25 
GeneralRe: My vote of 5 Pin
Meshack Musundi24-May-12 1:41
professionalMeshack Musundi24-May-12 1:41 
Questionnot bad Pin
BillW3317-May-12 3:18
professionalBillW3317-May-12 3:18 
AnswerRe: not bad Pin
Meshack Musundi17-May-12 3:34
professionalMeshack Musundi17-May-12 3:34 
QuestionOK so here is what I think Pin
Sacha Barber16-May-12 2:23
Sacha Barber16-May-12 2:23 
Its ok

But, once you collapse the code regions, there is about 8 lines of actual text left.

Also when I look at your code, there are clearly areas that are not good practice. For example look at this code

Public Sub SetTimeOnLoad()
        currentHour = DateTime.Now.Hour

        If (currentHour > 12) Then
            currentHour -= 12
        End If

        If (DateTime.Now.Minute.ToString.Length = 1) Then
            currentMinute_1 = 0
            currentMinute_2 = CInt(DateTime.Now.Minute.ToString.Substring(0, 1))
        Else
            currentMinute_1 = CInt(DateTime.Now.Minute.ToString.Substring(0, 1))
            currentMinute_2 = CInt(DateTime.Now.Minute.ToString.Substring(1, 1))
        End If

        ' Set hour.
        If (currentHour < HOUR_START_GRID) Then
            ' Move hour grids downwards.
            hourShiftVal = (HOUR_START_GRID - currentHour) * GRID_HEIGHT

            While (hourShiftVal > 0)
                For Each grd In hourCanvas.Children
                    initHourGrid_Y = Canvas.GetTop(grd)
                    newHourGrid_Y = initHourGrid_Y + 1
                    Canvas.SetTop(grd, newHourGrid_Y)
                    RepositionHourGridOnSet(grd)
                Next
                hourShiftVal -= 1
            End While
        ElseIf (currentHour > HOUR_START_GRID) Then
            ' Move hour grids upwards.
            hourShiftVal = (currentHour - HOUR_START_GRID) * GRID_HEIGHT

            While (hourShiftVal > 0)
                For Each grd In hourCanvas.Children
                    initHourGrid_Y = Canvas.GetTop(grd)
                    newHourGrid_Y = initHourGrid_Y - 1
                    Canvas.SetTop(grd, newHourGrid_Y)
                    RepositionHourGridOnSet(grd)
                Next
                hourShiftVal -= 1
            End While
        End If

        ' Set first part of minutes.
        If (currentMinute_1 < MIN1_START_GRID) Then
            ' Move min1 grids downwards.
            min1ShiftVal = (MIN1_START_GRID - currentMinute_1) * GRID_HEIGHT

            While (min1ShiftVal > 0)
                For Each grd In min1Canvas.Children
                    initMin1Grid_Y = Canvas.GetTop(grd)
                    newMin1Grid_Y = initMin1Grid_Y + 1
                    Canvas.SetTop(grd, newMin1Grid_Y)
                    RepositionMin1GridOnSet(grd)
                Next
                min1ShiftVal -= 1
            End While
        ElseIf (currentMinute_1 > MIN1_START_GRID) Then
            ' Move min1 grids upwards.
            min1ShiftVal = (currentMinute_1 - MIN1_START_GRID) * GRID_HEIGHT

            While (min1ShiftVal > 0)
                For Each grd In min1Canvas.Children
                    initMin1Grid_Y = Canvas.GetTop(grd)
                    newMin1Grid_Y = initMin1Grid_Y - 1
                    Canvas.SetTop(grd, newMin1Grid_Y)
                    RepositionMin1GridOnSet(grd)
                Next
                min1ShiftVal -= 1
            End While
        End If

        ' Set second part of minutes.
        If (currentMinute_2 < MIN2_START_GRID) Then
            ' Move min2 grids downwards.
            min2ShiftVal = (MIN2_START_GRID - currentMinute_2) * GRID_HEIGHT

            While (min2ShiftVal > 0)
                For Each grd In min2Canvas.Children
                    initMin2Grid_Y = Canvas.GetTop(grd)
                    newMin2Grid_Y = initMin2Grid_Y + 1
                    Canvas.SetTop(grd, newMin2Grid_Y)
                    RepositionMin2GridOnSet(grd)
                Next
                min2ShiftVal -= 1
            End While
        ElseIf (currentMinute_2 > MIN2_START_GRID) Then
            ' Move min2 grids upwards.
            min2ShiftVal = (currentMinute_2 - MIN2_START_GRID) * GRID_HEIGHT

            While (min2ShiftVal > 0)
                For Each grd In min2Canvas.Children
                    initMin2Grid_Y = Canvas.GetTop(grd)
                    newMin2Grid_Y = initMin2Grid_Y - 1
                    Canvas.SetTop(grd, newMin2Grid_Y)
                    RepositionMin2GridOnSet(grd)
                Next
                min2ShiftVal -= 1
            End While
        End If
    End Sub


There looks like you could at least extract some of the common elements to a method and pass in some parameters. Seems like a lot of repetitive code.

I also think the XAML shows signed of needing some thought. For example each MinRollerX could be a usercontrol, which you simply specify the gradient stops on. Which would also clean up the xaml.


So for this article its a 3 from me. Sorry
Sacha Barber
  • Microsoft Visual C# MVP 2008-2012
  • Codeproject MVP 2008-2012
Open Source Projects
Cinch SL/WPF MVVM

Your best friend is you.
I'm my best friend too. We share the same views, and hardly ever argue

My Blog : sachabarber.net

AnswerRe: OK so here is what I think Pin
Meshack Musundi16-May-12 3:39
professionalMeshack Musundi16-May-12 3:39 
GeneralRe: OK so here is what I think Pin
Sacha Barber16-May-12 3:57
Sacha Barber16-May-12 3:57 
GeneralRe: OK so here is what I think Pin
Meshack Musundi16-May-12 5:13
professionalMeshack Musundi16-May-12 5:13 
GeneralRe: OK so here is what I think Pin
Sacha Barber17-May-12 0:40
Sacha Barber17-May-12 0:40 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.