Skip to main content

TabbedPage

TabbedPage displays a collection of pages through a tab strip. Each child Page becomes one tab. The tab header is built from the page's Header property for the label and its Icon property for the icon. The tab strip position adapts to the target platform by default.

TabbedPage extends SelectingMultiPage, which extends MultiPage. This inheritance chain provides:

  • Pages collection
  • ItemsSource
  • PageTemplate
  • SelectedIndex
  • SelectedPage
  • CurrentPage
  • SelectionChanged event
  • PagesChanged event
  • CurrentPageChanged event

Useful Properties

You will probably use these properties most often:

PropertyTypeDefaultDescription
PagesIEnumerable<Page>?nullThe collection of child pages. This is the XAML content property. Supports any IEnumerable<Page>, including observable collections.
ItemsSourceIEnumerable?nullView-model collection. When set, takes precedence over Pages as the item source. Use together with PageTemplate to convert each item into a Page.
PageTemplateIDataTemplate?nullData template used to generate Page instances when the source contains data objects rather than pages directly.
TabPlacementTabPlacementAutoPosition of the tab strip. See the TabPlacement values table below.
IsKeyboardNavigationEnabledbooltrueEnables arrow keys and Ctrl+Tab, Ctrl+Shift+Tab to switch tabs.
IsGestureEnabledboolfalseEnables swipe gestures to switch tabs. Off by default.
PageTransitionIPageTransition?nullTransition animation played when switching between tabs.
IndicatorTemplateIDataTemplate?nullData template used to render the selection indicator on each tab item.
SelectedIndexint-1Zero-based index of the currently selected tab.
SelectedPagePage?nullRead-only. The currently selected page.

TabPlacement Values

ValueDescription
AutoPlatform-adaptive. Resolves to Bottom on Android and iOS, and Top on all other platforms.
TopTab strip at the top of the content area.
BottomTab strip at the bottom of the content area.
LeftTab strip along the left side of the content area.
RightTab strip along the right side of the content area.

Attached Property

Set on individual child Page instances to control tab enablement.

PropertyTypeDefaultDescription
TabbedPage.IsTabEnabledbooltrueWhen false, the tab is visually disabled and is skipped during keyboard and swipe navigation. If the currently selected tab is disabled, selection moves automatically to the nearest enabled tab.

Tab Icons

The Icon property on each child Page controls the icon in the tab header. TabbedPage passes Icon and IconTemplate from the page to the underlying TabItem, so the standard Content/ContentTemplate rendering applies.

When no IconTemplate is set, the default handling accepts these values:

  • Geometry: rendered as a path shape.
  • PathIcon: used directly as a one-to-one control mapping.
  • DrawingImage with a GeometryDrawing: the geometry is extracted.
  • string: parsed as an SVG path geometry string.
  • IImage: rendered as a bitmap image.

Alternatively, set IconTemplate on the page to control how any icon data is rendered:

<ContentPage Header="Home">
<ContentPage.Icon>
<StreamGeometry>M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z</StreamGeometry>
</ContentPage.Icon>
<ContentPage.IconTemplate>
<DataTemplate DataType="Geometry">
<PathIcon Data="{Binding}" Width="20" Height="20" />
</DataTemplate>
</ContentPage.IconTemplate>
<!-- content -->
</ContentPage>

Events

EventDescription
SelectionChangedRaised when the selected tab changes. Provides PreviousPage and CurrentPage.
CurrentPageChangedRaised when CurrentPage changes.
PagesChangedRaised when the Pages collection changes.

Navigation lifecycle events (NavigatedTo, Navigating, NavigatedFrom) fire on each child Page as the active tab changes.

Keyboard Navigation

When IsKeyboardNavigationEnabled is true:

  • Horizontal tabs (Top, Bottom): left and right arrow keys switch tabs. RTL layouts reverse the direction.
  • Vertical tabs (Left, Right): up and down arrow keys switch tabs.
  • Ctrl+Tab moves to the next enabled tab. Ctrl+Shift+Tab moves to the previous.

Disabled tabs (via IsTabEnabled = false) are always skipped.

Examples

Basic TabbedPage in XAML

<TabbedPage xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MyApp.MainTabs">

<ContentPage Header="Home">
<TextBlock Text="Home content"
VerticalAlignment="Center"
HorizontalAlignment="Center" />
</ContentPage>

<ContentPage Header="Profile">
<TextBlock Text="Profile content"
VerticalAlignment="Center"
HorizontalAlignment="Center" />
</ContentPage>

<ContentPage Header="Settings">
<TextBlock Text="Settings content"
VerticalAlignment="Center"
HorizontalAlignment="Center" />
</ContentPage>

</TabbedPage>

TabbedPage in Code

var tabbedPage = new TabbedPage
{
TabPlacement = TabPlacement.Bottom,
Pages = new AvaloniaList<Page>
{
new ContentPage { Header = "Home", Content = homeView },
new ContentPage { Header = "Profile", Content = profileView },
new ContentPage { Header = "Settings", Content = settingsView }
}
};

window.Page = tabbedPage;

Tab Icons

Set the Icon property on each child page:

