Maui icon indicating copy to clipboard operation
Maui copied to clipboard

Provide an extension method to detect if popup was previous page

Open bijington opened this issue 4 months ago • 2 comments
trafficstars

Description of Change

This introduces the ability for developers to determine whether the previous page displayed in their app was a PopupPage as below

public class MyPage : Page
{
    protected override void OnNavigatedTo(NavigatedToEventArgs args)
    {
        base.OnNavigatedTo(args);

        if (args.WasPreviousPageAToolkitPopup() is true)
        {
            return;
        }

        // do other stuff
    }
}

The reason for the extension method over allowing a developer to just check the PreviousPage property is twofold:

  1. PreviousPage is currently internal
  2. PopupPage is also internal

I understand that internal access to MAUI will be removed from MAUI for .NET 10.0 but it also appears that this will be introduced https://github.com/dotnet/maui/pull/28384. If that is the case the code in this PR will continue to function when .NET 10.0 ships.

Linked Issues

  • Fixes #

PR Checklist

  • [ ] Has a linked Issue, and the Issue has been approved(bug) or Championed (feature/proposal)
  • [ ] Has tests (if omitted, state reason in description) - the constructor for NavigatedToEventArgs is internal so it would require some reflection to construct something to test.
  • [ ] Has samples (if omitted, state reason in description)
  • [x] Rebased on top of main at time of PR
  • [x] Changes adhere to coding standard
  • [ ] Documentation created or updated: https://github.com/MicrosoftDocs/CommunityToolkit/pulls

Additional information

bijington avatar Jun 25 '25 05:06 bijington

Thanks for the approval @pictos

@jfversluis do you have any understanding on whether https://github.com/dotnet/maui/pull/28384 will make it into .NET 10.0?

bijington avatar Jun 25 '25 15:06 bijington

Asked @PureWeen or maybe @jsuarezruiz knows

jfversluis avatar Jun 26 '25 09:06 jfversluis

While this seems useful, I'd need the same extension on ShellNavigatingEventArgs to address my use case where what I want to do is block navigation to anything except a popup by setting Shell.Current.Navigating += PreventPrematureNavigation; and defining this nasty looking function:

    private static void PreventPrematureNavigation(object sender, ShellNavigatingEventArgs e)
    {
        if (e.CanCancel)
        {// If we can cancel the navigation, do so if it is not for a popup
            if (e.Source == ShellNavigationSource.Push && e.Target.Location.OriginalString.Contains("Popup"))
            {
                // Don't cancel if the navigation is to a popup
                RecordMsg("Splash Page: Allowed Navigation to " + e.Target.Location.OriginalString);
            }
            else if (e.Source == ShellNavigationSource.PopToRoot && e.Current.Location.OriginalString.Contains("Popup"))
            {
                // Don't cancel if the navigation is from a popup
                RecordMsg("Splash Page: Allowed Navigation from " + e.Current.Location.OriginalString);
            }
            else
            {
                e.Cancel();
                RecordMsg("Splash Page: Navigation canceled because it was not for a popup");
            }
        }
        else
        {
            // If we can't cancel the navigation, just log it
            RecordMsg("Splash Page: Navigation not canceled because CanCancel was false");
        }
    }

david-maw avatar Jul 04 '25 19:07 david-maw

@david-maw what's your use case for preventing navigation? I know we've discussed it somewhere but I don't recall the exact reason.

bijington avatar Jul 04 '25 20:07 bijington

Annoyingly, Play Store testing seems to be able to cause navigation even when there's no UI to do it (it gets to various pages before initialization has completed, which is a Bad Thing). I suspect it may be a timing window that I have not been able to duplicate, and I originally solved it by prohibiting all navigation while initialization was in process. That's no longer sufficient because popups now use navigation, so I need an explicit exception for them.

david-maw avatar Jul 04 '25 20:07 david-maw

Hmm yeah I'd try to get to the bottom of why it's happening rather than try to cancel things in this way. One way might be to collect analytics if it only happens in a released version. Can you reproduce in a released version without going through the play store?

bijington avatar Jul 04 '25 20:07 bijington

I have not been able to reproduce it outside Play Store testing which is why I suspect some sort of timing window.

david-maw avatar Jul 04 '25 22:07 david-maw

Of course once I had a workaround I went on to more pressing issues. Still, the need to create an exception for popup is reason enough to take another run at it. Who knows, in the intervening couple of years the problem, whatever it is, could have gone away and then there would be no need for the workaround. Also, pigs might fly.

david-maw avatar Jul 04 '25 23:07 david-maw

I should add that current turnaround time for Play Store submissions varies between an hour and several days and this only ever failed one time in ten or so, meaning it might be a while before I learn anything.

david-maw avatar Jul 06 '25 22:07 david-maw

