Update GUI from background worker or event

Go To StackoverFlow.com

1

I want to understand how I can update my GUI with a simple text string on a regular basis. Essentially, I'm writing a twitter application which regularly polls twitter for updates. I want the contents of the update to be shown in a text block, one by one on a constant loop.

In order to keep the GUI responsive I need to perform the query in a background worker thread, however updating the GUI from this thread is not possible. As a learner, I'm struggling implement a way of updating the GUI by using events.

In my code below, I appreciated 'MainWindowGoUpdate' is going to be on the 'wrong thread' but how can I get the GUI thread to listen for the event?

A pointer appreciated.

public partial class MainWindow : Window
{
  public MainWindow()
  {
    public static event UpdateTimeLineEvent _goUpdate;

    public static string TheTimeLine;
    UpdateTimeLine();
  }

  private void UpdateTimeLine()
  {        
   txtb_timeline.Text = "Updating...";

   BackgroundWorker startTimelineUpdater = new BackgroundWorker();
   startTimelineUpdater.DoWork += new DoWorkEventHandler(startTimelineUpdater_DoWork);
   startTimelineUpdater.RunWorkerCompleted += new RunWorkerCompletedEventHandler(startTimelineUpdater_RunWorkerCompleted);
   startTimelineUpdater.RunWorkerAsync();        
  }

  void startTimelineUpdater_DoWork(object sender, DoWorkEventArgs e)
  {
    while (true)
    {
      Xtweet getSQL = new Xtweet();
      var sqlset = getSQL.CollectLocalTimelineSql();

      int i = 0;
      while (i < 10)
      {
        foreach (var stringse in sqlset)
        {
          StringBuilder sb = new StringBuilder();

          sb.Append(stringse[0] + ": ");
          sb.Append(stringse[1] + " @ ");
          sb.Append(stringse[2]);

          sb.Append("\n");

          TheTimeLine = sb.ToString();

          _goUpdate += new UpdateTimeLineEvent(MainWindowGoUpdate);
          _goUpdate.Invoke();

          Thread.Sleep(10000);
          i++;
        } 
      } 
    }        
  }
  void MainWindowGoUpdate()
  {
    txtb_timeline.Text = TheTimeLine;
  }
  void startTimelineUpdater_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
  {
    txtb_timeline.Text = "should not see this";
  }  
 }
2012-04-04 22:08
by Damo
SO is already filled with tens of thousands of questions about this. I guess what's unique about yours is that you subscribe the event handler in the worker thread instead of subscribing it before you start it - Hans Passant 2012-04-04 22:36


0

I will try these change:
In your UpdateTimeLine add

 startTimelineUpdater.WorkerReportsProgress = true;
 startTimelineUpdater.ProgressChanged += new ProgressChangedEventHandler
                                      (startTimelineUpdater_ProgressChanged);

In startTimelineUpdater_DoWork remove these lines

TheTimeLine = sb.ToString();  
_goUpdate += new UpdateTimeLineEvent(MainWindowGoUpdate);  
_goUpdate.Invoke();  

and insert these:

BackgroundWorker bkgwk = sender as BackgroundWorker;
bkgwk.ReportProgress(0, sb.ToString());

finally add the progress event

private void startTimelineUpdater_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
   txtb_timeline.Text = e.ObjectState.ToString();
}

Now you can leave only the call to UpdateTimeLine and remove the MainWindowGoUpdate method

2012-04-04 22:44
by Steve
Hi steve, thanks for the input. When I try your suggestion I encounter "This BackgroundWorker states that it doesn't report progress. Modify WorkerReportsProgress to state that it does report progress. - Damo 2012-04-06 10:28
Right, I forgot to add that line. It's a mistery for me why MS requires this line. Should be obvious when you add an event handler for ProgressChanged. Look at my updated answer now - Steve 2012-04-06 10:43
Thank you very much. Just a minor change on startTimelineUpdater_ProgressChanged to use e.UserState.ToString(); but it works great. : - Damo 2012-04-06 10:54


2

You can use the Dispatcher class:

Dispatcher.BeginInvoke(
    DispatcherPriority.Input, new Action(() => 
    { 
        //update UI
    }));

However in your case I would collect the results from the BackgroundWorker in a local variable and then change your label to loop through the results based on a WPF Timer object.

2012-04-04 22:44
by aKzenT


1

You can use the Dispatcher to update your GUI. Check this blog post for a rather good example on how to do it.

2012-04-04 22:22
by shriek


0

Where you

  txtb_timeline.Text = "should not see this";

Is where you get the results. Results will be in e.Result And you can pack e.Result with multiple properties.

If you want to get intermediate results you can use the progress.

2012-04-04 22:35
by paparazzo
startTimelineUpdater_DoWork should never end to invoke the completed metho - Damo 2012-04-04 22:41
OK understand, then will Progress do what you need? http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.progresschanged.asp - paparazzo 2012-04-04 22:59
Ads