2

I am building a WPF application that calls web services and displays the data returned from the service after being broken down and analyzed by my application. The problem that I am facing is with multithreading. One of the API calls is made using a DispatcherTimer every 60 seconds. The issue is that when this event fires, it blocks the UI thread. I have attempted (in all ways that I can think) to update the UI from the background thread using BackgroundWorker and Dispatcher objects (also delegates) and I cannot figure this out. I need an example showing a label on the UI thread being updated by the background thread. Any help with this would be fantastic as I am about to freak out :).

I have looked at the other articles and it is just not making a terrible amount of sense to me. Please, bear with me as I am pretty new to this. Here is an example of what I would like to do. I have a label on the window named lblCase. I call pullData() every 60 seconds and I want to update lblCase with the returned data without blocking the UI.

private void pullData() { //API call goes here... lblCase.Content = iCase; } public MainWindow() { InitializeComponent(); DispatcherTimer timer = new DispatcherTimer(); timer.Tick += new EventHandler(timer_Tick); timer.Interval = new TimeSpan(0,0,60); timer.Start(); } private void timer_Tick(object sender, EventArgs e) { pullData(); } 

5 Answers 5

5

Have a look at this question...

cheers,

EDIT:

Joe - not sure if you're getting any closer to groking this, so I thought I'd try to put together a simple usage of BackgroundWorker to demonstrate how simple and powerful this class is!

first - in your constructor...

public MainWindow() { InitializeComponent(); BackgroundWork worker = new BackgroundWorker(); worker = new BackgroundWorker(); worker.DoWork += new DoWorkEventHandler(worker_DoWork); worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted); System.Timers.Timer t = new System.Timers.Timer(10000); // 10 second intervals t.Elapsed += (sender, e) => { // Don't try to start the work if it's still busy with the previous run... if (!worker.IsBusy) worker.RunWorkerAsync(); }; } } 

so we have set up something to delegate some work (in the method 'worker_DoWork') on a background thread... whatever happends in that method will not impact the UI thread, and it should look something like:

private void worker_DoWork(object sender, DoWorkEventArgs e) { // Whatever comes back from the lengthy process, we can put into e.Result e.Result = DoMyBigOperation(); } 

Now when this thread completes, it will fire the RunWorkerCompleted event, which we have handled as such:

private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { // First, handle the case where an exception was thrown. if (e.Error != null) { // handle the System.Exception MessageBox.Show(e.Error.Message); } else if (e.Cancelled) { // now handle the case where the operation was cancelled... lblCase.Content = "The operation was cancelled"; } else { // Finally, handle the case where the operation succeeded lblCase.Content = e.Result.ToString(); } } 

Hope this helps! IanR

Sign up to request clarification or add additional context in comments.

5 Comments

Another sample (of mine) which does much the same thing: stackoverflow.com/questions/1794402/… Ian's code is a little better though ;-) The only comment I would make is that the use of variable capture in anonymous delegates can be a little mind bending when you first see it. To make the code a little more obvious I would make "worker" a member of the MainWindow class.
Thanks for the replies, gentlemen. I will begin testing these suggestions shortly and will let you know what works for me.
Thank you so much. I got this working. You are the man. Thank you everyone for your input.
Just used this code in one of my projects--thx for being thorough
I know this is an old post, but I'm just investigating multithreading for my own app name... Just have 1 question. Why are you initializing worker twice?
1

You can only update a control from a the thread which created the control. Having said that just use a background worker object to get the data and once that job is complete, update the control using the UI thread.

Comments

1

I prefer to create a worker class, pass it an update delegate, and launch the worker's DoSomething method in a separate thread. Whenever that thread needs to update the UI it calls back to the update delegate, which does the updating through the control's dispatcher. In this example I also pass the control since I have multiple threads each updating their own textblock:

 private void UpdateTextBlock(TextBlock textBlockArg, string textArg) { textBlockArg.Dispatcher.Invoke( System.Windows.Threading.DispatcherPriority.Normal , new System.Windows.Threading.DispatcherOperationCallback(delegate { textBlockArg.Text = textArg; return null; }), null); } 

Comments

0

Use BeginInvoke() to update the UI thread from another thread. Here's an article describing how to use it.

Edit: I tried the following program based on the code you gave in your question and it updates the UI fine (because everything is happening on the UI thread).

using System; using System.Threading; using System.Windows.Threading; namespace BeginInvoke { public partial class Window1 { public Window1() { InitializeComponent(); x_tid.Text = Thread.CurrentThread.ManagedThreadId.ToString(); DispatcherTimer timer = new DispatcherTimer(); timer.Tick += timer_Tick; timer.Interval = new TimeSpan(0, 0, 1); timer.Start(); } private void timer_Tick(object sender, EventArgs e) { Thread.SpinWait(900); x_text.Text = DateTime.Now.ToString(); x_tid.Text = Thread.CurrentThread.ManagedThreadId.ToString(); } } } 

.xaml is:

<Window x:Class="BeginInvoke.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="300" Width="300"> <StackPanel> <TextBlock x:Name="x_text" /> <TextBlock x:Name="x_tid" /> </StackPanel> </Window> 

1 Comment

I read through this article and am still pretty unclear on how this whole thing works. Would you mind posting an example to go with the snippet I posted?
0

To elaborate on my original answer about using BeginInvoke to update the UI from a non UI thread, here's a working example. BeginInvoke works off the dispatcher. If you are using Silverlight, you can use the BeginInvoke from System.Windows.Deployment.Current.Dispatcher.BeginInvoke(), instead.

using System; using System.Threading; using System.Windows.Threading; namespace BeginInvoke2 { public partial class Window1 { public Window1() { InitializeComponent(); ThreadPool.QueueUserWorkItem(Proc, Dispatcher); } private void Proc(object state) { var disp = (Dispatcher) state; var tid = Thread.CurrentThread.ManagedThreadId.ToString(); // Use BeginInvoke to do the operations on the UI thread disp.BeginInvoke((Action)(() => { x_tid1.Text = "threadpool thread: " + Thread.CurrentThread.ManagedThreadId.ToString(); x_tid2.Text = " ui thread: " + tid; })); // Can't do the following operations because we are not in the UI // thread //x_text.Text = "In Proc"; //x_tid.Text = Thread.CurrentThread.ManagedThreadId.ToString(); } } } 

.xaml file

<Window x:Class="BeginInvoke2.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="300" Width="300"> <StackPanel> <TextBlock x:Name="x_tid1" /> <TextBlock x:Name="x_tid2" /> </StackPanel> </Window> 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.