How can I keep the master-detail icon in a Navigation page instead of having a back button?

I have read through a lot of threads on here asking similar things, and I have tried all of the fixes suggested, but nothing I do seems to work. I am pretty sure my code is not that different. I have made the master page (navigation drawer) available on every page, but without an icon to say it is there, it is not very useful to users. It is available only with a gesture.

NavigationMenuMasterDetailPage.cs

using MyApp.Localization;
using MyApp.Statics;
using Xamarin.Forms;

namespace MyApp.Pages.Common
{
    public class NavigationMenuMasterDetailPage : MasterDetailPage
    {
        NavigationMenuMasterPage masterPage;
        NavigationPage _navPage = new NavigationPage();

        public NavigationMenuMasterDetailPage()
        {
            masterPage = new NavigationMenuMasterPage();
            Master = masterPage;
            Detail = _navPage;
            _navPage.PushAsync(new StartPage());

            masterPage.MenuListView.ItemSelected += OnItemSelected;
        }

        void OnItemSelected(object sender, SelectedItemChangedEventArgs e)
        {
            var item = e.SelectedItem as MasterPageItem;
            if (item != null && item.Title == TextResources.Settings)
            {
                Detail = _navPage;
                _navPage.PushAsync(new SettingsPage());
            }
            else if (item != null && item.Title == TextResources.Message_Box)
            {
                Detail = _navPage;
                _navPage.PushAsync(new MessageBoxPage());
            }
            else if (item != null && item.Title == TextResources.Login_Log_Out)
            {
                MessagingCenter.Send(this, MessagingServiceConstants.LOGOUT);
            }
            masterPage.MenuListView.SelectedItem = null;
            IsPresented = false;
        }
    }
}

In this page I make the NavigationPage separate so I can also use the back button to go back to the start page, rather than close the app. StartPage displays fine, with the hamburger menu icon as expected.

NavigationMenuMasterPage.xaml

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MyApp.Pages.Common.NavigationMenuMasterPage"
             xmlns:statics="clr-namespace:MyApp.Statics;assembly=MyApp"
             Title="Navigation drawer"
             Icon="hamburger.png">
  <ContentPage.Content>
    <StackLayout VerticalOptions="FillAndExpand">
      <ListView x:Name="menuListView" VerticalOptions="FillAndExpand" SeparatorVisibility="None">
        <ListView.ItemTemplate>
          <DataTemplate>
            <TextCell Text="{Binding Title}" TextColor="{x:Static statics:Palette.Primary}" />
          </DataTemplate>
        </ListView.ItemTemplate>
      </ListView>
    </StackLayout>
  </ContentPage.Content>
</ContentPage>

NavigationMenuMasterPage.xaml.cs

using MyApp.Localization;
using System.Collections.Generic; 
using Xamarin.Forms;

namespace MyApp.Pages.Common
{
    public partial class NavigationMenuMasterPage : ContentPage
    {
        public ListView MenuListView { get { return menuListView; } }

        public NavigationMenuMasterPage()
        {
            InitializeComponent();

            var masterPageItems = new List<MasterPageItem>();
            masterPageItems.Add(new MasterPageItem
            {
                Title = TextResources.Settings,
                TargetType = typeof(SettingsPage)
            });
            masterPageItems.Add(new MasterPageItem
            {
                Title = TextResources.Message_Box,
                TargetType = typeof(MessageBoxPage)
            });
            masterPageItems.Add(new MasterPageItem
            {
                Title = TextResources.Login_Log_Out,
            });
            menuListView.ItemsSource = masterPageItems;
        }
    }
}

The problem arises when I go to any page from the Master page, or go further in the flow from the StartPage.

I can post my SettingsPage as an example, but it is extremely basic. All the .cs file does at this point is InitializeComponent();.

SettingsPage.xaml

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="HolteApp.Pages.Common.SettingsPage"
             Icon="hamburger.png">
  <Label Text="This will be the settings page." VerticalOptions="Center" HorizontalOptions="Center" />
</ContentPage>

