Browse DevX
Sign up for e-mail newsletters from DevX


How To Pass Parameters to Threads in Windows Forms Applications—and Get Results : Page 3

Launching new threads is easy, but it's not as obvious how you can pass parameters to the threads and get the results back. In addition, accessing Windows Forms controls from multiple threads can cause problems. In this article, you'll see how to pass parameters, receive results, and access controls safely from multiple threads in a Windows Forms application.




Building the Right Environment to Support AI, Machine Learning and Deep Learning

Accessing Windows Controls from Launched Threads
You might ask: Why not just have the Count() method grab the iteration parameter value from the txtIterations control? That's a good question. The answer is that Windows Forms are not thread-safe—they run on a single thread, and if you try to access the controls on a form from multiple threads, you're bound to have problems. You should only access Windows Forms controls from the thread on which they were created. In this article, I've called that the "main form thread".

To make matters worse, you can access the controls from other threads without causing an exception—despite the fact that you shouldn't. For example, if you write code to grab the txtIteration.Text value, it will usually work. More commonly, you want to write to Windows Forms controls from multiple threads, for example, changing the contents of a TextBox—and because that alters the control data, it's where most control threading problems occur.

The sample Form3.vb file shows you how to access and update control values safely. The form launches the threads in the same way you've already seen, but displays the results in a multi-line TextBox. To access Windows controls safely from a different thread, you should query the InvokeRequired property implemented by the Control class and inherited by all the standard controls. The property returns True if the current thread is not the thread on which the control was created—in other words, a return value of True means you should not change the control directly from the executing thread. Instead, call the control's Invoke() method using a delegate and (optionally) a parameter array of type Object. The control uses the delegate to call the method in the context of its own thread, which solves the multithreading problems. This is the only thread-safe way to update control values.

In Form 3, the button click code is identical to the code in Form 2—it creates a thread and starts it. The Count() method is different; it contains the check for InvokeRequired, and a SetDisplayText() method to handle the control update. The Count() method code looks more complicated than it is.

Private Sub Count(ByVal iterations As Integer) Dim i As Integer Dim t As Thread = Thread.CurrentThread For i = 0 To iterations If Me.txtDisplay.InvokeRequired Then Me.txtDisplay.Invoke( _ New ChangeTextControlDelegate( _ AddressOf SetDisplayText), New Object() _ {t.Name, txtDisplay, i.ToString}) Else Me.SetDisplayText(t.Name, txtDisplay, _ i.ToString) End If If Me.txtDisplay.InvokeRequired Then t.Sleep(100) End If Next If Me.txtDisplay.InvokeRequired Then Me.txtDisplay.Invoke( _ New ChangeTextControlDelegate( _ AddressOf SetDisplayText), New Object() _ {t.Name, txtDisplay, "Ending Thread"}) Else Me.SetDisplayText(t.Name, txtDisplay, _ "Ending Thread") End If End Sub

Within the loop, the application checks to see if the current thread is capable of directly altering the txtDisplay TextBox contents, by checking the value of InvokeRequired. When the value returns True the code uses the Control.Invoke method to call the SetDisplayText() method via a ChangeTextControl delegate:

Delegate Sub ChangeTextControlDelegate( _ ByVal aThreadName As String, _ ByVal aTextBox As TextBox, _ ByVal newText As String)

The SetDisplayText() method appends the name of the thread and the newText parameter to the specified TextBox. Because TextBoxes have a maximum length, the SetDisplayText() method checks to ensure that the TextBox is not "full" of text; if it is, it removes all but the last 1,000 characters before appending the new text. If the TextBox is not full, the code simply appends the new text.

Public Sub SetDisplayText( _ ByVal aThreadName As String, ByVal aTextBox As TextBox, ByVal newText As String) If aTextBox.Text.Length + newText.Length > _ aTextBox.MaxLength Then aTextBox.Text = Strings.Right( _ aTextBox.Text, 1000) End If aTextBox.AppendText(aThreadName & ": " & _ newText & System.Environment.NewLine) End Sub

Comment and Contribute






(Maximum characters: 1200). You have 1200 characters left.



Thanks for your registration, follow us on our social networks to keep up-to-date