Home > Articles > Programming > Windows Programming

  • Print
  • + Share This
From the author of

Working with the User Interface from Another Thread

A Windows Form runs on a single thread, and any manipulation of the Form, including its properties and the properties of its controls, is supposed to occur only on that specific thread. This fact doesn't usually cause any problems, until you start creating background threads that, as in the file list example, want to update some aspect of the Form.

In the code provided so far, proper procedure has not been followed. Currently, the background process raises an event whenever a new file is found. Then the corresponding event handler on the Form adds the filename to the ListBox. This method appears to be correct because a procedure on the actual form (the event handler) does the only manipulation of the Form anywhere in the code. In reality, the nature of events and event handlers make this approach flawed. When a background process raises an event, any event handlers (regardless of where in your code they are located) are executed by the background process's thread.

NOTE

You might be wondering, quite logically, why the code detailed so far appears to work just fine if it is incorrect. In some areas of programming (and threading is one of those areas), doing the wrong thing may cause a problem, but the code may also run fine. The end result of improperly manipulating a Form from another thread is an unstable system, and this instability may not show up in a predictable fashion. You must be careful to use threads correctly (including following this rule about Windows Forms), if you want to produce a solid system.

To properly ensure that the Form's thread is handling the ListBox update, you need to create a delegate pointing to a procedure that will do the actual UI update. Then you need to call that delegate using the Invoke method of your Form. Using this special Invoke method correctly executes the UI updating procedure using the Form's thread, as Listing 3 illustrates.

Listing 3: Use Invoke to Ensure That All UI Work Occurs on the Form's Thread

  Dim WithEvents myScanner As FileScan
  Private Delegate Sub NewFileHandler(ByVal fileName As String)


  Private Sub frmWMAList_Load(ByVal sender As Object, _
    ByVal e As System.EventArgs) Handles MyBase.Load
    lbFiles.Sorted = True
    myScanner = New FileScan()
    myScanner.Filter = "*.wma"

    Dim myThread As New _
      System.Threading.Thread(AddressOf myScanner.StartScan)
    myThread.Start()
  End Sub

  Private Sub myScanner_FoundFile(ByVal fileName As String) _
      Handles myScanner.FoundFile
    Dim myNewDelegate As NewFileHandler
    Dim myArgs(0) As Object
    myArgs(0) = fileName

    myNewDelegate = _
      New NewFileHandler(AddressOf AddFileToList)

    Me.Invoke(myNewDelegate, myArgs)
  End Sub

  Private Sub AddFileToList(ByVal fileName As String)
    lbFiles.Items.Add(fileName)
  End Sub
  • + Share This
  • 🔖 Save To Your Account