StartPage.xaml

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MyApp.Pages.StartPage"
             xmlns:l10n="clr-namespace:MyApp.Localization;assembly=MyApp"
             xmlns:statics="clr-namespace:MyApp.Statics;assembly=MyApp"
             Icon="hamburger.png">
  <ContentPage.Content>
    <StackLayout VerticalOptions="FillAndExpand"
                 HorizontalOptions="FillAndExpand"
                 Orientation="Vertical"
                 Spacing="0">
      <Label Text="{l10n:Translate Company_Label}"
             TextColor="{x:Static statics:Palette.Primary}"
             Margin ="16, 16, 16, 8"
             FontAttributes="Bold"/>
      <Label x:Name="CompanyNameView"
             Text="{Binding CompanyName}"
             TextColor="{x:Static statics:Palette.PrimaryText}"
             Margin ="16, 8, 16, 8" >
        <Label.GestureRecognizers>
          <TapGestureRecognizer Tapped="CompanyNameClicked" NumberOfTapsRequired="1" />
        </Label.GestureRecognizers>
      </Label>
      <Label Text="{l10n:Translate Projects_Label}"
             TextColor="{x:Static statics:Palette.Primary}"
             Margin ="16, 8, 16, 8"
             FontAttributes="Bold"/>
      <ListView ItemsSource="{Binding Projects}"
                x:Name="ProjectsView"
                ItemSelected="ProjectClicked">
        <x:Arguments>
          <ListViewCachingStrategy>RecycleElement</ListViewCachingStrategy>
        </x:Arguments>
        <ListView.ItemTemplate>
          <DataTemplate>
            <TextCell Text="{Binding ProjectName}" TextColor="{x:Static statics:Palette.PrimaryText}"/>
          </DataTemplate>
        </ListView.ItemTemplate>
      </ListView>
    </StackLayout>
  </ContentPage.Content>
</ContentPage>

StartPage.xaml.cs

using MyApp.Localization;
using MyApp.Pages.Routines;
using MyApp.ViewModels;
using MyCloudContracts.DTOs;
using System;
using Xamarin.Forms;

namespace MyApp.Pages
{
    public partial class StartPage : ContentPage
    {
        private StartPageViewModel _viewModel;

        public StartPage()
        {
            InitializeComponent();
            Title = TextResources.AppName;
            _viewModel = new StartPageViewModel();
            _viewModel.Init();
            BindingContext = _viewModel;            
        }

        private async void ProjectClicked(object sender, SelectedItemChangedEventArgs e)
        {
            //since this is also called when an item is deselected, return if set to null
            if (e.SelectedItem == null)
                return;

            var selectedProject = (CMSProjectInfoDTO) e.SelectedItem;
            var projId = selectedProject.Id;
            var name = selectedProject.ProjectName;
            var handbooksPage = new HandbooksPage(projId, name, false);
            await Navigation.PushAsync(handbooksPage);

            //take away selected background
            ((ListView)sender).SelectedItem = null;
        }

        private async void CompanyNameClicked(object sender, EventArgs e)
        {
            var name = _viewModel.CompanyName;
            var handbooksPage = new HandbooksPage(null, name, true);
            await Navigation.PushAsync(handbooksPage);
        }
    }
}

And then the HandbooksPage comes up, also with the back arrow instead of the master detail icon. I can still access the master page by swiping from the left side of the screen.

Does anybody know what I am doing wrong? I have set the "Icon" attribute on the Master page. I have tried to put my navigation all inside of one Detail page. I really just want to replace the detail page every time, but I need the stack to navigate.

