Xamarin Forms adding a button to the bottom of a Detail page to navigate to the next detail page

I've created a new solution using a .net standard shared project and added an out-of-the-box MasterDetail page to the share project.

Out of the box, it creates a set of pages which work well for what I'm doing:

MasterDetailPageMaster houses a ListView which displays a "menu" of items created by the MasterDetailPageMasterViewModel observable collection "MasterDetailPageMenuItem".

Then, the ListView_ItemSelected in the MasterDetailPage event sets the MasterDetailPageDetail Title to the ListView MasterDetailPageMenuItem "Title" property, which is used to populate a ContentView which I manually insert into the Layout of this page from another ContentView I created specifically for each ListView menu item.

This all works great, but I don't want users to have to go back to the Master menu to go to the next Detail page, and I want to insert a button at the bottom of the detail page to navigate to the next page.

I can't figure out how to do that with the file structure the way it is set up out-of-the-box.

Here's some code snippets. If anyone can help me figure this out, it will be greatly appreciated!

MasterDetailPageMaster 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="bpntBook.MasterDetailPageMaster"
             Title="Master">
  <StackLayout>
    <ListView x:Name="MenuItemsListView"
              SeparatorVisibility="None"
              HasUnevenRows="true"
              ItemsSource="{Binding MenuItems}">
      <ListView.Header>
        <Grid BackgroundColor="#03A9F4">
          <Grid.ColumnDefinitions>
            <ColumnDefinition Width="10"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="10"/>
          </Grid.ColumnDefinitions>
          <Grid.RowDefinitions>
            <RowDefinition Height="30"/>
            <RowDefinition Height="80"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="10"/>
          </Grid.RowDefinitions>
          <Label
              Grid.Column="1"
              Grid.Row="2"
              Text="AppName"
              Style="{DynamicResource SubtitleStyle}"/>
        </Grid>
      </ListView.Header>
      <ListView.ItemTemplate>
        <DataTemplate>
          <ViewCell>
            <StackLayout Padding="15,10" HorizontalOptions="FillAndExpand">
              <Label VerticalOptions="FillAndExpand" 
                    VerticalTextAlignment="Center" 
                    Text="{Binding Title}" 
                    FontSize="24"/>
            </StackLayout>
          </ViewCell>
        </DataTemplate>
      </ListView.ItemTemplate>
    </ListView>
  </StackLayout>
</ContentPage>

MasterDetailPageMaster code behind:

public partial class MasterDetailPageMaster : ContentPage
        {
        public ListView ListView;

        public MasterDetailPageMaster()
        {
            InitializeComponent();

            BindingContext = new MasterDetailPageMasterViewModel();
            ListView = MenuItemsListView;
        }

        class MasterDetailPageMasterViewModel : INotifyPropertyChanged
        {
            public ObservableCollection<MasterDetailPageMenuItem> MenuItems { get; set; }

            public MasterDetailPageMasterViewModel()
            {
                MenuItems = new ObservableCollection<MasterDetailPageMenuItem>(new[]
                {
                    new MasterDetailPageMenuItem { Id = 0, Title = "Chapter 1" },
                    new MasterDetailPageMenuItem { Id = 1, Title = "Chapter 2" },
                    new MasterDetailPageMenuItem { Id = 2, Title = "Chapter 3" },
                    new MasterDetailPageMenuItem { Id = 3, Title = "Chapter 4" },
                    new MasterDetailPageMenuItem { Id = 4, Title = "Chapter 5" },
                });
            }

            #region INotifyPropertyChanged Implementation
            public event PropertyChangedEventHandler PropertyChanged;
            void OnPropertyChanged([CallerMemberName] string propertyName = "")
            {
                if (PropertyChanged == null)
                    return;

                PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
            }
            #endregion
        }
    }

MasterDetailPage xaml:

<MasterDetailPage.Master>
    <pages:MasterDetailPageMaster x:Name="MasterPage" />
  </MasterDetailPage.Master>
  <MasterDetailPage.Detail>
    <NavigationPage>
      <x:Arguments>
        <pages:MasterDetailPageDetail x:Name="DetailPage" />

MasterDetailPage code behind:

public partial class MasterDetailPage
{
    public MasterDetailPage()
    {
        InitializeComponent();
        MasterPage.ListView.ItemSelected += ListView_ItemSelected;
    }

    private void ListView_ItemSelected(object sender, SelectedItemChangedEventArgs e)
    {
        var item = e.SelectedItem as MasterDetailPageMenuItem;
        if (item == null)
            return;

        var page = (Page)Activator.CreateInstance(item.TargetType);
        page.Title = item.Title;

        Detail = new NavigationPage(page);

        IsPresented = false;

        MasterPage.ListView.SelectedItem = null;
    }
}

MasterDetailPageDetail 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="bpntBook.MasterDetailPageDetail"
             Title="Detail">
  <StackLayout x:Name="contentLayout" Padding="10">

MasterDetailPageDetail Code behind:

public partial class MasterDetailPageDetail : ContentPage
{
    public ContentView sContent { get; set; }
    public ContentView nextContent { get; set; }
    public Button next { get; set; }

