Question

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

Was it helpful?

Solution

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.

OTHER TIPS

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).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top