Public Class Form1
Dim WinFormsTimer As Windows.Forms.Timer
Dim TimersTimer As Timers.Timer
Dim ThreadingTimer As Threading.Timer
' The purpose of this delegate will be exlained below
Delegate Sub ChangeLabelCallback(ByVal [text] As String)
' We're going to need it for output
Dim lbl As Label
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
' Adding our label
lbl = New Label
With lbl
.Location = New Point(20, 20)
.AutoSize = False
.Size = New Size(Me.Width - 40, Me.Height - 40)
.Font = New Font(Me.Font.FontFamily, 14, FontStyle.Bold, GraphicsUnit.Pixel)
End With
Me.Controls.Add(lbl)
' System.Windows.Forms.Timer
' *********************************************************
' A component timer.
' The simplest one. Compiled as a Windows.Forms.component
' and can be 'placed' on a form
' It's syncronous and starts disabled
WinFormsTimer = New Windows.Forms.Timer()
' To enable it we must set the interval:
WinFormsTimer.Interval = 1000 ' It will generate an event each second
' And we should also provide a handler to its Tick event
AddHandler WinFormsTimer.Tick, AddressOf WinFormsTimerTickHandler
' To start the timer we should invoke its Start method:
WinFormsTimer.Start()
' System.Timers.Timer
' *********************************************************
' The .NET Framework documentation refers to the System.Timers.Timer class
' as a server-based timer that was designed and optimized for use in
' multithreaded environments. Instances of this timer class can be safely
' accessed from multiple threads. Unlike the System.Windows.Forms.Timer,
' the System.Timers.Timer class will, by default, call your timer event handler
' on a worker thread obtained from the common language runtime (CLR) thread pool.
' This means that the code inside your Elapsed event handler must conform to a
' golden rule of Win32 programming: an instance of a control should never be
' accessed from any thread other than the thread that was used to instantiate it.
' The System.Timers.Timer class provides an easy way to deal with this dilemma — it
' exposes a public SynchronizingObject property.
' Setting this property to an instance of a Windows Form (or a control on a Windows Form)
' will ensure that the code in your Elapsed event handler runs on the same thread on
' which the SynchronizingObject was instantiated.
TimersTimer = New Timers.Timer
TimersTimer.Interval = 3000 ' We want it to 'fire' each 3 second
' We need some event handler to perform periodical tasks (it has the Elapsed event)
AddHandler TimersTimer.Elapsed, AddressOf TimersTimerElapsedHandler
' This class works in a thread-friendly manner
' We don't have to check whether we're in the working thread or not
' To do so we must tell the timer what object it has to be syncronous to.
' We do it this way:
TimersTimer.SynchronizingObject = Me
' And of cours we have to start this timer
' (there is also Stop method)
TimersTimer.Start()
' System.Threading.Timer
' *********************************************************
' Threading timer. The most precise timer available
' Since it is asyncronous it doesn't depend on the current
' execution thread.
' To syncronize with the main workflow you can use
' a syncronization object which can report the
' state of an executing thread to the callback procedure
' Callback procedure is a procedure with a signature
' identical to Threading.TimerCallBack delegate.
' You provide its address to the timer and it will be
' invoking it each time a timer event is generated.
Dim SyncObject As New Object ' Specifies a syncronization object
Dim Delay As Integer = 5000 ' Specifies a delay before the first callback is invoked
Dim Interval As Integer = 5000 ' Specifies the time interval in milliseconds between each callback call
ThreadingTimer = New Threading.Timer(AddressOf ThreadingTimerCallback, SyncObject, Delay, Interval)
End Sub
' Handling of Timer events
' *****************************************************************
' This is our Winforms Timer Tick handler it is called each second.
' But it can 'miss' some ticks when our form class is busy with something
Private Sub WinFormsTimerTickHandler(ByVal sender As Object, ByVal e As System.EventArgs)
' We'll get this handler invoked each second
Static X As Integer
' We're going to change the title of our window each time this timer is fired
X += 1
lbl.Text = String.Format("Ticks {0}", X)
Debug.WriteLine("Windows.Forms.Timer Tick")
End Sub
' This is a handler for Timers.Timer
' It's generally more precise but still not as precise as Threading.Timer
' It is invoked every 3 seconds
Private Sub TimersTimerElapsedHandler(ByVal sender As Object, ByVal e As System.Timers.ElapsedEventArgs)
' Since we provided the syncronization object we don't have to worry about
' cross-thread calls and thus we can access our form properties directly:
lbl.Text = " Winforms Timer stopped with TimersTimer"
' We'll control winforms timer here.
' Every three secodns we're going to turn it off
' and after another three seconds - turn it on again.
WinFormsTimer.Enabled = Not WinFormsTimer.Enabled
Debug.WriteLine("Timer Elapsed event")
End Sub
' This is the ThreadingTimer callback procedure
' It will be invoked each 5 seconds and receive a syncronization object
' we passed in its initializer
Private Sub ThreadingTimerCallback(ByVal state As Object)
' Note that the code here will be executed asyncronously in another thread
' Try to uncomment the following line and see what happens in 5 seconds:
' lbl.Text = "We don't care about cross-thread calls!"
' Spoiler: You'd get an exception.
' Instead, let's call ChangeLabel method (see below)
ChangeLabel("We care about cross-thread calls!")
Debug.WriteLine("Threading Timer event")
End Sub
' The whole purpose of this sub is to change the label text
Private Sub ChangeLabel(ByVal Text As String)
' To work safe we need to act thread-aware and
' check whether we're in the same thread:
If lbl.InvokeRequired Then
' Apparently not, we're in the wrong thread
' so we need to marshal this call back to the UI thread
' Do you remember that delegate in the beginning?
' We need it to invoke this very method once again,
' but in the right thread:
Dim d As New ChangeLabelCallback(AddressOf ChangeLabel)
Me.Invoke(d, New Object() {[Text]})
Else
' Yes! We're in the right thread and can finally change the label text:
lbl.Text = Text
End If
End Sub
End Class