Writing a Basic Analog Clock UserControl in Visual Basic.Net (Part 1)

AID: 5329
  • Status: Published

2030 points

  • ByIdle_Mind
  • TypeTutorial
  • Posted on2011-05-02 at 14:11:20
Creating an analog clock UserControl seems fairly straight forward.  It is, after all, essentially just a circle with several lines in it!  Two common approaches for rendering an analog clock typically involve either manually calculating points with trigonometric functions, or rotating the entire drawing the surface to draw lines at specific angles.  This article will demonstrate just how easy it is to make an analog clock in VB.Net using the latter rotation method.

The “Bare Bones” Analog Clock: A Circle with Several Lines

Let’s begin by building a “bare bones” analog clock as described above: “a circle with several lines in it.”  Start by adding a new UserControl to your Project.  Click on Project --> Add User Control, change the name in the box to “AnalogClock” and press Enter to create it.  Most of our work will be done in the Paint() event so let’s start there.  With the UserControl selected, go to the Properties Pane and click on the “Lightning Bolt” icon to get a list of events.
LightningBolt.jpg
  • 20 KB
  • "Lightning Bolt" Icon for Events
"Lightning Bolt" Icon for Events

Scroll down to the “Paint” entry and double click it.  You should now have this in your editor:
Public Class AnalogClock

    Private Sub AnalogClock_Paint(ByVal sender As System.Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint

    End Sub

End Class
                                    
1:
2:
3:
4:
5:
6:
7:

Select allOpen in new window


The Paint() event supplies a Graphics surface via “e.Graphics” that allows us to draw on the UserControl.  In the .Net Framework, the default coordinate system places the origin at the upper left corner with x values increasing as you move to the right, and y values increasing as you move down.  It would be much nicer if we could instead treat the center of our analog clock as the origin, thus allowing us to easily rotate around the center of the clock.  This can be achieved using the Graphics.TranslateTransform() method.  The first order of business is to calculate the center point of the UserControl, which is computed by simply halving the width and height:
Dim center As New Point(Me.ClientSize.Width / 2, Me.ClientSize.Height / 2)
                                    
1:

Select allOpen in new window


Next we move the origin by passing the X and Y values of our center point to TranslateTransform():
e.Graphics.TranslateTransform(center.X, center.Y)
                                    
1:

Select allOpen in new window


Now the point (0, 0) is located at the center of our UserControl!  Let’s use this fact to draw the circle of our clock at the origin.  First we need to compute an appropriate radius for our circle by selecting the smaller value between the width and height of our control, and then taking a percentage of that:
Dim radius As Integer = Math.Min(Me.ClientSize.Width, Me.ClientSize.Height) / 2 * 0.8
                                    
1:

Select allOpen in new window


I’ve used 0.8, or 80%, to ensure that our circle will always be completely visible and have a small margin between it and the edges of the control.  The next step is to create a bounding box that our circle will be drawn in.  We start with a rectangle located at (0, 0) and having a size of 1x1 pixel.  Then we use the Inflate() method and our computed radius to make the rectangle expand to our desired target size while still keeping the same center point (the origin).  Lastly, we render the circle with DrawEllipse():
Dim clock As New Rectangle(New Point(0, 0), New Size(1, 1))
clock.Inflate(radius, radius)
e.Graphics.DrawEllipse(Pens.Black, clock)
                                    
1:
2:
3:

Select allOpen in new window


At this point, the code in our analog clock control should look like this:
Public Class AnalogClock

    Private Sub AnalogClock_Paint(ByVal sender As System.Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint
        Dim center As New Point(Me.ClientSize.Width / 2, Me.ClientSize.Height / 2)
        Dim radius As Integer = Math.Min(Me.ClientSize.Width, Me.ClientSize.Height) / 2 * 0.8
        e.Graphics.TranslateTransform(center.X, center.Y)

        Dim clock As New Rectangle(New Point(0, 0), New Size(1, 1))
        clock.Inflate(radius, radius)
        e.Graphics.DrawEllipse(Pens.Black, clock)
    End Sub

End Class
                                    
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:

Select allOpen in new window


This should produce a circle in the center of our control no matter what its width or height is.  To verify this, in the menus, click on Build --> Build Solution.  Once compiled, double click on your default Form in Solution Explorer to go back to design mode.  You should now find a new UserControl called “AnalogClock” at the top of your Toolbox:
AnalogClock-in-the-ToolBox.jpg
  • 21 KB
  • AnalogClock UserControl in the ToolBox
AnalogClock UserControl in the ToolBox

Select the AnalogClock control in the Toolbox and drag a new one somewhere on your form.  Grab one of its corners and drag it around to see what happens as the control is resized.  Does the circle stay centered and adjust its size accordingly?  Hopefully you answered with, “Yes!”
AnalogClockCircle.jpg
  • 20 KB
  • The Circle for our Analog Clock on a Form
The Circle for our Analog Clock on a Form


All that is left to do now is draw the hour, minute, and second hands of our clock.  At the beginning of the article I stated that I would draw the clock by rotating the graphics surface instead of using trigonometric functions.  What does that really mean though?  Specifically, I will draw all three hands of the clock as horizontal lines on the x-axis, extending from the origin and to the right.  They will appear in different locations, though, because we will literally rotate around the center of the clock and move the x-axis to where we want it!

How will we know what angle to use for each hand?  Start with the fact that a circle has 360 degrees.  In the .Net framework, 0 (zero) degrees is on the x-axis pointing to the right.  Increasing the value of our angle moves us in a clockwise direction.  This would put 90 degrees pointing straight down on the y-axis, 180 degrees pointing left on the x-axis, and 270 degrees pointing straight up on the y-axis.  A positive increase in angle moves us clockwise, while a negative decrease in angle moves us counter-clockwise.  Thus 45 degrees is pointing down and to the right halfway between the X and Y axis, while -45 degrees is pointing up and to the right halfway between the X and Y axis.

A standard clock has 12 hour positions on it.  If we divide 360 degrees by 12, we get 30 degrees.  This is how many degrees are between each hour position on the clock face.  The following video sweeps through the angles from 0 to 360, in both the positive and negative directions, showing the multiples of 30 and 45 degrees as red dashed lines:  
Idle-Mind-449853.flv
  • 678 KB
  • Angles in the .Net Framework
Idle-Mind-449853.flv

With a clear understanding of the angle system in .Net, we can now move on to computing angles from the current time at which to draw the hands of our analog clock.  The current time can be retrieved using the DateTime.Now function.  From that return value, we can extract the hour, minute, and second values using the respective Hour, Minute, and Second properties.  Let’s deal with the hour value first.  The Hour property returns a value between 0 and 23.  Values less than or equal to 12 are AM times, and values greater than or equal to 13 are PM times.  Since an analog clock only has positions between 1 and 12, we must convert all PM times (which are greater than 12) to AM times by subtracting 12 from them.  This is an exercise quite familiar to anyone accustomed to converting between military and standard times.  The conversion from a 24 hour to a 12 hour value can be accomplished with the below line of code:
Dim Hour As Integer = IIf(DateTime.Now.Hour >= 12, DateTime.Now.Hour - 12, DateTime.Now.Hour)
                                    
1:

Select allOpen in new window


The IIf() function evaluates the first parameter and returns the second parameter if the first is true, or the third parameter if the first is false.  Note that 12 in the afternoon becomes a zero, since 12 minus 12 equals zero.  Experienced programmers will immediately point out that this can be accomplished with much shorter code using the Mod function:
Dim Hour As Integer = DateTime.Now.Hour Mod 12
                                    
1:

Select allOpen in new window


Use whatever you’re more comfortable with, as both versions are valid.  One will likely make your eyebrow rise like Dr. Spock, while the other will magically seem to just “make more sense”.  With the Hour component scaled down to values between 0 and 11, we can compute what percentage of 360 it represents:
Dim HourAngle As Integer = Hour / 12 * 360
                                    
1:

Select allOpen in new window


Pretend the time is currently 1:00 PM.  The Hour value would return 13, which we scale down to 1.  Next we divide 1 by 12, and multiply that by 360 to get the angle of 30.  A value of 30 degrees would be pointing down and to the right with respect to the center of our control (see the angles video above).  Unfortunately, this isn’t where the hour hand should be on an analog clock for 1:00 PM!  To get the hour hand at the correct angle, we have to rotate it 90 degrees in the counter-clockwise direction.  This is done by subtracting 90 from the previous calculation making the correct formula for the angle of the hour hand:
Dim HourAngle As Integer = Hour / 12 * 360 – 90
                                    
1:

Select allOpen in new window


The following lists the correct angles for all 24 hours as computed by the above formula:
  • Hours | Angle
  • 0,12 | -90
  • 1,13 | -60
  • 2,14 | -30
  • 3,15 |  0
  • 4,16 |  30
  • 5,17 |  60
  • 6,18 |  90
  • 7,19 |  120
  • 8,20 |  150
  • 9,21 |  180
  • 10,22 |  210
  • 11,23 |  240

To rotate the graphics surface, we simply pass the angle to rotate by to the Graphics.RotateTransform() method.  Keep in mind that the value passed in is not an absolute value, but a relative one.  It instructs the graphics surface to rotate in the clockwise (positive angle) or counter-clockwise (negative angle) direction by the amount passed in.  If you need the rotation to be absolute then you must ensure that the graphics was at zero degrees to begin with.  This can be done by either resetting the surface with Graphics.ResetTransform(), or by rotating the graphics backwards by the opposite amount of any previous rotation.  If you use ResetTranform() don’t forget to move the origin back to your desired center with TranslateTransform() before calling RotateTransform() again.  So to rotate our surface in preparation for drawing the hour hand we would use this line of code:
 
e.Graphics.RotateTransform(HourAngle)
                                    
1:

Select allOpen in new window


Now that we have the angle computed and the surface rotated, let’s actually draw the hour hand on our clock.  Previously I had stated that I would draw all the hands as horizontal lines on the x-axis, extending from the origin and to the right.  So the line will start at (0, 0) and extend to some point on the right (x, 0).  The length of the hand should obviously be less than the radius of our circle, but what is a good value?  Just as we calculated the radius as a percentage of the UserControl size, our hand length can be calculated as a percentage of the radius.  It should be less than radius but allow enough room for the minute hand to be longer.  On most clocks the hour hand is shorter than the minute hand, so I will use 65% for the hour hand and 80% for the minute and second hands.  Here is the code I used to calculate the length of the hour hand and draw it on the clock face in red:
x = radius * 0.65
e.Graphics.DrawLine(Pens.Red, 0, 0, x, 0)
                                    
1:
2:

Select allOpen in new window


Since the surface had already been rotated, drawing on the x-axis to the desired length will make the line appear at the correct angle instead of as a horizontal line (unless the current hour angle happened to be zero).  In preparation for drawing the next hand of the clock, I immediately undo the rotation by passing in the opposite of HourAngle to RotateTransform().  This will leave the graphics surface at a net rotation of zero degrees:
e.Graphics.RotateTransform(-HourAngle)
                                    
1:

Select allOpen in new window


Put all together, the code for computing the angle, rotating the surface, and drawing the hour hand is:
x = radius * 0.65
Dim Hour As Integer = IIf(DateTime.Now.Hour >= 12, DateTime.Now.Hour - 12, DateTime.Now.Hour)
Dim HourAngle As Integer = Hour / 12 * 360 - 90
e.Graphics.RotateTransform(HourAngle)
e.Graphics.DrawLine(Pens.Red, 0, 0, x, 0)
e.Graphics.RotateTransform(-HourAngle)
                                    
1:
2:
3:
4:
5:
6:

Select allOpen in new window


The code for drawing the minute and seconds hands is very similar except that we don’t have to do any scaling of the values before we use them.  Simply compute the percentage of 60 that the current minute/second value is and multiply that by 360.  There are six degrees between each position of the minute/second hands.  Just as with the hour hand, we still need to offset it by -90 degrees so that zero is pointing straight up instead of to the right.  I draw the minute hand in blue and the second hand in black.  Since the hour hand is shorter than the minute hand, we should draw the minute hand first and the hour hand second so that it will be visible when they are the same value.  Here is the complete Paint() event code that draws the clock circle along with hour, minute, and second hands:
Private Sub BareBonesAnalogClock_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint
    Dim center As New Point(Me.ClientSize.Width / 2, Me.ClientSize.Height / 2)
    Dim radius As Integer = Math.Min(Me.ClientSize.Width, Me.ClientSize.Height) / 2 * 0.8
    e.Graphics.TranslateTransform(center.X, center.Y)

    ' the clock circle
    Dim clock As New Rectangle(New Point(0, 0), New Size(1, 1))
    clock.Inflate(radius, radius)
    e.Graphics.DrawEllipse(Pens.Black, clock)

    ' draw the minute hand
    Dim x As Integer = radius * 0.8
    Dim MinuteAngle As Integer = DateTime.Now.Minute / 60 * 360 - 90
    e.Graphics.RotateTransform(MinuteAngle)
    e.Graphics.DrawLine(Pens.Blue, 0, 0, x, 0)
    e.Graphics.RotateTransform(-MinuteAngle)

    ' draw the hour hand
    x = radius * 0.65
    Dim Hour As Integer = IIf(DateTime.Now.Hour >= 12, DateTime.Now.Hour - 12, DateTime.Now.Hour)
    Dim HourAngle As Integer = Hour / 12 * 360 - 90
    e.Graphics.RotateTransform(HourAngle)
    e.Graphics.DrawLine(Pens.Red, 0, 0, x, 0)
    e.Graphics.RotateTransform(-HourAngle)

    ' draw the second hand
    x = radius * 0.8
    Dim SecondAngle As Integer = DateTime.Now.Second / 60 * 360 - 90
    e.Graphics.RotateTransform(SecondAngle)
    e.Graphics.DrawLine(Pens.Black, 0, 0, x, 0)
    e.Graphics.RotateTransform(-SecondAngle)
End Sub
                                    
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:

Select allOpen in new window


Recompile the UserControl by hitting Build --> Build Solution again and the AnalogClock control on your form should now look something like this:
AnalogClockBareBones.jpg
  • 22 KB
  • "Bare Bones" AnalogClock
"Bare Bones" AnalogClock

We just need to add one last component to our control to make it complete.  In its current state, the clock is static and doesn’t update with the current time.  Ideally, the clock should refresh every second to make the hands move properly.  This can be accomplished by adding a Timer() control to our code and handling the Tick() event.  First, let’s declare a new instance of the Timer() control as WithEvents at the class level:
Private WithEvents Tmr As New System.Windows.Forms.Timer
                                    
1:

Select allOpen in new window


Next, across the top of the code editor, change the left dropdown to “Tmr” and the right dropdown to “Tick”.  This should add the blank stub event handler shown below; to which I’ve added one line of code, “Me.Refresh()”, to make the UserControl repaint itself whenever the Tick() event fires:
Private Sub Tmr_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles Tmr.Tick
    Me.Refresh()
End Sub
                                    
1:
2:
3:

Select allOpen in new window


For the Timer to do its job properly we need to set its Interval() property and turn it on.  One good place to do this from would be the Load() event of the UserControl which fires before the control becomes visible for the first time.  To get this event, across the top of the code editor, change the left dropdown to “(AnalogClock Events)” and the right dropdown to “Load”.  In the stub inserted for me, I set the Timer Interval to 1000 to make it fire once every second, and called the Start() method to make it start firing:
Private Sub AnalogClock_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
   Tmr.Interval = 1000
   Tmr.Start()
End Sub
                                    
1:
2:
3:
4:

Select allOpen in new window


That’s it!  We now have a working analog clock UserControl that shows the correct time.  Here is the complete code listing for the “bare bones” version of the control:
Public Class AnalogClock

    Private WithEvents Tmr As New System.Windows.Forms.Timer

    Private Sub AnalogClock_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        Tmr.Interval = 1000
        Tmr.Start()
    End Sub

    Private Sub Tmr_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles Tmr.Tick
        Me.Refresh()
    End Sub

    Private Sub AnalogClock_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint
        Dim center As New Point(Me.ClientSize.Width / 2, Me.ClientSize.Height / 2)
        Dim radius As Integer = Math.Min(Me.ClientSize.Width, Me.ClientSize.Height) / 2 * 0.8
        e.Graphics.TranslateTransform(center.X, center.Y)

        ' the clock circle
        Dim clock As New Rectangle(New Point(0, 0), New Size(1, 1))
        clock.Inflate(radius, radius)
        e.Graphics.DrawEllipse(Pens.Black, clock)

        ' draw the minute hand
        Dim x As Integer = radius * 0.8
        Dim MinuteAngle As Integer = DateTime.Now.Minute / 60 * 360 - 90
        e.Graphics.RotateTransform(MinuteAngle)
        e.Graphics.DrawLine(Pens.Blue, 0, 0, x, 0)
        e.Graphics.RotateTransform(-MinuteAngle)

        ' draw the hour hand
        Dim Hour As Integer = IIf(DateTime.Now.Hour >= 12, DateTime.Now.Hour - 12, DateTime.Now.Hour)
        Dim HourAngle As Integer = Hour / 12 * 360 - 90
        e.Graphics.RotateTransform(HourAngle)
        x = radius * 0.65
        e.Graphics.DrawLine(Pens.Red, 0, 0, x, 0)
        e.Graphics.RotateTransform(-HourAngle)

        ' draw the second hand
        Dim SecondAngle As Integer = DateTime.Now.Second / 60 * 360 - 90
        x = radius * 0.8
        e.Graphics.RotateTransform(SecondAngle)
        e.Graphics.DrawLine(Pens.Black, 0, 0, x, 0)
        e.Graphics.RotateTransform(-SecondAngle)
    End Sub

End Class
                                    
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:

Select allOpen in new window


Admittedly, this is a pretty unimpressive clock!  The control may not be immediately recognizable as an analog clock, and it’s hard to read.  It is true, though, to the original description of “a circle with several lines in it”.  In the next article, we will transform this “bare bones” control into an analog clock that is actually usable in a project:
BareBonesToBasicAnalogClock.jpg
  • 44 KB
  • Boring....but it has potential!
Boring....but it has potential!
    Asked On
    2011-05-02 at 14:11:20ID5329
    Tags

    Visual Basic.Net

    ,

    VB.Net

    ,

    .Net

    ,

    Analog

    ,

    Clock

    ,

    UserControl

    ,

    TranslateTransform

    ,

    RotateTransform

    Topic

    Microsoft Visual Basic.Net

    Views
    1304

    Comments

    Expert Comment

    by: adamtrask on 2011-12-14 at 15:44:43ID: 33670

    extremly useful.... looking for part 2

    Expert Comment

    by: adamtrask on 2011-12-14 at 15:49:58ID: 33671

    where Is part 2 to this article/tutorial.....??
    Please respond.... thanks

    Author Comment

    by: Idle_Mind on 2011-12-14 at 15:55:17ID: 33672

    Hi adamtrask!  I think I'll write up Part 2 over Christmas break.  I'll post a link here when it's up. =)

    Expert Comment

    by: adamtrask on 2011-12-14 at 16:03:31ID: 33673

    Thank you.....

    I downloaded your "Kids stopwatch" from youtube and loved it.... but, I am eager to learn how to code some thing similar... I searched the internet and found a lot of stop watch projects, but nothing so elegantly done..... espcially the way you control the clock handles to se the time.
    I hope you will turn this into a project since you are putting it up for free.....

    Thank you

    Author Comment

    by: Idle_Mind on 2011-12-14 at 16:21:06ID: 33674

    Then I'll definitely include how to set the time with a mouse.  Maybe I'll add in the circular segments that show how much time is left to the alarm as well.

    Expert Comment

    by: younghv on 2011-12-15 at 03:21:06ID: 33693

    Idle_Mind -
    You do some of the best work I've seen on EE.
    Nice Article & a "Yes" vote.

    Expert Comment

    by: adamtrask on 2011-12-15 at 04:05:21ID: 33699

    Adding the circular segments would be great.....

    I was surprised by the degree of clarity and logically lucid step by step approach which was used in the article. Why not start a book in the form of teaching by example.

    A collection of projects that you explain in the manner employed in your article.
    Just an eBook put on sale at a reasonable price.

    It's an idea worth thinking about and would really fill a gap in the current flood of books that teach programming.

    As far as I know there is nothing on the market in this form...!


    Expert Comment

    by: adamtrask on 2012-01-08 at 14:18:29ID: 34308

    Idle_Mind:

    I know we make far too many plans for a holiday like Christmas.
    Usually the holiday comes and goes and we discover it wasn't long enough to do half of what we intended...

    We the above in mind, I ask if we are going to see a second part for the analogue clock article...?

    Thanks

    Add your Comment

    Please Sign up or Log in to comment on this article.

    Join Experts Exchange Today

    Gain Access to all our Tech Resources

    Get personalized answers

    Ask unlimited questions

    Access Proven Solutions

    Search 3.2 million solutions

    Read In-Depth How-To Guides

    1000+ articles, demos, & tips

    Watch Step by Step Tutorials

    Learn direct from top tech pros

    And Much More!

    Your complete tech resource

    See Plans and Pricing

    30-day free trial. Register in 60 seconds.

    Loading Advertisement...

    Top Visual Basic.NET Experts

    1. CodeCruiser

      1,541,075

      Genius

      8,400 points yesterday

      Profile
      Rank: Genius
    2. kaufmed

      303,871

      Wizard

      500 points yesterday

      Profile
      Rank: Genius
    3. Idle_Mind

      230,817

      Guru

      2,010 points yesterday

      Profile
      Rank: Savant
    4. nepaluz

      192,076

      Guru

      0 points yesterday

      Profile
      Rank: Sage
    5. PaulHews

      161,438

      Guru

      520 points yesterday

      Profile
      Rank: Genius
    6. BuggyCoder

      150,598

      Guru

      0 points yesterday

      Profile
      Rank: Sage
    7. JamesBurger

      123,179

      Master

      0 points yesterday

      Profile
      Rank: Sage
    8. emoreau

      112,211

      Master

      0 points yesterday

      Profile
      Rank: Genius
    9. Masteraco

      102,128

      Master

      0 points yesterday

      Profile
      Rank: Wizard
    10. TheLearnedOne

      80,982

      Master

      0 points yesterday

      Profile
      Rank: Savant
    11. Dhaest

      63,803

      Master

      2,000 points yesterday

      Profile
      Rank: Genius
    12. MlandaT

      53,803

      Master

      2,100 points yesterday

      Profile
      Rank: Genius
    13. wdosanjos

      53,796

      Master

      0 points yesterday

      Profile
      Rank: Genius
    14. mlmcc

      53,048

      Master

      0 points yesterday

      Profile
      Rank: Savant
    15. RolandDeschain

      41,679

      10 points yesterday

      Profile
      Rank: Sage
    16. srosebabu

      31,025

      2,000 points yesterday

      Profile
      Rank: Guru
    17. mas_oz2003

      28,400

      0 points yesterday

      Profile
      Rank: Genius
    18. sedgwick

      27,350

      0 points yesterday

      Profile
      Rank: Genius
    19. jacko72

      26,596

      0 points yesterday

      Profile
      Rank: Genius
    20. tommyBoy

      25,850

      0 points yesterday

      Profile
      Rank: Genius
    21. dlmille

      22,160

      0 points yesterday

      Profile
      Rank: Genius
    22. imnorie

      21,664

      1,600 points yesterday

      Profile
      Rank: Genius
    23. Cluskitt

      21,418

      0 points yesterday

      Profile
      Rank: Wizard
    24. robert_schutt

      20,440

      0 points yesterday

      Profile
      Rank: Guru
    25. navneethegde

      20,332

      0 points yesterday

      Profile
      Rank: Wizard

    Hall Of Fame