Click here to Skip to main content
15,867,488 members
Articles / Programming Languages / XML

Asynchronous Event-driven Controls Update in Winforms

Rate me:
Please Sign up or sign in to vote.
5.00/5 (4 votes)
14 Oct 2020Public Domain4 min read 8.1K   204   6   10
Updating controls with a background worker with event-driven trigger
Updating controls, while performing blocking operations, can be tricky in Winforms. In the example below, we’ll show how to update TX-RX virtual LEDs pictureboxes, and at the same time, perform serial communication.

Introduction

As obsolete as it may seem, serial communication over RS232, RS485 or virtual USB COM ports still plays a role in the industry. Whether it is exchanging data with a CNC machine or other industrial machinery, or interfacing via Modbus with a HVAC equipment or appliance, serial communication is hidden but ubiquitous.

When designing the user interface of SCADAs or graphic panels, it is paramount to make sure proper visual updates to the user are constantly returned, even during long and excruciating data exchange session, or while performing time-consuming operations. Labels, picture boxes, gauges and other controls need to be constantly updated in the background.

One of the frequently asked features from my colleagues is to have a visual representation of RX/TX data over the serial line, as most of the cheap serial adapters and interfaces do not feature any data LEDs.

This article will show how to user the background workers for the UI update of two picture boxes mimicking a red and a green LEDs. The concept can be extended, of course, to all visual controls that need to be updated asynchronously.

Setting It Up

Step by Step Walk-through

We’ll start setting up the form. For this example, we just need a form with two pictureboxes, one for the TX LED and the other for the RX LED. We’ll also set their visibility to False and resize them to properly resemble two LEDs in the up left corner of our form. We’ll call them pbSend (TX) and pbReceive (RX).

The next step is declaring the delegates and background workers needed. The UI update must be in the main thread, and because the background workers run on threads other than the main, we shall set up a delegate that will do that for us.

We’ll also declare the background workers themselves, one for the TX led, and the other for the RX led.

VB.NET
Private WithEvents sp As New SerialPort
Private Delegate Sub LEDSwitchDelegate(ByRef a As PictureBox, ByVal b As Boolean)
Private WithEvents backgroundWorker1 As New BackgroundWorker()
Private WithEvents backgroundWorker2 As New BackgroundWorker()

The actual sub that will provide switching ON and OFF the two LEDs is the one below. We’ll call it by passing the picturebox name and its visibility as argument. For example:

VB.NET
LEDSwitch(pbSend, True)

It will cause the pbSend LED to be switched ON. This will work on main thread and any other thread, thanks to the delegate!

VB.NET
Private Sub LEDSwitch(ByRef a As PictureBox, ByVal b As Boolean)
    If Me.InvokeRequired Then
        Dim d As New LEDSwitchDelegate(AddressOf Me.LEDSwitch)
        Me.BeginInvoke(d, New Object() {a, b})
    Else
        Try
            a.Visible = b
        Catch ex As Exception
        End Try
    End If
End Sub

In my project, the LEDs shall switch ON for 500ms any time one or more bytes is sent or received, therefore the background worker shall run for that exact amount of time.

VB.NET
Private Sub backgroundWorker1_DoWork(ByVal sender As Object, _
ByVal e As DoWorkEventArgs) Handles backgroundWorker1.DoWork
    Thread.Sleep(500)
End Sub
Private Sub backgroundWorker2_DoWork(ByVal sender As Object, _
ByVal e As DoWorkEventArgs) Handles backgroundWorker2.DoWork
    Thread.Sleep(500)
End Sub

There’s not much they will do, except waiting for 500ms (here your mileage may vary and might make a better use of the background workers wasted cycles).

After the work is completed (just a 500ms non-blocking snooze), the LED shall be turned OFF again. We’ll make use of the RunWorkerCompleted event associated with each of the two background workers.

VB.NET
    Private Sub backgroundWorker1_RunWorkerCompleted(ByVal sender As Object, _
ByVal e As RunWorkerCompletedEventArgs) Handles backgroundWorker1.RunWorkerCompleted
        LEDSwitch(pbSend, False)
    End Sub
    Private Sub backgroundWorker2_RunWorkerCompleted(ByVal sender As Object, _
    ByVal e As RunWorkerCompletedEventArgs) Handles backgroundWorker2.RunWorkerCompleted
        LEDSwitch(pbReceive, False)
    End Sub

