Took these lines from WinUI 3 expander class documentation.
Use an Expander to focus on the most important content when display space is limited. The Expander control lets you show or hide less important content that's related to a piece of primary content that's always visible. Items contained in the Header are always visible. The user can expand and collapse the content area, where secondary content is displayed, by interacting with the header. When the content area is expanded, it pushes other UI elements out of the way; it does not overlay other UI. The Expander can expand upwards or downwards.
You can create a custom control that derives from the Expander
control, restyle it wrapping the content with a Popup.
Expander
's style at the generic.xaml file.Expander
's content Background
is semi-transparent by default. Here I replaced it with AcrylicInAppFillColorDefaultBrush
.VisualState
animations.public partial class PopupExpander : Expander
{
public PopupExpander()
{
this.Expanding += PopupExpander_Expanding;
this.Collapsed += PopupExpander_Collapsed;
}
private Popup? ContentPopup { get; set; }
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
ContentPopup = GetTemplateChild(nameof(ContentPopup)) as Popup;
}
private void PopupExpander_Expanding(Expander sender, ExpanderExpandingEventArgs args)
{
if (ContentPopup is null)
{
return;
}
ContentPopup.VerticalOffset = this.ActualHeight;
ContentPopup.IsOpen = true;
if (ContentPopup.Child is not FrameworkElement child)
{
return;
}
child.Width = this.ActualWidth;
child.MaxWidth = this.ActualWidth;
}
private void PopupExpander_Collapsed(Expander sender, ExpanderCollapsedEventArgs args)
{
if (ContentPopup is null)
{
return;
}
ContentPopup.IsOpen = false;
}
}
<Style x:Key="PopupExpanderStyle"
TargetType="local:PopupExpander">
<!--
We won't make this control tab stoppable, since the tab focus should go
to the toggle button. For this logic, several things were made:
1. Expander::OnApplyTemplate - We set the toggle button's events source to the expander's. This will announce expander properties
(expand/collapse) changes to narrator. Without this, narrator would announce the toggle button's "on/off" logic
instead of the "expand/collapse" logic.
2. ExpanderAutomationPeer::GetChildrenCore - Because of 1., we need to override GetChildrenCore to NOT include the toggle button.
If we don't do this, we create a weird dependency cycle between the toggle button and the expander.
3. ExpanderAutomationPeer::GetPeerFromPointCore Finally, when using narrator with a touch screen, this was overriden as well, to
focus the toggle button programmatically to sync the narrator focus with the keyboard focus.
Without this override, the narrator user that focuses the expander on a touch screen will see that pressing "Tab"
doesn't work how they would expect.
-->
<Setter Property="IsTabStop" Value="False" />
<Setter Property="Background" Value="{ThemeResource ExpanderContentBackground}" />
<Setter Property="BackgroundSizing" Value="InnerBorderEdge" />
<Setter Property="MinWidth" Value="{ThemeResource FlyoutThemeMinWidth}" />
<Setter Property="MinHeight" Value="{StaticResource ExpanderMinHeight}" />
<Setter Property="BorderThickness" Value="{ThemeResource ExpanderContentDownBorderThickness}" />
<Setter Property="BorderBrush" Value="{ThemeResource ExpanderContentBorderBrush}" />
<Setter Property="Padding" Value="{StaticResource ExpanderContentPadding}" />
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="CornerRadius" Value="{ThemeResource ControlCornerRadius}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Expander">
<Grid
MinWidth="{TemplateBinding MinWidth}"
MaxWidth="{TemplateBinding MaxWidth}">
<Grid.RowDefinitions>
<RowDefinition x:Name="Row0"
Height="Auto" />
<RowDefinition x:Name="Row1"
Height="*" />
</Grid.RowDefinitions>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="ExpandStates">
<VisualState x:Name="ExpandUp">
<VisualState.Setters>
<Setter Target="ExpanderHeader.CornerRadius" Value="{Binding CornerRadius, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource BottomCornerRadiusFilterConverter}}" />
</VisualState.Setters>
<VisualState.Storyboard>
<Storyboard>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetName="ExpanderContent"
Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame
KeyTime="0"
Value="Visible" />
</ObjectAnimationUsingKeyFrames>
<!--<DoubleAnimationUsingKeyFrames
Storyboard.TargetName="ExpanderContent"
Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.TranslateY)">
<DiscreteDoubleKeyFrame
KeyTime="0"
Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.ContentHeight}" />
<SplineDoubleKeyFrame
KeySpline="0.0, 0.0, 0.0, 1.0"
KeyTime="0:0:0.333"
Value="0" />
</DoubleAnimationUsingKeyFrames>-->
</Storyboard>
</VisualState.Storyboard>
</VisualState>
<VisualState x:Name="CollapseDown">
<VisualState.Storyboard>
<Storyboard>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetName="ExpanderContent"
Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame
KeyTime="0:0:0.2"
Value="Collapsed" />
</ObjectAnimationUsingKeyFrames>
<!--<DoubleAnimationUsingKeyFrames
Storyboard.TargetName="ExpanderContent"
Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.TranslateY)">
<DiscreteDoubleKeyFrame
KeyTime="0"
Value="0" />
<SplineDoubleKeyFrame
KeySpline="1.0, 1.0, 0.0, 1.0"
KeyTime="0:0:0.167"
Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.ContentHeight}" />
</DoubleAnimationUsingKeyFrames>-->
</Storyboard>
</VisualState.Storyboard>
</VisualState>
<VisualState x:Name="ExpandDown">
<VisualState.Setters>
<Setter Target="ExpanderHeader.CornerRadius" Value="{Binding CornerRadius, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource TopCornerRadiusFilterConverter}}" />
</VisualState.Setters>
<VisualState.Storyboard>
<Storyboard>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetName="ExpanderContent"
Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame
KeyTime="0"
Value="Visible" />
</ObjectAnimationUsingKeyFrames>
<!--<DoubleAnimationUsingKeyFrames
Storyboard.TargetName="ExpanderContent"
Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.TranslateY)">
<DiscreteDoubleKeyFrame
KeyTime="0"
Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.NegativeContentHeight}" />
<SplineDoubleKeyFrame
KeySpline="0.0, 0.0, 0.0, 1.0"
KeyTime="0:0:0.333"
Value="0" />
</DoubleAnimationUsingKeyFrames>-->
</Storyboard>
</VisualState.Storyboard>
</VisualState>
<VisualState x:Name="CollapseUp">
<VisualState.Storyboard>
<Storyboard>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetName="ExpanderContent"
Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame
KeyTime="0:0:0.167"
Value="Collapsed" />
</ObjectAnimationUsingKeyFrames>
<!--<DoubleAnimationUsingKeyFrames
Storyboard.TargetName="ExpanderContent"
Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.TranslateY)">
<DiscreteDoubleKeyFrame
KeyTime="0"
Value="0" />
<SplineDoubleKeyFrame
KeySpline="1.0, 1.0, 0.0, 1.0"
KeyTime="0:0:0.167"
Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.NegativeContentHeight}" />
</DoubleAnimationUsingKeyFrames>-->
</Storyboard>
</VisualState.Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="ExpandDirectionStates">
<VisualState x:Name="Down" />
<VisualState x:Name="Up">
<VisualState.Setters>
<Setter Target="ExpanderHeader.Style" Value="{StaticResource ExpanderHeaderUpStyle}" />
<Setter Target="ExpanderContent.BorderThickness" Value="{StaticResource ExpanderContentUpBorderThickness}" />
<Setter Target="ExpanderContent.CornerRadius" Value="{Binding CornerRadius, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource TopCornerRadiusFilterConverter}}" />
<Setter Target="ExpanderHeader.(Grid.Row)" Value="1" />
<Setter Target="ExpanderContentClip.(Grid.Row)" Value="0" />
<Setter Target="Row0.Height" Value="*" />
<Setter Target="Row1.Height" Value="Auto" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<ToggleButton x:Name="ExpanderHeader"
MinHeight="{TemplateBinding MinHeight}"
Padding="{StaticResource ExpanderHeaderPadding}"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="{StaticResource ExpanderHeaderHorizontalContentAlignment}"
VerticalContentAlignment="{StaticResource ExpanderHeaderVerticalContentAlignment}"
AutomationProperties.AutomationId="ExpanderToggleButton"
Background="{ThemeResource ExpanderHeaderBackground}"
BackgroundSizing="{TemplateBinding BackgroundSizing}"
BorderBrush="{ThemeResource ExpanderHeaderBorderBrush}"
BorderThickness="{ThemeResource ExpanderHeaderBorderThickness}"
Content="{TemplateBinding Header}"
ContentTemplate="{TemplateBinding HeaderTemplate}"
ContentTemplateSelector="{TemplateBinding HeaderTemplateSelector}"
CornerRadius="{TemplateBinding CornerRadius}"
IsChecked="{Binding Path=IsExpanded, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
IsEnabled="{TemplateBinding IsEnabled}"
Style="{StaticResource ExpanderHeaderDownStyle}" />
<!-- The clip is a composition clip applied in code -->
<Popup x:Name="ContentPopup">
<Border x:Name="ExpanderContentClip"
Grid.Row="1">
<Border x:Name="ExpanderContent"
MinHeight="{TemplateBinding MinHeight}"
Padding="{TemplateBinding Padding}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Background="{ThemeResource AcrylicInAppFillColorDefaultBrush}"
BackgroundSizing="{TemplateBinding BackgroundSizing}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{StaticResource ExpanderContentDownBorderThickness}"
CornerRadius="{Binding CornerRadius, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource BottomCornerRadiusFilterConverter}}"
Visibility="Collapsed">
<ContentPresenter
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
ContentTemplateSelector="{TemplateBinding ContentTemplateSelector}" />
<Border.RenderTransform>
<CompositeTransform />
</Border.RenderTransform>
</Border>
</Border>
</Popup>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>