我正在尝试更新绑定到UI的数据ObservableCollection
。我知道为了做到这一点,我需要使用Dispatcher
和BeginvInvoke()
,并且为了使UI在我这样做时不会冻结,使用BackgroundWorker是一个很好的方法去做它。无论如何,我拥有所有这些,编译和运行,但没有任何反应。我需要每2分钟左右更新一次用户界面,所以我也使用了DispatcherTimer
这很有效,因为DispatcherTimer是Dispatcher的一部分,但冻结了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();
}
所以,使用BackgroundWorker我拼凑了这个:
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();
}));
}
但UI没有任何反应。我错过了什么/没做错?
答案 0 :(得分:3)
你有两个问题:
当您从后台线程使用Dispatcher.CurrentDispatcher
时,它将获取后台线程的Dispatcher,而不是UI线程的Dispatcher。
根据您的描述,我收集您的PartialEmployees.Clear()
方法需要花费大量时间来执行,并且您希望避免在执行期间锁定UI线程。但是,在UI线程上调用BackgroundWorker PartialEmployees.Clear()
与使用DispatcherTimer具有相同的效果,因此您需要的解决方案与您的解决方案不同。
如果您只想解决Dispatcher.CurrentDispatcher问题,只需将当前的Dispatcher存储在本地变量中,如下所示:
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();
}
这将导致您的UI更改生效,但在更改期间仍会锁定UI,就像您没有使用BackgroundWorker一样。原因是:
因此,您的行为与DispatcherTimer回调直接调用PartialEmployees.Clear()的行为相同:在每种情况下,都会在UI线程上执行耗时的操作。
锁定的原因是,无论何时在UI线程上执行大量工作,您都会在运行时获得瞬间锁定。解决方案是将您的工作分成更小的部分,并从DispatcherTimer或BackgroundWorker中一次执行一个。在您的情况下,请检查PartialEmployees.Clear()
的代码,看看是否可以逐步完成。
答案 1 :(得分:1)
这里的问题是您正在使用后台线程中的Dispatcher.CurrentDispatcher
方法。您需要的是UI线程的Dispatcher
实例。
_worker.DoWork += delegate { DoWork(Dispatcher.CurrentDispatcher); };
...
private void DoWork(Dispatcher dispatcher) {
dispatcher.BeginInvoke(new Action(() => {
PartialEmployees.Clear();
});
}
答案 2 :(得分:0)
我不认为您需要后台工作,因为BeginInvoke
上的Dispatcher
在Threadpool线程上运行。
这样的事情应该有效,而且更简洁
DispatcherTimer dispTimer = new DispatcherTimer
{Interval = TimeSpan.FromSeconds(45)};
dispTimer.Tick += (o,e) => Dispatcher.CurrentDispatcher
.BeginInvoke((Action)PartialEmployees.Clear);
dispTimer.Start();