VS.NET 2005 introduces a ToolStripContainer control which allows you to develop applications with toolbars that can be docked, undocked, reordered and otherwise rearranged to the edges of a form. This is an excellent idea, however, what was an obvious miss for the product was the ommission of a dockable window. We are all used to seeing these windows in the VS.NET IDE. The are, essentially, dockable, sizable toolwindows that can be removed, docked , undocked, resized, pinned against a border of the IDE, or unpinned. This feature has been suggested multiple times and would definitely add a professional touch to many applications. Microsoft ommitted this feature from VS.NET 2005, but perhaps if we can get enough votes, this feature will be reimplemented. Realizing that the release date is only 5 months away, its unlikely, but we can always try. Go to [I have removed the link as it is now 404/archived] to and vote for this suggestion!
Updating Controls From Worker Threads
I thought this was an issue that has been hashed over enough times, but the other day I was asked this question and I’m still amazed that many folks are unfamiliar with this concept.
In WinForms applications, sometimes its desirable to execute a long running method in a worker thread. For instance, you may be retreiving data over a web service that has a heavy load and usually takes several seconds to execute.. When the work has completed, it’s typical that you would want to update the user interface: be that a grid, a tree view control, or some other container that displays the results of your call to the long running process. Sounds simple enough right?
Before you get in a hurry, consider one thing. Data created on one thread is owned by that thread as long as it is local data. This can be overcome though by marshaling the data across threads. This is a function of Thread Local Storage implemented through managed code. Virtual address space of a process is shared across all threads. While the data of a thread is unique, it can be “shared” or copied but you need to tell the application to copy the data to a the UI thread before updating the UI with the data. This can be done using Control.Invoke().
Take a look at what happens when you don’t use this method. Createa new Windows Forms project, add a TreeView control (tvResults) and two buttons.( btnNormalExe and btnThreadExe ). Add the following methods on the form:
public void WorkerThread() { Cursor.Current = Cursors.WaitCursor; // Simulate Long Process (5 seconds) Thread.Sleep( new TimeSpan( 0,0,0,5,0 ) ); UpdateResults(); Cursor.Current = Cursors.Arrow; } public void UpdateResults() { // Update UI tvResults.Nodes.Add( String.Format( "UI Updated on Thread ID {0} at {1}", AppDomain.GetCurrentThreadId().ToString(), DateTime.Now.ToString() ) ); }
For btnNormalExe add the following code:
private void btnNormalExe_Click(object sender, System.EventArgs e) { WorkerThread(); }
For btnThreadExe add the following code:
private void btnThreadExe_Click(object sender, System.EventArgs e) { Thread t = new Thread( new ThreadStart( WorkerThread) ); t.Start(); }
Now run the form and click on the normal execution button first. Once the method completes, click on the thread execution button. You’ll receive an exception when the threaded execution completes:
An unhandled exception of type 'System.InvalidOperationException' occurred in system.windows.forms.dll Additional information: The action being performed on this control is being called from the wrong thread. You must marshal to the correct thread using Control.Invoke or Control.BeginInvoke to perform this action.
Just as the exception says, we are going to modify our call to make sure we get back on the UI’s thread before updating it. In this way, the form’s thread owns the data, not the worker thread. Modify the UpdateResults() method as follows:
public void WorkerThread() { Cursor.Current = Cursors.WaitCursor; // Simulate Long Process (5 seconds) Thread.Sleep( new TimeSpan( 0,0,0,5,0 ) ); //UpdateResults(); this.Invoke( new TreeViewUpdater( UpdateResults ) ); Cursor.Current = Cursors.Arrow; }
Notice that when you execute both the normal execution button and the threaded execution button the tree view control updates with the same thread ID. This is because the call to control.Invoke puts the call back on the form’s thread.
I will cover this in more depth in the weeks to come, to shed more light on what’s happening in the background.