Answers

  • LucasVelosoLucasVeloso USMember ✭✭

    Your ProjectClicked method calls Navigation.PushAsync(handbooksPage); which puts handbooksPage on top of the NavigationStack, which makes the hamburguer menu item turn into an arrow, that's just the natural behaviour of the navigation stack + MasterDetailPage.

    If you want to keep the hamburguer menu item and not add another page to the navigation stack, what you need to do is change your DetailPage to your handbooksPage instead:

    var masterDetailPage = App.Current.MainPage as MasterDetailPage; masterDetailPage.Detail = handbooksPage;

  • DavidS.4178DavidS.4178 USMember ✭✭

    @JasonToms said:
    I have read through a lot of threads on here asking similar things, and I have tried all of the fixes suggested, but nothing I do seems to work. I am pretty sure my code is not that different. I have made the master page (navigation drawer) available on every page, but without an icon to say it is there, it is not very useful to users. It is available only with a gesture.

    NavigationMenuMasterDetailPage.cs

    using MyApp.Localization;
    using MyApp.Statics;
    using Xamarin.Forms;
    
    namespace MyApp.Pages.Common
    {
        public class NavigationMenuMasterDetailPage : MasterDetailPage
        {
            NavigationMenuMasterPage masterPage;
            NavigationPage _navPage = new NavigationPage();
    
            public NavigationMenuMasterDetailPage()
            {
                masterPage = new NavigationMenuMasterPage();
                Master = masterPage;
                Detail = _navPage;
                _navPage.PushAsync(new StartPage());
    
                masterPage.MenuListView.ItemSelected += OnItemSelected;
            }
    
            void OnItemSelected(object sender, SelectedItemChangedEventArgs e)
            {
                var item = e.SelectedItem as MasterPageItem;
                if (item != null && item.Title == TextResources.Settings)
                {
                    Detail = _navPage;
                    _navPage.PushAsync(new SettingsPage());
                }
                else if (item != null && item.Title == TextResources.Message_Box)
                {
                    Detail = _navPage;
                    _navPage.PushAsync(new MessageBoxPage());
                }
                else if (item != null && item.Title == TextResources.Login_Log_Out)
                {
                    MessagingCenter.Send(this, MessagingServiceConstants.LOGOUT);
                }
                masterPage.MenuListView.SelectedItem = null;
                IsPresented = false;
            }
        }
    }
    

    In this page I make the NavigationPage separate so I can also use the back button to go back to the start page, rather than close the app. StartPage displays fine, with the hamburger menu icon as expected.

    NavigationMenuMasterPage.xaml

    <?xml version="1.0" encoding="utf-8" ?>
    <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 x:Class="MyApp.Pages.Common.NavigationMenuMasterPage"
                 xmlns:statics="clr-namespace:MyApp.Statics;assembly=MyApp"
                 Title="Navigation drawer"
                 Icon="hamburger.png">
      <ContentPage.Content>
        <StackLayout VerticalOptions="FillAndExpand">
          <ListView x:Name="menuListView" VerticalOptions="FillAndExpand" SeparatorVisibility="None">
            <ListView.ItemTemplate>
              <DataTemplate>
                <TextCell Text="{Binding Title}" TextColor="{x:Static statics:Palette.Primary}" />
              </DataTemplate>
            </ListView.ItemTemplate>
          </ListView>
        </StackLayout>
      </ContentPage.Content>
    </ContentPage>
    

    NavigationMenuMasterPage.xaml.cs

    using MyApp.Localization;
    using System.Collections.Generic; 
    using Xamarin.Forms;
    
    namespace MyApp.Pages.Common
    {
        public partial class NavigationMenuMasterPage : ContentPage
        {
            public ListView MenuListView { get { return menuListView; } }
    
            public NavigationMenuMasterPage()
            {
                InitializeComponent();
    
                var masterPageItems = new List<MasterPageItem>();
                masterPageItems.Add(new MasterPageItem
                {
                    Title = TextResources.Settings,
                    TargetType = typeof(SettingsPage)
                });
                masterPageItems.Add(new MasterPageItem
                {
                    Title = TextResources.Message_Box,
                    TargetType = typeof(MessageBoxPage)
                });
                masterPageItems.Add(new MasterPageItem
                {
                    Title = TextResources.Login_Log_Out,
                });
                menuListView.ItemsSource = masterPageItems;
            }
        }
    }
    

    The problem arises when I go to any page from the Master page, or go further in the flow from the StartPage.

    I can post my SettingsPage as an example, but it is extremely basic. All the .cs file does at this point is InitializeComponent();.

    SettingsPage.xaml

    <?xml version="1.0" encoding="utf-8" ?>
    <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 x:Class="HolteApp.Pages.Common.SettingsPage"
                 Icon="hamburger.png">
      <Label Text="This will be the settings page." VerticalOptions="Center" HorizontalOptions="Center" />
    </ContentPage>
    

    StartPage.xaml

    <?xml version="1.0" encoding="utf-8" ?>
    <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 x:Class="MyApp.Pages.StartPage"
                 xmlns:l10n="clr-namespace:MyApp.Localization;assembly=MyApp"
                 xmlns:statics="clr-namespace:MyApp.Statics;assembly=MyApp"
                 Icon="hamburger.png">
      <ContentPage.Content>
        <StackLayout VerticalOptions="FillAndExpand"
                     HorizontalOptions="FillAndExpand"
                     Orientation="Vertical"
                     Spacing="0">
          <Label Text="{l10n:Translate Company_Label}"
                 TextColor="{x:Static statics:Palette.Primary}"
                 Margin ="16, 16, 16, 8"
                 FontAttributes="Bold"/>
          <Label x:Name="CompanyNameView"
                 Text="{Binding CompanyName}"
                 TextColor="{x:Static statics:Palette.PrimaryText}"
                 Margin ="16, 8, 16, 8" >
            <Label.GestureRecognizers>
              <TapGestureRecognizer Tapped="CompanyNameClicked" NumberOfTapsRequired="1" />
            </Label.GestureRecognizers>
          </Label>
          <Label Text="{l10n:Translate Projects_Label}"
                 TextColor="{x:Static statics:Palette.Primary}"
                 Margin ="16, 8, 16, 8"
                 FontAttributes="Bold"/>
          <ListView ItemsSource="{Binding Projects}"
                    x:Name="ProjectsView"
                    ItemSelected="ProjectClicked">
            <x:Arguments>
              <ListViewCachingStrategy>RecycleElement</ListViewCachingStrategy>
            </x:Arguments>
            <ListView.ItemTemplate>
              <DataTemplate>
                <TextCell Text="{Binding ProjectName}" TextColor="{x:Static statics:Palette.PrimaryText}"/>
              </DataTemplate>
            </ListView.ItemTemplate>
          </ListView>
        </StackLayout>
      </ContentPage.Content>
    </ContentPage>
    

    StartPage.xaml.cs

    using MyApp.Localization;
    using MyApp.Pages.Routines;
    using MyApp.ViewModels;
    using MyCloudContracts.DTOs;
    using System;
    using Xamarin.Forms;
    
    namespace MyApp.Pages
    {
        public partial class StartPage : ContentPage
        {
            private StartPageViewModel _viewModel;
    
            public StartPage()
            {
                InitializeComponent();
                Title = TextResources.AppName;
                _viewModel = new StartPageViewModel();
                _viewModel.Init();
                BindingContext = _viewModel;            
            }
    
            private async void ProjectClicked(object sender, SelectedItemChangedEventArgs e)
            {
                //since this is also called when an item is deselected, return if set to null
                if (e.SelectedItem == null)
                    return;
    
                var selectedProject = (CMSProjectInfoDTO) e.SelectedItem;
                var projId = selectedProject.Id;
                var name = selectedProject.ProjectName;
                var handbooksPage = new HandbooksPage(projId, name, false);
                await Navigation.PushAsync(handbooksPage);
    
                //take away selected background
                ((ListView)sender).SelectedItem = null;
            }
    
            private async void CompanyNameClicked(object sender, EventArgs e)
            {
                var name = _viewModel.CompanyName;
                var handbooksPage = new HandbooksPage(null, name, true);
                await Navigation.PushAsync(handbooksPage);
            }
        }
    }
    

    And then the HandbooksPage comes up, also with the back arrow instead of the master detail icon. I can still access the master page by swiping from the left side of the screen.

    Does anybody know what I am doing wrong? I have set the "Icon" attribute on the Master page. I have tried to put my navigation all inside of one Detail page. I really just want to replace the detail page every time, but I need the stack to navigate.

    Are you trying to have both a hamburger button and a back button at the same time? If so, you'll need a custom renderer to accomplish this. You can change all your pages to derive from some custom class you define and then write a custom renderer for that class. In particular, look at NavigationController.TopViewController.NavigationItem.SetLeftBarButtonItems.

  • JasonTomsJasonToms USMember ✭✭

    @DavidS.4178 said:
    Are you trying to have both a hamburger button and a back button at the same time? If so, you'll need a custom renderer to accomplish this. You can change all your pages to derive from some custom class you define and then write a custom renderer for that class. In particular, look at NavigationController.TopViewController.NavigationItem.SetLeftBarButtonItems.

    On Android I want the back button gone and replaced with the hamburger icon (since Android phones have a back button). On iOS, the hamburger icon SHOULD be on the right side, according to screenshots. Then the back button should be on the left side. I do not have a mac to build with to see if the hamburger icon stays on the iOS page or not. I will look into custom renderers to try and force that hamburger icon on the top. I know I can make the back button go away with NavigationPage.setHasBackButton(this, false).

  • JasonTomsJasonToms USMember ✭✭

    Not sure this is even possible really. It is starting to seem like modifying navigation in Xamarin.Forms is prohibitively difficult. I have tried making a custom navigation page with a custom renderer. I can replace the back button with the correct icon, but it only seems to work for one "push". Any further through the navigation stack and it does not work. Going back through the navigation stack it does not work. I haven't even tried to make the button open the master page. We have a future app with some potentially complicated navigation, so I think the solution is to just use Xamarin.Android and Xamarin.iOS.

  • DhruDhru INMember

    Try this:
    App.Current.MainPage = new MainPage { Detail = new NavigationPage(new pagename()) };

    Hope this helps!

    Regards,
    Dhru

  • RJ_11RJ_11 Member ✭✭

    still having this problem !
    any solution?

  • JasonTomsJasonToms USMember ✭✭

    @RJ_11 said:
    still having this problem !
    any solution?

    My solution was to stop using Xamarin.Forms. Switched to Xamarin.Android/Xamarin.iOS instead.

Sign In or Register to comment.