Lesson 27 · Using the Timer

Using the Timer Control

Build real-time clocks, countdown timers, stopwatches, progress indicators, and auto-refresh loops using VB 2026's Timer control — the engine behind all time-driven Windows applications.

Key Takeaway: A Timer fires its Tick event repeatedly at a fixed interval (in milliseconds). Set Timer1.Interval and call Timer1.Start() — or set Enabled = True — to begin. Stop it with Timer1.Stop(). The Tick handler runs on the UI thread, so you can safely update labels, progress bars, and call picBox.Invalidate() without marshalling. For one-shot delays, start the timer, then call Timer1.Stop() inside the first Tick. The minimum practical interval is around 15–20 ms on Windows; for sub-millisecond precision use System.Diagnostics.Stopwatch instead.
Interval
Integer (ms)
Milliseconds between Tick events. 1000 = once per second.
Enabled
Boolean
True = running, False = stopped. Setting True starts the timer.
Start()
Method
Shorthand for Enabled = True. Begins firing Tick events.
Stop()
Method
Shorthand for Enabled = False. Stops all future Tick events.
Tick event
Event
Fires every Interval ms while Enabled = True. Your code goes here.
DateTime.Now
DateTime
Current system clock. Use in Tick to display real time.
Stopwatch
Class
High-resolution elapsed time. Start/Stop/Reset. ElapsedMilliseconds.
Invalidate()
Method
Call in Tick to trigger a Paint event — the animation heartbeat.

27.1 Timer Basics — Interval, Enabled, Tick

Drag a Timer from the Toolbox onto your form — it appears in the component tray below the designer, not on the form surface. Set its Interval (in milliseconds), then double-click it to create the Tick handler. Everything you put in Tick runs repeatedly at that interval.

TimerBasics.vb — Visual Basic 2026
' --- Designer setup (set in Properties window) ---
' Timer1.Interval = 1000   ' 1 second
' Timer1.Enabled  = False  ' don't start until user clicks

' --- Start / Stop from buttons ---
Private Sub btnStart_Click(...) Handles btnStart.Click
    Timer1.Start()              ' same as Timer1.Enabled = True
    btnStart.Enabled = False
    btnStop.Enabled  = True
End Sub

Private Sub btnStop_Click(...) Handles btnStop.Click
    Timer1.Stop()               ' same as Timer1.Enabled = False
    btnStart.Enabled = True
    btnStop.Enabled  = False
End Sub

' --- The Tick handler ---
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
    ' This runs every Interval ms on the UI thread — safe to update controls
    lblClock.Text = DateTime.Now.ToString("HH:mm:ss")
End Sub

' --- Change interval at runtime ---
Timer1.Stop()
Timer1.Interval = 500   ' change to 0.5 s
Timer1.Start()

' --- One-shot delay (fire once, then stop) ---
Private Sub ShowSplashThenHide()
    frmSplash.Show()
    Timer1.Interval = 3000   ' 3 seconds
    Timer1.Start()
End Sub

Private Sub Timer1_Tick(...) Handles Timer1.Tick
    Timer1.Stop()              ' stop immediately — runs only once
    frmSplash.Hide()
End Sub
Timer Resolution on Windows

The Windows Timer control has a minimum practical resolution of about 15–20 ms (the OS timer slice). Setting Interval = 1 will not fire every 1 ms — it fires roughly every 15 ms. For precise elapsed-time measurement use System.Diagnostics.Stopwatch, which uses the hardware performance counter and is accurate to sub-microseconds.

Try It — Simulation 27.1: Digital Clock

A live digital clock driven by a simulated Timer1.Interval = 1000. Start, stop, and change the time format. The Tick counter shows how many times the event has fired.

Digital Clock — Timer1.Interval = 1000
--:--:--
Tick #0  |  Interval: 1000 ms  |  Stopped
Format:
Interval:

27.2 Countdown Timer

A countdown timer decrements a counter each second. When it reaches zero, stop the timer and notify the user. Track elapsed time with a module-level variable decremented in each Tick.

Countdown.vb — Visual Basic 2026
Private _secondsLeft As Integer

