Get Started with DevExpress Tabs for .NET MAUI. How to Specify Tabs in View Model
- 6 minutes to read
This topic explains how to use the TabView component with tab items generated from a data source to create a tab bar that allows users to filter lists:
Create a New .NET MAUI Application and Add a Tab View
Create a new .NET MAUI solution.
Refer to Microsoft .NET Multi-platform App UI documentation for more information on how to get started with .NET MAUI.
Remove default content from the main page (MainPage.xaml) and event handlers from the code-behind file (MainPage.xaml.cs). Clear also the application’s resource dictionary (App.xaml).
Install the DevExpress.Maui.Controls package from your DevExpress NuGet feed.
In the MauiProgram.cs file, call the UseDevExpress method to register a handler for the TabView class:
using Microsoft.Maui;
using Microsoft.Maui.Hosting;
using Microsoft.Maui.Controls.Hosting;
using DevExpress.Maui.Controls;
namespace TabView_GenerateItems {
public static class MauiProgram {
public static MauiApp CreateMauiApp() {
var builder = MauiApp.CreateBuilder();
builder
.UseDevExpress()
.UseMauiApp<App>()
.ConfigureFonts(fonts => {
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
});
return builder.Build();
}
}
}
In the MainPage.xaml file, use the dxco prefix to declare the DevExpress.Maui.Controls namespace and add a TabView object to the main page:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:dxco="clr-namespace:DevExpress.Maui.Controls;assembly=DevExpress.Maui.Controls"
x:Class="TabView_GenerateItems.MainPage">
<dxco:TabView/>
</ContentPage>
Note
DevExpress Navigation Controls for .NET MAUI support iOS and Android. The project should contain only these platforms.
Create Models and View Models
Add a CarModel class that specifies a data object in the application:
namespace TabView_GenerateItems {
public class CarModel {
public string BrandName { get; }
public string ModelName { get; }
public string FullName => $"{BrandName} {ModelName}";
public CarModel(string brand, string model) {
this.BrandName = brand;
this.ModelName = model;
}
}
}
Create a CarBrandViewModel class that defines content for the tab view: car make and corresponding models. This view model will be used to display brands as tabs in the header and matching models in the tab item’s content area:
using System;
using System.Linq;
using System.ComponentModel;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace TabView_GenerateItems {
public class CarBrandViewModel : INotifyPropertyChanged {
private bool isSelected = false;
public string BrandName { get; }
public IReadOnlyList<CarModel> CarModels { get; }
// This property is used to change the appearance of a tab depending on its state.
public bool IsSelected {
get { return isSelected; }
set {
if (value == isSelected) return;
isSelected = value;
RaisePropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
public CarBrandViewModel(string brandName, IEnumerable<CarModel> carModels) {
if (String.IsNullOrEmpty(brandName)) {
this.BrandName = String.Empty;
}
else {
this.BrandName = brandName;
}
if (carModels == null) {
this.CarModels = new List<CarModel>();
}
else {
this.CarModels = carModels.ToList();
}
}
private void RaisePropertyChanged([CallerMemberName] string caller = "") {
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) {
handler.Invoke(this, new PropertyChangedEventArgs(caller));
}
}
}
}
Create a MainViewModel class that defines content for the MainPage (models grouped by make/brand):
using System.Linq;
using System.ComponentModel;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace TabView_GenerateItems {
public class MainViewModel : INotifyPropertyChanged {
private const int UnselectedIndex = -1;
private static readonly IReadOnlyList<CarModel> allCarModels = new List<CarModel> {
new CarModel("Mercedes-Benz", "SL500 Roadster"),
new CarModel("Mercedes-Benz", "CLK55 AMG Cabriolet"),
new CarModel("Mercedes-Benz", "C230 Kompressor Sport Coupe"),
new CarModel("BMW", "530i"),
new CarModel("Rolls-Royce", "Corniche"),
new CarModel("Jaguar", "S-Type 3.0"),
new CarModel("Cadillac", "Seville"),
new CarModel("Cadillac", "DeVille"),
new CarModel("Lexus", "LS430"),
new CarModel("Lexus", "GS430"),
new CarModel("Ford", "Ranger FX-4"),
new CarModel("Dodge", "RAM 1500"),
new CarModel("GMC", "Siera Quadrasteer"),
new CarModel("Nissan", "Crew Cab SE"),
new CarModel("Toyota", "Tacoma S-Runner"),
};
public IReadOnlyList<CarBrandViewModel> CarModelsByBrand { get; }
int selectedIndex = UnselectedIndex;
public int SelectedIndex {
get => selectedIndex;
set {
if (selectedIndex == value) return;
if (selectedIndex != UnselectedIndex) {
CarModelsByBrand[selectedIndex].IsSelected = false;
}
selectedIndex = value;
if (selectedIndex != UnselectedIndex) {
CarModelsByBrand[selectedIndex].IsSelected = true;
}
RaisePropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
public MainViewModel() {
List<CarBrandViewModel> carBrandViewModels = new List<CarBrandViewModel>();
carBrandViewModels.Add(new CarBrandViewModel("All", allCarModels));
IEnumerable<IGrouping<string, CarModel>> groupedCarModels =
allCarModels.GroupBy(v => v.BrandName);
foreach (IGrouping<string, CarModel> carModelGroup in groupedCarModels) {
carBrandViewModels.Add(new CarBrandViewModel(carModelGroup.Key, carModelGroup));
}
CarModelsByBrand = carBrandViewModels;
}
private void RaisePropertyChanged([CallerMemberName] string caller = "") {
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) {
handler.Invoke(this, new PropertyChangedEventArgs(caller));
}
}
}
}
Bind the Tab View to View Models
In the MainPage.xaml file:
- Assign a MainViewModel instance to the ContentPage.BindingContext property.
- Bind the TabView’s ItemsSource property to the CarModelsByBrand property of the view model, 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 view displayed in the tab item’s content area.
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:dxco="clr-namespace:DevExpress.Maui.Controls;assembly=DevExpress.Maui.Controls"
xmlns:local="clr-namespace:TabView_GenerateItems"
x:Class="TabView_GenerateItems.MainPage">
<ContentPage.BindingContext>
<local:MainViewModel/>
</ContentPage.BindingContext>
<dxco:TabView ItemsSource="{Binding CarModelsByBrand}"
SelectedItemIndex="{Binding SelectedIndex, Mode=TwoWay}">
<dxco:TabView.ItemHeaderTemplate>
<Grid>
<Label Text="{Binding BrandName}"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand"/>
</Grid>
</dxco:TabView.ItemHeaderTemplate>
<dxco:TabView.ItemTemplate>
<DataTemplate>
<Grid>
<ListView ItemsSource="{Binding CarModels}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Label Padding="5" Text="{Binding FullName}"/>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</DataTemplate>
</dxco:TabView.ItemTemplate>
</dxco:TabView>
</ContentPage>
Customize the Tab View
Configure the appearance of the TabView’s header panel and header items.
Specify the minimum and maximum sizes of items, the spacing between them, and item padding:
<dxco:TabView ItemsSource="{Binding CarModelsByBrand}"
SelectedItemIndex="{Binding SelectedIndex, Mode=TwoWay}"
ItemHeaderMinWidth="90"
ItemHeaderMaxWidth="360"
HeaderPanelItemSpacing="8">
<dxco:TabView.ItemHeaderTemplate>
<DataTemplate>
<Grid>
<Label Text="{Binding BrandName}"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand"
Padding="5,0"/>
</Grid>
</DataTemplate>
</dxco:TabView.ItemHeaderTemplate>
<!-- Other Tab View settings.-->
</dxco:TabView>
Specify the header panel’s background, and assign a color to a header item depending on whether the tab is selected:
using System;
using System.Globalization;
using Microsoft.Maui.Controls;
using Microsoft.Maui.Graphics;
namespace TabView_GenerateItems {
public partial class MainPage : ContentPage {
public MainPage() {
InitializeComponent();
}
}
class IsSelectedToColorConverter : IValueConverter {
public Color DefaultColor { get; set; }
public Color SelectedColor { get; set; }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
if (!(value is bool boolValue)) return DefaultColor;
return (boolValue) ? SelectedColor : DefaultColor;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
throw new NotImplementedException();
}
}
}
<ContentPage.Resources>
<ResourceDictionary>
<local:IsSelectedToColorConverter x:Key="isSelectedToColorConverter"
DefaultColor="Transparent"
SelectedColor="#40FFFFFF"/>
</ResourceDictionary>
</ContentPage.Resources>
<dxco:TabView ItemsSource="{Binding CarModelsByBrand}"
SelectedItemIndex="{Binding SelectedIndex, Mode=TwoWay}"
ItemHeaderMinWidth="90"
ItemHeaderMaxWidth="360"
HeaderPanelItemSpacing="8"
HeaderPanelBackgroundColor="#1e88e5">
<dxco:TabView.ItemHeaderTemplate>
<DataTemplate>
<Grid>
<BoxView BackgroundColor="{Binding IsSelected,
Converter={StaticResource isSelectedToColorConverter}}"/>
<Label HorizontalOptions="Center"
VerticalOptions="CenterAndExpand"
Text="{Binding BrandName}"
Padding="5,0"/>
</Grid>
</DataTemplate>
</dxco:TabView.ItemHeaderTemplate>
<!-- Other Tab View settings.-->
</dxco:TabView>
Configure also the header panel’s shadow, hide the selected item indicator, and specify the frame corner radius, margin and text color for header items:
<dxco:TabView ItemsSource="{Binding CarModelsByBrand}"
SelectedItemIndex="{Binding SelectedIndex, Mode=TwoWay}"
ItemHeaderMinWidth="90"
ItemHeaderMaxWidth="360"
HeaderPanelItemSpacing="8"
HeaderPanelBackgroundColor="#1e88e5"
HeaderPanelShadowHeight="3"
HeaderPanelShadowRadius="3"
IsSelectedItemIndicatorVisible="False">
<dxco:TabView.ItemHeaderTemplate>
<DataTemplate>
<Grid>
<BoxView BackgroundColor="{Binding IsSelected,
Converter={StaticResource isSelectedToColorConverter}}"
Margin="0,8,0,8"
CornerRadius="25"/>
<Label HorizontalOptions="Center"
VerticalOptions="CenterAndExpand"
Text="{Binding BrandName}"
Padding="5,0"
TextColor="White"/>
</Grid>
</DataTemplate>
</dxco:TabView.ItemHeaderTemplate>
<!-- Other Tab View settings.-->
</dxco:TabView>