简体   繁体   中英

Update GUI from background worker or event

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";
  }  
 }

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.

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

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.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM