Skip to content
Shiny Controls v1.0 - The Ultra Control Suite for .NET MAUI & BlazorO...M...G!

Expander

A header you tap and content that animates in beneath — or above — it. Use it for FAQ lists, settings groups, filter panels, “show advanced options”, or anywhere an IsVisible / @if toggle would otherwise appear.

Stack several inside an Accordion and they agree on how many may be open at once.

  • NuGet downloads for Shiny.Maui.Controls
  • NuGet downloads for Shiny.Blazor.Controls
Frameworks
.NET MAUI
Blazor
  • Header text plus an optional detail line, or a header view / template of your own
  • Combinable reveal animations — Fade, Slide and Height are flags, not alternatives
  • SlideFrom aims the slide at Top, Bottom, Left or Right
  • ExpandDirection opens the panel downwards (header on top) or upwards (content above the header)
  • Two-way IsExpanded, with cancelable Expanding / Collapsing events
  • LoadContentOnDemand holds the body back until the first open
  • Rotating or swapping indicator, on either edge, or one of your own
  • Border colour, thickness, radius, elevation, header and content fills, paddings and a separator — each falling back to the active theme when left alone
  • Keyboard and screen-reader ready on Blazor (role="button", aria-expanded, aria-controls, Enter and Space, an inert collapsed panel)
<shiny:Expander HeaderText="Shipping" HeaderDetail="Arrives Tuesday">
<VerticalStackLayout Spacing="4">
<Label Text="123 Fake Street" />
<Label Text="Springfield" />
</VerticalStackLayout>
</shiny:Expander>

Expander derives from Grid and Content is its XAML content property, so the body goes straight between the tags.

Animation is a flags enum, so the three effects combine. The default is Height | Fade.

Flag What it does
Height Grows and shrinks the panel between zero and the content’s size, so everything below it moves with the reveal. This is what makes an accordion read as an accordion.
Fade Cross-fades the content.
Slide Translates the content in from the edge named by SlideFrom.
None Snaps open and closed.
<shiny:Expander HeaderText="Everything at once"
Animation="Height,Slide,Fade"
SlideFrom="Top"
AnimationDuration="250"
AnimationEasing="CubicOut">
<Label Text="…" />
</shiny:Expander>

SlideFrom does nothing unless the Slide flag is on. AnimationDuration="0" snaps regardless of the flags.

ExpandDirection="Up" puts the content above the header — what a panel pinned to the bottom of a page wants. The indicator turns the other way to match, so it always points at where the content actually is.

<shiny:Expander HeaderText="Filters"
ExpandDirection="Up"
SlideFrom="Bottom"
Animation="Height,Slide,Fade">
<Label Text="…" />
</shiny:Expander>

HeaderText and HeaderDetail give you a title with an optional second line. For anything else, set Header to a view or HeaderTemplate to a DataTemplate — the indicator and the tap target are still handled for you.

<shiny:Expander>
<shiny:Expander.Header>
<HorizontalStackLayout Spacing="10">
<shiny:PillView Text="LIVE" Type="Critical" />
<Label Text="Custom header" FontAttributes="Bold" VerticalOptions="Center" />
</HorizontalStackLayout>
</shiny:Expander.Header>
<Label Text="…" />
</shiny:Expander>

LoadContentOnDemand="True" holds ContentTemplate back until the expander is first opened, and then keeps what it built. A list of twenty expanders over twenty forms builds one form rather than twenty.

<shiny:Expander HeaderText="Report" LoadContentOnDemand="True">
<shiny:Expander.ContentTemplate>
<DataTemplate>
<local:ExpensiveReportView />
</DataTemplate>
</shiny:Expander.ContentTemplate>
</shiny:Expander>
Property Values
IndicatorMode Rotate (default — one glyph, turned a quarter turn), Swap, None
IndicatorPosition End (default), Start
CollapsedIcon / ExpandedIcon Glyphs; default and
IndicatorColor, IndicatorSize Colour and font size
IndicatorView A view of your own — still rotates under Rotate

Setting AutomationId on a MAUI Expander also names its header row <AutomationId>_Header — the suffix is the constant Expander.HeaderAutomationIdSuffix. The tap gesture lives on that row rather than on the expander itself, so automation driving the expander’s own id finds nothing to tap:

maui devflow ui tap --automationId BasicExpander_Header

On Blazor the header is a focusable role="button" element, so a click or an Enter/Space key press on it works without anything extra.

Every one of these falls back to a theme token when you leave it alone, so a theme swap reaches inside the control:

BorderColor, BorderThickness, CornerRadius, HasShadow, HeaderBackgroundColor, HeaderTextColor, HeaderDetailColor, HeaderFontSize, HeaderFontFamily, HeaderFontAttributes, HeaderPadding, HeaderHeight, ContentBackgroundColor, ContentPadding, ShowSeparator, SeparatorColor.

<shiny:Expander HeaderText="Coloured border"
BorderColor="#7C3AED"
BorderThickness="2"
CornerRadius="18"
HeaderBackgroundColor="#EDE9FE"
HeaderTextColor="#4C1D95">
<Label Text="…" />
</shiny:Expander>

IsExpanded is two-way. Expand(), Collapse() and Toggle() drive it from code.

Event When
Expanding / Collapsing Before the change; set e.Cancel to abandon it
Expanded / Collapsed After the change
ExpandedChanged After either change
ExpandedChangedCommand Invoked with the new IsExpanded value
expander.Expanding += (_, e) => e.Cancel = !this.form.IsValid;

IsToggleEnabled="False" stops the header responding to taps without disabling the control. CanCollapse="False" lets an open expander refuse to close on a tap — an Accordion sets it for you when closing the last open item would leave nothing open. It guards taps only: setting IsExpanded in code still wins.