WPF TextBlock 仅在所有工作完成后才显示所有日志消息

Posted

技术标签:

【中文标题】WPF TextBlock 仅在所有工作完成后才显示所有日志消息【英文标题】:WPF TextBlock shows all log messages only after all work is done 【发布时间】:2017-03-29 17:04:34 【问题描述】:

当用户单击Execute 按钮时,我想做一些事情并逐步将日志消息输出到TextBlock - 这样用户就可以看到当前正在发生的事情。

问题是我的TextBlock 在所有工作完成后更改了它的内容(为时已晚)。如何在处理过程中强制 WPF 重新绘制自身?

代码如下:

    private void btn_execute_Click(object sender, RoutedEventArgs e)
    
        .... stuff ....
    

我尝试在对TextBlock 进行任何更改后添加output_log.InvalidateVisual();,但没有按预期工作。

【问题讨论】:

你为什么要在后面的代码中这样做?你没有使用 MVVM 吗? 【参考方案1】:

如果您在ButtonClick 处理程序中运行同步代码,则此代码将在Dispatcher 线程中执行,从而阻止Dispatcher 运行任何其他代码,例如显示消息的更改在TextBlock

有(至少)三种可能的方法来解决这个问题。

首先,您可以在另一个ThreadTaskasync 事件处理程序中运行您的Execute 代码,并使用Dispatcher 设置Text

private async void btn_execute_Click(object sender, RoutedEventArgs e)

     for (int i = 0; i < 100; i++)
     
         // Simulate doing some stuff...
         await Task.Delay(100);

         // Thanks to async/await the current context is captured and
         // switches automatically back to the Dispatcher thread after the await.
         output_log.Text += i + ", ";

         // If you were using Task.Run() instead then you would have to invoke it manually.
         // Dispatcher.Invoke(() => output_log.Text += i + ", ");
     

主要优点是您不会阻止Dispatcher - 强烈建议您执行所有操作。


第二,您可以继续在Dispatcher 中执行Execute 代码,但是每次要刷新文本时都必须“刷新”Dispatcher,所以它可以处理所有等待的 UI 操作:

private void btn_execute_Click(object sender, RoutedEventArgs e)

    for (int i = 0; i < 100; i++)
    
        // Simulate doing some stuff...
        Thread.Sleep(100);
        output_log.Text += i + ", ";
        Dispatcher.Invoke(DispatcherPriority.Background, new Action(() =>  ));
    

这当然是可能的,但我真的不推荐。


第三个

您可以将MVVM 用于您的架构, 在async 事件处理程序(或Command)中运行您的Execute 代码, 仅更改ViewModelLogText 属性和 使用数据绑定将TextBlock.Text 绑定到此MyLogViewModel.LogText 属性。

很遗憾,我无法为您提供此场景的快速示例代码,但肯定值得考虑一下,因为MVVM 对任何类型的 WPF 应用程序来说都是一种非常自然的架构。

【讨论】:

以上是关于WPF TextBlock 仅在所有工作完成后才显示所有日志消息的主要内容,如果未能解决你的问题,请参考以下文章

多个线程等待所有线程完成,直到新工作开始

Vue:在路线更改时,仅在完成加载后才呈现页面

JQuery:如何仅在完成调整大小后才调用 RESIZE 事件?

仅在第一个功能完成后才执行功能 - ios

如何仅在第一个完成后才发出后续 API 请求?

仅在完成操作IOS Swift 3后才使用户访问视图[关闭]