Click here to Skip to main content
Licence BSD
First Posted 5 Apr 2002
Views 178,591
Downloads 3,061
Bookmarked 79 times

The key to multi-threaded Windows Forms UI interaction

By James T. Johnson | 5 Apr 2002
Teaches how to use the Invoke functionality so that interaction with UI elements can be safely done.
2 votes, 5.4%
1
1 vote, 2.7%
2
4 votes, 10.8%
3
9 votes, 24.3%
4
21 votes, 56.8%
5
4.61/5 - 49 votes
3 removed
μ 4.33, σa 1.95 [?]

Sample Image - winformsthreading.jpg

Introduction

I don't intend for this article to teach multi-threaded programming in .NET, I do want to point out some important things when dealing with multi-threaded programs in a Windows Forms environment.  This article will be code light, however I have provided a full sample illustrating how to employ the practices required of a Multi-Threaded Windows Forms application.

What not to do

First and foremost (and the reason I'm writing this article) is that you absolutely, positively, cannot interact with UI elements from a thread that did not create them.  You may get away with it in some cases, but it will fail at some point and then heads will roll because you will have a lot of code to change.  Now you may ask how should you interact with the UI from another thread. 

What you should do

Luckily the designers for Windows Forms realized the plight that may be caused without being able to interact with the UI from another thread.  So they add the Invoke method to the Control class.  Invoke runs the specified delegate on the thread that create the control's window (or the parents window if it doesn't have one).

public void Invoke(System.Delegate delegate);
public void Invoke(System.Delegate delegate, object [] args);

The second form of Invoke takes an object array containing any parameters you wish to be passed into the delegate.  For those still not used to delegates, think of them as the .NET way to use function pointers.

A brief example

A simple example of using this is to create a new form, add a textbox and a button to it.  Call the textbox myTextbox.

At the top of the code file add another using statement for the threading library.

using System.Threading;

In the button's Click event place the following code.

Thread t = new Thread(new ThreadStart(SayHiThread));
t.IsBackground = true;
t.Start();

Now add this code to the form

private void SayHiThread()
{
	Invoke(new SayHiDelegate(SayHi), new object [] { "Hello from the thread" });
}
private void SayHi(string msg)
{
	myTextbox.Text = msg;
}

Outside of the form's class add this declaration

public delegate void SayHiDelegate(string msg);

Run the application and click the button, the textbox should now say "Hello from the thread".

What it did

Well that probably confused you more than it helped so I'll try to explain piece by piece what it does.

The Invoke method requires an instance of a delegate.  A delegate is nothing more than a type-safe function pointer.  You declare a delegate by creating the function signature you wish to have, then preceding the signature with the delegate keyword.  In the example above the function will take a string as its only parameter and return nothing.  You create a new delegate instance as if you were creating an instance of a class, except you pass in the name of the method you want the delegate to call.

The code inside of the button's click event creates a new thread, sets it as a background thread, then runs it.  All the thread does is run the function SayHiThread.  The thread only does one thing, it calls Invoke on the form (since SayHiThreadis a member of the form it calls the form's Invoke method).   The Invoke method takes an instance of a delegate, and an object array containing the parameters to give to the delegate.  In this case there is only one parameter; a string. 

To sum it up, Invoke calls the function which the delegate points to, passing in the parameters in the object array.

Summary

When you get down to it the hardest part of writing the code to interact with the UI from another thread is getting to understand delegates and how to use them.  As always, if there are any questions e-mail me or post them below. The source/demo show a example that's a bit more advanced, but still nothing too advanced.

License

This article, along with any associated source code and files, is licensed under The BSD License

About the Author

James T. Johnson

Product Manager
GrapeCity, inc.
United States United States

Member
James has been programming in C/C++ since 1998, and grew fond of databases in 1999. His latest interest has been in C# and .NET where he has been having fun writing code starting when v1.0 was in beta 1.
 
He is currently employed by GrapeCity-Data Dynamics as a Product Manager for Data Dynamics Reports and Data Dynamics Analysis.
 
Code contained in articles where he is the sole author is licensed via the new BSD license.
 