Private Sub btnStartCountdown_Click(...) Handles btnStartCountdown.Click
    _secondsLeft = CInt(nudSeconds.Value)    ' set from NumericUpDown
    If _secondsLeft < 1 Then Return
    UpdateCountdownDisplay()
    Timer1.Interval = 1000
    Timer1.Start()
    btnStartCountdown.Enabled = False
    btnReset.Enabled          = True
End Sub

Private Sub Timer1_Tick(...) Handles Timer1.Tick
    _secondsLeft -= 1
    UpdateCountdownDisplay()
    If _secondsLeft <= 0 Then
        Timer1.Stop()
        lblDisplay.Text = "TIME'S UP!"
        btnStartCountdown.Enabled = True
        SystemSounds.Beep.Play()      ' play system beep
    End If
End Sub

Private Sub UpdateCountdownDisplay()
    Dim mins = _secondsLeft \ 60
    Dim secs = _secondsLeft Mod 60
    lblDisplay.Text = $"{mins:D2}:{secs:D2}"
    ' Change colour when under 10 seconds
    lblDisplay.ForeColor = If(_secondsLeft <= 10, Color.Red, Color.LimeGreen)
End Sub

Private Sub btnReset_Click(...) Handles btnReset.Click
    Timer1.Stop()
    _secondsLeft = 0
    lblDisplay.Text = "00:00"
    lblDisplay.ForeColor = Color.LimeGreen
    btnStartCountdown.Enabled = True
    btnReset.Enabled          = False
End Sub
Try It — Simulation 27.2: Countdown Timer

Set a time in seconds, start the countdown. The display turns amber at 10s and red at 5s — then "TIME'S UP!" when it reaches zero. Shows the exact Tick handler logic each second.

Countdown Timer
00:30
Ready — set time and press Start
Seconds:

27.3 Stopwatch — Elapsed Time Measurement

System.Diagnostics.Stopwatch measures elapsed time with hardware-level precision. Combine it with a Timer to update a display every 100 ms — you get a smooth stopwatch while measuring time accurately.

Stopwatch.vb — Visual Basic 2026
Imports System.Diagnostics

Private _sw As New Stopwatch()
Private _lapTimes As New List(Of TimeSpan)

Private Sub btnStart_Click(...) Handles btnStart.Click
    _sw.Start()          ' resumes if already running
    Timer1.Interval = 50 ' update display 20× per second
    Timer1.Start()
    btnStart.Enabled = False
    btnStop.Enabled  = True
End Sub

Private Sub btnStop_Click(...) Handles btnStop.Click
    _sw.Stop()
    Timer1.Stop()
    btnStart.Enabled = True
    btnStop.Enabled  = False
End Sub

Private Sub btnReset_Click(...) Handles btnReset.Click
    _sw.Reset()               ' stops and zeroes the stopwatch
    Timer1.Stop()
    _lapTimes.Clear()
    lstLaps.Items.Clear()
    lblDisplay.Text = "00:00.000"
End Sub

Private Sub btnLap_Click(...) Handles btnLap.Click
    If Not _sw.IsRunning Then Return
    _lapTimes.Add(_sw.Elapsed)
    lstLaps.Items.Add($"Lap {_lapTimes.Count}: {_sw.Elapsed:mm\:ss\.fff}")
End Sub

Private Sub Timer1_Tick(...) Handles Timer1.Tick
    ' Timer updates display; Stopwatch measures time
    Dim ts = _sw.Elapsed
    lblDisplay.Text = $"{ts.Minutes:D2}:{ts.Seconds:D2}.{ts.Milliseconds:D3}"
End Sub
Try It — Simulation 27.3: Stopwatch with Laps

A fully functional stopwatch. The Stopwatch class tracks elapsed time precisely; the Timer (50 ms) updates the display. Record laps and see the split times.

Stopwatch with Laps — Timer + System.Diagnostics.Stopwatch
00:00.000
Lap times:
(no laps recorded)

27.4 Progress Bar and Auto-Refresh Patterns

Timers are ideal for updating progress indicators and auto-refreshing data at regular intervals — polling a sensor, refreshing a dashboard, or running a background heartbeat.

