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!

Leave a comment

Design a site like this with WordPress.com
Get started