Reputation: 107
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 the 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?
Upvotes: 3
Views: 6954
Reputation: 20461
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();
Upvotes: 0
Reputation: 62919
You have two problems:
When you use Dispatcher.CurrentDispatcher
from the background thread, it is getting the background thread's Dispatcher, not the UI thread's Dispatcher.
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:
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.
Upvotes: 3
Reputation: 754735
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();
});
}
Upvotes: 1