ProgressRefresh.vb — Visual Basic 2026
' --- Animated progress bar (simulating a long task) ---
Private _progress As Integer = 0

Private Sub btnBeginTask_Click(...) Handles btnBeginTask.Click
    _progress = 0
    progressBar1.Value = 0
    Timer1.Interval    = 80    ' tick every 80 ms
    Timer1.Start()
    btnBeginTask.Enabled = False
End Sub

Private Sub Timer1_Tick(...) Handles Timer1.Tick
    _progress += 2             ' 2% per tick → 100% in 50 ticks × 80ms = 4 s
    progressBar1.Value = Math.Min(_progress, 100)
    lblPercent.Text    = $"{_progress}%"
    If _progress >= 100 Then
        Timer1.Stop()
        lblPercent.Text      = "Complete!"
        btnBeginTask.Enabled = True
    End If
End Sub

' --- Auto-refresh dashboard every 5 seconds ---
Private Sub Form_Load(...) Handles MyBase.Load
    RefreshDashboard()                  ' show data immediately
    Timer1.Interval = 5000
    Timer1.Start()                      ' refresh every 5 s
End Sub

Private Sub Timer1_Tick(...) Handles Timer1.Tick
    RefreshDashboard()
End Sub

Private Sub RefreshDashboard()
    lblLastUpdated.Text = $"Updated: {DateTime.Now:HH:mm:ss}"
    ' ... load from database, API, sensor, etc.
End Sub

' --- Blinking label alert ---
Private Sub Timer1_Tick(...) Handles Timer1.Tick
    lblAlert.Visible = Not lblAlert.Visible   ' toggle every Tick
End Sub
Try It — Simulation 27.4: Progress Bar & Auto-Refresh

Run a simulated task with animated progress, or watch the auto-refresh dashboard update on a 3-second cycle. Toggle the blinking alert to see the visibility-flip pattern.

Progress Bar & Auto-Refresh
💾 Task Progress
0% Idle
📊 Dashboard (3 s refresh)
Not started
Blinking alert:
⚠ ALERT

27.5 Multiple Timers and Precise Timing

You can add as many Timer components as your form needs — each with its own interval and Tick handler. Use different timers for different update rates: one at 1000 ms for the clock, one at 50 ms for smooth animation, one at 30 000 ms for data polling.

MultiTimer.vb — Visual Basic 2026
' Three timers with different rates on one form

' Timer1 — 1000 ms — update clock label
Private Sub Timer1_Tick(...) Handles Timer1.Tick
    lblClock.Text = DateTime.Now.ToString("HH:mm:ss")
End Sub

' Timer2 — 50 ms — smooth animation repaint
Private Sub Timer2_Tick(...) Handles Timer2.Tick
    picCanvas.Invalidate()   ' ~20 fps
End Sub

' Timer3 — 30000 ms — poll database / API
Private Sub Timer3_Tick(...) Handles Timer3.Tick
    LoadLatestDataAsync()
End Sub

' --- Precise elapsed time with Stopwatch ---
Dim sw As New System.Diagnostics.Stopwatch()

Private Sub btnBenchmark_Click(...) Handles btnBenchmark.Click
    sw.Restart()                    ' reset and start in one call
    DoExpensiveWork()
    sw.Stop()
    lblTime.Text = $"Elapsed: {sw.ElapsedMilliseconds} ms"
    ' For sub-millisecond: sw.ElapsedTicks / Stopwatch.Frequency
End Sub

' --- Avoid UI freezes: don't do heavy work in Tick ---
' BAD:  Thread.Sleep(5000) inside Tick freezes the UI
' GOOD: Start an Async task and await it, or use BackgroundWorker
Private Async Sub Timer1_Tick(...) Handles Timer1.Tick
    Timer1.Stop()                   ' prevent re-entrancy
    Await LoadDataAsync()           ' non-blocking await
    Timer1.Start()
