简体   繁体   English

清除 Xamarin 表单模式堆栈

[英]Clear Xamarin Forms Modal Stack

I have a Xamarin.Forms application that uses a NavigationPage for normal screen navigation.我有一个 Xamarin.Forms 应用程序,它使用 NavigationPage 进行普通屏幕导航。 From one of the screens (Stay Detail), I need to display a series of 4 sequential modal pages (like a wizard) that collects data to complete a process related to Stay Detail.在其中一个屏幕(Stay Detail)中,我需要显示一系列 4 个连续模式页面(如向导),这些页面收集数据以完成与 Stay Detail 相关的流程。 Each page in the process has a "Cancel" button that should allow the user to cancel out of the wizard and return to Stay Detail.流程中的每个页面都有一个“取消”按钮,该按钮应允许用户取消向导并返回到“保持详细信息”。 Here is the general flow:这是一般流程:

            modal1 -> modal2 -> modal3 -> modal4
           /                                    \
 StayDetail                                      StayDetail

It's easy enough to do a PushModalAsync from StayDetail to launch modal1, then PushModalAsync/PopModalAsync to go between the individual modal pages.从 StayDetail 执行 PushModalAsync 以启动 modal1,然后在各个模式页面之间执行 PushModalAsync/PopModalAsync 非常容易。 However, I can't figure out a clean way to exit out of the modal stack from the 2nd modal or later.但是,我想不出一种干净的方法从第二个模态或更高版本退出模态堆栈。 What's the best way to do that?这样做的最佳方法是什么?

Can't you just do the following every time the user navigates to the next modal? 每次用户导航到下一个模态时,您不能只执行以下操作吗?

await Navigation.PushModalAsync(new modal2()); //Send user to new modal2 pages
await Navigation.PopModalAsync(); //Pop modal1 page

Then when they hit the cancel button, you would do one more PopModalAsync() and would be back on the StayDetail page since you already popped all modals underneath. 然后当他们点击取消按钮时,你会再做一次PopModalAsync()并返回到StayDetail页面,因为你已经弹出了下面的所有模态。

If they needed a way to go back to modal1 after getting to modal2, you could simple pass any data modal1 needs while creating a new page like so: 如果他们在获得modal2之后需要一种方法回到modal1,你可以简单地传递modal1需要的任何数据,同时创建一个新的页面:

Modal1Data modalData = new Modal1Data();

await Navigation.PushModalAsync(new modal1(modalData)); //Push old modal with previously entered data
await Navigation.PopModalAsync(); //Pop modal2 page

You could even save the already entered data from modal2 before navigating back to modal1, so that when the user went back to modal2, all of their info would still show up on the screen. 您甚至可以在导航回modal1之前保存已从modal2输入的数据,这样当用户返回到modal2时,他们的所有信息仍会显示在屏幕上。

This solution will probably look overly complicated for your case, but it's what i'm using to communicate between modal actions to resolve dependencies (no account present, id missing, data deleted in the meantime etc) at random without losing type safety. 对于你的情况,这个解决方案可能看起来过于复杂,但是我正在使用模式操作之间进行通信以解决依赖关系(没有帐户存在,id缺失,同时删除数据等)而不会丢失类型安全性。

One way to think about wizards / a modal series is, that the next page depends on the previous and that mostly you will want to work with the result of your child modal. 考虑向导/模态系列的一种方法是,下一页取决于前一页,而且大多数情况下你会想要使用你的子模态的结果。

I hope this helps: 我希望这有帮助:

public static class ModalManager
{
    public static bool TryRespondModal<TData>(this IModalResponse<TData> source, TData data, bool autoPop = true, bool animate = false)
    {
        if (Application.Current.MainPage.Navigation.ModalStack.Count <= 0)
            return false;

        source.ModalRequestComplete.SetResult(data);

        if (autoPop)
            Application.Current.MainPage.Navigation.PopModalAsync(animate);

        return true;
    }

    public static Task<TData> GetResponseAsync<TData>(this IModalResponse<TData> source)
    {
        source.ModalRequestComplete = new TaskCompletionSource<TData>();
        return source.ModalRequestComplete.Task;
    }
}

public enum WizardState
{
    Indeterminate = 0,
    Complete = 1,
    Canceled = 2
}

public class WizardModel
{
    public WizardState State { get; set; }

    public List<string> Page1Values { get; set; } = new List<string>();
    public List<string> Page2Values { get; set; } = new List<string>();
    public List<string> Page3Values { get; set; } = new List<string>();
}

public abstract class WizardPage : IModalResponse<WizardModel>
{
    public WizardModel Model { get; set; }

    public ICommand NextCommand { get; set; }

    public ICommand PreviousCommand { get; set; }

    public ICommand CancelCommand { get; set; }

    protected WizardPage(WizardModel model)
    {
        Model = model;
        NextCommand = new Command(NextButtonPressed);
        PreviousCommand = new Command(PreviousButtonPressed);
        CancelCommand = new Command(PreviousButtonPressed);
    }

    protected abstract IModalResponse<WizardModel> GetNextPage();

    protected virtual void CancelButtonPressed()
    {
        Model.State = WizardState.Canceled;
        this.TryRespondModal(Model);
    }

    protected virtual void PreviousButtonPressed()
    {
        // you're dropping down on the level of dependent windows here so you can tell your previous modal window the result of the current response
        this.TryRespondModal(Model);
    }

    protected virtual async void NextButtonPressed()
    {
        var np = GetNextPage();
        if (Model.State == WizardState.Complete || np == null || (await np?.GetResponseAsync()).State == WizardState.Complete)
            PersistWizardPage();

        // all following modal windows must have run through - so you persist whatever your page has done, unless you do that on the last page anyway. and then tell the previous
        // modal window that you're done
        this.TryRespondModal(Model);
    }

    protected virtual void PersistWizardPage()
    {
        // virtual because i'm lazy
        throw new NotImplementedException();
    }

    public TaskCompletionSource<WizardModel> ModalRequestComplete { get; set; }
}

public class Page1 : WizardPage
{

    public Page1(WizardModel model) : base(model)
    {
    }

    protected override IModalResponse<WizardModel> GetNextPage()
    {
        return new Page2(Model);
    }
}

public class Page2 : WizardPage
{
    public Page2(WizardModel model) : base(model)
    {
    }

    protected override IModalResponse<WizardModel> GetNextPage()
    {
        return new Page3(Model);
    }
}

public class Page3 : WizardPage
{
    public Page3(WizardModel model) : base(model)
    {
    }

    protected override IModalResponse<WizardModel> GetNextPage()
    {
        return null;
    }

    protected override void NextButtonPressed()
    {
        this.Model.State = WizardState.Complete;
        base.NextButtonPressed();
    }
}

public interface IModalResponse<TResponseType>
{
    TaskCompletionSource<TResponseType> ModalRequestComplete { get; set; }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM