By invoking Task.Run you broke your association(SynchronizationContext) with the GUI thread (or WPF Dispatcher) and lost most of the async/await 'goodness'.
Why not use an async void event handler and just come back to the SynchronizationContext(GUI Thread/Dispatcher) for each step?
private async void Button_Click_1(object sender, RoutedEventArgs e)
{
while (true)
{
string result = await LoadNextItem();
lbl1.Content = result;
}
}
private static int ir11 = 0;
Task<string> LoadNextItem()
{
await Task.Delay(1000); // placeholder for actual async work
ir11++;
return "aa " + ir11;
}
Or if you really want to separate the state machine for the 'on-going' operations, try passing an IProgress<T>
(the default impl. Progress<T>
or specifically Progress<string>
should work great in this case). See this article by @Stephen Cleary
His example is very close to what you stated in the question. I've copied it here for SO independence.
public async void StartProcessingButton_Click(object sender, EventArgs e)
{
// The Progress<T> constructor captures our UI context,
// so the lambda will be run on the UI thread.
var progress = new Progress<int>(percent =>
{
textBox1.Text = percent + "%";
});
// DoProcessing is run on the thread pool.
await Task.Run(() => DoProcessing(progress));
textBox1.Text = "Done!";
}
public void DoProcessing(IProgress<int> progress)
{
for (int i = 0; i != 100; ++i)
{
Thread.Sleep(100); // CPU-bound work
if (progress != null)
progress.Report(i);
}
}
Edit: I must admit, while Progress<T>
is a nice abstraction, in this case it is just going to fall down to Dispatcher.Invoke as @Pamparanpa suggested.