Adding header items in the Xamarin.Forms Shell Flyout menu

Currently, in Xamarin.Forms Shell you only can specify a header in the flyout menu. If you have a somewhat larger menu that you would like to divide in sections, then Shell doesn’t provide an out-of-the-box solution. So time to think of a solution ourselves. Let’s start with the final result. Suppose you want to have a menu like in the following image (note that I’m taking the Xaminals example app as a base for this lesson).

This is the Flyout section of the AppShell.xaml file from the Xaminals app. Please note that I’ve removed some irrelevant attributes for readability. I have added two header items: Main items and Some other menu items.

<!-- Header item: Main items -->
<ShellContent Route="Header1"
              Title="Main items"
              ContentTemplate="{DataTemplate views:AboutPage}" />

<FlyoutItem x:Name="shellAnimals"
            Title="Animals"
            FlyoutDisplayOptions="AsMultipleItems">

    <Tab x:Name="shellDomestic" Title="Domestic">
        <ShellContent x:Name="shellCats" Title="Cats" />
        <ShellContent x:Name="shellDogs" Title="Dogs" />
    </Tab>

    <ShellContent x:Name="shellMonkeys" Title="Monkeys" />
    <ShellContent x:Name="shellElephants" Title="Elephants" />
    <ShellContent x:Name="shellBears" Title="Bears" />

</FlyoutItem>

<!-- Header item: Some other menu items -->
<ShellContent Route="Header2"
              Title="Some other menu items"
              ContentTemplate="{DataTemplate views:AboutPage}" />

<ShellContent Route="about" Title="About" Icon="info.png" />

The ShellContent tags for the header items contain Route=”HeaderX” tags. We will need these later to determine if we’re dealing with a header item or a navigation items. Also these ShellContent tags contain a ContentTemplate tag which is mandatory. We can just set these to a random page. After all, we will make the header items non-clickable, so it won’t have any effect.

Next, we will define the template for a header item in the <Shell.Resources> section. To have total control over a regular item, we’ll also create a template for that:

<Shell.Resources>
    <ResourceDictionary>
        <DataTemplate x:Key="FlyoutItemTemplate">
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="0.25*" />
                    <ColumnDefinition Width="0.75*" />
                </Grid.ColumnDefinitions>
                <Image Grid.Column="0"
                       Source="{Binding Icon}"
                       HeightRequest="30"
                       Margin="20,8,0,8" />
                <Label Grid.Column="1"
                       Text="{Binding Title}"
                       TextColor="Black"
                       VerticalTextAlignment="Center" />
            </Grid>
        </DataTemplate>
        <DataTemplate x:Key="FlyoutHeaderTemplate">
            <StackLayout Orientation="Vertical">
                <Label HeightRequest="35"
                       Margin="20,0,0,0"
                       Text="{Binding Title}"
                       TextColor="DarkGreen"
                       VerticalTextAlignment="Center" />
            </StackLayout>
        </DataTemplate>

        <controls:FlyoutItemTemplateSelector
            x:Key="FlyoutTemplateSelector"
            NavigationHeaderTemplate="{StaticResource FlyoutHeaderTemplate}"
            NavigationItemTemplate="{StaticResource FlyoutItemTemplate}" />

        <!--
            All your other styles
        -->

    </ResourceDictionary>
</Shell.Resources>

For this to work we will make use of the Xamarin.Forms DataTemplateSelector object. So go ahead and create a custom control called FlyoutItemTemplateSelector:

public class FlyoutItemTemplateSelector : DataTemplateSelector
{
    public DataTemplate NavigationHeaderTemplate { get; set; }
    public DataTemplate NavigationItemTemplate { get; set; }


    protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
    {
        if (item is ShellGroupItem && ((ShellGroupItem)item).Route.Contains("Header"))
        {
            // Make sure a header item is not clickable.
            ((ShellGroupItem)item).IsEnabled = false;
            return NavigationHeaderTemplate;
        }
        else
            return NavigationItemTemplate;
    }
}

This is where the Route=”HeaderX” comes is. When the route contains Header then we know it is a header item, so we return the NavigationHeaderTemplate. In all other cases we’re dealing with a regular flyout menu item, so we return the NavigationItemTemplate. In case of a header item, let’s also set IsEnabled to false, so nothing happens when we click the header item.

