When user clicks on Execute
button, I want to do some stuff and output log messages to TextBlock
progressively - so user can see what is currently happening.
The problem is that my TextBlock
changes it content after all work is finished (too late). How can I force WPF to repaint itself during process ?
Code looks like this:
private void btn_execute_Click(object sender, RoutedEventArgs e)
{
.... stuff ....
}
I have tried adding output_log.InvalidateVisual();
after any change to TextBlock
, didn't work as expected.
If you run synchronous code in a Click
handler of a Button
, this code is being executed in the Dispatcher
thread and thus prevents the Dispatcher
from running any other code like displaying the changes of your messages in a TextBlock
.
There are (at least) three possible ways to solve this issue.
First, you can run your Execute
code in another Thread
, Task
or async
event handler and set the Text
using the 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 + ", ");
}
}
The main advantage is that you are not blocking the Dispatcher
- which is highly recommended for everything you do.
Second, you can keep doing your Execute
code in the Dispatcher
, but then you have to "flush" the Dispatcher
every time when you want to refresh your text, so that it can handle all waiting UI actions:
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(() => { }));
}
}
This is certainly possible but I really wouldn't recommend it.
Or third,
MVVM
for your architecture,Execute
code in an async
event handler (or Command
),LogText
property of your ViewModel
andTextBlock.Text
to this MyLogViewModel.LogText
property.Unfortunately I can't give you a quick sample code for this scenario, but it's surely worth thinking about it because MVVM
is just a really natural architecture for any kind of WPF application.