全局路由当前不能是堆栈上的唯一页面

Red*_*ddy 9 navigation xaml flyout xamarin.forms xamarin.forms.shell

让弹出菜单按照我想要的方式在 Xamarin Forms 中工作一直是一个很大的痛苦。我试图让某个菜单项(我的统计信息)在用户登录时转到某个页面,或者在用户未登录时转到另一个页面(登录页面)。我尝试了两种不同的方法但没有成功。

方法一

首先,我尝试在 XAML 中使用 Shell Content 中的指定路径设置弹出项目,如下所示:

<FlyoutItem Title="My Stats" Icon="icon_feed.png">
        <ShellContent x:Name="shellContent_myStats" Route="MyStatsPage" ContentTemplate="{DataTemplate local:MyStatsPage}" />
</FlyoutItem>
Run Code Online (Sandbox Code Playgroud)

然后,当用户登录/注销时,我会以编程方式修改路由:

//Logging Out
            shellContent_myStats.ContentTemplate = new DataTemplate(typeof(LoginPage));
            Routing.SetRoute(shellContent_myStats, "LoginPage");
...
//Logging In
            shellContent_myStats.ContentTemplate = new DataTemplate(typeof(MyStatsPage));
            Routing.SetRoute(shellContent_myStats, "MyStatsPage");
Run Code Online (Sandbox Code Playgroud)

我无法让它发挥作用。无论出于何种原因,只有第一个被执行的部分才会生效。例如,如果我在注销时启动应用程序,它会按预期定向到登录页面,但在我注销后不会切换。反之亦然,如果我在登录时启动应用程序,它会按预期导航到“我的统计”页面,但即使在我注销后也会继续这样做。

我尝试了上述代码的几种不同变体,但均无济于事。


方法2

我尝试的第二种方法是使用 OnClick 事件处理程序在 XAML 中指定菜单项,如下所示:

<MenuItem Text="My Journey" StyleClass="MenuItemLayoutStyle" Clicked="OnMyStatsClicked"/>
Run Code Online (Sandbox Code Playgroud)

背后代码:

public AppShell()
{
    //Route now needs to be registered in page constructor
    Routing.RegisterRoute(nameof(MyStatsPage), typeof(MyStatsPage));
    ...

public async void OnMyStatsClicked(object sender, EventArgs e)
{
    Shell.Current.FlyoutIsPresented = false;
    if (LoggedIn)
    {
        // Prefixing with `//` switches to a different navigation stack instead of pushing to the active one
        await Shell.Current.GoToAsync($"//{nameof(MyStatsPage)}");
    }
    else
    {
        await Shell.Current.GoToAsync($"//{nameof(LoginPage)}");
    }
}
...
Run Code Online (Sandbox Code Playgroud)

现在,当我选择具有此设置的菜单项(登录时)时,它会抛出以下错误:

System.Exception: 'Global routes currently cannot be the only page on the stack, so absolute routing to global routes is not supported. For now, just navigate to: MyStatsPage'
Run Code Online (Sandbox Code Playgroud)

如果我按照删除路线中的两个斜杠(“//”)给出的建议,导航可以工作,但弹出菜单在左上角不可用,而是用后退按钮代替。这是不希望的,我希望菜单仍然可用。为什么我不能在后面的代码中定义的路由上使用绝对路由......?

我感谢任何有关上述两种方法中任何一种使其发挥作用的建议。几天来一直在尝试做一些看似简单的事情..谢谢

Cfu*_*fun 11

您可以使用 属性 设置一个技巧FlyoutItemIsVisibleShellContent(因此相关页面)将在 Shell 层次结构中注册,但在弹出窗口中不可见,而且只有在需要时才会加载它,因为我们正在使用ContentTemplate.

假设您仅使用弹出按钮而没有底部选项卡:

AppShell.xaml

<Shell Shell.TabBarIsVisible="False"
...

  <FlyoutItem>
        <ShellContent ContentTemplate="{DataTemplate local:MainPage}"/>
        <ShellContent Route="AnotherPage"
                      FlyoutItemIsVisible="False"
                      ContentTemplate="{DataTemplate local:AnotherPage}"/>

        <ShellContent Route="LoginPage"
                      FlyoutItemIsVisible="False"
                      ContentTemplate="{DataTemplate local:LoginPage}"/>
    </FlyoutItem>


    <MenuItem Text="My Stats Page"
              Clicked="MenuItem_Clicked"/>
Run Code Online (Sandbox Code Playgroud)

AppShell.xaml.cs

<Shell Shell.TabBarIsVisible="False"
...

  <FlyoutItem>
        <ShellContent ContentTemplate="{DataTemplate local:MainPage}"/>
        <ShellContent Route="AnotherPage"
                      FlyoutItemIsVisible="False"
                      ContentTemplate="{DataTemplate local:AnotherPage}"/>

        <ShellContent Route="LoginPage"
                      FlyoutItemIsVisible="False"
                      ContentTemplate="{DataTemplate local:LoginPage}"/>
    </FlyoutItem>


    <MenuItem Text="My Stats Page"
              Clicked="MenuItem_Clicked"/>
Run Code Online (Sandbox Code Playgroud)

笔记

如果您同时使用底部选项卡,或者作为另一种方法,我建议隐藏与当前日志状态无关的页面(例如:如果用户已登录,则隐藏 LoginPage)。

如何根据登录状态/角色限制/控制用户可以访问的导航路线?