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

时间:2016-11-15 17:35:05

标签: c# wpf user-interface components textblock

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

问题是我的TextBlock在所有工作完成后(太晚)更改了内容。如何在进程中强制WPF重绘自己?

代码如下所示:

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

我尝试在output_log.InvalidateVisual();发生任何更改后添加TextBlock,但未按预期运行。

1 个答案:

答案 0 :(得分:1)

如果在Click的{​​{1}}处理程序中运行同步代码,则此代码将在Button线程中执行,从而阻止Dispatcher运行任何其他代码代码,例如在Dispatcher中显示邮件的更改。

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

首先,您可以在另一个TextBlockExecuteThread事件处理程序中运行Task代码并设置async使用Text

Dispatcher

主要优点是你没有阻止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中执行Execute代码,但是每次您需要时都必须“刷新”Dispatcher刷新文本,以便它可以处理所有等待的UI操作:

Dispatcher

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

第三

  • 您可以使用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事件处理程序(或Execute)中运行您的async代码,
  • 仅更改Command
  • LogText属性
  • 使用数据绑定将ViewModel绑定到此TextBlock.Text属性。

不幸的是,我无法为您提供此方案的快速示例代码,但它肯定值得考虑,因为MyLogViewModel.LogText对于任何类型的WPF应用程序来说都是一个非常自然的架构。