WPF TextBlock 仅在所有工作完成后显示所有日志消息
WPF TextBlock shows all log messages only after all work is done
当用户点击 Execute
按钮时,我想做一些事情并逐步将日志消息输出到 TextBlock
- 这样用户就可以看到当前发生的事情。
问题是我的 TextBlock
在所有工作完成后更改了它的内容(为时已晚)。如何强制 WPF 在处理过程中重新绘制自身?
代码如下所示:
private void btn_execute_Click(object sender, RoutedEventArgs e)
{
.... stuff ....
}
我尝试在对 TextBlock
进行任何更改后添加 output_log.InvalidateVisual();
,但没有按预期工作。
如果您在 Button
的 Click
处理程序中 运行 同步代码,则此代码将在 Dispatcher
线程中执行,从而阻止 Dispatcher
来自 运行ning 任何其他代码,例如在 TextBlock
.
中显示消息的更改
有(至少)三种可能的方法来解决这个问题。
首先,你可以在另一个Thread
、Task
或async
中运行你的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
代码,但是你必须"flush" [=14] =] 每次当你想刷新你的文本时,它可以处理所有等待的 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
代码,
- 仅更改
ViewModel
和 的 LogText
属性
- 使用数据绑定将
TextBlock.Text
绑定到此 MyLogViewModel.LogText
属性。
遗憾的是,我无法为您提供此场景的快速示例代码,但肯定值得考虑一下,因为 MVVM
对于任何类型的 WPF 应用程序来说都是一种非常自然的架构。
当用户点击 Execute
按钮时,我想做一些事情并逐步将日志消息输出到 TextBlock
- 这样用户就可以看到当前发生的事情。
问题是我的 TextBlock
在所有工作完成后更改了它的内容(为时已晚)。如何强制 WPF 在处理过程中重新绘制自身?
代码如下所示:
private void btn_execute_Click(object sender, RoutedEventArgs e)
{
.... stuff ....
}
我尝试在对 TextBlock
进行任何更改后添加 output_log.InvalidateVisual();
,但没有按预期工作。
如果您在 Button
的 Click
处理程序中 运行 同步代码,则此代码将在 Dispatcher
线程中执行,从而阻止 Dispatcher
来自 运行ning 任何其他代码,例如在 TextBlock
.
有(至少)三种可能的方法来解决这个问题。
首先,你可以在另一个Thread
、Task
或async
中运行你的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
代码,但是你必须"flush" [=14] =] 每次当你想刷新你的文本时,它可以处理所有等待的 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
代码, - 仅更改
ViewModel
和 的 - 使用数据绑定将
TextBlock.Text
绑定到此MyLogViewModel.LogText
属性。
LogText
属性
遗憾的是,我无法为您提供此场景的快速示例代码,但肯定值得考虑一下,因为 MVVM
对于任何类型的 WPF 应用程序来说都是一种非常自然的架构。