Getting Started

  • 8 minutes to read

This topic explains how to use a DrawerPage to add a navigation drawer to your Xamarin.Forms application.

Drawer Page

Add a Drawer Page to Your Application

NOTE

This lesson requires an empty Xamarin.Forms solution.
DevExpress Navigation components are available for iOS and Android, and can be used in Xamarin.Forms solutions that use the .NET Standard code sharing strategy.

  1. Add DevExpress Navigation components to your solution in one of the following ways:

    • Install the NuGet package

      Visual Studio
      1. Obtain your NuGet feed URL
      2. Register the DevExpress NuGet feed as a package source.
        Navigate to Tools | Options | NuGet Package Manager | Package Source and add the DevExpress feed to the list of package sources.

        Register NuGet Feed - Visual Studio

      3. Install the DevExpress.XamarinForms.Navigation package from the DevExpress NuGet feed.

        1. Select Tools | NuGet Package Manager | Manage NuGet Packages for Solution... in Visual Studio's main menu, or right-click the solution in Solution Explorer and select Manage NuGet Packages for Solution....
        2. Search for DevExpress.XamarinForms.Navigation in the DevExpress package source, select all the solution's projects and click Install.

          Install NuGet Package - Visual Studio

      Visual Studio for Mac
      1. Obtain your NuGet feed URL
      2. Register the DevExpress NuGet feed as a package source.
        Navigate to Visual Studio | Preferences | NuGet | Sources and add the DevExpress feed to the list of sources.

        Register NuGet Feed - Visual Studio for Mac

      3. Install the DevExpress.XamarinForms.Navigation package from the DevExpress NuGet feed.

        1. Select Project | Manage NuGet Packages... in the main menu, or right-click the solution in Solution Pad and select Manage NuGet Packages...
        2. In the invoked Manage NuGet Packages dialog, select DevExpress from the Source drop-down list in the top left corner, search for DevExpress.XamarinForms.Navigation and click Add Package.
          Select all the solution's projects in the invoked Select Projects dialog and click Ok.

          Install NuGet Package - Visual Studio for Mac

      – or –

    • Add libraries from the downloaded bundle

      1. Download the Mobile UI Controls for Xamarin.Forms bundle from the Client Center.
      2. Add the following assembly references to your Xamarin.Forms solution's projects:

        Project

        Assembly

        <YourAppName>

        (A .NET Standard project that contains the shared code)

        DevExpress.XamarinForms.Core.dll

        DevExpress.XamarinForms.Navigation.dll

        <YourAppName>.Android

        (A project that contains Android-specific code)

        DevExpress.XamarinForms.Navigation.Android.dll

        DevExpress.Xamarin.Android.Navigation.dll

        <YourAppName>.iOS

        (A project that contains iOS-specific code)

        DevExpress.XamarinForms.Navigation.iOS.dll

        DevExpress.Xamarin.iOS.Navigation.dll

        NOTE

        These files are in the <DevExpress.Xamarin bundle>/Binaries directory. Ensure the downloaded <DevExpress.Xamarin> bundle was unzipped.

  2. Add the following initialization code before the LoadApplication method call in your iOS project's AppDelegate.cs file:

    DevExpress.XamarinForms.Navigation.Navigation.Init();
    
  1. In the MainPage.xaml and MainPage.xaml.cs files of the .NET Standard project that contains the shared code, use the dxg prefix to declare a namespace that contains the DrawerPage class and create a DrawerPage instance:

    <dxn:DrawerPage 
        xmlns="http://xamarin.com/schemas/2014/forms"
        xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
        xmlns:dxn="clr-namespace:DevExpress.XamarinForms.Navigation;assembly=DevExpress.XamarinForms.Navigation"
        xmlns:local="clr-namespace:DrawerPageExample"
        x:Class="DrawerPageExample.MainPage">
    
    </dxn:DrawerPage>
    

