Как я могу ожидать отклонения модальной формы с помощью Xamarin.Forms?

Использование Xamarin.Forms, как я могу использовать метод async, ожидающий отклонения формы? Если я использую

await Navigation.PushModalAsync(page);

он вернется после завершения анимации, когда страница не будет удалена.

Я хочу создать модальный метод TaskInAsync Task, который возвращает true, если вход успешно завершен.

Ответы

Ответ 1

Вы можете сделать это, инициировав событие на своей странице входа и прослушав это событие, прежде чем продолжить, но вы хотите, чтобы полная поддержка TAP и я второй вам там. Вот простое, но работающее 2-страничное приложение, которое делает именно это. Очевидно, вы захотите использовать ContentPage пользовательский подкласс и иметь правильные методы вместо моих быстрых Command s, но вы получите эту идею, и это избавит меня от ввода.

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;
}

Небольшой недостаток заключается в том, что Task<bool> возвращается до конца анимации pop popal, но это:

  • легко исправить
  • только проблема, если вы ожидаете этого результата, чтобы нажать новый модальный Page. В противном случае, meh, просто продолжайте.

Ответ 2

Override OnAppearing

Во-первых, стоит отметить, что просто переопределение OnAppearing на вызывающей странице может быть достаточно во многих случаях.

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

(обратите внимание, что переадресованная страница OnDisappearing override вызывается после вызова OnAppearing - кажется немного назад мне!)


AwaitableContentPage

Во-вторых... это мой ответ на @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);
    }
}

И затем назовите его так:

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

Ответ 3

В моей реализации я использовал:

await navigation.PopModalAsync();

Полный пример:

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);
        }
    }

Ответ 4

Просто подумал, что буду способствовать этому, хотя прошло некоторое время с тех пор, как его спросили и ответили. Я основывался на ответе @noelicus. Мне нужен общий способ сделать это с несколькими ситуациями, поэтому задача должна иметь возможность возвращать не только bool, но и все. Затем, используя generics:

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);
    }
}

Теперь, на странице, которую вы хотите запустить как модальный, вы можете сделать:

public partial class NewPerson : AwaitableContentPage<Person>

и когда это будет сделано, просто выполните:

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

Затем, чтобы сделать его очень простым в использовании, используйте метод расширения:

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;
    }

Это все. Теперь в вашем коде на любой странице, где вы хотите использовать это, синтаксис заканчивается следующим образом:

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

            if (newPerson != null)
                UseNewPersonCreatedByOtherPage();

Надеюсь, это поможет!