Wie verwende ich Xamarin.Forms, um eine asynchrone Methode zu erstellen, die darauf wartet, dass das Formular geschlossen wird? Wenn ich benutze
await Navigation.PushModalAsync(page);
sobald die Animation beendet ist, wird sie zurückgegeben, nicht wenn die Seite geschlossen wird.
Ich möchte eine modale Task-SignInAsync-Methode erstellen, die true zurückgibt, wenn die Anmeldung erfolgreich ist.
Sie können dies tun, indem Sie auf Ihrer Anmeldeseite ein Ereignis auslösen und das Ereignis abhören, bevor Sie fortfahren, aber Sie möchten die volle TAP-Unterstützung, und ich unterstütze Sie dort. Hier ist eine einfache, aber funktionierende 2-Seiten-App, die genau dies tut. Natürlich möchten Sie die benutzerdefinierte Unterklasse ContentPage
verwenden und die richtigen Methoden anstelle meiner schnellen Command
s verwenden, aber Sie haben die Idee, und ich muss nicht mehr tippen.
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;
}
Der kleinere Nachteil ist, dass Task<bool>
vor dem Ende der Pop-Modal-Animation zurückgegeben wird.
Page
zu verschieben. Ansonsten, meh, mach einfach weiter.Erstens ist es erwähnenswert, dass das einfache Überschreiben von OnAppearing
in der aufrufenden Seite unter vielen Umständen ausreichen kann.
protected override void OnAppearing()
{
base.OnAppearing();
...
// Handle any change here from returning from a Pushed Page
}
(Beachten Sie, dass die OnDisappearing
-Überschreibung der gepufferten Seite after die OnAppearing
des Aufrufers genannt wird - scheint mir ein wenig rückwärts zu sein!)
Zweitens ... das ist meine Meinung zu @Chad Bonthuys Antwort:
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);
}
}
Und dann nenne es so:
SettingsPage sp = new SettingsPage();
await Navigation.PushAsync(sp);
await sp.PageClosedTask; // Wait here until the SettingsPage is dismissed
In meiner Implementierung habe ich verwendet:
await navigation.PopModalAsync();
Vollständiges Beispiel:
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);
}
}
Ich dachte nur, ich würde zu diesem Beitrag beitragen, obwohl es schon eine Weile her ist, seit es gefragt und beantwortet wurde. Ich habe auf der Antwort von @noelicus aufgebaut. Ich wollte eine generische Methode, um dies in mehreren Situationen zu tun, sodass die Aufgabe nicht nur bool, sondern alles zurückgeben muss. Dann mit Generika:
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);
}
}
Auf der Seite, die Sie als modal ausführen möchten, können Sie Folgendes tun:
public partial class NewPerson : AwaitableContentPage<Person>
und wenn Sie fertig sind, machen Sie einfach:
base.PageResult = newPerson; // object you created previously
await base.Navigation.PopAsync();
Verwenden Sie dann eine Erweiterungsmethode, um die Verwendung zu vereinfachen:
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;
}
Das ist alles. Nun, in Ihrem Code, auf jeder Seite, auf der Sie dies verwenden möchten, endet die Syntax einfach wie folgt:
Person newPerson = await Navigation.GetResultFromModalPage<string>(new NewPersonCreatePage());
if (newPerson != null)
UseNewPersonCreatedByOtherPage();
Hoffe das hilft!