End Sub
Use caseRecommended IntervalNotes
Digital clock (seconds)1000 ms1 Tick per second; use DateTime.Now
Countdown / up timer1000 msDecrement / increment a counter variable
Smooth animation (20 fps)50 msCall Invalidate() each Tick
Smooth animation (60 fps)16 msPractical minimum; GPU-driven games prefer Game Loop
Progress bar update50–100 msVisual smoothness without CPU waste
Auto-refresh (dashboard)5 000–60 000 msAvoid heavy DB calls more than once per 5 s
Blinking alert500 msToggle Label.Visible each Tick
Debounce (ignore rapid input)300–500 msStart timer on input, reset on each keystroke; fire on Tick
Try It — Simulation 27.5: Multi-Timer Dashboard

Three independent timers running simultaneously. Start each one separately and watch them fire at their own rates. The Tick log shows which timer fired and when.

Multi-Timer Dashboard
TIMER1 — 1000 ms
00:00:00
Ticks: 0
TIMER2 — 200 ms
0%
Ticks: 0
TIMER3 — 3000 ms
No data yet
Ticks: 0
Tick log (most recent first):

27.6 GitHub Copilot — Pomodoro Timer

GitHub Copilot — Pomodoro Work/Break Timer
You typed: ' Build a Pomodoro timer: 25 min work → 5 min short break → repeat. After 4 pomodoros, 15 min long break. Show progress ring drawn with GDI+, play a sound on phase change, track completed pomodoro count.'
' Pomodoro timer — work/break cycles with GDI+ progress ring.
Enum PomodoroPhase
    Work : ShortBreak : LongBreak
End Enum

Private _phase      As PomodoroPhase = PomodoroPhase.Work
Private _totalSecs  As Integer = 25 * 60   ' 25 min work
Private _secsLeft   As Integer = _totalSecs
Private _pomodoros  As Integer = 0

Private Sub Timer1_Tick(...) Handles Timer1.Tick
    _secsLeft -= 1
    picRing.Invalidate()                         ' redraw progress ring
    If _secsLeft <= 0 Then AdvancePhase()
End Sub

Private Sub AdvancePhase()
    SystemSounds.Beep.Play()
    If _phase = PomodoroPhase.Work Then
        _pomodoros += 1
        lblPomodoros.Text = $"🍅 × {_pomodoros}"
        _phase = If(_pomodoros Mod 4 = 0, PomodoroPhase.LongBreak, PomodoroPhase.ShortBreak)
    Else
        _phase = PomodoroPhase.Work
    End If
    _totalSecs = Select Case _phase
        Case PomodoroPhase.Work       : 25 * 60
        Case PomodoroPhase.ShortBreak : 5  * 60
        Case Else                      : 15 * 60
    End Select
    _secsLeft = _totalSecs
End Sub

Private Sub picRing_Paint(...) Handles picRing.Paint
    Dim g = e.Graphics
    g.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
    Dim cx = picRing.Width \ 2, cy = picRing.Height \ 2, r = 55
    ' Background ring
    Using pen As New Pen(Color.LightGray, 10)
        g.DrawEllipse(pen, cx-r, cy-r, r*2, r*2)
    End Using
    ' Progress arc
    Dim pct   = 1 - (_secsLeft / _totalSecs)
    Dim sweep = CSng(pct * 360)
    Dim arc   = If(_phase = PomodoroPhase.Work, Color.Tomato, Color.MediumSeaGreen)
    Using pen As New Pen(arc, 10)
        pen.StartCap = Drawing2D.LineCap.Round
        pen.EndCap   = Drawing2D.LineCap.Round
        g.DrawArc(pen, cx-r, cy-r, r*2, r*2, -90, sweep)
    End Using
    ' Time label
    Dim mins = _secsLeft \ 60, secs = _secsLeft Mod 60
    Using font  As New Font("Segoe UI", 14, FontStyle.Bold)
    Using brush As New SolidBrush(Color.DimGray)
        Dim t = $"{mins:D2}:{secs:D2}"
        Dim sz = g.MeasureString(t, font)
        g.DrawString(t, font, brush, cx - sz.Width/2, cy - sz.Height/2)
    End Using : End Using
End Sub
Copilot Chat Prompts for This Lesson

