search
Japanese Chinese Nederlands Espanol Italiano Deutsch Francais Twitter Rss Feeds
MicrosoftArticlesForumsFAQs
C# .NET
VB.NET
Visual Studio .NET
ADO.NET
Xml / Xslt
VB 6.0
.NET CF
GDI+
LINQ
Deployment
Security
FoxPro
Silverlight / WPF
Entity Framework
RIA Services

Web ProgrammingArticlesForumsFAQs
JavaScript
ASP
ASP.NET
Web Services

Non-MicrosoftArticlesForumsFAQs
NHibernate
Perl
PHP
Ruby
Java
Linux / Unix
Apple
Open Source

DatabasesArticlesForumsFAQs
SQL Server
Access
Oracle
MySQL
Other Databases

OfficeArticlesForumsFAQs
Excel
Word
Powerpoint
Outlook
Publisher
Money

Operating SystemsArticlesForumsFAQs
Windows 7
Windows Server
Windows Vista
Windows XP
Windows Update
MAC
Linux / UNIX

Server PlatformsArticlesForumsFAQs
BizTalk
Site Server
Exhange Server
IIS

Graphic DesignArticlesForumsFAQs
Macromedia Flash
Adobe PhotoShop
Expression Blend
Expression Design
Expression Web

OtherArticlesForumsFAQs
Subversion / CVS
Ask Dr. Dotnetsky
Active Directory
Networking
Uninstall Virus
Job Openings
Product Reviews
Search Engines
Resumes

 

Tools for Updating Windows Forms UI from Background Threads


By Peter Bromberg
Printer Friendly Version
View My Articles
57 Views
    

One of the most common frustrations of new .NET programmers revolves around an incomplete understanding of how the Windows Forms UI model works. This shows how to use the Control.Invoke method as well as the BackgroundWorker class to perform updates to the UI from a secondary thread.


One of the most common frustrations of new .NET programmers revolves around an incomplete understanding of how the Windows Forms UI model works. A very simple example, to illustrate, comes from a recent post on one of our Eggheadcafe.com forums:

private void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
textBox3.Text = "ı"; // "Cross-thread operation not valid" here
}

The port_DataReceived event handler is being called on a secondary thread, but the user is attempting to update a control on the Main UI thread from it, and you cannot do that.

The Windows OS uses a single-threaded, message-processing based user interface, and any alternate thread interaction needs to be marshaled through the Windows message pump. Normally this involves checking a component's InvokeRequired property to determine if marshaling is necessary. You can call Invoke() directly and it will check this anyway, but it is more efficient to do it explicitly beforehand. Here is a complete example (minus of course, the progress bar and button you would need to drop onto the actual Form) that illustrates how this pattern might be used:

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Text;

using System.Windows.Forms;

using System.Threading;

 

namespace WFInvoke

{

    public partial class Form1 : Form

    {     

        public Form1()

        {

            InitializeComponent();

        }

 

        [STAThread]

        static void Main()

        {

            Application.Run(new Form1());

        }

 

        private  void IncrementMe()

        {

            for (int i = 0; i < 100; i++)

            {

                UpdateProgress();

                Thread.Sleep(100);

            }

            if(InvokeRequired)

            {

                Invoke(new MethodInvoker(Close));

            }

            else

            {

                Close();

            }

        }

 

        private  void UpdateProgress()

        {

            if (this.progressBar1.InvokeRequired)

            {

                MethodInvoker updateProgress = UpdateProgress;

                progressBar1.Invoke(updateProgress);

            }

            else

            {

                progressBar1.Increment(1);

            }

        }

 

        private void button1_Click(object sender, EventArgs e)

        {

            ThreadStart threadStart = IncrementMe;

            threadStart.BeginInvoke(null, null);

        }       

    }

}

We are running the IncrementMe method on a secondary thread, and we use the Invoke pattern to safely update the progress bar on the Main UI thread from inside the method.

BackgroundWorker Pattern

In .NET 1.1 it was necessary to wire up a lot of delegate-invoking to do something that turns out to be pretty common in Winforms development - to be able to be notified when a background thread method is complete, and also to be able to be notified periodically on its status. Being able to cancel a long-running background thread task is also desirable. This set of tasks is so common that the developers of the 2.0 Framework added the BackgroundWorker class, which makes all this much easier to do.

