Lesson 2: Populate Tab Items from a Data Source

  • 7 minutes to read

The DevExpress TabPage can generate tab items from a data source. This topic explains how to create a tab bar that allows users to filter a list:

Tab Page

Add a Tab 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 initialization code to your projects.

    • In the AppDelegate.cs file of the iOS project, before the LoadApplication method call:

      DevExpress.XamarinForms.Navigation.iOS.Initializer.Init();
      
    • In the MainPage.xaml.cs file of the project with the shared code, before the InitializeComponent method call:

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

    <dxn:TabPage 
         xmlns="http://xamarin.com/schemas/2014/forms"
         xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
         xmlns:dxn="http://schemas.devexpress.com/xamarin/2014/forms/navigation"
         xmlns:local="clr-namespace:TabPageExample_ItemsSource"
         x:Class="TabPageExample_ItemsSource.MainPage">
    
    </dxn:TabPage>
    

Prepare Models and View Models

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

    namespace TabPageExample_ItemsSource.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 is a tab item’s view model. This view model provides content to be displayed within the header and a tab item’s main area:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Linq;
    using System.Runtime.CompilerServices;
    using TabPageExample_ItemsSource.Models;
    
    namespace TabPageExample_ItemsSource.ViewModels {
        public class GroupedVehiclesViewModel : INotifyPropertyChanged {
            private bool isSelected = false;
    
            public string GroupKey { get; }
            public IReadOnlyList<Vehicle> Vehicles { get; }
            // This property is used to change the 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.Collections.Generic;
    using System.ComponentModel;
    using System.Runtime.CompilerServices;
    using TabPageExample_ItemsSource.Models;
    
    namespace TabPageExample_ItemsSource.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 Tab View

  1. Assign a MainViewModel instance to the MainPage’s BindingContext:

    <dxn:TabPage 
        xmlns="http://xamarin.com/schemas/2014/forms"
        xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
        xmlns:dxn="http://schemas.devexpress.com/xamarin/2014/forms/navigation"
        xmlns:local="clr-namespace:TabPageExample_ItemsSource"
        xmlns:viewmodels="clr-namespace:TabPageExample_ItemsSource.ViewModels"
        x:Class="TabPageExample_ItemsSource.MainPage"
        ItemsSource="{Binding VehiclesByMake}"
        SelectedItemIndex="{Binding SelectedIndex, Mode=TwoWay}">
        <dxn:TabPage.BindingContext>
            <viewmodels:MainViewModel/>
        </dxn:TabPage.BindingContext>
    </dxn:TabPage>
    
  2. In the MainPage.xaml file:

    • Bind the TabPage’s ItemsSource property to the view model’s VehiclesByMake property, and the SelectedItemIndex property to the view model’s SelectedIndex property.
    • Use the ItemHeaderTemplate property to define the tab item header’s appearance, and the ItemTemplate property to specify a page that should be displayed in the tab item’s content area.
    <dxn:TabPage 
        xmlns="http://xamarin.com/schemas/2014/forms"
        xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
        xmlns:local="clr-namespace:TabPageExample_ItemsSource"
        xmlns:dxn="http://schemas.devexpress.com/xamarin/2014/forms/navigation"
        xmlns:viewmodels="clr-namespace:TabPageExample_ItemsSource.ViewModels"
        x:Class="TabPageExample_ItemsSource.MainPage"
        ItemsSource="{Binding VehiclesByMake}"
        SelectedItemIndex="{Binding SelectedIndex, Mode=TwoWay}">
        <dxn:TabPage.BindingContext>
            <viewmodels:MainViewModel/>
        </dxn:TabPage.BindingContext>
        <dxn:TabPage.ItemHeaderTemplate>
            <DataTemplate>
                <Label Text="{Binding GroupKey}"
                       HorizontalTextAlignment="Center"
                       VerticalTextAlignment="Center"/>
            </DataTemplate>
        </dxn:TabPage.ItemHeaderTemplate>
        <dxn:TabPage.ItemTemplate>
            <DataTemplate>
                <ContentPage>
                    <ListView ItemsSource="{Binding Vehicles}">
                        <ListView.ItemTemplate>
                            <DataTemplate>
                                <TextCell Text="{Binding FullName}"/>
                            </DataTemplate>
                        </ListView.ItemTemplate>
                    </ListView>
                </ContentPage>
            </DataTemplate>
        </dxn:TabPage.ItemTemplate>
    </dxn:TabPage>
    

    Tab Page - Loaded Items

Customize the Tab Page

Configure the appearance of the TabPage’s header panel and tab item headers.

  1. Specify the minimum and maximum sizes of items (the ItemHeaderMinWidth and ItemHeaderMaxWidth properties), the spacing between items (HeaderPanelItemSpacing), and item padding:

    <dxn:TabPage 
        xmlns="http://xamarin.com/schemas/2014/forms"
        xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
        xmlns:local="clr-namespace:TabPageExample_ItemsSource"
        xmlns:dxn="http://schemas.devexpress.com/xamarin/2014/forms/navigation"
        xmlns:viewmodels="clr-namespace:TabPageExample_ItemsSource.ViewModels"
        x:Class="TabPageExample_ItemsSource.MainPage"
        ItemsSource="{Binding VehiclesByMake}"
        SelectedItemIndex="{Binding SelectedIndex, Mode=TwoWay}"
        ItemHeaderMinWidth="90"
        ItemHeaderMaxWidth="360"
        HeaderPanelItemSpacing="8">
        <dxn:TabPage.ItemHeaderTemplate>
            <DataTemplate>
                <Frame Padding="16,0,16,0"
                       HasShadow="False">
                    <Label Text="{Binding GroupKey}"
                           HorizontalTextAlignment="Center"
                           VerticalTextAlignment="Center"/>
                </Frame>
            </DataTemplate>
        </dxn:TabPage.ItemHeaderTemplate>
        <!-- Other TabPage settings.-->
    </dxn:TabPage>
    

    Tab Page - Item Header Width and Spacing

  2. Modify the header panel’s background (HeaderPanelBackgroundColor), and bind the item’s background color to a view model state:

     <dxn:TabPage 
         xmlns="http://xamarin.com/schemas/2014/forms"
         xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
         xmlns:local="clr-namespace:TabPageExample_ItemsSource"
         xmlns:dxn="http://schemas.devexpress.com/xamarin/2014/forms/navigation"
         xmlns:viewmodels="clr-namespace:TabPageExample_ItemsSource.ViewModels"
         x:Class="TabPageExample_ItemsSource.MainPage"
         ItemsSource="{Binding VehiclesByMake}"
         SelectedItemIndex="{Binding SelectedIndex, Mode=TwoWay}"
         ItemHeaderMinWidth="90"
         ItemHeaderMaxWidth="360"
         HeaderPanelItemSpacing="8"
         HeaderPanelBackgroundColor="#1e88e5">
         <dxn:TabPage.Resources>
             <ResourceDictionary>
                 <local:IsSelectedToColorConverter x:Key="isSelectedToColorConverter"
                                                   DefaultColor="Transparent"
                                                   SelectedColor="#40FFFFFF"/>
             </ResourceDictionary>
         </dxn:TabPage.Resources>
         <dxn:TabPage.ItemHeaderTemplate>
             <DataTemplate>
                 <Frame Padding="16,0,16,0"
                        HasShadow="False"
                        BackgroundColor="{Binding IsSelected, Converter={StaticResource isSelectedToColorConverter}}">
                        <!-- Other Frame settings -->
                 </Frame>
             </DataTemplate>
         </dxn:TabPage.ItemHeaderTemplate>
         <!-- Other TabPage settings.-->
     </dxn:TabPage>
    

    Tab Page - Modified Item HeaderBackground

  3. Specify the item header frame’s border radius and margin, change header text color, configure the header panel’s shadow (HeaderPanelShadowHeight and HeaderPanelShadowRadius properties), and hide the selected item indicator (IsSelectedItemIndicatorVisible):

    <dxn:TabPage 
        xmlns="http://xamarin.com/schemas/2014/forms"
        xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
        xmlns:local="clr-namespace:TabPageExample_ItemsSource"
        xmlns:dxn="http://schemas.devexpress.com/xamarin/2014/forms/navigation"
        xmlns:viewmodels="clr-namespace:TabPageExample_ItemsSource.ViewModels"
        x:Class="TabPageExample_ItemsSource.MainPage"
        ItemsSource="{Binding VehiclesByMake}"
        SelectedItemIndex="{Binding SelectedIndex, Mode=TwoWay}"
        ItemHeaderMinWidth="90"
        ItemHeaderMaxWidth="360"
        HeaderPanelItemSpacing="8"
        HeaderPanelBackgroundColor="#1e88e5"
        HeaderPanelShadowHeight="3"
        HeaderPanelShadowRadius="3"
        IsSelectedItemIndicatorVisible="False">
        <dxn:TabPage.BindingContext>
            <viewmodels:MainViewModel/>
        </dxn:TabPage.BindingContext>
        <dxn:TabPage.Resources>
            <ResourceDictionary>
                <local:IsSelectedToColorConverter x:Key="isSelectedToColorConverter"
                                                  DefaultColor="Transparent"
                                                  SelectedColor="#40FFFFFF"/>
            </ResourceDictionary>
        </dxn:TabPage.Resources>
        <dxn:TabPage.ItemHeaderTemplate>
            <DataTemplate>
                <Frame Margin="0,8,0,8"
                       Padding="16,0,16,0"
                       CornerRadius="16"
                       HasShadow="false"
                       BackgroundColor="{Binding IsSelected, Converter={StaticResource isSelectedToColorConverter}}">
                    <Label Text="{Binding GroupKey}"
                           HorizontalTextAlignment="Center"
                           VerticalTextAlignment="Center"
                           TextColor="White"/>
                </Frame>
            </DataTemplate>
        </dxn:TabPage.ItemHeaderTemplate>
        <dxn:TabPage.ItemTemplate>
            <DataTemplate>
                <ContentPage>
                    <ListView ItemsSource="{Binding Vehicles}">
                        <ListView.ItemTemplate>
                            <DataTemplate>
                                <TextCell Text="{Binding FullName}"/>
                            </DataTemplate>
                        </ListView.ItemTemplate>
                    </ListView>
                </ContentPage>
            </DataTemplate>
        </dxn:TabPage.ItemTemplate>
    </dxn:TabPage>
    

    Tab Page