Learn more about the products James works with at the CodeProject Catalog
ActiveReports | Data Dynamics Analysis | Data Dynamics Reports

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
QuestionThanks ! PinmemberDavide Calabro6:49 7 Oct '11  
Generalthis is not a really multi thread UI PinmemberE.T.7:04 21 Dec '08  
GeneralRe: this is not a really multi thread UI PinmemberJames T. Johnson16:36 21 Dec '08  
Generalcreating multithreads in C# Pinmemberalzrrog12:22 11 May '07  
GeneralRe: creating multithreads in C# PinmemberPatrick Sears12:47 11 May '07  
GeneralAvoid Invoke() - prefer BeginInvoke() PinmemberKristof Verbiest4:02 27 Feb '07  
GeneralVery useful and easy to follow! Thanks! Pinmemberanichin7:12 17 Mar '06  
GeneralVery Helpful PinmemberTim Friesen7:54 23 Aug '05  
GeneralAccessing UI Element's Properties PinmemberJohnCBowman3:10 27 Apr '04  
GeneralRe: Accessing UI Element's Properties PinmemberS. Senthil Kumar23:27 18 May '05  
GeneralSame concept, different location PinmemberXiNull17:38 9 Dec '03  
GeneralRe: Same concept, different location PinmemberTodd Gray8:16 21 Dec '04  
GeneralRe: Same concept, different location PinmemberS. Senthil Kumar23:24 18 May '05  
Generalchanging the value of a boolean when a loop is going on Pinmemberpasous12:42 6 Nov '03  
GeneralRe: changing the value of a boolean when a loop is going on PinmemberJames T. Johnson6:07 7 Nov '03  
Questionwrong code on www.ondotnet.com? PinsussCristian Balcanu8:23 19 Apr '03  
AnswerRe: wrong code on www.ondotnet.com? PinmemberJames T. Johnson12:52 19 Apr '03  
GeneralCannot use Invoke if calling Join inside Closing event - causes deadlock PinmemberJoergB22:26 13 Apr '03  
GeneralRe: Cannot use Invoke if calling Join inside Closing event - causes deadlock PinmemberJames T. Johnson7:14 14 Apr '03  
JoergB wrote:
Any ideas what I am doing the wrong way, any work-arounds ?
 
The problem is that the Thread.Join method causes the current thread to wait for the specified thread to exit. In this case, the main thread waits for 'myThread' to finish.
 
Then you turn around and call Invoke while in 'myThread', which causes the second thread to wait until the main thread calls your delegate. But the main thread is waiting for 'myThread' to finish so it can't call the delegate.
 
Result: Dead-lock
 
I can think of a work-around, which looks rather hackish to me but it should still help.
 
Add another member variable to the class:
private bool isClosing = false;
 
Now in the form closing event:
private void Form1_Closing(object sender,
    System.ComponentModel.CancelEventArgs e)
{
    lock (this)
    {
        this.isClosing = true;
    } // release lock so the second thread can check the variable
    if (myThread != null)
        myThread.Join();
}
And in the AddLine method:
private void AddLine(string strText)
{
    if (!this.isClosing)
    {
        lock (this)
        {
            // double check in case
            // we were interrupted between the first if 
            // and the lock (such as if the main thread had locked)
            if (!this.isClosing)
            {
                listBox1.Items.Add(strText);
                listBox1.Invoke(new UpdateDelegate(Update)); 
            }
        }
    }
}
This should work, though I haven't tested it.
 
James
 
"It is self repeating, of unknown pattern"
Data - Star Trek: The Next Generation

GeneralRe: Cannot use Invoke if calling Join inside Closing event - causes deadlock PinmemberJoergB20:00 14 Apr '03  
GeneralRe: Cannot use Invoke if calling Join inside Closing event - causes deadlock PinmemberJames T. Johnson20:52 14 Apr '03  
GeneralRe: Cannot use Invoke if calling Join inside Closing event - causes deadlock PinmemberJoergB21:10 14 Apr '03  
GeneralRe: Cannot use Invoke if calling Join inside Closing event - causes deadlock PinmemberJames T. Johnson7:11 15 Apr '03  
GeneralRe: Cannot use Invoke if calling Join inside Closing event - causes deadlock PinmemberJoergB19:57 15 Apr '03  
GeneralThreaded Progress Bar Pinmemberjtmtv1814:16 13 Mar '03  

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

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

Permalink | Advertise | Privacy | Mobile
Web04 | 2.5.120210.1 | Last Updated 6 Apr 2002
Article Copyright 2002 by James T. Johnson
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid