0

I have a button, when I click it, I want it to update the UI with the value

this is my button

       private async void Button_Click(object sender, RoutedEventArgs e)
       {
        await DoItAsync();
       }

and this is DoIt!

    public async Task DoSomethingAsync()
    {
       await Task.Run(()=>{
            for(int i = 0; i < 2000; i++)
            {
               //labelName.Content = i.ToString();
               Console.WriteLine(i);
            }

        });
    }

so if I run the above code, click my button, DoItAysnc is called and all the numbers are displayed in the console.

If I un-comment

labelName.Content = i.ToString(); 

I get an exception, the following in fact : The calling thread cannot access this object because a different thread owns it.

I know Async and await don't solve threading for you, but I did think this would work. I would like to know how to get this to work so that the UI is updated for each i value without the UI becoming unresponsive.

Any good resources on this would be helpful also.

As always, kinds regards

James
  • 1,363
  • 19
  • 28
  • What is this? WPF, winforms, windows runtime? – Smeegs May 12 '14 at 15:20
  • Welcome to the UI Thread world. For WPF learn Dispatcher, for Winforms learn Invoke. – T McKeown May 12 '14 at 15:20
  • 2
    You need to do actual work for async/await to be useful. Please show what you actually are trying to do instead of a contrived example, there is 0 reason to use async/await or threads in your current code example. – Scott Chamberlain May 12 '14 at 15:21
  • Also, you can just return `Task.Run` if you're going to await the function call. – Smeegs May 12 '14 at 15:23
  • Hello, yes this is a WPF form. My understanding was that using async in this way would free up the UI and allow the results of i to be displayed in the label, is this not the case? I will read up on Dispatchers – James May 12 '14 at 15:26

2 Answers2

8

In this specific case it appears that all you're doing is essentially counting to 2000 in a label as fast as you possibly can. This is unlikely to be your actual use case.

If you want to be updating the UI between multiple different asynchronous operations (in which that operation is, for example, just waiting for some period of time) then you can structure your code like so:

private async void Button_Click(object sender, RoutedEventArgs e)
{
    for (int i = 0; i < 2000; i++)
    {
        await Task.Delay(TimeSpan.FromSeconds(1));
        labelName.Content = i.ToString();
    }
}

If what you're conceptually doing is updating the UI with progress of a long running non-UI operation, then the Progress class is designed to specifically handle that:

private async void Button_Click(object sender, RoutedEventArgs e)
{
    Progress<int> progress = new Progress<int>(
        i => labelName.Content = i.ToString());
    await Task.Run(() => DoWork(progress));
}
private void DoWork(IProgress<int> progress)
{
    for (int i = 0; i < 2000; i++)
    {
        //Do Stuff
        progress.Report(i);
    }
}

In general the first option makes sense if you can break up your problem into a group of small asynchronous operations in which the UI is updated between each. The second approach is done if there is a single long non-UI operation that cannot be easily broken up into smaller operations.

Servy
  • 202,030
  • 26
  • 332
  • 449
0

Take a look at the answer here. Regardless of how you kick off your worker threads (Thread.Start, Task.Run, BackgroundWorker.RunWorkerAsync), you cannot affect a change to the UI unless you are running on the UI thread.

As @t-mckeown states, the solution is to force your way onto the UI thread. How you do that depends on the technology (WFP=Dispatcher, WinForms=InvokeRequired/BeginInvoke).

Community
  • 1
  • 1
Colby Cavin
  • 492
  • 2
  • 6
  • `await` captures the current synchronization context and marshals callbacks to that context, thus preventing the developer from needing to explicitly marshal to the UI thread. It's huge advantage of using that style of programming to begin with. – Servy May 12 '14 at 17:10