BackgroundWorker basically works like this:

1) You attach your long-running method to the BackgroundWorker's DoWork event.
2) To wire up progress notifications, you hook up an event listener to the BackgroundWorker class's ProgressChanged event, and set it's WorkerReportsProgress property to true.
3) You register your "Complete" method with the BackgroundWorker's RunWorkerCompleted event.
4) You can set the WorkerSupportsCancellation property to true, and a call to the BackgroundWorker.CancelAsync method will set the DoWorkEventArgs.CancellationPending flag, which you can then check and act accordingly in your long-running method that has been supplied to the BackgroundWorker class.
5) To start your method, you call the BackgroundWorker.RunWorkerAsync() method, passing a state parameter that is sent into your long-running method.

The only major drawback to using this substantially easier way to hook up all this eventing and reporting is that you cannot just use it with "any method" -- your method needs to conform to the System.ComponentModel.DoWorkEventHandler delegate, which requires arguments of type Object, and DoWorkEventArgs. Otherwise, a wrapper method will be required. In the example below, I illustrate the passing of multiple parameters by sending in an object[] array containing different types as the state parameter.

Aside from the above, you only need to remember to check EventArgs.Error inside the RunWorkerCompleted callback for any exception, otherwise the exception will be unreported; it doesn't even propagate to the AppDomain's UnhandledException event. Here is a somewhat trivial example, but it illustrates all the features described:

using System;

using System.Threading;

using System.ComponentModel;

 

class Program

{

    static BackgroundWorker bwkr;

    static void Main()

    {

        bwkr = new BackgroundWorker();

        bwkr.WorkerReportsProgress = true;

        bwkr.WorkerSupportsCancellation = true;

        bwkr.DoWork += bwkr_DoWork;

        bwkr.ProgressChanged += bwkr_ProgressChanged;

        bwkr.RunWorkerCompleted += bwkr_RunWorkerCompleted;

        object[] parms = {"one",2,DateTime.Now};

        bwkr.RunWorkerAsync(parms);

        Console.WriteLine("Press Enter during run to cancel.");

        Console.ReadLine();

        if (bwkr.IsBusy) bwkr.CancelAsync();

        Console.ReadLine();

    }

 

    static void bwkr_DoWork(object sender, DoWorkEventArgs e)

    {

        object[] o = (object[])e.Argument;

        foreach (object ob in o)

            Console.WriteLine("arg: " + ob.ToString());

        Console.WriteLine("==================================");

        int tot = 0;

        for (int i = 0; i <= 100; i += 10)

        {

            tot += i;

            if (bwkr.CancellationPending)

            {

                e.Cancel = true;

                return;

            }

            bwkr.ReportProgress(i);

            Thread.Sleep(1000);

        }

        e.Result = tot;    // This gets passed to RunWorkerCompleted

    }

 

    static void bwkr_RunWorkerCompleted(object sender,

    RunWorkerCompletedEventArgs e)

    {

        if (e.Cancelled)

            Console.WriteLine("Canceled by User.");

        else if (e.Error != null)

            Console.WriteLine("Worker exception: " + e.Error.ToString());

        else

            Console.WriteLine("Complete - " + e.Result);      // from DoWork

    }

 

    static void bwkr_ProgressChanged(object sender,

    ProgressChangedEventArgs e)

    {

        Console.WriteLine("bwkr: " + e.ProgressPercentage + "%");

    }

}

You can download this sample Visual Studio 2005 Solution containing both working examples.

Biography - Peter Bromberg
Peter Bromberg is a C# MVP, MCP, and .NET expert who has worked in banking, financial and telephony for over 20 years. Pete focuses exclusively on the .NET Platform, and currently develops SOA and other .NET applications for a Fortune 500 clientele. Peter enjoys producing digital photo collage with Maya,playing jazz flute, the beach, and fine wines. You can view Peter's UnBlog and IttyUrl sites.
Please post questions at forums, not via email!

button
Article Discussion: Tools for Updating Windows Forms UI from Background Threads
Peter Bromberg posted at Saturday, January 06, 2007 7:50 PM
Original Article
 

Great article
Atreyu Green replied to Peter Bromberg at Tuesday, January 20, 2009 8:00 AM

Very Useful your article.

Thank for your exp.