I think I have a clue as to what is happening that creates the requirement to selectively inhibit navigation. My app uses the Shell implementation and its AppShell.xaml looks like this:

    <TabBar>
        <!--This is the initial page, used to decide what to show the user first, it is normally never visible -->
        <ShellContent ContentTemplate="{DataTemplate view:GettingStartedPage}" Route="{x:Static local:Routes.GettingStartedPage}"/>
        <!--This is the initialization page the user normally sees, ShellContent items with routes are navigated to in code-->
        <ShellContent ContentTemplate="{DataTemplate view:SplashPage}" Route="{x:Static local:Routes.SplashPage}"/>
    </TabBar>
    <ShellContent Title="Items" ContentTemplate="{DataTemplate view:LineItemsPage}" Route="{x:Static local:Routes.LineItemsPage}"/>
    <ShellContent Title="Totals" ContentTemplate="{DataTemplate view:TotalsPage}" Route="{x:Static local:Routes.TotalsPage}"/>
    <MenuItem      Text="Camera" Clicked="GoToImagePageWithCamera"/>
    <ShellContent Title="Image" ContentTemplate="{DataTemplate view:ImagePage}" Route="{x:Static local:Routes.ImagePage}"/>
    <ShellContent Title="Properties" ContentTemplate="{DataTemplate view:PropertiesPage}" Route="{x:Static local:Routes.PropertiesPage}"/>
    ...

By setting Shell.TabBarIsVisible="False" and Shell.FlyoutBehavior="Disabled" on GettingStartedPage the user is presented with a page that has no user navigation capabilities. What the page actually does is push a help page onto the stack (conditionally, but that's a detail). When the help page is done it invokes Shell.Current.Navigation.PopAsync to return to GettingStartedPage. This time round GettingStartedPage navigates to SplashPage and is never used again.

Based on a recent failure (so a sample size of one), I think what happens is that when the help page invokes PopAsync it occasionally goes to LineItemsPage (and I see a failure there because SplashPage hasn't been invoked yet). Usually, this attempt is preempted by GettingStartedPage navigating to SplashPage, which is the intended flow, but rarely it goes toLineItemsPage instead and faults.

I think the fix is just to inhibit any navigation to places I don't approve of until the app is done initializing, but hopefully I've got enough tracing logic that I can catch the next attempt at a failure and confirm my suspicions.

david-maw avatar Aug 03 '25 21:08 david-maw

I think I have a clue as to what is happening that creates the requirement to selectively inhibit navigation. My app uses the Shell implementation and its AppShell.xaml looks like this:

    <TabBar>
        <!--This is the initial page, used to decide what to show the user first, it is normally never visible -->
        <ShellContent ContentTemplate="{DataTemplate view:GettingStartedPage}" Route="{x:Static local:Routes.GettingStartedPage}"/>
        <!--This is the initialization page the user normally sees, ShellContent items with routes are navigated to in code-->
        <ShellContent ContentTemplate="{DataTemplate view:SplashPage}" Route="{x:Static local:Routes.SplashPage}"/>
    </TabBar>
    <ShellContent Title="Items" ContentTemplate="{DataTemplate view:LineItemsPage}" Route="{x:Static local:Routes.LineItemsPage}"/>
    <ShellContent Title="Totals" ContentTemplate="{DataTemplate view:TotalsPage}" Route="{x:Static local:Routes.TotalsPage}"/>
    <MenuItem      Text="Camera" Clicked="GoToImagePageWithCamera"/>
    <ShellContent Title="Image" ContentTemplate="{DataTemplate view:ImagePage}" Route="{x:Static local:Routes.ImagePage}"/>
    <ShellContent Title="Properties" ContentTemplate="{DataTemplate view:PropertiesPage}" Route="{x:Static local:Routes.PropertiesPage}"/>
    ...

By setting Shell.TabBarIsVisible="False" and Shell.FlyoutBehavior="Disabled" on GettingStartedPage the user is presented with a page that has no user navigation capabilities. What the page actually does is push a help page onto the stack (conditionally, but that's a detail). When the help page is done it invokes Shell.Current.Navigation.PopAsync to return to GettingStartedPage. This time round GettingStartedPage navigates to SplashPage and is never used again.

Based on a recent failure (so a sample size of one), I think what happens is that when the help page invokes PopAsync it occasionally goes to LineItemsPage (and I see a failure there because SplashPage hasn't been invoked yet). Usually, this attempt is preempted by GettingStartedPage navigating to SplashPage, which is the intended flow, but rarely it hits LineItemsPage instead and faults.

I think the fix is just to inhibit any navigation to places I don't approve of until the app is done initializing, but hopefully I've got enough tracing logic that I can catch the next attempt at a failure and confirm my suspicions.

david-maw avatar Aug 03 '25 21:08 david-maw

Would it be possible to add this to ShellNavigatingEventArgs also, to handle the navigation to a PopupPage different than other pages when using the Shell?

DavidV1603 avatar Aug 12 '25 09:08 DavidV1603