MAUI Environment Ribbon - NavigationPage integration (Part 3)

Author:
|
Publish date:

Chapters in this series:

  1. Intro and basic UI
  2. Shell integration
  3. NavigationPage integration
  4. UI customization
  5. Optimization and wrap up
  6. Download the code

When dealing with NavigationPage navigation we can use a similar approach to Shell for the basic functionality. We will use the Pushed event for adding the control to individual pages.

public static class NavigationPageExtensions
{
    public static NavigationPage AddEnvironmentRibbon(this NavigationPage navigationPage)
    {
        navigationPage.Pushed += NavigationPage_Pushed;
        return navigationPage;
    }

    private static void NavigationPage_Pushed(object? sender, ShellNavigatedEventArgs e)
    {
        if (sender is NavigationPage navigationPage
            && navigationPage.CurrentPage)
        {
            var newRootGrid = new Grid();

            newRootGrid.Children.Add(contentPage.Content);
            newRootGrid.Children.Add(new EnvironmentRibbon());
            contentPage.Content = newRootGrid;
        }
    }
}

So far the code looks pretty much the same. There are however some more scenarios that we need to cover when dealing with NavigationPage as it offers different page types within the navigation which can be arranged in custom order depending on what the developers choose rather than what Shell allows. These are:

  • NavigationPage
  • FlyoutPage
  • TabbedPage

First of all there can be another NavigationPage deeper in the hierarchy - and this is commonly used for example as child pages within a TabbedPage. So in order to add EnvironmentRibbon to all pages we also need to deal with the inner NavigationPages' Pushed events. Then we have FlyoutPage and TabbedPage which have inner pages and they are structured differently for each of these page types. We also need to pass the creation of EnvironmentRibbon to the inner pages.

When we put all of this together we can create a universal method that handles all these page types:

public class EnvironmentRibbonService
{
    public static void AddEnvironmentRibbonToPage(Page? page)
    {
        if (page is ContentPage contentPage)
        {
            var newRootGrid = new Grid();

            newRootGrid.Children.Add(contentPage.Content);
            newRootGrid.Children.Add(new EnvironmentRibbon());
            contentPage.Content = newRootGrid;
        }
        else if (page is NavigationPage navigationPage)
        {
            navigationPage.Pushed += NavigationPage_Pushed;
            AddEnvironmentRibbonToPage(navigationPage.CurrentPage);
        }
        else if (page is TabbedPage tabbedPage)
        {
            foreach (var child in tabbedPage.Children)
            {
                AddEnvironmentRibbonToPage(child);
            }
        }
        else if (page is FlyoutPage flyoutPage)
        {
            AddEnvironmentRibbonToPage(flyoutPage.Detail);
        }
    }
}

Now we can use environment ribbon even with NavigationPage easily when we use NavigationPage as our main page.

public partial class App
{
    public App()
    {
        InitializeComponent();

        var mainPage = new NavigationPage(new MainPage())
			.AddEnvironmentRibbon();

		MainPage = mainPage;
    }
}

Similarly if the app is small and uses either FlyoutPage or TabbedPage as the main page (not wrapped in an additional NavigationPage), we can just add the respective extension methods for these page types.

public static class TabbedPageExtensions
{
    public static TabbedPage AddEnvironmentRibbon(this TabbedPage tabbedPage)
    {
        foreach (var child in tabbedPage.Children)
        {
            EnvironmentRibbonService.AddEnvironmentRibbonToPage(child);
        }

        return tabbedPage;
    }
}
public static class FlyoutPageExtensions
{
    public static FlyoutPage AddEnvironmentRibbon(this FlyoutPage flyoutPage)
    {
        EnvironmentRibbonService.AddEnvironmentRibbonToPage(flyoutPage.Detail);
        return flyoutPage;
    }
}

Unifying the code

We created a new static class that contains the shared logic for handling the EnvironmentRibbon for both Shell and NavigationPage. Now instead of having the same code in the separate extension classes we can modify them this way:

public static class ShellExtensions
{
    public static Shell AddEnvironmentRibbon(this Shell shell)
    {
        shell.Navigated += EnvironmentRibbonService.Shell_Navigated;
        return shell;
    }
}

public static class NavigationPageExtensions
{
    public static NavigationPage AddEnvironmentRibbon(this NavigationPage navigationPage)
    {
        EnvironmentRibbonService.AddEnvironmentRibbonToPage(navigationPage.CurrentPage);
        navigationPage.Pushed += EnvironmentRibbonService.NavigationPage_Pushed;
        return navigationPage;
    }
}

And add the event handler methods to the EnvironmentRibbonService:

public static class EnvironmentRibbonService
{
    public static void Shell_Navigated(object? sender, ShellNavigatedEventArgs e)
    {
        if (sender is Shell shell)
        {
            AddEnvironmentRibbonToPage(shell.CurrentPage);
        }
    }

    public static void NavigationPage_Pushed(object? sender, NavigationEventArgs e)
    {
        if (sender is NavigationPage navigationPage)
        {
            AddEnvironmentRibbonToPage(navigationPage.CurrentPage);
        }
    }

    // ...
}

Now we have the integration ready for both Shell and NavigationPage. In the following posts we will take a look at some optimization that is needed and customization options for the control.

Previous chapter Next chapter

Roman Jašek
Roman Jašek

BIO: 

Roman is a Microsoft MVP working as software architect at RIGANTI. His main area is mobile application development using MAUI. He also works with other areas of the .NET ecosystem including web backend using ASP.NET, Azure etc. In his spare time Roman deals with organizing talks and conferences as part of the local Windows User Group and teaches application development using .NET MAUI and Blazor at universities in the Czech Republic.

Others blog posts from category: RIGANTI Blog