.net - WPF UI 更新细节

标签 .net wpf multithreading user-interface

我的一般问题的说明:
使用 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 吗?

有哪些可能的解决方案或解决方法?

这是一个真正的问题还是只是我理解不足的结果?

最佳答案

该问题并非 WPF 特有 - 大多数 UI 框架的行为方式相同。

这里的问题是,在 UI 线程可以处理消息之前,您的 UI 不会更新。只要您正在做工作(UI 相关操作),UI 线程就会很忙,无法处理重绘屏幕所需的消息。在这种情况下,这意味着忙碌指示器在一切完成之前不会重绘。

这里真正的解决方案是将操作移至后台线程中。虽然您说所有这些都与 UI 线程相关,但通常有一种方法可以在 WPF 中分离出其中的各个部分。关键是要考虑那些需要很长时间的部分,然后将它们移出,而且只移出它们。这通常意味着加载数据,但在完全加载和处理数据之前不会将其设置到 UI(即:设置绑定(bind)属性)。

如果这完全不可能,唯一真正的选择是设置繁忙指示器,然后使用某种机制来推迟其他工作,直到 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 会更可靠地工作,但同样,这确实不是一个很好的处理方法。

关于.net - WPF UI 更新细节,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7999024/

相关文章:

.net - 如何通过 Windows 应用程序发送短信

c# - 如何在 C# 中使用 LINQ 从 ListView 中删除项目

c# - 如何序列化 Windows.Media.Brush

c# - DataTemplate 在 WPF 中传递不正确的命令参数

c# - 多线程时循环索引超出范围 ArgumentOutOfRangeException

c# - 如何在 WPF 中捕获从子组件到父组件的激活命令?

wpf - 如何实现在XamDataGrid中导致多列记录过滤的搜索?

c# - 如何使用 CommandManager 并且仍然能够手动触发 ICommand.CanExecuteChanged 事件,即明确地?

c++ - tbb::concurrent_hash_map 抛出 SIGSEGV

java - 如何在一个线程中发生异常后停止可调用线程进程?