Accueil

Créer un menu hamburger Xamarin Forms

Tutoriel Xamarin

Posté par Véronique le 11 October 2017

Voici comment créer un menu hamburger dans un projet Xamarin Forms en quelques étapes.

(Source : developer.xamarin.com)


Dans un premier temps, nous allons créer un ViewModel.

// MenuItem.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Project.Views;

namespace Project.ViewsModels
{
    public class MenuItem
    {
        public MenuItem()
        {
            TargetType = typeof(HomePage);
        }
        public int Id { get; set; }
        public string Title { get; set; }
        public string ImageName { get; set; }
        public Type TargetType { get; set; }
    }
}

Il faut ensuite créer un autre ViewModel qui créera tous les MenuItems que l'on souhaite afficher.

// MenuPageViewModel.cs

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using Project.Views;
using Project.ViewsModels;

namespace Project.ViewModels
{
    class MenuPageViewModel : INotifyPropertyChanged
    {
        public ObservableCollection MenuItems { get; set; }

        public MenuPageViewModel()
        {
            MenuItems = new ObservableCollection(new[]
            {
                new MenuItem { Id = 0, 
                    Title = Localization.AppResources.Menu_Home,
                    TargetType = typeof(HomePage),
                    ImageName = "home.png" },
                new MenuItem { Id = 1, 
                    Title = Localization.AppResources.Menu_Options,
                    TargetType = typeof(OptionsPage),
                    ImageName = "options.png" },
                new MenuItem { Id = 2,
                    Title = Localization.AppResources.Menu_About,
                    TargetType = typeof(AboutPage),
                    ImageName = "about.png" },
            });
        }

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

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

A noter que HomePage, OptionsPage et AboutPage sont toutes les trois des ContentPage que j'ai créé au préalable.

Nous allons à présent créer une nouvelle ContentPage dans le dossier Views, qui affichera la liste des MenuItems.

<!-- MenuPage.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"
             xmlns:i18n="clr-namespace:Project.Utils"
             x:Class="Project.Views.MenuPage"
             Title="{i18n:Translate Menu_Title}">
    <StackLayout>
        <ListView x:Name="MenuItemsListView"
              SeparatorVisibility="None"
              HasUnevenRows="true"
              ItemsSource="{Binding MenuItems}">
            <ListView.Header>
                <Grid>
                    <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>
                    <Image Source="logo.png"  HorizontalOptions="Center"
                        Grid.Column="1" Grid.Row="1"/>
                </Grid>
            </ListView.Header>
            <ListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell>
                        <StackLayout Padding="15,10" HorizontalOptions="FillAndExpand" Orientation="Horizontal">
                            <Image Source="{Binding ImageName}" WidthRequest="32"/>
                            <Label VerticalOptions="FillAndExpand" 
                    VerticalTextAlignment="Center" 
                    Text="{Binding Title}"/>
                        </StackLayout>
                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </StackLayout>
</ContentPage>

Dans le code behind nous allons rajouter une propriété ListView et initialiser le binding avec MenuPageViewModel.

// MenuPage.xaml.cs

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using Project.ViewModels;
using Project.Views.MenuViews;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace Project.Views
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class MenuPage : ContentPage
    {
        public ListView ListView;

        public MenuPage()
        {
            InitializeComponent();

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

La dernière étape, en ce qui concerne les vues, consiste à créer la MasterPageDetail.

<!-- RootPage.xaml -->

<?xml version="1.0" encoding="utf-8" ?>
<MasterDetailPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:pages="clr-namespace:Project.Views"
             x:Class="Project.Views.RootPage"
             MasterBehavior="Popover">
    <MasterDetailPage.Master>
        <pages:MenuPage x:Name="MasterPage" />
    </MasterDetailPage.Master>
    <MasterDetailPage.Detail>
        <NavigationPage>
            <x:Arguments>
                <pages:HomePage />
            </x:Arguments>
        </NavigationPage>
    </MasterDetailPage.Detail>
</MasterDetailPage>
// RootPage.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Project.ViewsModels;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace Project.Views
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class RootPage : MasterDetailPage
    {
        public RootPage()
        {
            InitializeComponent();
            MasterPage.ListView.ItemSelected += ListView_ItemSelected;
        }

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

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

            if (item.Id == 0)
            {
                // Reinit the navigation stack
                Detail = new NavigationPage(page);
            }
            else
            {
                // Push the new page to the navigation stack
                ((NavigationPage)Detail).Navigation.PushAsync(page);
            }
            IsPresented = false;

            MasterPage.ListView.SelectedItem = null;
        }
    }
}

Pour finir, il faut initialiser notre MasterPageDetail lors de l'initialisation de l'application.

// App.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Project.Services;
using Xamarin.Forms;

namespace Project
{
    public partial class App : Application
    {
        public App()
        {
            InitializeComponent();
            MainPage = new Project.Views.RootPage();
        }

        protected override void OnStart()
        {
            // Handle when your app starts
        }

        protected override void OnSleep()
        {
            // Handle when your app sleeps
        }

        protected override void OnResume()
        {
            // Handle when your app resumes
        }
    }
}

Petit bonus, pour modifier la couleur de fond du menu.

<!-- App.xaml -->

<?xml version="1.0" encoding="utf-8" ?>
<Application xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="Project.App">
    <Application.Resources>
        <!-- Application resource dictionary -->
        <ResourceDictionary>
            <!-- Colors -->
            <Color x:Key="BackgroundColor">#2E2E2E</Color>

            <!-- Styles -->
            <Style TargetType="MasterDetailPage">
                <Setter Property="BackgroundColor" Value="{StaticResource BackgroundColor}"/>
            </Style>

            <Style TargetType="ListView" x:Key="ListViewMenu">
                <Setter Property="BackgroundColor" Value="{StaticResource BackgroundColor}"/>
            </Style>
        </ResourceDictionary>
    </Application.Resources>
</Application>

Puis ajouter la propriété Style dans la balise ListView de MenuPage.xaml :

Style="{StaticResource ListViewMenu}"