当用户点击Execute
按钮时,我想要做一些事情并逐步向TextBlock
输出日志消息 - 这样用户就可以看到当前正在发生的事情。
问题是我的TextBlock
在所有工作完成后(太晚)更改了内容。如何在进程中强制WPF重绘自己?
代码如下所示:
private void btn_execute_Click(object sender, RoutedEventArgs e)
{
.... stuff ....
}
我尝试在output_log.InvalidateVisual();
发生任何更改后添加TextBlock
,但未按预期运行。
答案 0 :(得分:1)
如果在Click
的{{1}}处理程序中运行同步代码,则此代码将在Button
线程中执行,从而阻止Dispatcher
运行任何其他代码代码,例如在Dispatcher
中显示邮件的更改。
有(至少)三种可能的方法来解决这个问题。
首先,您可以在另一个TextBlock
,Execute
或Thread
事件处理程序中运行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应用程序来说都是一个非常自然的架构。