0

I need some info. I got a login function called after clicking on the login button. At the beginning of the async function, I set IsBusy=true, then I call my await LoginAsynx(user, pass) function. The loading indicator does not pop. So I try to add a await Task.Delay(100); just after IsBusy=true and before the login function and now the loading animation work.

I am new to xamarin.form, so I don't understand why this behaviour occurred. Like if the await function came to fast and lock the UI thread before the IsBusy=true complete the binding.

EDIT 1 Command calling the fucntion:

public ICommand SignInCommand => new Command(async () => await SignInAsync());

here my LoginViewModel

    private async Task SignInAsync()
    {
        IsBusy = true;
        await Task.Delay(100);   // If I remove that, no more wating indicator
        bool isValid = Validate();

        if (isValid)
        {

            try
            {
                KelvinLoginResult LoginResult = await _kelvinService.LoginAsync(UserName.Value, Password.Value);
                if (LoginResult.EstAuthtifie == false)
                {
                    await DialogService.ShowAlertAsync("Authentication error", "Authentication error", "Ok");
                }
                else
                {
                    await NavigationService.NavigateToAsync<MainViewModel>();
                    //await NavigationService.RemoveLastFromBackStackAsync();
                }
            }
            catch (Exception ex)
            {

                await DialogService.ShowAlertAsync(ex.Message, "Error", "Ok");
            }            
        }

        IsBusy = false;
    }

Edit 2: Activity indicator on the form

    <!-- INDICATOR -->
    <ActivityIndicator      
      Color="{StaticResource LightGreenColor}"
      IsRunning="{Binding IsBusy}"
      IsVisible="{Binding IsBusy}"
      VerticalOptions="Center"
      HorizontalOptions="Center">
        <ActivityIndicator.WidthRequest>
            <OnPlatform x:TypeArguments="x:Double">
                <On Platform="iOS, Android" Value="100" />
                <On Platform="UWP, WinRT, WinPhone" Value="400" />
            </OnPlatform>
        </ActivityIndicator.WidthRequest>
    </ActivityIndicator>

Edit 3

    public bool IsBusy
    {
        get
        {
            return _isBusy;
        }

        set
        {
            _isBusy = value;
            RaisePropertyChanged(() => IsBusy);
        }
    }
Pierre-D Savard
  • 529
  • 3
  • 16

3 Answers3

0

Set the IsBusy property value must be done in the Main thread, the async method might or might not be called on the Main thread and this is not preemptive.

So my suggestions are:

  • Set the IsBusy to true property before calling the login method.
  • Use the Device.BeginInvokeOnMainThread method when setting the IsBusy property to false it will ensure that it is running in the Main thread.

Also, if your login method is too fast and won't take long enough to display the waiting indicator, then, you could consider creating a delay task to make sure that the waiting indicator will be displayed. You could code something like:

IsBusy = true;
var delayTask = Task.Delay(100);
var loginTask = Login();
await Task.WhenAll(delay, login);
IsBusy = false;
Elton Santana
  • 950
  • 3
  • 11
  • 22
  • I already set IsBusy before calling the login method. The command functions binding on the button is a "async" too, do you think that my be the problem? For now it works if I do: 1- Set IsBusy to true, 2- await task.Delay(100); 3- Calling the login function. – Pierre-D Savard Jan 21 '19 at 20:41
  • Check the edited answer @Pierre-DSavard, if the login method doesn't take time long enough to display the awaiting indicator, then, you can use a delay in parallel. – Elton Santana Jan 22 '19 at 12:11
  • Thanks for the update. But the login function takes 5-6 sec to complete. So normally this is long enough to be able to display the waiting indicator. For now My code is like yours, but I do not have the line with the await Task.WhenAll(). Just adding the await task.delay(100) let the loading indicator appear for all the login process... wierd. – Pierre-D Savard Jan 22 '19 at 13:53
0

OK so after all those updates... this should be the answer - you need to use Task.Run to run the SigninAsync method.

Ivan Ičin
  • 9,672
  • 5
  • 36
  • 57
0

At the beginning of the async function, I set IsBusy=true, then I call my await LoginAsynx(user, pass) function. The loading indicator does not pop. So I try to add a await Task.Delay(100); just after IsBusy=true and before the login function and now the loading animation work.

That behavior tells me that this code:

KelvinLoginResult LoginResult = await _kelvinService.LoginAsync(UserName.Value, Password.Value);

is not actually asynchronous. It is almost certainly synchronous.

The proper solution is to make LoginAsync asynchronous, e.g., using async and await within that method.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • After validating the LoginAsync function, I see effectively the first part of the function is synchronous and the second part has an Await. In fact, I was having some weird problem when I try to call httpclient.postasync in asynchronous way. See this post: https://stackoverflow.com/questions/54205419/cant-call-old-webservice-in-xamarin-android-with-httpclient – Pierre-D Savard Jan 28 '19 at 15:42
  • @Pierre-DSavard: That's probably it, then. [By default, the POST/GET/etc operations on HttpClient only complete after the response is completely read](https://learn.microsoft.com/en-us/uwp/api/windows.web.http.httpcompletionoption). – Stephen Cleary Jan 28 '19 at 17:41