Chris
Chris

Reputation: 65

UI Progress bar not updating until after async method is called

I'm trying to display a simple progress bar while a service loads. I can't for the life of my figure out why this isn't executing immediately.

View:

<ProgressBar x:Name="ProgressBar" 
    IsIndeterminate="True" 
    Visibility="{Binding IsLoading, Mode=OneWay, Converter={StaticResource BoolToVisiblityConverter}}"   />

View Model:

private Boolean _isLoading;
public Boolean IsLoading
    {
        get { return _isLoading; }
        set
        {
            Set(ref _isLoading, value);
        }
    }

public SignInViewModel(INavigationService navigationService)
    {
        IsLoading = false;
        _navigationService = navigationService;
        Username = "";
        SignInCommand = new RelayCommand( SignIn, CanSignIn);
    }

private async void SignIn()
    {

  //    DispatcherHelper.CheckBeginInvokeOnUI(() =>
  //       {
               IsLoading = true;
               RaisePropertyChanged("IsLoading");
   //      });

        var response = await OrchestratorContext.Instance.SignInWithPassword(_username, _password);

        if (response.IsSuccessful)
        {
            //     _navigationService.NavigateTo("LandingPage");
        }
    }

The progress bar doesn't start loading until after the Sign In function is finished. I've tried using the dispatcher, and also tried raising the property changed after setting. Any suggestions?

Upvotes: 0

Views: 527

Answers (2)

Harald Coppoolse
Harald Coppoolse

Reputation: 30492

You correctly took care that if you want to use await somewhere in your function that this function should be declared async.

However, every async function should return either Task (if it would normally would return void) or Task<TResult> if it normally would return TResult.

There is one exception: the event handler can return void instead of Task, however, the event handler still has to be declared async.

If you follow these rules, your UI remains responsive and the progress bar keeps being updated:

private async void Button1_clicked(object sender, ...)
{
    await Signin();
}

private async Task SignIn()
{
    // do some processing
    var response = await OrchestratorContext...
    // do some more processing
}

Note the Task return value of Signin()

Now during your await the UI remains responsive.

Upvotes: 0

Chris
Chris

Reputation: 65

I'm not sure if this is the best approach, but I got it working by forcing a new thread. Is there a better approach?

private void SignIn()
    {
        IsLoading = true;

        Task.Factory.StartNew(async () =>
        {
            var response = await OrchestratorContext.Instance.SignInWithPassword(_username, _password);

            if (response.IsSuccessful)
            {
                DispatcherHelper.CheckBeginInvokeOnUI(() =>
                    {
                        _navigationService.NavigateTo("LandingPage");
                    });
            }
        });
    }

Upvotes: 1

Related Questions