【问题标题】:.Net Maui - How to go back to root page.Net Maui - 如何返回根页面
【发布时间】:2022-10-18 04:38:12
【问题描述】:

我觉得我只是没有向谷歌霸主提出这个问题,所以我要看看是否有人可以帮助解释如何做到这一点。我有一个新的 .Net Maui 应用程序,它使用 4 个不同的视图/页面。我创建的 MainPage (root) 让我可以从我们的数据库中搜索用户,然后将您转到新页面;我称之为ResultsPage。从那里,您可以选择一个用户并被带到一个可以执行编辑的 DetailPage。进行编辑并保存后,它会将您发送到我的最后一页 (SuccessPage),其中包含有关更改的消息。

这一切都像一个魅力。我可以使用 ViewModels 和 QueryProperties 将数据传递给页面,并将更改保存到数据库中。失败的地方是 SuccessPage。在您收到有关更新的消息后,我想要一个按钮,用户可以在该按钮上直接返回 MainPage,执行新的搜索并重复上述所有步骤。

对于所有其他页面转换,我可以利用 Shell.Current.GoToAsync() 进入新页面和/或传递数据,如下所示:

await Shell.Current.GoToAsync(nameof(ResultsPage));

或者

await Shell.Current.GoToAsync($"{nameof(DetailsPage)}?Parameter={param}");

在成功页面中,我尝试输入await Shell.Current.GoToAsync(nameof(MainPage));,但这会引发“相对路由到当前不支持的 shell 元素”的异常,并建议我尝试在我的 uri 中添加 /// 前缀。我尝试过,但唯一改变的是页面标题;它实际上并没有恢复 MainPage UI 元素。那么我怎样才能让 shell 回到 MainPage 呢?

此外,我也尝试过await Shell.Current.Navigation.PopToRootAsync();,但遇到了同样的问题,就像我在 uri 前面加上斜杠时一样;它会更改标题但不会更改任何 UI 元素

编辑

