Chapter 10 - Lesson 3 code updated - Delegates

Sep 24, 2009 at 12:16 AM

So i was curious why Robert was having pbs with the form controls ( text and gridview ) not updating correctly.

I put a break point on where he was updating the text and looked into the textbox object..There was an exception thrown due to cross thread control update.

The watchers are in fact different threads, and calling a function to update controls on a different thread require the use of delegates and InvokeRequired..

I have fixed this part and added the form closing event to do some cleanup.

Enjoy.

 

 

 

Coordinator
Sep 24, 2009 at 2:48 PM

Cross thread UI updates?!  Oh man, that's not a trivial thing to deal with!  Or at least I don't think so.  This would make for a great tutorial Souheil!  Can you post your thought patterns here to help noob like me on this?  Thanks.

Sep 24, 2009 at 10:27 PM

 

The code had this VB sub

========================================

Private Sub setProcessCount()

      Me.Label2.Text = String.Format("Number of processes: {0}", _dataTable.Rows.Count)

End Sub

========================================

This sub is called from 3 different places. The from load and the watchers.

The problem was that Label2 wasnt getting updated when the watchers' event handlers were called.

At first I suspected that there was something wrong with _dataTable.Rows.Count. So I put a break point there and looked at the number. Hmmm its correct.

So I hovered my mouse over the Me.Label2.Text and noticed that there was an exception thrown:

"Cross-thread operation not valid: Control 'Label2' accessed from a thread other than the thread it was created on." ...Interesting.

Next I Highlighted "ManagementEventWatcher" and pressed F1 and dug into the MSDN help for that class. At the bottom, in the Thread Safety section it says that Any instance members are not guaranteed to be thread safe..Bingo!

How To fix this issue.

There is a good artilce on MSDN "How to: Make Thread-Safe Calls to Windows Forms Controls"  http://msdn.microsoft.com/en-us/library/ms171728(VS.80).aspx

Solution: Make use of InvokeRequired and delegates

========================================

Delegate Sub SetTextCallback(ByVal [text] As String)

Private Sub SetText(ByVal [text] As String)

If Me.Label2.InvokeRequired Then
   Dim del As New SetTextCallback(AddressOf SetText)
   Me.Invoke(del, New Object() {[text]})
Else
   Me.Label2.Text = [text]
   'good place to refresh the datagridview and DoEvents since we are now thread safe
   Me.DataGridView1.Invalidate()
   My.Application.DoEvents()
   End If
End Sub

Private Sub setProcessCount()
 
   SetText(_dataTable.Rows.Count.ToString())

End Sub

========================================

Cheers