清除Xamarin形式模态堆栈

pco*_*rey 5 c# xamarin.forms

我有一个Xamarin.Forms应用程序,该应用程序使用NavigationPage进行常规屏幕导航。从一个屏幕(Stay Detail)中,我需要显示一系列4个连续的模式页面(如向导),这些页面收集数据以完成与Stay Detail相关的过程。该过程中的每个页面都有一个“取消”按钮,该按钮应允许用户取消向导并返回到“停留详细信息”。这是一般流程:

            modal1 -> modal2 -> modal3 -> modal4
           /                                    \
 StayDetail                                      StayDetail
Run Code Online (Sandbox Code Playgroud)

从StayDetail进行PushModalAsync来启动modal1,然后在各个模式页面之间进行PushModalAsync / PopModalAsync都很容易。但是,我想不出一种从第二个模态或更高版本退出模态堆栈的干净方法。最好的方法是什么?

Dbl*_*Dbl 0

对于您的情况,这个解决方案可能看起来过于复杂,但它是我用来在模态操作之间进行通信的方法,以随机解决依赖关系(不存在帐户、ID 丢失、同时删除数据等),而不会失去类型安全性。

考虑向导/模态系列的一种方法是,下一页依赖于上一页,并且大多数情况下您会希望使用子模态的结果。

我希望这有帮助:

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; }
}
Run Code Online (Sandbox Code Playgroud)