作为参考,这里是按钮背后的代码(注意:我留下了我注释掉的尝试,旁边有注释显示它们如何没有帮助

private async void ReturnSearchButton_Clicked(object sender, EventArgs e)
    {
        //await Shell.Current.GoToAsync("../"); //exception, ambiguous routes matched

        //List<Page> previousPages = Navigation.NavigationStack.ToList();
        //foreach (Page page in previousPages)
        //{
        //  Navigation.RemovePage(page); //exception, null value
        //}

        await Shell.Current.Navigation.PopToRootAsync();
    }

这是单击按钮之前和之后的一些 UI 屏幕截图:

在单击按钮之前

点击按钮后

主页添加编辑

主页 XAML

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="UserNameReset.Views.MainPage"
             Title="Home">
    <VerticalStackLayout Padding="10"
                         Margin="5">

        <Label 
            Text="Reset Users"
            SemanticProperties.HeadingLevel="Level1"
            FontSize="32"
            HorizontalOptions="Center" />

        <Label
            Text="Enter the users email address or their ID with Pin" 
            SemanticProperties.HeadingLevel="Level3"
            HorizontalOptions="Center"
            FontSize="18"/>

        <Label
            x:Name="fullWarningLabel"
            SemanticProperties.HeadingLevel="Level3"
            HorizontalOptions="Center"
            FontSize="18"
            TextColor="Red"
            Text="You must provide either the users email OR their ID and pin"
            IsVisible="false"/>

        <Entry 
            x:Name="emailEntry"
            Placeholder="example@demo.org"
            ClearButtonVisibility="WhileEditing"
            Completed="Entry_Completed"
            Keyboard="Email"
            IsSpellCheckEnabled="False"/>

        <Label
            Text="OR"
            SemanticProperties.HeadingLevel="Level3"
            HorizontalOptions="Center"
            FontSize="18"/>

        <Grid ColumnDefinitions="*,*" ColumnSpacing="4" RowDefinitions="*,*" RowSpacing="2">
            <Entry
                x:Name="idEntry"
                Placeholder="ID"
                ClearButtonVisibility="WhileEditing"
                Grid.Column="0"
                Completed="Entry_Completed"
                IsSpellCheckEnabled="False"/>
            <Label
                x:Name="idWarning"
                IsVisible="false"
                Text="Please enter the users ID"
                Grid.Column="0"
                Grid.Row="2"
                TextColor="Red"/>

            <Entry
                x:Name="pinEntry"
                Placeholder="PIN"
                ClearButtonVisibility="WhileEditing"
                Grid.Column="2"
                Completed="Entry_Completed"
                IsSpellCheckEnabled="False"/>
            <Label
                x:Name="pinWarning"
                IsVisible="false"
                Text="Please enter the users PIN"
                Grid.Column="2"
                Grid.Row="2"
                TextColor="Red"/>
        </Grid>

        <Button
            x:Name="SubmitButton"
            Text="Search"
            SemanticProperties.Hint="Click to search for the user by values you provided"
            Clicked="Entry_Completed"
            HorizontalOptions="Center"/>
    </VerticalStackLayout>
</ContentPage>

后面的 MainPage 代码

using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using UserNameReset.Models;

namespace UserNameReset.Views;

public partial class MainPage : ContentPage
{
    readonly ILogger<MainPage> _logger;

    public MainPage(ILogger<MainPage> logger)
    {
        InitializeComponent();
        _logger = logger;
    }

    /// <summary>
    /// Triggered when the user clicks the "Search" button, 
    /// when they finish entering an email,
    /// or when they successfully enter both Id and Pin
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    public void Entry_Completed(object sender, EventArgs e)
    {
        _logger.LogInformation("Entry fields filled, checking values...");

        // Cleans up layout each time search is triggered
        pinWarning.IsVisible = false;
        idWarning.IsVisible = false;
        fullWarningLabel.IsVisible = false;

        bool validUser = false;
        bool usingPinAndId = false;
        Queried_User user = new();

        // Check that both the plant ID and PIN were provided
        if (!string.IsNullOrWhiteSpace(idEntry.Text) && !string.IsNullOrWhiteSpace(pinEntry.Text))
        {
            _logger.LogInformation("Pin and ID provided!");
            validUser = true;
            usingPinAndId = true;
            user.Pin = pinEntry.Text;
            user.Id = idEntry.Text;
        }
        // Check if the email was provided (only if the plant ID and PIN weren't)
        else if (!string.IsNullOrWhiteSpace(emailEntry.Text))
        {
            _logger.LogInformation("Email provided!");
            validUser = true;
            user.Email = emailEntry.Text;
        }
        // If nothing was provided, add a warning to the appropriate entry fields
        else
        {
            if (!string.IsNullOrWhiteSpace(plantIdEntry.Text))
                pinWarning.IsVisible = true;
            else if (!string.IsNullOrWhiteSpace(pinEntry.Text))
                idWarning.IsVisible = true;
            else
                fullWarningLabel.IsVisible = true;
        }

        // Did we get a valid user obj? Navigate to the results page if so
        if (validUser)
        {
            // create a message of how the search is proceeding, changing text depending on search method
            string msg = "Searching via " + (usingPinAndId ? "plant ID: (" + user.Id + ") and pin: (" + user.Pin + ")" : "email: (" + user.Email + ")");


            _logger.LogInformation("User info validated. Going to get results from DB. " + msg);
            GoToResults(msg, user);
        }

        // Useful for displaying alerts or messages to the end user!!
        //await Shell.Current.DisplayAlert("Error!", $"Undable to return records: {ex.Message}", "OK");
    }


    /// <summary>
    /// Takes a simple user object and then redirects the user to the results page, 
    /// passing the user object as a query property
    /// </summary>
    /// <param name="user"></param>
    private async void GoToResults(string srchMthd, Queried_User user)
    {
        _logger.LogInformation($"User properties - email:{user.Email} - pin:{user.Pin} - ID:{user.Id}");

        await Shell.Current.GoToAsync($"{nameof(ResultsPage)}?SearchMethod={srchMthd}",
            new Dictionary<string, object>
            {
                ["User"] = user
            });
    }
}

GitHub 更新

我创建了一个存储库,其中托管了重复此问题的应用程序的简化版本:GitHub

编辑 2022-10-6

由于其他人的建议,我为这个问题在 Maui GitHub 上打开了一个新问题。要查看该问题并关注其进展,请转到 here

【问题讨论】:

  • 让我理解你,你需要 popToRootAsync 但你的主页有一个新实例吗? ui上没有变化吗?
  • 我相信是这样,如果我 popToRootAsync,我希望主页中的 UI 元素显示出来。它现在所做的只是更改顶部的标题,但保留上一页(成功页面)的 UI
  • 您的 MainPage 和 Shell 之间的关系是什么?您的 AppShell.xaml 是如何定义的?
  • MainPage 是 xaml 端的 AppShell 中列出的唯一一个(作为 ShellContent)。其他三个页面在 AppShell 构造函数中定义如下:Routing.RegisterRoute(nameoff(ResultsPage), typeof(ResultsPage));。让我知道上面的 sn-ps 是否有益
  • 请将此作为 GitHub 上的问题报告给 MAUI 团队,因为它看起来像一个错误。很高兴你有一个回购,你也应该在问题中提供链接。只需转到github.com/dotnet/maui/issues 并使用错误报告模板创建一个新问题

标签: c# xaml maui


【解决方案1】:

我创建了一个新示例进行测试,并尝试使用await Shell.Current.Navigation.PopToRootAsync();await Shell.Current.GoToAsync($"//{nameof(MainPage)}"); 导航到根页面。他们都显示了主页的标题,但没有显示页面的内容。

于是我点击了工具栏上的按钮,查看了实时可视化树,当我回到主页时,发现里面只有主页。然后我尝试在android平台上运行它,它工作正常。这应该是用户在 windows 平台上访问 rootpage 时的显示错误。

你可以尝试向github上的maui报告。

【讨论】:

  • 既然 (a) 您已经创建了一个最小示例,并且 (b) 您是 Microsoft 员工,那么如果您自己创建错误报告不是更好吗?让我感到奇怪的是,A 公司的一名员工在 A 公司的产品中复制了一个错误然后会问互联网上的一个随机陌生人创建错误报告!
  • 我已经通过内部渠道报告了它。 github是所有开发者的平台。 @Heinzi
  • “我是通过内频道举报的。”啊,好吧,我不知道。谢谢!
  • 更好的是包含指向您已填写的问题的链接,以便受此问题影响的开发人员可以交互并跟踪其进度。
  • 内部频道报告的bug不会出现在github上。 @Cfun
【解决方案2】:

当您获得“当前不支持的 shell 元素的相对路由”时,10 个案例中有 9 个,您忘记将它添加到您的 shell 或注册它。

在 AppShell.xaml 中,您需要带有页面的 ShellContent 的 ShellItem。 在 AppShell.xaml.cs 你需要 Routing.RegisterRoute(..)

然后您将能够使用“//”调用导航。

试试看。

编辑:只是在同一页面上,这不会神奇地清除您的 UI。导航到该页面时不会重建该页面。

【讨论】:

  • 所有四个页面都已以其中一种方式注册。 MainPage 在 AppShell(xaml 端)中注册为&lt;ShellContent /&gt;,其他三个在背面使用Routing.RegisterRoute(..); 注册
  • @StanMan3 如果您想对特定页面使用“//”,它必须在 AppShell xaml 中作为带有 ShellContent 的 ShellItem。并在 AppShell cs 文件中,作为注册路由。我正在使用登录页面,必须在我的应用程序中使用“//”进行导航,并且像这样添加它允许这种类型的导航。否则我得到你的错误。 (您不需要其中一种方式,而是两种方式)
【解决方案3】:

因此,我能够直接从 Microsoft 支持部门获得有关此问题的一些帮助。他们的支持建议我尝试在 xaml 代码隐藏中使用它弹出到根页面:

while (Navigation.NavigationStack.Count > 1)
{
    Navigation.RemovePage(Navigation.NavigationStack[1]);
}

await dispatcherProvider.DispatchAsync(() => Shell.Current.GoToAsync(".."));

while 循环遍历导航堆栈中的页面,并在将调度程序用于 GoToAsync() 之前将它们一一删除。除了在事件处理程序中包含上述代码之外,您还需要在构造函数中添加以下作为全局变量和赋值:

private readonly IDispatcher dispatcherProvider;
public ActionPage(ActionViewModel vm, IDispatcher dispatcher)
{
    BindingContext = vm;
    dispatcherProvider = dispatcher;
    InitializeComponent();
}

支持和我都同意文档令人困惑什么需要执行这样一个简单的功能。但这个解决方案至少也符合我的需要,希望能帮助那些发现自己处于同样困境的人

【讨论】:

    猜你喜欢
    • 2022-11-02
    • 2022-12-06
    • 2022-11-17
    • 2014-07-19
    • 2018-08-23
    • 2022-11-08
    • 2020-09-19
    • 2023-01-27
    • 2022-10-13
    相关资源
    最近更新 更多