    public MasterDetailPageDetail()
    {
        InitializeComponent();
    }

    protected override void OnAppearing()
    {
        base.OnAppearing();


        if (Title.Contains("Chapter 1"))
        {
            sContent = new Chapters.Chapter1();
            contentLayout.Children.Add(sContent);
            nextContent = new Chapters.Chapter2();
        }
        else if (Title.Contains("Chapter 2"))
        {
            sContent = new Chapters.Chapter2();
            contentLayout.Children.Add(sContent);
            nextContent = new Chapters.Chapter3();
        }
        else if (Title.Contains("Chapter 3"))
        {
            sContent = new Chapters.Chapter3();
            contentLayout.Children.Add(sContent);
            //nextContent = new Chapters.Chapter3();
        }

        if (nextContent != null)
        {
            next = new Button();
            next.Text = "Next Chapter";
            next.Clicked += onButtonClick;
            contentLayout.Children.Add(next);
        }
    }

    void onButtonClick(object sender, EventArgs e)
    {
        contentLayout.Children.Remove(next);
        contentLayout.Children.Remove(sContent);
        contentLayout.Children.Add(nextContent);

The above code works but only after clicking a menu item and it only works on the first detail page generated.

This code below here doesn't work:

//App.Current.MainPage = new MasterDetailPage { Title = "Chapter 3" };
        //Title = "Chapter 3";
        next = new Button();
        next.Text = "Next Chapter";
        next.Clicked += onButtonClick;
        contentLayout.Children.Add(next);
    }
}

Any help will be so appreciated!

Best Answer

  • DanNordquistDanNordquist US ✭✭
    Accepted Answer

    Well, I figured out how to do it with a global variable. Probably not the best way but it works.

Answers

  • DanNordquistDanNordquist USMember ✭✭
    Accepted Answer

    Well, I figured out how to do it with a global variable. Probably not the best way but it works.

  • DanNordquistDanNordquist USMember ✭✭

    If anyone stumbles on this and would like to know how I did it, just ask and I'll be happy to share.

  • docen67docen67 Member ✭✭

    @DanNordquist said:
    If anyone stumbles on this and would like to know how I did it, just ask and I'll be happy to share.

    Hi Dan can you share with me how you did it please?.

  • DanNordquistDanNordquist USMember ✭✭

    Hey @docen67, it was a bit of manual work, but I set up some global variables in App.xaml.cs:

            public static string currentChapter { get; set; }
            public static string nextChapter { get; set; }
            public static string prevChapter { get; set; }
            public static bool isNextNotPrev { get; set; }
    

    Then in my MasterDetailPageDetail.xaml.cs where the menu processing takes place, I set those variables depending on menu selections and then used them to create the navigation buttons.

        public ContentView nextContent { get; set; }
        public Button next { get; set; }
        public Button prev { get; set; }
    

    Then in the #region load chapters contentview

            else if (Title.Contains("Chapter 1 "))
            {
                sContent = new Chapters.Chapter1();
                contentLayout.Children.Add(sContent);
                nextContent = new Chapters.Chapter2();                
                App.Global.prevChapter = "Chapter 1 ";
                App.Global.currentChapter = "Chapter 1";
                App.Global.nextChapter = "Chapter 2 ";
            }
            else if (Title.Contains("Chapter 2 "))
            {
                sContent = new Chapters.Chapter2();
                contentLayout.Children.Add(sContent);
                nextContent = new Chapters.Chapter3();
                App.Global.prevChapter = "Chapter 1 ";
                App.Global.currentChapter = "Chapter 2";
                App.Global.nextChapter = "Chapter 3 ";
            }
    

    Do that for all pages you want to navigate to with the buttons.

    Then create the buttons on the fly:

            if (nextContent != null)
            {
                buttonContainer = new StackLayout();
                buttonContainer.Orientation = StackOrientation.Horizontal;
    
                prev = new Button();
                prev.HorizontalOptions = LayoutOptions.Start;
                prev.Text = "Previous Chapter";
                prev.Clicked += onButtonClick;
                buttonContainer.Children.Add(prev);
    
                Label barLabel = new Label();
                barLabel.Text = " _ ";
                buttonContainer.Children.Add(barLabel);
    
                next = new Button();
                next.HorizontalOptions = LayoutOptions.End;
                next.Text = "Next Chapter";
                next.Clicked += onButtonClick;
                buttonContainer.Children.Add(next);
    
                contentLayout.Children.Add(buttonContainer);
            }
    

    And finally set your onButtonClick event:

        void onButtonClick(object sender, EventArgs e)
        {
            if (sender == this.next)
                App.Global.isNextNotPrev = true;
            else
                App.Global.isNextNotPrev = false;
    
            App.Global.lvClicked = false;
    
            ((App.Current.MainPage as MasterDetailPage).Detail as NavigationPage).CurrentPage.Navigation.PushAsync(new MasterDetailPageDetail());
        }
    

    I hope that helps!

Sign In or Register to comment.