Prepare Models and View Models

  1. Add the Vehicle class that represents a data object in the application:

    namespace DrawerPageExample.Models {
        public class Vehicle {
            public string MakeName { get; }
            public string ModelName { get; }
            public string FullName => $"{MakeName} {ModelName}";
    
            public Vehicle(string make, string model) {
                this.MakeName = make;
                this.ModelName = model;
            }
        }
    }
    
  2. Create the GroupedVehiclesViewModel class that provides content to be displayed within the drawer and the content area:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Linq;
    using System.Runtime.CompilerServices;
    using DrawerPageExample.Models;
    
    namespace DrawerPageExample.ViewModels {
        public class GroupedVehiclesViewModel : INotifyPropertyChanged {
            private bool isSelected = false;
            public string GroupKey { get; }
            public IReadOnlyList<Vehicle> Vehicles { get; }
            // This property is used to change tab appearance depending on its state. 
            public bool IsSelected {
                get { return isSelected; }
                set {
                    if (value == isSelected) return;
                    isSelected = value;
                    RaisePropertyChanged();
                }
            }
            public event PropertyChangedEventHandler PropertyChanged;
    
            public GroupedVehiclesViewModel(string groupKey, IEnumerable<Vehicle> vehicles) {
                if (String.IsNullOrEmpty(groupKey)) {
                    this.GroupKey = String.Empty;
                }
                else {
                    this.GroupKey = groupKey;
                }
                if (vehicles == null) {
                    this.Vehicles = new List<Vehicle>();
                }
                else {
                    this.Vehicles = vehicles.ToList();
                }
            }
            private void RaisePropertyChanged([CallerMemberName] string caller = "") {
                PropertyChangedEventHandler handler = PropertyChanged;
                if (handler != null) {
                    handler.Invoke(this, new PropertyChangedEventArgs(caller));
                }
            }
        }
    }
    
  3. Create the MainViewModel class that provides the MainPage's content:

    using System.Linq;
    using System.Linq;
    using System.ComponentModel;
    using DrawerPageExample.Models;
    using System.Collections.Generic;
    using System.Runtime.CompilerServices;
    
    namespace DrawerPageExample.ViewModels {
        public class MainViewModel : INotifyPropertyChanged {
            private const int UnselectedIndex = -1;
            private static readonly IReadOnlyList<Vehicle> allVehicles = new List<Vehicle> {
                new Vehicle("Mercedes-Benz", "SL500 Roadster"),
                new Vehicle("Mercedes-Benz", "CLK55 AMG Cabriolet"),
                new Vehicle("Mercedes-Benz", "C230 Kompressor Sport Coupe"),
                new Vehicle("BMW", "530i"),
                new Vehicle("Rolls-Royce", "Corniche"),
                new Vehicle("Jaguar", "S-Type 3.0"),
                new Vehicle("Cadillac", "Seville"),
                new Vehicle("Cadillac", "DeVille"),
                new Vehicle("Lexus", "LS430"),
                new Vehicle("Lexus", "GS430"),
                new Vehicle("Ford", "Ranger FX-4"),
                new Vehicle("Dodge", "RAM 1500"),
                new Vehicle("GMC", "Siera Quadrasteer"),
                new Vehicle("Nissan", "Crew Cab SE"),
                new Vehicle("Toyota", "Tacoma S-Runner"),
            };
    
            public IReadOnlyList<GroupedVehiclesViewModel> VehiclesByMake { get; }
    
            int selectedIndex = UnselectedIndex;
            public int SelectedIndex {
                get => selectedIndex;
                set {
                    if (selectedIndex == value) return;
                    if (selectedIndex != UnselectedIndex) {
                        VehiclesByMake[selectedIndex].IsSelected = false;
                    }
                    selectedIndex = value;
                    if (selectedIndex != UnselectedIndex) {
                        VehiclesByMake[selectedIndex].IsSelected = true;
                    }
                    RaisePropertyChanged();
                }
            }
    
            public event PropertyChangedEventHandler PropertyChanged;
    
            public MainViewModel() {
                List<GroupedVehiclesViewModel> groupedVehiclesVMs = new List<GroupedVehiclesViewModel>();
                groupedVehiclesVMs.Add(new GroupedVehiclesViewModel("All", allVehicles));
    
                IEnumerable<IGrouping<string, Vehicle>> groupedVehicles = allVehicles.GroupBy(v => v.MakeName);
                foreach (IGrouping<string, Vehicle> vehiclesGroup in groupedVehicles) {
                    groupedVehiclesVMs.Add(new GroupedVehiclesViewModel(vehiclesGroup.Key, vehiclesGroup));
                }
    
                VehiclesByMake = groupedVehiclesVMs;
            }
    
            private void RaisePropertyChanged([CallerMemberName] string caller = "") {
                PropertyChangedEventHandler handler = PropertyChanged;
                if (handler != null) {
                    handler.Invoke(this, new PropertyChangedEventArgs(caller));
                }
            }
        }
    }
    

