Home > Back-end >  .Net Maui - How to go back to root page
.Net Maui - How to go back to root page

Time:10-06

I feel like I'm just not asking this question right to the Google overlord, so I'm going to see if someone can help explain how to do this. I have a new .Net Maui app that uses 4 different views/pages. The MainPage (root) I created lets me search for a user from our database and then transitions you to a new page; which I call the ResultsPage. From there, you can select a user and be taken to a DetailPage where edits can be performed. Once you make your edits and save, it sends you to my final page (SuccessPage) with a message about the changes.

This all works like a charm. I'm able to use ViewModels and QueryProperties to pass the data around to the pages and it saves the changes to the database. Where it fails is on the SuccessPage. After you get the message about the updates, I want to have a button where the user would then go right back to the MainPage and could perform a new search and repeat all the steps above.

For all the other page transitions, I'm able to leverage Shell.Current.GoToAsync() to get to the new page and/or pass data, like so:

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

or

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

From the Success Page, I tried putting await Shell.Current.GoToAsync(nameof(MainPage));, but this throws an exception about "relative routing to shell elements not currently being supported" and suggests I try prefixing /// to my uri. I try that, but the only thing that changes is the page title; it doesn't actually restore the MainPage UI elements. So how can I get the shell to go back to the MainPage?

Additionally, I've also tried await Shell.Current.Navigation.PopToRootAsync();, but get the same problem like when I prefixed the slashes to the uri; it changes the title but doesn't change any UI elements

Edit

For reference, here is the code behind for the button (note: I left my commented out attempts with remarks beside them showing how they didn't help

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

Here's some screenshots of the UI before and after the button is clicked:

Before button clicked Before button press

After button clicked After button pressed

MainPage addition edit

MainPage 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="[email protected]"
            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 code behind

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 update

I created a repo which hosts a simplified version of the application that duplicates this issue: GitHub

CodePudding user response:

I have created a new sample to test, and try to use the await Shell.Current.Navigation.PopToRootAsync(); and await Shell.Current.GoToAsync($"//{nameof(MainPage)}"); to navigate to the root page. Both of them showed the mainpage's title but didn't show the page's content.

So I clicked the button on the tool bar to see the live visual tree and found when I get back to the main page, there was only the mainpage in it. And then I try to run it on the android platform, it worked correctly. This should be a display bug when user gets to the rootpage on the windows platform.

You can try to report it to the maui on the github.

CodePudding user response:

When you are getting "relative routing to shell elements not currently being supported", 9 of 10 cases, you forgot to add it to your shell or register it.

In the AppShell.xaml you need ShellItem with ShellContent of the Page. And in AppShell.xaml.cs you need Routing.RegisterRoute(..)

Then you will be able to call navigation with "//".

Try it.

  • Related