There’s just one thing left. In the main <Shell> tag, we have to set the Shell.ItemTemplate attribute to bind to the FlyoutItemTemplateSelector control. We do this like this:

<Shell xmlns="http://xamarin.com/schemas/2014/forms"
       ... All your other attributes ...
       Shell.ItemTemplate="{StaticResource FlyoutTemplateSelector}">

That’s it, here is the result again:

This image has an empty alt attribute; its file name is image.png

As you may have noticed, we had to perform some tricks to make this work. We had to set a ContentTemplate for a header item to prevent some null reference exception. We also had to define the ItemTemplate for the regular flyout items. Finally we needed to add Route=”HeaderX” attributes to identify these as header items. Xamarin.Forms Shell is fairly new so it doesn’t support header items out-of-the-box. I’m not saying this is the best solution, but it works.

If you have any additions, other/better solutions, please share them in the comments!

Selecting a default Flyout menu item in Xamarin.Forms Shell

Intro

When you create your Flyout menu, Shell automatically sets the first Flyout items as the default page of your app. Based on your requirements, this might not always be the desired behaviour. This article will show you how to set a default Flyout item in the menu.

The code samples in this article are taken from the Xaminals example app. If you’re not already familiar with this app, you can check it out here. This app shows you the basics of creating a Shell application and you can use it as a base for your own app.

Setting a default Flyout Item

This is the Flyout section of the AppShell.xaml file from the Xaminals app. Please note that I’ve removed some irrelevant attributes for readability.

<FlyoutItem x:Name="shellFirstItem"></FlyoutItem>

<FlyoutItem x:Name="shellAnimals"
            Title="Animals"
            FlyoutDisplayOptions="AsMultipleItems">

    <Tab x:Name="shellDomestic" Title="Domestic">
        <ShellContent x:Name="shellCats" Title="Cats" />
        <ShellContent x:Name="shellDogs" Title="Dogs" />
    </Tab>

    <ShellContent x:Name="shellMonkeys" Title="Monkeys" />
    <ShellContent x:Name="shellElephants" Title="Elephants" />
    <ShellContent x:Name="shellBears" Title="Bears" />

</FlyoutItem>

<FlyoutItem x:Name="shellSomeOtherMenu"></FlyoutItem>

If you would run you app like this, Shell would set the shellFirstItem as the default startup item. In this case we would like to set the shellAnimals the default item. This can easily be done using the following code:

public AppShell()
{
    InitializeComponent();

    // Select a root Flyout item.
    CurrentItem = shellAnimals;
}

If we run the app, this is the result:

As you can see, the shellAnimals Flyout item is indeed our default menu item. Since it contains four ShellContent items, the first one (Domestic) is selected by default.

Setting a default ShellContent Item

Well that was pretty simple so far. But what if we want to set the Bears page as the default page instead of the Domestic page? This is a bit trickier. The Bears page is a ShellContent inside the shellAnimals Flyout item. The code for this is as follows:

// Select a root Flyout item.
CurrentItem = shellAnimals;

// Select the Bears item inside the root Flyout item.
shellAnimals.CurrentItem = shellBears;

This doesn’t work out-of-the-box unfortunately. The shellAnimals.CurrentItem is a variable of type ShellSection, while shellBears is of type ShellContent. If we would run this code, we would get a NullReference exception.

The solution here is to wrap the bears ShellContent inside a ShellSection tag, like this:

<ShellSection x:Name="shellBears" Title="Bears">
   <ShellContent
        Route="bears"
        Style="{StaticResource BearsShell}"
        ContentTemplate="{DataTemplate views:BearsPage}" />
</ShellSection>

Here is the result:

Setting a default ShellContent Item inside a Tab

The last thing we can do is settings a default ShellContent inside a Tab section. As you can see in the following screenshot, the Domestic menu item contains of two tabs: Cats and Dogs.

What if we want to select the Dogs tab by default when your app starts? I agree this will not often be a real life scenario, but it’s possible so why not show how to do this?

We need the following code:

// Select a root Flyout item.
CurrentItem = shellAnimals;

// Select the Domestic item inside the root Flyout item.
shellAnimals.CurrentItem = shellDomestic;

// Finally, select the Dogs tab.
shellDomestic.CurrentItem = shellDogs;

And the result:

Happy coding!

Design a site like this with WordPress.com
Get started