Bind View Models to the Drawer Page

In the MainPage.xaml file:

  1. Set the MainPage's BindingContext property to MainViewModel.
  2. Assign a ListView instance to the DrawerPage.DrawerContent property, and bind the list view to the view model's VehiclesByMake property.
  3. Assign a ContentPage with a ListView instance to the DrawerView.MainContent property, and bind the list view to an item selected in the drawer's list.
    <dxn:DrawerPage 
        xmlns="http://xamarin.com/schemas/2014/forms"
        xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
        xmlns:dxn="clr-namespace:DevExpress.XamarinForms.Navigation;assembly=DevExpress.XamarinForms.Navigation"
        xmlns:local="clr-namespace:DrawerPageExample"
        xmlns:viewmodel="clr-namespace:DrawerPageExample.ViewModels"
        x:Class="DrawerPageExample.MainPage">
        <dxn:DrawerPage.BindingContext>
            <viewmodel:MainViewModel/>
        </dxn:DrawerPage.BindingContext>
        <dxn:DrawerPage.DrawerContent>
            <ListView x:Name="categoryList" 
                        ItemsSource="{Binding VehiclesByMake}">
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <TextCell Text="{Binding GroupKey}" />
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
        </dxn:DrawerPage.DrawerContent>
        <dxn:DrawerPage.MainContent>
            <ContentPage>
                <ListView BindingContext="{x:Reference categoryList}"
                            ItemsSource="{Binding SelectedItem.Vehicles}">
                    <ListView.ItemTemplate>
                        <DataTemplate>
                            <TextCell Text="{Binding FullName}"/>
                        </DataTemplate>
                    </ListView.ItemTemplate>
                </ListView>
            </ContentPage>
        </dxn:DrawerPage.MainContent>
    </dxn:DrawerPage>
    

The following image demonstrates the current application state:

Drawer Page - Data

Customize the Drawer Page Appearance and Behavior

