开发者

BackgroundWorker with Dispatcher doesn't seem to do anything

I am trying to update an ObservableCollection that is data bound to the UI. I know that to do this I need to use Dispatcher and BeginvInvoke(), and to make it so that 开发者_如何学JAVAthe UI doesn't freeze up when I do so, using a BackgroundWorker is a good way to go about it. In any event, I have all this, compiled and running, but nothing happens. I need to update the UI every 2 minutes or so, so I am also using a DispatcherTimer

This works, because DispatcherTimer is part of Dispatcher, but freezes the UI:

DispatcherTimer dispTimer = new DispatcherTimer();
dispTimer.Tick += dispTimer_Tick;
dispTimer.Interval = new TimeSpan(0, 0, 45);
dispTimer.Start();

private void dispTimer_Tick(object sender, EventArgs e)
{
    PartialEmployees.Clear();          
}

So, using the BackgroundWorker I pieced together this:

DispatcherTimer dispTimer = new DispatcherTimer();
dispTimer.Tick += dispTimer_Tick;
dispTimer.Interval = new TimeSpan(0, 0, 45);
dispTimer.Start();

private void dispTimer_Tick(object sender, EventArgs e)
{
    BackgroundWorker _worker = new BackgroundWorker();
    _worker.DoWork += DoWork;            
    _worker.RunWorkerAsync();

}

private void DoWork(object sender, DoWorkEventArgs e)
{            
    Dispatcher.CurrentDispatcher.BeginInvoke( new Action(()=> 
        {
            PartialEmployees.Clear();
        }));
} 

But nothing happens to the UI. What am I missing/not doing correctly?


You have two problems:

  1. When you use Dispatcher.CurrentDispatcher from the background thread, it is getting the background thread's Dispatcher, not the UI thread's Dispatcher.

  2. From your description I gather that your PartialEmployees.Clear() method takes significant time to execute and you want to avoid locking the UI thread during the execution. However, having a BackgroundWorker invoke PartialEmployees.Clear() on your UI thread will have the same effect as using the DispatcherTimer, so you need a different solution than the one you are going for.

If you only want to fix the Dispatcher.CurrentDispatcher problem, just store the current Dispatcher in a local variable like this:

private void dispTimer_Tick(object sender, EventArgs e) 
{
  var uiDispatcher = Dispatcher.CurrentDispatcher;

  BackgroundWorker _worker = new BackgroundWorker(); 
  _worker.DoWork += (sender, e) =>
    uiDispatcher.BeginInvoke(new Action(() =>
    {
      PartialEmployees.Clear();
    }));
  _worker.RunWorkerAsync(); 
} 

This will cause your UI change to work but it will still lock up the UI during the change, exactly as if you had not used BackgroundWorker. The reason for this is:

  1. The DispatcherTimer fires, executing on the UI thread. All it does (dispTimer_Tick) is start a BackgroundWorker and then exit.
  2. The BackgroundWorker executes on its own therad. All it does is schedule a Dispatcher callback and then exit.
  3. The Dispatcher callback executes on the UI thread again. It calls PartialEmployees.Clear() which takes a while, locking up your UI thread while it executes.

So your behavior is the same as if the DispatcherTimer callback had called PartialEmployees.Clear() directly: In each case the time-consuming operation is executed on the UI thread.

The reason for the lockup is that any time you do a large piece of work on the UI thread you will get a momentary lockup while it runs. The solution is to break your work into smaller portions and do them one at a time, either from a DispatcherTimer or a BackgroundWorker. In your case, examine the code for PartialEmployees.Clear() to see if it can be done incrementally.


The problem here is that you're using the method Dispatcher.CurrentDispatcher from the back ground thread. What you need is the Dispatcher instance for the UI thread.

_worker.DoWork += delegate { DoWork(Dispatcher.CurrentDispatcher); };

...
private void DoWork(Dispatcher dispatcher) {
  dispatcher.BeginInvoke(new Action(() => {
    PartialEmployees.Clear();
  });
}


I dont think you need the background work, as BeginInvoke on the Dispatcher runs on a Threadpool thread.

something like this should work, and is more succinct

DispatcherTimer dispTimer = new DispatcherTimer 
    {Interval = TimeSpan.FromSeconds(45)};
dispTimer.Tick += (o,e) => Dispatcher.CurrentDispatcher
    .BeginInvoke((Action)PartialEmployees.Clear);
dispTimer.Start();
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