web-dev-qa-db-fra.com

Comment puis-je attendre le renvoi de formulaire modal à l'aide de Xamarin.Forms?

À l'aide de Xamarin.Forms, comment utiliser une méthode asynchrone qui attend que le formulaire soit ignoré? Si j'utilise

await Navigation.PushModalAsync(page);

il reviendra une fois l'animation terminée, pas lorsque la page sera fermée.

Je veux une méthode modale SignInAsync de tâche qui renvoie la valeur true si la connexion a abouti.

16
Jamey McElveen

Pour ce faire, vous pouvez déclencher un événement dans votre page de connexion et l'écouter avant de continuer, mais vous souhaitez bénéficier de la prise en charge intégrale de TAP et je vous l'appuie. Voici une application simple mais fonctionnelle qui fait exactement cela. Vous voudrez évidemment utiliser la sous-classe ContentPage personnalisée et disposer de méthodes appropriées à la place de ma rapide Commands, mais vous avez l’idée, et cela me évite de taper.

public static Page GetFormsApp ()
{
    NavigationPage navpage = null;
    return navpage = new NavigationPage (new ContentPage { 
        Content = new Button {
            Text = "Show Login dialog",
            Command = new Command (async o => {
                Debug.WriteLine ("Showing sign in dialog");
                var result = await SignInAsync (navpage);
                Debug.WriteLine (result);
            })
        }
    });
}

static Task<bool> SignInAsync (NavigationPage navpage)
{
    Random rnd = new Random ();
    var tcs = new TaskCompletionSource<bool> ();
    navpage.Navigation.PushModalAsync (new ContentPage {
        Content = new Button {
            Text = "Try login",
            Command = new Command ( o => {
                var result = rnd.Next (2) == 1;
                navpage.Navigation.PopModalAsync ();
                tcs.SetResult (result);
            })
        }
    });
    return tcs.Task;
}

L'inconvénient mineur est que le Task<bool> est renvoyé avant la fin de l'animation modale pop, mais c'est:

  1. facile à réparer
  2. seulement un problème si vous attendez ce résultat pour envoyer une nouvelle modale Page. Sinon, continuez.
21
Stephane Delcroix

Ignorer à l’apparition

Tout d'abord, il convient de noter que le simple remplacement de OnAppearing dans la page d'appel peut suffire dans de nombreuses circonstances. 

protected override void OnAppearing()
{
    base.OnAppearing();
    ...
    // Handle any change here from returning from a Pushed Page
}

(notez que la substitution OnDisappearing de la page poussée est appelée après la OnAppearing de l'appelant - me semble un peu en arrière!)


AwaitableContentPage

Deuxièmement, voici ce que je pense de @Chad Bonthuys:

public class AwaitableContentPage : ContentPage
{
    // Use this to wait on the page to be finished with/closed/dismissed
    public Task PageClosedTask { get { return tcs.Task; } }

    private TaskCompletionSource<bool> tcs { get; set; }

    public AwaitableContentPage()
    {
        tcs = new System.Threading.Tasks.TaskCompletionSource<bool>();
    }       

    // Either override OnDisappearing 
    protected override void OnDisappearing()
    {
        base.OnDisappearing();
        tcs.SetResult(true);
    }

    // Or provide your own PopAsync function so that when you decide to leave the page explicitly the TaskCompletion is triggered
    public async Task PopAwaitableAsync()
    {
        await Navigation.PopAsync();
        tcs.SetResult(true);
    }
}

Et appelez-le ainsi:

SettingsPage sp = new SettingsPage();
await Navigation.PushAsync(sp);
await sp.PageClosedTask; // Wait here until the SettingsPage is dismissed
13
noelicus

Dans mon implémentation, j'ai utilisé: 

await navigation.PopModalAsync();

Exemple complet:

private INavigation navigation;
    public LoginPageModel(INavigation navigation, LoginPage loginPage)
    {
        this.navigation = navigation;
        this.loginPage = loginPage;
    }


public bool IsValid { get; set; }

    protected async void ExecuteLoginCommand()
    {
        var loginResult = await AuthenticationHelper.Authenticate(Email, Password);

        var isValid = false;

        if (loginResult != null)
        {

            isValid = true;
        }

   //return isValid;
        AuthenticationResult(isValid);
    }

private async void AuthenticationResult(bool isValid)
    {
        if (isValid)
        {
            Debug.WriteLine("Logged in");
            await navigation.PopModalAsync();
        }
        else
        {
            Debug.WriteLine("Failed" + email + password);
            await loginPage.DisplayAlert("Authentication Failed", "Incorrect email and password combination","Ok", null);
        }
    }
1
Chad Bonthuys

Je pensais juste que je contribuerais à celui-ci, même si cela faisait longtemps que cela n’avait pas été demandé et répondu. Je me suis appuyé sur la réponse de @noelicus. Je voulais un moyen générique de faire cela avec plusieurs situations, donc la tâche doit être capable de retourner non seulement une erreur, mais n'importe quoi. Ensuite, en utilisant des génériques:

public class AwaitableContentPage<T> : ContentPage
{
    // Use this to wait on the page to be finished with/closed/dismissed
    public Task<T> PageClosedTask => tcs.Task;

    // Children classes should simply set this to the value being returned and pop async() 
    protected T PageResult { get; set; }

    private TaskCompletionSource<T> tcs { get; set; }

    public AwaitableContentPage()
    {
        tcs = new TaskCompletionSource<T>();
    }

    protected override void OnDisappearing()
    {
        base.OnDisappearing();
        tcs.SetResult(PageResult);
    }
}

Maintenant, dans la page que vous voulez exécuter en tant que modal, vous pouvez faire:

public partial class NewPerson : AwaitableContentPage<Person>

et quand c'est fait, faites simplement:

            base.PageResult = newPerson; // object you created previously
            await base.Navigation.PopAsync();

Ensuite, pour simplifier l'utilisation, utilisez une méthode d'extension:

public static class ExtensionMethods
{
    async public static Task<T> GetResultFromModalPage<T>(this INavigation nav, AwaitableContentPage<T> page)
    {
        await nav.PushAsync(page);
        return await page.PageClosedTask;
    }

C'est tout. Maintenant, dans votre code, dans n'importe quelle page où vous voulez utiliser ceci, la syntaxe se termine simplement comme ceci:

            Person newPerson = await Navigation.GetResultFromModalPage<string>(new NewPersonCreatePage());

            if (newPerson != null)
                UseNewPersonCreatedByOtherPage();

J'espère que cela t'aides!

1
David