Graphics – Screen Updating

Graphics – Screen Updating

A clock is used to show how to draw the image background separately from the image components that need to be updated periodically.  This process reduces the load on the system when the display is updated frequently.

This example does not require any design-time components.  It uses only a timer, and that is created from code.

The example refers to the procedure described here.  It involves creating the (relatively) static components of the display as a bitmap only when required,  This is at startup and when the form size changes.  This bitmap is then drawn to the display in the Paint event, and the dynamic components (the hour, minute and second hands) are drawn over the bitmap.

If the program had a menu and options to change the display (for instance, to change the background colours or to add numerals) then the clock face would be redrawn when these changes were made.  Otherwise, the code to draw the clockface (which is fairly complex, as it involves a large number of tickmarks) is only executed when required.  The hands, which are just three coloured lines, are re-drawn ten times every second. The process of copying in the prepared bitmap is many times faster than drawing the face each time.

Option Strict On
Public Class Form1

Friend WithEvents myTimer As New Timer
Private TickSum As Decimal
Private FormCenter As Point
Private BMP As Bitmap

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
  Me.WindowState = FormWindowState.Maximized
  Me.DoubleBuffered = True
  myTimer.Interval = 100
End Sub

Sub DrawClock()
  BMP = New Bitmap(Me.ClientSize.Width, Me.ClientSize.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb)
  Dim g As Graphics = Graphics.FromImage(BMP)
  'Used to hold the formCenter.>>
  Me.BackColor = Color.Black
  'Get the center of the form.>>
  Dim centreX As Integer = Me.Width \ 2
  Dim centreY As Integer = (Me.Height \ 2) - 10
  'Assign it to the formCenter point.>>
  FormCenter = New Point(centreX, centreY)
  'Set the radius to the full half-height-40.>>
  Dim radius As Integer = centreY - 40

  'Draw the minute positions.>>
  Dim x1 As Double
  Dim y1 As Double
  Dim x2 As Double
  Dim y2 As Double
  Dim myPen As New Pen(Color.White, 5)
  For num As Double = 0 To 2 * Math.PI Step (2 * Math.PI) / 60
    x1 = radius * Math.Cos(num) + FormCenter.X
    y1 = radius * Math.Sin(num) + FormCenter.Y
    x2 = (radius - 15) * Math.Cos(num) + FormCenter.X
    y2 = (radius - 15) * Math.Sin(num) + FormCenter.Y
    g.DrawLine(myPen, CInt(x1), CInt(y1), CInt(x2), CInt(y2))
  'Draw the 12 hour positions.>>
  myPen = New Pen(Color.Blue, 10)
  For num As Double = 0 To 2 * Math.PI Step (2 * Math.PI) / 12
    x1 = radius * Math.Cos(num) + formCenter.X
    y1 = radius * Math.Sin(num) + formCenter.Y
    x2 = (radius - 40) * Math.Cos(num) + formCenter.X
    y2 = (radius - 40) * Math.Sin(num) + formCenter.Y
    g.DrawLine(myPen, CInt(x1), CInt(y1), CInt(x2), CInt(y2))
End Sub

Private Sub Form1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint
  Dim x As Double
  Dim y As Double
  e.Graphics.DrawImage(BMP, New Point(0, 0))

  Dim hrHand As Double = tickSum Mod 43200
  Dim minHand As Double = tickSum Mod 3600
  Dim secHand As Double = tickSum Mod 60
  Dim radius As Integer = FormCenter.Y - 40

  Dim hrPen As New Pen(Color.LightGreen, 12)
  x = 0.7 * radius * Math.Cos((2 * Math.PI * hrHand / 43200) + 1.5 * Math.PI) + FormCenter.X
  y = 0.7 * radius * Math.Sin((2 * Math.PI * hrHand / 43200) + 1.5 * Math.PI) + FormCenter.Y
  e.Graphics.DrawLine(hrPen, FormCenter.X, FormCenter.Y, CInt(x), CInt(y))

  Dim minPen As New Pen(Color.Yellow, 6)
  x = 0.95 * radius * Math.Cos((2 * Math.PI * minHand / 3600) + 1.5 * Math.PI) + FormCenter.X
  y = 0.95 * radius * Math.Sin((2 * Math.PI * minHand / 3600) + 1.5 * Math.PI) + FormCenter.Y
  e.Graphics.DrawLine(minPen, FormCenter.X, FormCenter.Y, CInt(x), CInt(y))

  Dim secPen As New Pen(Color.Red, 1)
  x = radius * Math.Cos((2 * Math.PI * secHand / 60) + (1.5 * Math.PI)) + FormCenter.X
  y = radius * Math.Sin((2 * Math.PI * secHand / 60) + (1.5 * Math.PI)) + FormCenter.Y
  e.Graphics.DrawLine(secPen, FormCenter.X, FormCenter.Y, CInt(x), CInt(y))

End Sub

Private Sub myTimer_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles myTimer.Tick
  Dim ticksInHalfADay As Long = 864000000000 \ 2
  TickSum = CDec((Now.Ticks Mod ticksInHalfADay) / 10000000)
  Me.Text = "VB.Net clock: " & Now.ToLongTimeString
End Sub

Private Sub Form1_ResizeEnd(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.ResizeEnd
End Sub
End Class
  1. #1 by Mr John Anthony Oliver on February 10, 2016 - 3:38 am

    The three lines are actually updated more frequently than once every second in the above code.
    It is actually once every 1/10th of a second as there are 1000 computer ticks in a seconds, as far as I remember.
    This gives a smoother appearance to the movement of the second hand.
    How do I know?
    Well the above code was written by me, Mr John Anthony Oliver and it may be found on the Microsoft VB.Net forums.
    If I find the thread in question I will post a link here.

  2. #2 by vbdotnetblogg on February 10, 2016 - 5:46 am

    Thank you – I have updated the description to ‘ten times a second’.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s