![]() |
Languages »
C# »
Windows Forms
Intermediate
License: The GNU Lesser General Public License
How to Properly Handle Cross-thread Events and Update a Label with BeginInvoke and BackgroundWorkerBy TuiToThis article explains how to update a Label using cross-thread event handling, delegates, BeginInvoke and BackgroundWorker. |
C# (C# 2.0, C# 3.0), Windows (Win2K, WinXP, Win2003, Vista), .NET (.NET 2.0, .NET 3.0, .NET 3.5), Architect, Dev
|
|
Advanced Search Add to IE Search |
|
|
|
||||||||||||||||
This article proposes a solution for updating Windows Form components when using cross-thread method calls.
Since .NET Framework 2.0, you can't make an explicit method call to a Windows Form component method (say, you wanted to alter the text of a label). This happens because Windows Forms is not thread-safe (in other words, it can't guarantee that the behavior using threads will be the same as using only the main program thread).
What to do then, when your beautiful, multi-core optimized code has an user interface that needs to be updated from whatever thread is needed? You have to use the BeginInvoke method, so you can make a thread-safe call.
The article covers basically three areas:
BackgroundWorker classNote: English is not my mother language, so the text may not be accurate sometimes.
To understand this code, I recommend that you have at least some contact with the C# language (the more, the better).
If you have already worked with event handling before, this tutorial may improve your skills.
If you never had the chance or patience to learn how to implement a custom event or how to use it with multiple threads, this is specially for you.
The code is fully documented. But for those who are in a hurry and seek just some pointers and advice, I'll give a brief explanation.
The code consists basically of two classes:
The Door class has the delegates and events declaration. I have opted to include a BackgroundWorker to insert some delay between changing the Door state from closed to open and vice-versa.
The Form1 class is a Windows Form with two buttons (Open door and Close door) and a label to display the current status. The door starts up closed, so you have to click Open to raise two events.
The first event raised is the event telling that the door is opening. The second one is raised when the door is open (when the background worker finishes its job).
By design, you can't close a door that isn't open and can't open a door that isn't closed (the in-between events can't be altered even if you click the other button).
Now some code fun.
The following code is inside the Door class:
// event raised before the door opens
public delegate void OpeningDoor ();
public event OpeningDoor RaiseDoorOpening;
// event raised after the door is open
public delegate void OpenDoor ();
public event OpenDoor RaiseDoorOpened;
// event raised before the door closes
public delegate void ClosingDoor ();
public event ClosingDoor RaiseDoorClosing;
// event raised after the door is closed
public delegate void ClosedDoor ();
public event ClosedDoor RaiseDoorClosed;
And now the Open method for the Door class:
public void Open ()
{
// Asserts that the door isn't already open
if ( !_isOpen )
{
RaiseDoorOpening (); // raises event before opening door
#region Open door delay
// we're going to insert a small asynchronous delay here,
// so we can see both events happening.
// This has to be done in another thread, otherwise it would
// freeze the Form (and we don't want that)
BackgroundWorker delayer = new BackgroundWorker ();
delayer.DoWork += new DoWorkEventHandler ( OpenDelayer_DoDelay );
delayer.RunWorkerAsync ();
#endregion
}
else
return; // aborts if already open
}
private void OpenDelayer_DoDelay ( object sender, DoWorkEventArgs e )
{
Thread.Sleep ( 3000 );
_isOpen = true; // sets the door to open state
RaiseDoorOpened (); // Raise the event after the delay happens
}
As for the Form1 class, the relevant parts are:
The constructor:
public Form1 ()
{
InitializeComponent ();
// creates an instance of the Door class
door = new Door ();
// event handler for door opening
door.RaiseDoorOpening += new Door.OpeningDoor(door_OnDoorOpening);
// event handler for door opened
door.RaiseDoorOpened += new Door.OpenDoor ( door_OnDoorOpen );
// event handler for door closing
door.RaiseDoorClosing += new Door.ClosingDoor(door_OnDoorClosing);
// event handler for door closed
door.RaiseDoorClosed += new Door.ClosedDoor ( door_OnDoorClosed );
}
Now the event handlers for opening the door:
private void door_OnDoorOpening ()
{
string msg = "Opening the door...";
Console.WriteLine ( msg );
object [] p = GetInvokerParameters ( msg, Color.Orange );
BeginInvoke ( new UpdateDoorStatusLabel ( UpdateLabelText ), p );
}
private void door_OnDoorOpen ()
{
string msg = "Door opened.";
Console.WriteLine ( msg );
object [] p = GetInvokerParameters ( msg, Color.Green );
BeginInvoke ( new UpdateDoorStatusLabel ( UpdateLabelText ), p );
}
Helper method for creating the required parameters for the delegate:
private object [] GetInvokerParameters (string labelMessage,
Color labelColor )
{
// We have to create an object array as this is the only way our
// UpdateLabelText method can receive the parameters
object [] delegateParameter = new object [ 2 ];
delegateParameter [ 0 ] = labelMessage;
delegateParameter [ 1 ] = labelColor;
return delegateParameter;
}
Helper method to update the text and change the forecolor:
private void UpdateLabelText ( string text, Color color )
{
lblDoorStatus.Text = text;
lblDoorStatus.ForeColor = color;
}
The Open button Click event handler:
private void btnOpenDoor_Click ( object sender, EventArgs e )
{
door.Open ();
}
This code helped me to remember how to use delegates with C# and how to handle events. I also learned how to use the BackgroundWorker for the delay to work properly and the cross-thread call was a bonus (I didn't expect to use it at first when I was coding the event handling).
Opening, Open, Closing, Close in the Door class. | You must Sign In to use this message board. | |||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||
General
News
Question
Answer
Joke
Rant
Admin
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 8 Feb 2008 Editor: Deeksha Shenoy |
Copyright 2008 by TuiTo Everything else Copyright © CodeProject, 1999-2009 Web17 | Advertise on the Code Project |