Class Expander
A collapsible region: a clickable Header row that shows/hides arbitrary Content below it, with an animated expand/collapse transition driven entirely by the existing themed VisualState system (see ExpansionProgress).
[ContentProperty("Content")]
public class Expander : Control, INotifyPropertyChanging, IBindingTarget, INotifyPropertyChanged, INotifyFocusChanged, IContainerLayout
- Inheritance
-
Expander
- Implements
- Inherited Members
- Extension Methods
Remarks
The clickable header is a composed ExpanderHeader (a plain ToggleButton
subtype), exposed as a named template part - PART_Header - the same way Slider
exposes PART_Thumb and SplitPane exposes PART_Divider. All of the header's
hover/press/click/checked behavior comes from ToggleButton for free; Expander
itself never handles pointer input directly.
Content lives outside Chrome, managed directly by
Expander (mirrors First/Second) - needed
because Background/etc.'s hard (Border)Chrome cast means the single
Chrome.Child slot can only hold one more visual, and that slot is already the header.
Constructors
Expander()
Initializes a new instance of the Expander class.
public Expander()
Properties
Content
Gets or sets the element shown/hidden below Header as IsExpanded toggles.
public UIElement? Content { get; set; }
Property Value
Remarks
Skipped entirely (no measure, arrange, draw, or hit-test) while fully collapsed and idle - see ExpansionProgress.
ExpansionProgress
Gets or sets how far along the expand/collapse transition Content is, from
0 (fully collapsed) to 1 (fully expanded).
public float ExpansionProgress { get; set; }
Property Value
Remarks
A themable data slot (mirrors CheckBrush/ThumbBrush's own shape) - normally driven by a themed VisualState transition on Expanded rather than set directly, though nothing prevents a custom theme (or an untemplated caller) from doing so for an instant, non-animated toggle. Drives Content's own IsVisible, and how much of it ArrangeContent() reveals.
Header
Gets or sets the element displayed in the always-visible header row, before the collapsible Content.
public UIElement? Header { get; set; }
Property Value
IsExpanded
Gets or sets a value indicating whether Content is shown.
[BindingOverride(BindingMode.TwoWay)]
public bool IsExpanded { get; set; }
Property Value
Remarks
Setting this sets/clears Expanded, which a themed
VisualState transition uses to animate ExpansionProgress from
0 to 1 (or back) - see the class remarks. Mirrors IsChecked's
own shape, and is kept two-way synchronized with the internal header toggle's own
IsChecked (clicking the header sets this; setting this also checks/unchecks
the header).
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.
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.
OnApplyTemplate()
Called once, every time Template changes (in either direction - applied, cleared, or switched to a different template), right after Chrome and every decoration/content property have been fully re-wired for the new state.
protected override void OnApplyTemplate()
Remarks
Does nothing by default. A subclass that reaches into its own visual tree by reference - e.g. Slider wiring its drag logic directly to its thumb element - overrides this to call GetTemplateChild<T>(string) and repoint that reference to whatever the new Template (or, when reverting, the fresh default Border) actually provides, instead of hardcoding it once at construction. This is what actually makes a control "template-safe": until a control overrides this appropriately, applying a custom Template to it silently orphans whatever elements its own code still holds direct references to.
OnRender(IRenderContext)
Handles custom render logic when overridden in any UIElement instance.
protected override void OnRender(IRenderContext context)
Parameters
contextIRenderContextThe context to render with.
Events
IsExpandedChanged
Occurs when IsExpanded changes.
public event EventHandler? IsExpandedChanged