In this step, configure the drawer appearance, make it to be always visible in the landscape orientation, and add a button that toggles the drawer in the portrait orientation.

  1. Implement the MainPage's IsLandscapeOriented dependency property:

     using System;
     using Xamarin.Forms;
     using DevExpress.XamarinForms.Navigation;
    
     namespace DrawerPageExample {
         public partial class MainPage : DrawerPage {
             const string IsLandscapeOrientedPropertyName = "IsLandscapeOriented";
    
             public static readonly BindableProperty IsLandscapeOrientedProperty = BindableProperty.Create(
                 IsLandscapeOrientedPropertyName,
                 typeof(bool),
                 typeof(MainPage),
                 defaultValue: false);
    
             public bool IsLandscapeOriented {
                 get => (bool)GetValue(IsLandscapeOrientedProperty);
                 set => SetValue(IsLandscapeOrientedProperty, value);
             }
             public MainPage() {
                 InitializeComponent();
                 SizeChanged += OnSizeChanged;
             }
    
             protected void OnSizeChanged(object sender, EventArgs args) {
                 IsLandscapeOriented = this.Width > this.Height;
             }
    
             private void Button_Clicked(object sender, EventArgs e) {
                 drawer.IsDrawerOpened = !drawer.IsDrawerOpened;
             }
         }
     }
    
  2. Implement a value converter that converts a Boolean value to a DrawerBehavior value:

    using System.Globalization;
    
    // ...
     class BoolToDrawerBehaviorConverter : IValueConverter {
         public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
             if (targetType != typeof(DrawerBehavior)) return null;
             bool boolValue = (bool)value;
             return boolValue ? DrawerBehavior.Split : DrawerBehavior.SlideOnTop;
         }
    
         public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
             throw new NotImplementedException();
         }
     }
    
  3. Bind the DrawerPage.DrawerBehavior property to MainPage's IsLandscapeOriented:
     <dxn:DrawerPage 
         xmlns="http://xamarin.com/schemas/2014/forms"
         xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
         xmlns:local="clr-namespace:DrawerPageExample"
         xmlns:dxn="clr-namespace:DevExpress.XamarinForms.Navigation;assembly=DevExpress.XamarinForms.Navigation"
         xmlns:viewmodel="clr-namespace:DrawerPageExample.ViewModels"
         x:Class="DrawerPageExample.MainPage"
         x:Name="drawer"
         DrawerBehavior="{Binding IsLandscapeOriented, Source={x:Reference drawer}, Converter={StaticResource boolToDrawerBehaviorConverter}}">
         <dxn:DrawerPage.Resources>
             <ResourceDictionary>
                 <local:BoolToDrawerBehaviorConverter x:Key="boolToDrawerBehaviorConverter"/>
             </ResourceDictionary>
         </dxn:DrawerPage.Resources>
         <!-- Other drawer page's properteis are here. -->
     </dxn:DrawerPage>
    
  4. In the App.xaml.cs file, assign a NavigationPage instance to the Application.MainPage property and add the MainPage drawer page to the navigation stack (the application's root page).
    In the App.xaml file, create a style that defines the navigation bar appearance.

    namespace DrawerPageExample {
        public partial class App : Application {
            public App() {
                InitializeComponent();
    
                // MainPage = new MainPage();
                MainPage = new NavigationPage(new MainPage());
            }
    
            // ...
        }
    }
    
  5. Assign a StackLayout to the NavigationPage.TitleView property. Add a button that opens and closes the drawer, and a label that displays a title text. Handle the button's Clicked event.
     <dxn:DrawerPage 
         xmlns="http://xamarin.com/schemas/2014/forms"
         xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
         xmlns:local="clr-namespace:DrawerPageExample"
         xmlns:dxn="clr-namespace:DevExpress.XamarinForms.Navigation;assembly=DevExpress.XamarinForms.Navigation"
         xmlns:viewmodel="clr-namespace:DrawerPageExample.ViewModels"
         x:Class="DrawerPageExample.MainPage"
         x:Name="drawer"
         DrawerBehavior="{Binding IsLandscapeOriented, Source={x:Reference drawer}, Converter={StaticResource boolToDrawerBehaviorConverter}}">
         <NavigationPage.TitleView>
             <StackLayout Orientation="Horizontal" VerticalOptions="Center">
                 <Button Text="Menu" Clicked="Button_Clicked"/>
                 <Label Text="Drawer Page Example" VerticalTextAlignment = "Center"/>
             </StackLayout>
         </NavigationPage.TitleView>
         <!-- Drawer page properties are here. -->
     </dxn:DrawerPage>
    
  6. Customize the drawer's size (DrawerWidth), shadow (DrawerShadowHeight, DrawerShadowColor), and scrim (ScrimColor):

    <dxn:DrawerPage 
        xmlns="http://xamarin.com/schemas/2014/forms"
        xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
        xmlns:local="clr-namespace:DrawerPageExample"
        xmlns:dxn="clr-namespace:DevExpress.XamarinForms.Navigation;assembly=DevExpress.XamarinForms.Navigation"
        xmlns:viewmodel="clr-namespace:DrawerPageExample.ViewModels"
        x:Class="DrawerPageExample.MainPage"
        DrawerBehavior="{Binding IsLandscapeOriented, Source={x:Reference drawer}, Converter={StaticResource boolToDrawerBehaviorConverter}}"
        x:Name="drawer"
        DrawerWidth="180"
        DrawerShadowHeight="1"
        DrawerShadowColor="#808080"
        ScrimColor="#80000000">
        <!-- Other drawer page properties. -->
    </dxn:DrawerPage>
    
    NOTE

    The scrim does not affect the view when drawer behavior is set to Split.

Drawer Page