Try these in the Copilot Chat panel:

  • "Add a TypeWriter effect: a Timer fires every 60 ms and appends one character at a time from a long string into lblOutput.Text"
  • "Build a debounce helper: when the user types in a TextBox, reset a Timer (350 ms). On Tick, fire the search and stop the timer — so searching only triggers 350 ms after the user stops typing"
  • "Create a traffic-light simulation with three PictureBoxes (red, amber, green). Use a single Timer to cycle red→red+amber→green→amber→red with realistic UK timings"
  • "Show a Toast notification: a Panel slides up from the bottom of the form using a Timer-driven animation, stays for 3 seconds, then slides back down"

Lesson Summary

  • Drag a Timer component onto the form. Set Interval (ms) and call Timer1.Start(). The Tick event fires repeatedly on the UI thread — safe to update labels, progress bars, and controls directly.
  • Stop with Timer1.Stop(). For a one-shot delay, start the timer and call Timer1.Stop() inside the first Tick handler.
  • Build a countdown timer by storing _secondsLeft and decrementing it each Tick. Convert to MM:SS with integer division and Mod.
  • Use System.Diagnostics.Stopwatch to measure elapsed time precisely. Combine it with a Timer (50 ms) to update the display without sacrificing measurement accuracy.
  • Use a Timer for auto-refresh: call your data-loading method in Tick, set a comfortable interval (5–30 s). Call the method once in Form_Load for immediate first load.
  • Toggle Label.Visible = Not Label.Visible in Tick for a blinking alert at your chosen blink rate.
  • You can add multiple timers to one form, each with its own interval. Keep Tick handlers short — never call Thread.Sleep inside a Tick; use Async/Await for any I/O work.

Exercises

Exercise 27.1 — Digital Alarm Clock

  • Display the current time using DateTime.Now.ToString("HH:mm:ss") updated every second.
  • Let the user set an alarm time with two NumericUpDown controls (hour and minute).
  • In each Tick, compare DateTime.Now.Hour = alarmHour AndAlso DateTime.Now.Minute = alarmMinute and play SystemSounds.Exclamation.Play().
  • Show a flashing "ALARM!" label (toggle Visible in Tick) when the alarm fires. Stop blinking after 30 seconds.
  • Copilot challenge: "Draw the clock face as a GDI+ analog clock instead of a digital display, using the pattern from Lesson 26"

Exercise 27.2 — Reaction Time Tester

  • On button click, start a Timer with a random interval between 1000 and 5000 ms.
  • When Tick fires, show a green PictureBox and start a Stopwatch to measure how quickly the user clicks it.
  • On PictureBox click, stop the Stopwatch and display the reaction time in milliseconds.
  • Track the last 5 attempts and show average reaction time.
  • Copilot challenge: "Show a colour-coded rating: <200 ms = 'Lightning', 200–400 ms = 'Good', 400–600 ms = 'Average', >600 ms = 'Slow'"

Exercise 27.3 — Traffic Light Simulator

  • Three PictureBoxes (red, amber, green) stacked vertically in a panel.
  • A single Timer cycles through: Red (4 s) → Red+Amber (1 s) → Green (3 s) → Amber (1 s) → back to Red.
  • Each Tick checks a phase counter and sets the correct PictureBoxes' BackColor — active colour for the lit phase, dark grey for unlit.
  • Show a label with the phase name and remaining seconds.
  • Copilot challenge: "Add a pedestrian crossing button: pressing it starts a 3 s timer after the current green phase and then holds red for 5 s"

Next: Lesson 28 — Animation

Combine Timer with GDI+ to build smooth moving animations — bouncing balls, scrolling text, sprite movement, and collision detection.

Continue »

Related Resources


Featured Books

Visual Basic 2022 Made Easy

Visual Basic 2022 Made Easy

by Dr. Liew Voon Kiong

Timer-based projects including clocks, countdowns, and animated graphics.

View on Amazon →
VB Programming With Code Examples

VB Programming With Code Examples

by Dr. Liew Voon Kiong

Real-world programs using Timer for scheduling, UI animation, and data polling.

View on Amazon →