Class TabControl
A row of clickable tab headers, each showing/hiding its own Content - realizes each TabItem's header as a SelectorItem via the inherited SelectingItemsControl machinery, but never scrolls: unlike ListBox/ WrapGrid, this control doesn't participate in IVirtualizingScrollInfo's viewport-driven realize pipeline at all - it eagerly realizes every header itself, since a handful of tab headers never need virtualizing.
public class TabControl : SelectingItemsControl, INotifyPropertyChanging, IBindingTarget, INotifyPropertyChanged, INotifyFocusChanged, IContainerLayout, IVirtualizingScrollInfo
- Inheritance
-
TabControl
- Implements
- Inherited Members
- Extension Methods
Remarks
ItemsSource holds TabItem objects. CreateContainer(DataTemplate, object) is overridden to build each header's StackPanel+Icon+ContentPresenter tree directly in code from Header/Icon, rather than going through ItemTemplate/markup - this control has no dependency on any caller-supplied ItemTemplate, and doesn't offer one as a header-customization hook (a caller who wants a different header layout composes a custom Header instead).
Chrome stays the plain decorative Border spanning this control's whole ActualBounds (the same convention every ItemsControl subclass already follows - realized items are extra visual children, not Chrome's child). A second, separately-managed ContentPresenter child (outside Chrome, mirroring Content's own "extra managed child" shape) shows the selected Content, below the header row.
Constructors
TabControl()
Initializes a new instance of the TabControl class.
public TabControl()
Methods
ArrangeContent()
Handles custom arrange logic when overridden in any UIElement instance.
protected override void ArrangeContent()
Remarks
This method is called during the arrange pass to position and size the element's content within its final bounds. The element's position and size are already determined by the layout system and available in the ActualBounds property.
When implementing this method:
- Position child elements within the available space;
- Respect the element's padding when positioning content;
- Handle any content-specific layout requirements;
- Call Arrange() on child elements if needed.
AttachContainer(ItemContainer, int)
Stamps a freshly realized container's IsSelected from
SelectedIndex, on top of the base ItemsControl wiring - the case the
SelectedIndex setter's own stamp (see its own realizedContainers lookup) can't
reach: index selected before it's ever realized, or re-realized later after a
pool-and-reuse cycle. Selector overrides this again itself, to route into its popup
host instead of this base's default parenting.
protected override void AttachContainer(ItemContainer container, int index)
Parameters
containerItemContainerThe freshly realized container.
indexintThe item index
containerwas realized for.
Remarks
Also wires up click-to-select: subscribes Tapped (handled by
Container_Tapped(object, EventArgs)) and records index in ContainerIndices
so the handler can look it back up. This is inert for Selector and its subclasses
(Dropdown, ComboBox), since their own AttachContainer override
doesn't call this base implementation.
CreateContainer(DataTemplate, object)
Creates the header container for a tab.
protected override ItemContainer CreateContainer(DataTemplate template, object item)
Parameters
templateDataTemplateUnused - see the class remarks.
itemobjectThe TabItem the new header is being realized for.
Returns
- ItemContainer
A freshly built SelectorItem hosting the header's visual tree.
Remarks
Builds the header's visual tree directly in code - a StackPanel holding the source
Icon (when set) followed by a ContentPresenter wrapping
Header - instead of going through template (see the class
remarks for why template is otherwise unused here).
GetVisualChildren()
Enumerates this element's immediate visual children, in paint order (back-to-front), for hit-testing and tree traversal.
protected override IEnumerable<UIElement> GetVisualChildren()
Returns
- IEnumerable<UIElement>
The element's immediate children. The base implementation yields none (a leaf element).
Remarks
Container elements (e.g. Panel, Border) override this to expose their children, in the exact order they're drawn in — HitTest(Vector2) depends on this matching actual paint order, or clicks will target the wrong, visually-obscured element.
MeasureContent()
Handles custom measure logic when overridden in any UIElement instance.
protected override Size MeasureContent()
Returns
Remarks
This method is called during the measure pass to determine the desired size of the element. The returned size should represent the natural size of the element's content, without considering any constraints, margins, or padding.
When implementing this method:
- Return the natural size needed to display the content;
- Do not include padding, margins, or borders in the returned size;
- Consider the content's actual dimensions (e.g., text length, image size);
- Return Empty if the element has no content.
OnAttached()
Raises the Attached event.
protected override void OnAttached()
Remarks
Propagates Canvas to every element in GetVisualChildren() - this is what makes attaching a whole pre-built subtree (the common pattern: build children, then attach the root once via Add(UIElement)) reach every descendant, not just the immediate root. Each child's own Canvas setter recurses into its children the same way, so this walks the full subtree regardless of depth. Container-specific child-add paths (e.g. OnChildAdded(object?, GenericEventArgs<UIElement>)) still assign Canvas directly for children added after the parent is already attached - this cascade is what covers everything built beforehand.
OnDetached()
Raises the Detached event.
protected override void OnDetached()
Remarks
Propagates null to every element in GetVisualChildren(), mirroring OnAttached() - see its remarks.
OnRender(IRenderContext)
Handles custom render logic when overridden in any UIElement instance.
protected override void OnRender(IRenderContext context)
Parameters
contextIRenderContextThe context to render with.
OnSelectionChanged()
Called after SelectedIndex/SelectedItem have been updated, before the realized-container stamp and the public SelectionChanged event - empty by default. A subclass with derived state of its own that depends on the new selection (e.g. Selector's SelectedValue) overrides this to refresh it, rather than reacting to its own SelectionChanged subscription (which would depend on subscriber-ordering relative to any external subscriber this control's own consumer adds).
protected override void OnSelectionChanged()