<ContentPage Header="Home">
<ContentPage.Icon>
<StreamGeometry>M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z</StreamGeometry>
</ContentPage.Icon>
<!-- content -->
</ContentPage>

<ContentPage Header="Profile">
<ContentPage.Icon>
<StreamGeometry>M12,4A4,4 0 0,1 16,8A4,4 0 0,1 12,12A4,4 0 0,1 8,8A4,4 0 0,1 12,4M12,14C16.42,14 20,15.79 20,18V20H4V18C4,15.79 7.58,14 12,14Z</StreamGeometry>
</ContentPage.Icon>
<!-- content -->
</ContentPage>
var homePage = new ContentPage
{
Header = "Home",
Icon = Geometry.Parse("M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"),
Content = homeView
};

Controlling TabPlacement

// Explicit bottom tabs (default on mobile)
tabbedPage.TabPlacement = TabPlacement.Bottom;

// Sidebar tabs on desktop
tabbedPage.TabPlacement = TabPlacement.Left;

In XAML:

<TabbedPage TabPlacement="Left">
<!-- pages -->
</TabbedPage>

Dynamic Tab Management

Add and remove pages at runtime. The tab strip updates automatically:

var pages = new AvaloniaList<Page>();
tabbedPage.Pages = pages;

// Add a tab
pages.Add(new ContentPage
{
Header = "Reports",
Content = new ReportsView()
});

// Remove a tab
if (pages.Count > 1)
pages.RemoveAt(pages.Count - 1);

Enabling Swipe Gestures

tabbedPage.IsGestureEnabled = true;

Swipe left or right on horizontal tab strips. Swipe up or down on vertical tab strips.

Disabling a Tab

TabbedPage.SetIsTabEnabled(settingsPage, false);

In XAML:

<ContentPage TabbedPage.IsTabEnabled="False" Header="Settings">
<!-- ... -->
</ContentPage>

A disabled tab is skipped during keyboard and swipe navigation. If it is currently selected when disabled, selection moves to the nearest enabled tab automatically.

Responding to Selection Changes

tabbedPage.SelectionChanged += (sender, e) =>
{
Console.WriteLine($"Switched from {e.PreviousPage?.Header} to {e.CurrentPage?.Header}");
};

Use the navigation lifecycle events on child pages to react when a tab becomes active:

public partial class ProfilePage : ContentPage
{
protected override void OnNavigatedTo(NavigatedToEventArgs args)
{
base.OnNavigatedTo(args);
// This tab is now active, refresh data.
_ = LoadProfileAsync();
}
}

Programmatic Tab Selection

// Select by index
tabbedPage.SelectedIndex = 2;

Independent Navigation Stack per Tab

Each tab can host its own NavigationPage to maintain an independent navigation stack. Navigation inside one tab does not affect the other tabs. There is no special integration between TabbedPage and NavigationPage: TabbedPage treats a NavigationPage child the same as any other Page, reading its Header and Icon for the tab strip.

<TabbedPage xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MyApp.MainShell">

<!-- Home tab: full navigation stack -->
<NavigationPage Header="Home">
<local:HomeRootPage />
</NavigationPage>

<!-- Explore tab: independent stack -->
<NavigationPage Header="Explore">
<local:ExploreRootPage />
</NavigationPage>

<!-- Profile tab: single page, no nav stack needed -->
<ContentPage Header="Profile">
<local:ProfileView />
</ContentPage>

</TabbedPage>
// Navigate within a specific tab's stack from that tab's ContentPage:
private async void OnItemClick(object? sender, RoutedEventArgs e)
{
await Navigation.PushAsync(new ItemDetailPage());
}

Each NavigationPage maintains its own back stack independently. Switching tabs does not reset the stack.

Data-Driven Tabs with ItemsSource

Bind a view-model collection to ItemsSource and use PageTemplate to generate the pages:

<TabbedPage xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:vm="clr-namespace:MyApp.ViewModels"
x:Class="MyApp.MainTabs"
ItemsSource="{Binding Sections}">

<TabbedPage.PageTemplate>
<DataTemplate x:DataType="vm:SectionViewModel">
<ContentPage Header="{Binding Title}">
<TextBlock Text="{Binding Description}"
VerticalAlignment="Center"
HorizontalAlignment="Center" />
</ContentPage>
</DataTemplate>
</TabbedPage.PageTemplate>

</TabbedPage>

TabbedPage Inside a DrawerPage

Combine a drawer navigation menu with tabbed content in the detail area:

<DrawerPage xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MyApp.Shell">

<DrawerPage.Drawer>
<ContentPage Header="Menu">
<StackPanel Margin="12" Spacing="8">
<Button Content="Dashboard" Click="OnDashboardClick" />
<Button Content="Reports" Click="OnReportsClick" />
</StackPanel>
</ContentPage>
</DrawerPage.Drawer>

<TabbedPage>
<ContentPage Header="Overview">
<local:OverviewView />
</ContentPage>
<ContentPage Header="Analytics">
<local:AnalyticsView />
</ContentPage>
</TabbedPage>

</DrawerPage>

See also