Ok, we switched OFF the LEDs, but how do we switch then ON in the first place, for example when data is received?

We’ll use a Sub dedicated to that, and we will bind the Sub to the DataReceived event of the Serialport class instance. Because TransmittingData() is on the main thread, we can switch ON the LED directly. We make sure the background workers aren’t already busy to avoid calling them multiple times.

VB.NET
Private Sub TransmittingData()
    LEDSwitch(pbSend, True)
    If Not backgroundWorker1.IsBusy Then
        ' Start the asynchronous operation.
        backgroundWorker1.RunWorkerAsync()
    End If
End Sub
Private Sub ReceivingData()
    LEDSwitch(pbReceive, True)
    If Not backgroundWorker2.IsBusy Then
        ' Start the asynchronous operation.
        backgroundWorker2.RunWorkerAsync()
    End If
End Sub

The last Sub we need is just the form_Load method, where we add the event handler of the DataReceived event and bind it to ReceivingData().

VB.NET
Private Sub AsynchPictureboxes_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    AddHandler sp.DataReceived, AddressOf ReceivingData
End Sub

Unfortunately, the System.IO.Ports.SerialPort class does not include a DataSent event, so we can’t directly bind it, but this is easily overcome by the fact that whenever one or more bytes need to be transmitted, we would probably already have a Sub for that. We’ll use the Sub where the Serialport.Write method is called to call the TransmittingData() Sub too.

As seen, we do not need to worry to switch OFF the LEDs. The background workers will take care of that.

Further Developments

It would be possible to extend the methods:

  1. A port or data error can be displayed and identified, e.g., the two LEDs blinking rapidly at the same time. In this case, we can bind the event as follows, and write a specific DataError method to deal with such occurrence.
    VB.NET
    AddHandler sp.ErrorReceived, AddressOf DataError
  2. Further methods can be developed to display, e.g., CRC, Checksum errors, etc.
  3. The frequency of the LEDs blink can be changed programmatically, by passing the appropriate pulse time to the background workers, in response to specific messages or datagrams.
  4. One or more comboboxes and textboxes can be programmatically populated by background workers, e.g., in real time with data incoming from the serial communication, without keeping the main thread busy and unresponsive.

Conclusion and Points of Interest

The article was intended to show a quick and dirty way to update the UI from background workers, especially in conjunction with slow data rate transfers, or time-consuming operations that might keep the UI busy. The example provided can easily be extended and modified to suit the needs of programmers still dealing with Winforms technology, and in need to programmatically and frequently update multiple controls in a form, without blocking the user interaction with the application interface.

History

  • 14th October, 2020: Initial version

License

This article, along with any associated source code and files, is licensed under A Public Domain dedication


Written By
Italy Italy
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
SuggestionReactive extensions Pin
Chad Rotella15-Oct-20 14:20
professionalChad Rotella15-Oct-20 14:20 
GeneralRe: Reactive extensions Pin
Padanian15-Oct-20 23:36
Padanian15-Oct-20 23:36 
BugBad example using serialport Pin
Rene Balvert15-Oct-20 1:10
Rene Balvert15-Oct-20 1:10 
GeneralRe: Bad example using serialport Pin
Padanian15-Oct-20 1:32
Padanian15-Oct-20 1:32 
BugRe: Bad example using serialport Pin
Rene Balvert15-Oct-20 1:39
Rene Balvert15-Oct-20 1:39 
GeneralRe: Bad example using serialport Pin
Padanian15-Oct-20 1:53
Padanian15-Oct-20 1:53 
GeneralRe: Bad example using serialport Pin
Rene Balvert15-Oct-20 2:22
Rene Balvert15-Oct-20 2:22 
GeneralRe: Bad example using serialport Pin
Padanian15-Oct-20 2:47
Padanian15-Oct-20 2:47 
GeneralRe: Bad example using serialport Pin
Don Pierman16-Oct-20 9:47
Don Pierman16-Oct-20 9:47 
GeneralRe: Bad example using serialport Pin
Rene Balvert19-Oct-20 1:57
Rene Balvert19-Oct-20 1:57 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.