我的一般问题的插图:
使用WPF我常常遇到以下问题:在启动UI操作之前,我打开一个视觉反馈控件来显示系统正忙(某种繁忙的指示器);然而,UI在没有显示繁忙指示符之前冻结。
private void MyTree_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
this.MyBusyIndicator.IsBusy = true;
...
//then some UI related operation
...
this.MyBusyIndicator.IsBusy = false;
}
(我无法将这个耗时的操作移动到另一个线程,因为它是与UI线程相关的操作。)
在寻找解决此类问题的可能解决方案或建议时,我阅读了一些关于某些WPF(甚至.NET框架)UI更新细节的信息,这是造成此类问题的主要原因。
所以我的问题:
什么是WPF(或.NET框架)UI更新机制细节,阻止UI以与代码中定义的完全相同的顺序更新?
它确实只针对WPF吗?
有哪些可行的解决方案或解决方法?
这是一个真正的问题,还是仅仅是我理解不善的结果?
答案 0 :(得分:3)
问题并非特定于WPF - 大多数UI框架的行为方式相同。
这里的问题是,在UI线程可以处理消息之前,您的UI不会更新。只要您正在进行工作(与UI相关的操作),UI线程就会忙,无法处理重绘屏幕所需的消息。在这种情况下,这意味着繁忙的指标在完成所有操作后才会重绘。
这里真正的解决方案是将操作移动到后台线程中。虽然你说所有这些都是与UI线程相关的,但通常有一种方法可以在WPF中分离出部分内容。关键是要考虑需要很长时间的部件,并将它们移动,只移动它们。这通常意味着加载数据,但不将其设置为UI(即:设置绑定属性),直到完全加载和处理完毕后。
如果完全不可能,唯一真正的选择是设置繁忙指示器,然后使用某种机制推动其他工作,直到UI线程可以处理。这可以通过使用Dispatcher.BeginInvoke将剩余的工作推迟到“稍后”来完成,尽管这有点不太常见。这看起来像是:
private void MyTree_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
this.MyBusyIndicator.IsBusy = true;
this.Dispatcher.BeginInvoke( (Action) () =>
{
// This is now sent to the dispatcher to process later, which gives the busy indicator a chance to refresh...
//then some UI related operation
this.MyBusyIndicator.IsBusy = false;
});
}
话虽这么说,但这并不总是完全可靠 - 因为你仍然在锁定你的UI线程。理想情况下,你永远不应该阻止UI,因为它会阻止重绘正确发生...... DispatcherTimer可以更可靠地工作,但同样,它确实不是一个非常好的方法来处理它。
答案 1 :(得分:0)
在.NET中您可以使用Thread Queue
private void Button_Click( object sender, RoutedEventArgs e )
{
ThreadPool.QueueUserWorkItem(
delegate
{
// GUI Thread call
this.Dispatcher.BeginInvoke(
DispatcherPriority.Normal,
new Action( () => { this.MyBusyIndicator.IsBusy = true; }) )
// Action in non-GUI Thread
...
// GUI Thread call
this.Dispatcher.BeginInvoke(
DispatcherPriority.Normal,
new Action( () => { this.MyBusyIndicator.IsBusy = false; }) )
} );
}
}