19

使用 Xamarin.Forms 如何使用 make 一个等待表单关闭的异步方法?如果我使用

await Navigation.PushModalAsync(page);

它会在动画完成后返回,而不是在页面关闭时返回。

我想要一个创建模式任务 SignInAsync 方法,如果登录成功则返回 true。

4

5 回答 5

24

您可以通过在登录页面中触发一个事件并在继续之前侦听该事件来做到这一点,但您需要完整的 TAP 支持,我在此为您提供支持。这是一个简单但有效的 2 页应用程序,它就是这样做的。您显然希望使用ContentPage自定义子类并使用适当的方法而不是我的 quick Command,但是您明白了,它可以节省我的打字时间。

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>在弹出模态动画结束之前返回,但那是:

  1. 易于修复
  2. 仅当您正在等待该结果以推送新的 modal 时才会出现问题Page。否则,嗯,继续。
于 2014-06-12T07:39:43.567 回答
19

覆盖 OnAppearing

首先,值得注意的是,OnAppearing在许多情况下,简单地覆盖调用 Page 可能就足够了。

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

(请注意,推送页面的OnDisappearing覆盖是在调用者之后调用的OnAppearing- 对我来说似乎有点倒退!)


等待内容页面

其次......这是我对@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
于 2016-07-16T11:25:43.310 回答
2

只是想我会为这个做出贡献,尽管它已经有一段时间没有被问到和回答了。我建立在@noelicus 的答案之上。我想要一种通用的方法来处理多种情况,因此任务不仅需要返回布尔值,还需要返回任何内容。然后,使用泛型:

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

希望这可以帮助!

于 2017-11-23T22:00:18.993 回答
1

在我的实现中,我使用了:

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);
        }
    }
于 2014-07-21T13:53:35.350 回答
1

上面@Stephane Delcroix 选择并给出的答案很棒。但是对于任何愿意进一步推动这一点的人,通过等待页面完成并以良好的 MVVM 方式返回更多结构化数据,您可以执行以下操作:

通过从页面的 OnDisapearing 方法调用事件,您创建的导航服务可以订阅此事件,然后您可以使用"TaskCompletionSource"to wati 直到您的页面完成其工作,然后完成任务。有关完成此操作的更多详细信息,您可以查看此博客文章。

这是基本页面的实现,这个演示应用程序中的每个页面都继承了这个页面:

public class BasePage<T> : ContentPage
{
    public event Action<T> PageDisapearing;
    protected T _navigationResut;

    public BasePage()
    {
    }

    protected override void OnDisappearing()
    {
        PageDisapearing?.Invoke(_navigationResut);
        if (PageDisapearing != null)
        {
            foreach (var @delegate in PageDisapearing.GetInvocationList())
            {
                PageDisapearing -= @delegate as Action<T>;
            }
        }
        base.OnDisappearing();
    }
}

以下是您应该使用的导航服务的概述:

public async Task<T> NavigateToModal<T>(string modalName)
    {
        var source = new TaskCompletionSource<T>();
        if (modalName == nameof(NewItemPage))
        {
            var page = new NewItemPage();
            page.PageDisapearing += (result) =>
            {
                var res = (T)Convert.ChangeType(result, typeof(T));
                source.SetResult(res);
            };
            await App.Current.MainPage.Navigation.PushModalAsync(new NavigationPage(page));
        }
        return await source.Task;
    }

要使用导航服务调用此页面,可以使用以下代码:

 var item = await new SimpleNavigationService().NavigateToModal<Item>(nameof(NewItemPage));
        Items.Add(item);
于 2020-01-20T10:28:24.413 回答