Customize Shell template breaks navigation

Rainer 171 Reputation points
2023-03-01T12:25:31.4666667+00:00

I'm trying to customize my Shell Flyout and I'm running into a few problems.

Problem #1 — Can't style the content's vertical alignment

I've managed to style my items and menu items using Shell.ItemTemplate and Shell.MenuItemTemplate, but when I do this the list of items is centered vertically and I would like to have it at the top vertically. As far as I can tell there is no property to adjust this.

Problem #2 — Setting Shell.FlyoutContent breaks navigation

Because of problem #1, I decided to fully customize the shell using Shell.FlyoutContent with a CollectionView and appropriate templates in it. This works and the content looks like how I want it to look. But, when I do this, navigation stops working, that is, when I click on an item nothing happens at all.

Problem #3 — Using a TapGestureRecognizer doesn't work for the first ShellContent

To solve problem #2 I decided to attache a TapGestureRecognizer to the ShellContent. This almost works, but doesn't work for the first item in the collection. I've tried reordering the items and it's always just the first item that doesn't work. As you can see in the code below, I bind to NavigateTo. In my code if I put a break point in that it does not get hit when I tap on the first item, but does work for every other item.

Actually, it's even slightly weirder. Tapping on the first item never works. Tapping on the second item only sometimes works. Sometimes I need to tap it multiple times for it to work. For all of the other items it always works on the first tap.

My relevant code is:

<Shell.FlyoutContent>
	<CollectionView
		BackgroundColor="{AppThemeBinding Light={StaticResource LightColorSemiTransparent},
											Dark={StaticResource DarkColorSemiTransparent}}"
		BindingContext="{x:Reference This}"
		IsGrouped="True"
		ItemsSource="{Binding FlyoutItems}">
		<CollectionView.ItemTemplate>
			<DataTemplate x:DataType="ShellContent">
				<StackLayout
					Padding="6,10"
					Style="{DynamicResource StackHorizontal}"
					VerticalOptions="CenterAndExpand">
					<StackLayout.GestureRecognizers>
						<TapGestureRecognizer
							Command="{Binding NavigateTo, Source={x:Reference This}}"
							CommandParameter="{Binding .}"
							NumberOfTapsRequired="1" />
					</StackLayout.GestureRecognizers>
					<Image
						Source="{Binding FlyoutIcon}"
						Style="{DynamicResource ImageMenuIcon}"
						VerticalOptions="Center" />
					<Label
						HorizontalOptions="FillAndExpand"
						Style="{DynamicResource LabelBody}"
						Text="{Binding Title}"
						VerticalOptions="Center" />
				</StackLayout>
			</DataTemplate>
		</CollectionView.ItemTemplate>
	</CollectionView>
</Shell.FlyoutContent>

Bonus question:

I'm trying to understand the difference between Shell.FlyoutContent and Shell.FlyoutContentTemplate. Is it true that there are 100% equivalent?

<Shell.FlyoutContentTemplate>
  <DataTemplate>
    <CollectionView
      ...
    </CollectionView>
  </DataTemplate>
</Shell.FlyoutContentTemplate>

and

<Shell.FlyoutContent>
  <CollectionView
    ...
  </CollectionView>
</Shell.FlyoutContent>
Visual Studio
Visual Studio
A family of Microsoft suites of integrated development tools for building applications for Windows, the web and mobile devices.
4,604 questions
.NET MAUI
.NET MAUI
A Microsoft open-source framework for building native device applications spanning mobile, tablet, and desktop.
2,866 questions
{count} votes

Accepted answer
  1. Leon Lu (Shanghai Wicresoft Co,.Ltd.) 68,571 Reputation points Microsoft Vendor
    2023-03-02T07:51:34.0333333+00:00

    Hello,

    You can achieve this NavigateTo in the AppShell backgroud code. Then we can get the current selected ShellItem.then traverse IList<ShellSection>, if get the same title, change the current CurrentItem by  Shell.Current.CurrentItem.CurrentItem= shellSection; and close this Flyout page with Shell.Current.FlyoutIsPresented = false;

    public partial class AppShell : Shell
    {
        public ICommand NavigateTo { get; set; }
        public AppShell()
        {
            InitializeComponent();
            Routing.RegisterRoute("CatsPage", typeof(CatsPage));
            Routing.RegisterRoute("DogsPage", typeof(DogsPage));
            Routing.RegisterRoute("MainPage", typeof(MainPage));
            Routing.RegisterRoute("MonkeysPage", typeof(MonkeysPage));
            NavigateTo = new Command((e) =>{
                BaseShellItem res = e as BaseShellItem;
                IList<ShellSection> shellSections= Shell.Current.CurrentItem.Items;
                foreach (ShellSection shellSection in shellSections)
                {
                    if(res.Title.Equals(shellSection.Title))
                    {
                        Shell.Current.CurrentItem.CurrentItem= shellSection;
                        Shell.Current.FlyoutIsPresented = false;
                    }
                }
               });
                    BindingContext = this;
            }
    
    }
    

    Please do not forget to add Title for your <Tab> or <ShellContent> like following code.

    <FlyoutItem FlyoutDisplayOptions="AsMultipleItems">
    <Tab Title="Domestic"
                 Icon="paw.png">
                <ShellContent Title="Cats"
                              Icon="cat.png"
                              Route="CatsPage"
                              ContentTemplate="{DataTemplate local:CatsPage}" />
                <ShellContent Title="Dogs"
                              Icon="dog.png"
                              Route="DogsPage"
                              ContentTemplate="{DataTemplate local:DogsPage}" />
            </Tab>
            <ShellContent Title="Monkeys"
                          Icon="monkey.png"
                          Route="MonkeysPage"
                          ContentTemplate="{DataTemplate local:MonkeysPage}" />
    </FlyoutItem>
    

    As note:Avoid duplicate questions in one thread

    Best Regards,

    Leon Lu


    If the answer is the right solution, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment".

    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.


1 additional answer

Sort by: Most helpful
  1. Rainer 171 Reputation points
    2023-03-03T11:51:16.4466667+00:00

    Ok, I figured out problem #3.

    My header was the code below. With this the image actually invisibly extends below the bottom of the header. It seems when I was tapping on the first item in my list the image was receiving that tap, so it didn't get handled my my item. If I add Margin="0" to the Grid then this fixes that problem. That seems like a kind of random hack that is not very understandable, but at least it works. I would consider this a bug in MAUI.

    BTW, if you're curious, the reason I have the -30 margins it because my logo image has margins in the image that I don't want shown in this case. Of course I could just have multiple versions of my logo image (with and without those margins) but I felt reusing this image was more efficient.

    	<Shell.FlyoutHeader>
    		<Grid HeightRequest="200">
    			<Image
    				Margin="0,-30,0,-30"
    				Aspect="AspectFit"
    				Source="app_logo.png" />
    		</Grid>
    	</Shell.FlyoutHeader>
    
    
    0 comments No comments