Introducing the new .NET MAUI Expander view


Why another expander control?
There are many free expander controls out there but none of them is built to leverage the .NET MAUI layout system properly.
On top of that, they all include some kind of accordion-like style which may not be what you’re looking for in your UI/UX.
Nalu ExpanderViewBox
is a simple container which can eventually constrain its content and animate size changes to provide a nice looking UI with a simple and easy to use API.
Animating size changes
Sometimes we simply have a list of items which eventually change over time and we’re using a BindableLayout
to show them.
Wouldn’t it be nice to have our app automatically animate the containing card to fit its new content size?
With ExpanderViewBox
this is super simple, you can simply set CollapsedHeight
and/or CollapsedWidth
to +Infinity
: this will not constrain the related dimension but it will enable size-change animations.
<Grid>
<Border StrokeShape="RoundRectangle 12"
VerticalOptions="Center"
HorizontalOptions="Center"
Padding="8"
BackgroundColor="Coral">
<nalu:ExpanderViewBox CollapsedHeight="+Infinity"
CollapsedWidth="+Infinity">
<toolkit:UniformItemsLayout MaxColumns="2">
<Button Text="Add button!"
Margin="8"
Clicked="Button_OnClicked"/>
</toolkit:UniformItemsLayout>
</nalu:ExpanderViewBox>
</Border>
</Grid>
How can I create a collapsible section?
Sometimes we don’t know ahead of times the size of the content, so we may want to constrain the content to a given size and eventually show a button in case the content was collapsed.
In this case we can constrain the CollapsedHeight
to a given size and show the button only when CanCollapse
is true
.
<VerticalStackLayout>
<nalu:ExpanderViewBox x:Name="TheExpander"
CollapsedHeight="200">
<VerticalStackLayout VerticalOptions="Start">
<Label Text="List of my friends" />
<!--
The height of this stack layout depends on the number of friends,
so the height will change at runtime and
may or may not exceed the collapsed height.
-->
<VerticalStackLayout BindableLayout.ItemsSource="{Binding Friends}"
BindableLayout.ItemTemplate="{StaticResource FriendTemplate}" />
</VerticalStackLayout>
</nalu:ExpanderViewBox>
<!--
This button is only visible when the expander's content
is bigger than the collapsed size.
-->
<Button Text="Toggle expanded"
Clicked="ToggleExpended"
IsVisible="{Binding Path=CanCollapse,
Source={x:Reference TheExpander},
x:DataType=nalu:ExpanderViewBox}"/>
</VerticalStackLayout>
private void ToggleExpended(object? sender, EventArgs e)
{
TheExpander.IsExpanded = !TheExpander.IsExpanded;
}
How can I build an accordion?
Build an accordion control is easy, you can simply implement many collapsible sections as I just showed you and toggle the IsExpanded
flag accordingly.
The only point of attention is setting CollapsedHeight="0"
so that it completely hides the content when the section is not expanded.
<VerticalStackLayout>
<Button Text="Section one" Clicked="ToggleAccordion" />
<nalu:ExpanderViewBox CollapsedHeight="0">
<!-- content here -->
</nalu:ExpanderViewBox>
<Button Text="Section two" Clicked="ToggleAccordion" />
<nalu:ExpanderViewBox CollapsedHeight="0">
<!-- content here -->
</nalu:ExpanderViewBox>
<Button Text="Section N" Clicked="ToggleAccordion" />
<nalu:ExpanderViewBox CollapsedHeight="0">
<!-- content here -->
</nalu:ExpanderViewBox>
</VerticalStackLayout>
private void ToggleAccordion(object? sender, EventArgs e)
{
if (sender is View { Parent: VerticalStackLayout layout } button)
{
var index = layout.IndexOf(button);
var expander = layout[index + 1];
foreach (var child in layout)
{
if (child is ExpanderViewBox expanderViewBox)
{
expanderViewBox.IsExpanded = expanderViewBox == expander;
}
}
}
}
Expanders and CollectionView
s
Using collapsible content within a CollectionView
hides a bit of complexity given different platforms have different implementations of virtualization logic and that may trigger resize animations while scrolling due to views being reused.
ExpanderViewBox
handles this pretty well (starting from 9.0.1
version) thanks to its default AnimationBehavior
which avoids animations when BindingContext
changes.
You can simply hook the expander state to your view model with IsExpanded=”{BindingContext IsExpanded}”
, and then use it like I’ve shown above.
<CollectionView ItemsSource="{Binding Items}"
x:Name="TheCollectionView">
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="pageModels:MyItem">
<nalu:ViewBox Padding="16,8">
<Border StrokeShape="RoundRectangle 12"
BackgroundColor="Coral">
<VerticalStackLayout>
<nalu:ExpanderViewBox CollapsedHeight="126"
IsExpanded="{Binding IsExpanded}">
<VerticalStackLayout Padding="16,8">
<Label Text="{Binding Name}" FontSize="Large"/>
<Label Text="{Binding Description}"/>
</VerticalStackLayout>
</nalu:ExpanderViewBox>
<Button Text="Toggle"
BackgroundColor="Coral"
TextColor="Black"
Clicked="ScrollToMe"
Command="{Binding ToggleCommand}"/>
</VerticalStackLayout>
</Border>
</nalu:ViewBox>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
private void ScrollToMe(object? sender, EventArgs e)
{
TheCollectionView.ScrollTo((sender as BindableObject)?.BindingContext);
}
To summarize
In conclusion, the new Nalu ExpanderViewBox
, offers a versatile and efficient solution for creating dynamic and interactive UI components. By leveraging the .NET MAUI layout system, it provides seamless size-change animations and customizable collapsible sections without the constraints of traditional accordion styles.
Whether you're looking to animate content size changes or build an accordion, the ExpanderViewBox
simplifies the process with its intuitive API, enhancing both the functionality and aesthetics of your application.
Subscribe to my newsletter
Read articles from Alberto Aldegheri directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
