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.
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.
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.
' --- 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
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.
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.
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.
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
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.
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.
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
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.
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.
' --- 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
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.
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.
' 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 case | Recommended Interval | Notes |
|---|---|---|
| Digital clock (seconds) | 1000 ms | 1 Tick per second; use DateTime.Now |
| Countdown / up timer | 1000 ms | Decrement / increment a counter variable |
| Smooth animation (20 fps) | 50 ms | Call Invalidate() each Tick |
| Smooth animation (60 fps) | 16 ms | Practical minimum; GPU-driven games prefer Game Loop |
| Progress bar update | 50–100 ms | Visual smoothness without CPU waste |
| Auto-refresh (dashboard) | 5 000–60 000 ms | Avoid heavy DB calls more than once per 5 s |
| Blinking alert | 500 ms | Toggle Label.Visible each Tick |
| Debounce (ignore rapid input) | 300–500 ms | Start timer on input, reset on each keystroke; fire on Tick |
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.
27.6 GitHub Copilot — Pomodoro Timer
' 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
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 callTimer1.Start(). TheTickevent 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 callTimer1.Stop()inside the first Tick handler. - Build a countdown timer by storing
_secondsLeftand decrementing it each Tick. Convert to MM:SS with integer division and Mod. - Use
System.Diagnostics.Stopwatchto 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_Loadfor immediate first load. - Toggle
Label.Visible = Not Label.Visiblein 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.Sleepinside a Tick; useAsync/Awaitfor 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 = alarmMinuteand playSystemSounds.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
Stopwatchto 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"
Related Resources
← Lesson 26
Graphics & GDI+ — drawing with Timer.
Lesson 28 →
Animation — moving objects with Timer.
MS Docs — Timer
Full Timer class API reference.
MS Docs — Stopwatch
High-resolution elapsed time measurement.
Featured Books
Visual Basic 2022 Made Easy
Timer-based projects including clocks, countdowns, and animated graphics.
View on Amazon →
VB Programming With Code Examples
Real-world programs using Timer for scheduling, UI animation, and data polling.
View on Amazon →