Tabs
A tab bar in its default, outline, pill, segmented and underline looks, as TabStrip and TabControl.
<TabControl Classes="underline" Width="320" HorizontalAlignment="Left">
<TabItem Header="Account">
<TextBlock Text="Make changes to your account here." Margin="0,12" />
</TabItem>
<TabItem Header="Profile">
<TextBlock Text="Edit your public profile." Margin="0,12" />
</TabItem>
<TabItem Header="Settings">
<TextBlock Text="Change your preferences." Margin="0,12" />
</TabItem>
</TabControl>Usage
The theme covers TabStrip and TabStripItem (a bar on its own) and the strip of TabControl and TabItem (tabs with their content). The look is a class on the strip or control: the default look without one, or outline, pill, segmented or underline. Pill, segmented and underline show the selection with an indicator that springs to the selected tab on the UIKit.Spring.Move spring and reverses midway when the selection changes again; a pill's label fades in as the indicator slides. Sizes: xsmall, small, large (medium without one). Put icon-only on an item whose content is an icon. A tab shows a ring on keyboard focus only. Tabs can be closed, added and dragged with the uikit:Tabs attached properties (see Closing, adding and dragging).
Examples
The five looks; the last underline tab is disabled.
<StackPanel Spacing="16" HorizontalAlignment="Left">
<TabStrip Width="320" SelectedIndex="0">
<TabStripItem Content="Account" />
<TabStripItem Content="Profile" />
<TabStripItem Content="Settings" />
</TabStrip>
<TabStrip Classes="outline" Width="320" SelectedIndex="0">
<TabStripItem Content="Account" />
<TabStripItem Content="Profile" />
<TabStripItem Content="Settings" />
</TabStrip>
<TabStrip Classes="pill" Width="320" SelectedIndex="0">
<TabStripItem Content="Account" />
<TabStripItem Content="Profile" />
<TabStripItem Content="Settings" />
</TabStrip>
<TabStrip Classes="segmented" Width="320" SelectedIndex="0">
<TabStripItem Content="Account" />
<TabStripItem Content="Profile" />
<TabStripItem Content="Settings" />
</TabStrip>
<TabStrip Classes="underline" Width="320" SelectedIndex="0">
<TabStripItem Content="Account" />
<TabStripItem Content="Profile" />
<TabStripItem Content="Settings" IsEnabled="False" />
</TabStrip>
</StackPanel>The four sizes of a pill strip.
<StackPanel Spacing="16" HorizontalAlignment="Left">
<TabStrip Classes="pill xsmall" SelectedIndex="0">
<TabStripItem Content="Account" />
<TabStripItem Content="Profile" />
<TabStripItem Content="Settings" />
</TabStrip>
<TabStrip Classes="pill small" SelectedIndex="0">
<TabStripItem Content="Account" />
<TabStripItem Content="Profile" />
<TabStripItem Content="Settings" />
</TabStrip>
<TabStrip Classes="pill" SelectedIndex="0">
<TabStripItem Content="Account" />
<TabStripItem Content="Profile" />
<TabStripItem Content="Settings" />
</TabStrip>
<TabStrip Classes="pill large" SelectedIndex="0">
<TabStripItem Content="Account" />
<TabStripItem Content="Profile" />
<TabStripItem Content="Settings" />
</TabStrip>
</StackPanel>icon-only items in a pill and in a segmented strip.
<StackPanel Spacing="16" HorizontalAlignment="Left">
<TabStrip Classes="pill" SelectedIndex="0">
<TabStripItem Classes="icon-only">
<PathIcon Data="{StaticResource UIKit.Icon.Inbox}" />
</TabStripItem>
<TabStripItem Classes="icon-only">
<PathIcon Data="{StaticResource UIKit.Icon.Star}" />
</TabStripItem>
<TabStripItem Classes="icon-only">
<PathIcon Data="{StaticResource UIKit.Icon.User}" />
</TabStripItem>
</TabStrip>
<TabStrip Classes="segmented" SelectedIndex="0">
<TabStripItem Classes="icon-only">
<PathIcon Data="{StaticResource UIKit.Icon.Inbox}" />
</TabStripItem>
<TabStripItem Classes="icon-only">
<PathIcon Data="{StaticResource UIKit.Icon.Star}" />
</TabStripItem>
<TabStripItem Classes="icon-only">
<PathIcon Data="{StaticResource UIKit.Icon.User}" />
</TabStripItem>
</TabStrip>
</StackPanel>Menu, prefix and suffix
Tabs that do not fit the bar scroll sideways, and the selected tab scrolls into view. The menu class adds a caret button at the end of the bar that lists the tabs, with the selected one checked and disabled ones disabled, and selects the one that is picked. uikit:Tabs.Prefix and uikit:Tabs.Suffix put content at the start and the end of the bar, outside the scrolling tabs; with a suffix or the menu the tabs end with 12px of space. They work the same on TabStrip and TabControl.
Six tabs in a 400px bar with a menu, a prefix and a suffix.
<TabStrip Classes="menu" Width="400" VerticalAlignment="Top" SelectedIndex="0">
<uikit:Tabs.Prefix>
<StackPanel Orientation="Horizontal" Margin="4,0">
<Button Classes="ghost xsmall icon-only"><PathIcon Data="{StaticResource UIKit.Icon.ChevronLeft}" /></Button>
<Button Classes="ghost xsmall icon-only"><PathIcon Data="{StaticResource UIKit.Icon.ChevronRight}" /></Button>
</StackPanel>
</uikit:Tabs.Prefix>
<uikit:Tabs.Suffix>
<StackPanel Orientation="Horizontal" Margin="4,0">
<Button Classes="ghost xsmall icon-only"><PathIcon Data="{StaticResource UIKit.Icon.Inbox}" /></Button>
<Button Classes="ghost xsmall icon-only"><PathIcon Data="{StaticResource UIKit.Icon.Ellipsis}" /></Button>
</StackPanel>
</uikit:Tabs.Suffix>
<TabStripItem Content="Account" />
<TabStripItem Content="Profile" />
<TabStripItem Content="Settings" />
<TabStripItem Content="Billing" />
<TabStripItem Content="Notifications" IsEnabled="False" />
<TabStripItem Content="Integrations" />
</TabStrip>Closing, adding and dragging
These attached properties go on a TabStrip or a TabControl. They change the list the tabs come from: its ItemsSource, which must be a list that can change (such as an ObservableCollection), or its Items. Tabs whose list cannot change are not closed or moved.
| Attached property | Effect |
|---|---|
uikit:Tabs.Closable | A close button in each tab, as GPUI Kit's closable tab: the tab is padded px_2 and ends with a ghost xsmall Close button. On the strip it applies to every tab; on a tab, to that tab. |
uikit:Tabs.NewTabFactory | An add button after the last tab. It adds the item the function returns at the end and selects it. |
uikit:Tabs.Reorderable | Tabs are dragged along the bar to reorder them: the dragged tab follows the pointer, the others make room, and the drop moves its item. The dragged tab has the :dragging pseudo-class and is drawn over the tabs it passes: with the indicator, or, in outline and underline, with the active tab's background. A bar whose tabs overflow scrolls while the tab is held at its end. |
uikit:Tabs.DragGroup | With Reorderable, a tab dragged onto another bar of the same group moves to it. Bars in different windows exchange tabs; in a browser, only bars in the same view do. |
uikit:Tabs.DetachedWindowFactory | With a DragGroup, a tab dragged off every bar of its group moves into the window the function returns, which follows the pointer until the drop. In a window only. |
The close button raises uikit:Tabs.TabClosing on the strip before it removes the tab; set Cancel to keep it. Closing the selected tab selects the next one.
A closable tab bar whose add button opens new tabs, and whose tabs can be reordered. The last tab cannot be closed.
<TabControl Name="Tabs" Width="480" HorizontalAlignment="Left"
uikit:Tabs.Closable="True" uikit:Tabs.Reorderable="True"
Loaded="OnLoaded">
<TabItem Header="Account">
<TextBlock Text="Make changes to your account here." Margin="0,12" />
</TabItem>
<TabItem Header="Profile">
<TextBlock Text="Edit your public profile." Margin="0,12" />
</TabItem>
<TabItem Header="Settings">
<TextBlock Text="Change your preferences." Margin="0,12" />
</TabItem>
</TabControl>Tabs.SetNewTabFactory(tabs, () => new TabItem { Header = $"Tab {++added}", Content = new TextBlock { Text = "A new tab." } });
Tabs.AddTabClosingHandler(tabs, (_, args) => args.Cancel = tabs.ItemCount == 1);Tabs reordered by dragging.
<TabStrip Classes="pill" SelectedIndex="0" uikit:Tabs.Reorderable="True">
<TabStripItem Content="Account" />
<TabStripItem Content="Profile" />
<TabStripItem Content="Settings" />
<TabStripItem Content="Billing" />
<TabStripItem Content="Notifications" />
</TabStrip>Two bars of one group: drag a tab from one to the other.
<StackPanel Spacing="16" HorizontalAlignment="Left">
<TabStrip Width="360" SelectedIndex="0"
uikit:Tabs.Reorderable="True" uikit:Tabs.DragGroup="files">
<TabStripItem Content="index.html" />
<TabStripItem Content="style.css" />
<TabStripItem Content="app.js" />
</TabStrip>
<TabStrip Width="360" SelectedIndex="0"
uikit:Tabs.Reorderable="True" uikit:Tabs.DragGroup="files">
<TabStripItem Content="README.md" />
<TabStripItem Content="LICENSE" />
</TabStrip>
</StackPanel>Dragging tabs out into windows
uikit:Tabs.DetachedWindowFactory gets the item of the tab being dragged off and returns the window it moves to. The window must hold a TabStrip or TabControl of the same group, empty and reorderable; the tab is put in it, and the window is shown under the pointer and follows it. Dragged over a bar of the group, the tab moves back and the window is hidden. A window made this way closes when its last tab is dragged out or closed, and dragging its only tab moves the window itself. Windows the app opens itself stay open when their bars empty.
Tabs.SetDetachedWindowFactory(documents, _ => NewDocumentWindow());
static Window NewDocumentWindow()
{
var tabs = new TabControl();
Tabs.SetClosable(tabs, true);
Tabs.SetReorderable(tabs, true);
Tabs.SetDragGroup(tabs, "documents");
Tabs.SetDetachedWindowFactory(tabs, _ => NewDocumentWindow());
return new Window { Title = "Documents", Width = 640, Height = 400, Content = tabs };
}A page has no windows, so this does not run in the live demos.