Updating the user interface from multiple workflows (.NET)

I have a pet project that I'm working on that has several workflows. The output of everything to the console is becoming increasingly difficult, so I want to develop an interface that will have one output area per stream. I want to know how threads can best send updates to the user interface. I have two ideas:

1) Each thread sets the “DataUpdated” flag when new data is available, and the user interface periodically checks for new data.

2) Create each thread with a callback to the UI update method (...), which will be called when new data appears.

I currently tend to (2) for two reasons: I don't like the idea of ​​“checking” each thread, and because this is my first multithreaded application, and (2) seems simpler than possible. I want to know:

  • Which option is preferable in terms of simplicity and effectiveness?
  • Do you have any implementation tips (2) or something like that (i.e. more events)?
+6
multithreading user-interface c #
Jan 19 '10 at 21:35
source share
6 answers

You can easily implement (2) by creating the BackgroundWorker components and doing the work in your DoWork handlers:

BackgroundWorker bw = new BackgroundWorker(); bw.WorkerReportsProgress = true; bw.DoWork += /* your background work here */; bw.ProgressChanged += /* your UI update method here */; bw.RunWorkerAsync(); 

Each BackgroundWorker can report progress in the user interface thread by calling ReportProgress: although this is primarily intended to report progress in a limited process, which is optional - you can also transfer your own user data if this requires your update for the user interface, you called ReportProgress from your DoWork handler.

The good thing about BackgroundWorker is that it takes care of a lot of dirty cross-threading parts for you. It also matches the event-driven update model, which you (rightfully) prefer overtly callbacks.

+12
Jan 19 '10 at 21:38
source share

I also voted for # 2, but BackgroundWorkers instead of System.Threading.Threads.

+1
Jan 19 '10 at 21:37
source share

In most cases, it is easiest to use the BackgroundWorker component, as suggested by itowlson, and I highly recommend using this approach if possible. If for some reason you cannot use the BackgroundWorker component for your purpose, for example, if you are working with .Net 1.1 (yikes!) Or with a compact card, you may need to use an alternative approach:

Using Winform controls, you should avoid changing controls in any thread other than the thread that originally created the control. The BackgroundWorker component handles this for you, but if you are not using it, you can and should use the InvokeRequired and Invoke properties found in the System.Windows.Forms.Control class. The following is an example of using this property and method:

 public partial class MultithreadingForm : Form { public MultithreadingForm() { InitializeComponent(); } // a simple button event handler that starts a worker thread private void btnDoWork_Click(object sender, EventArgs e) { Thread t = new Thread(WorkerMethod); t.Start(); } private void ReportProgress(string message) { // check whether or not the current thread is the main UI thread // if not, InvokeRequired will be true if (this.InvokeRequired) { // create a delegate pointing back to this same function // the Invoke method will cause the delegate to be invoked on the main UI thread this.Invoke(new Action<string>(ReportProgress), message); } else { // txtOutput is a UI control, therefore it must be updated by the main UI thread if (string.IsNullOrEmpty(this.txtOutput.Text)) this.txtOutput.Text = message; else this.txtOutput.Text += "\r\n" + message; } } // a generic method that does work and reports progress private void WorkerMethod() { // step 1 // ... ReportProgress("Step 1 completed"); // step 2 // ... ReportProgress("Step 2 completed"); // step 3 // ... ReportProgress("Step 3 completed"); } } 
+1
Jan 19 '10 at 22:31
source share

You can activate your workflow threads and use the main user interface thread to add event handlers. You have to be careful, you are not raising too many events, as this can become ugly if your worker threads raise several events per second.

This article gives an overview.

0
Jan 19 '10 at 21:39
source share

The preferred way to implement multithreading in your application is to use BackgroundWorker . The BackgroundWorker component uses an event-driven model for multithreading. The workflow fires the DoWork event handler, and the thread that creates your controls fires the ProgressChanged and RunWorkerCompleted event handlers.
When you update user interface controls in the ProgressChanged event handler, they are automatically updated in the main thread, which will prevent crossthread exceptions from being thrown.

Look here for an example on how to use a background artist.

0
Jan 19 '10 at 21:40
source share

If you create your own threads (non BackgroundWorker or ThreadPool), you can pass a callback method from the main thread, which is called from the work thread. It also allows you to pass arguments for a callback and even return a value (e.g., the go / no-go flag). In your callback, you update the user interface through the target management manager:

 public void UpdateUI(object arg) { controlToUpdate.Dispatcher.BeginInvoke( System.Windows.Threading.DispatcherPriority.Normal , new System.Windows.Threading.DispatcherOperationCallback(delegate { controToUpdate.property = arg; return null; }), null); } } 
0
Jan 20 '10 at 0:24
source share



All Articles