Search code examples
wpfexpander

Expander Direction Right with a Default Header


I have an Expander with Expand Direction right. This works fine but I would like to have the content still appear under the Expander Button and Header. Also I would like to have these, the Button and Header, still appear on the same line. What would be the best way of doing this?

<Expander Header="Test" ExpandDirection="Right">
    <ListBox VerticalAlignment="Top" MaxHeight="400" Name="lbxTest"/>
</Expander>

Solution

  • You need to re-template the entire Expander control. You can right-click on the Expander in design mode in Visual Studio or in Blend and select Edit Template->Edit a Copy to copy the default template into your XAML markup and then edit it as per your requirements:

    <Expander Header="Test" ExpandDirection="Right">
        <Expander.Resources>
            <SolidColorBrush x:Key="Expander.MouseOver.Circle.Stroke" Color="#FF5593FF"/>
            <SolidColorBrush x:Key="Expander.MouseOver.Circle.Fill" Color="#FFF3F9FF"/>
            <SolidColorBrush x:Key="Expander.MouseOver.Arrow.Stroke" Color="#FF000000"/>
            <SolidColorBrush x:Key="Expander.Pressed.Circle.Stroke" Color="#FF3C77DD"/>
            <SolidColorBrush x:Key="Expander.Pressed.Circle.Fill" Color="#FFD9ECFF"/>
            <SolidColorBrush x:Key="Expander.Pressed.Arrow.Stroke" Color="#FF000000"/>
            <SolidColorBrush x:Key="Expander.Disabled.Circle.Stroke" Color="#FFBCBCBC"/>
            <SolidColorBrush x:Key="Expander.Disabled.Circle.Fill" Color="#FFE6E6E6"/>
            <SolidColorBrush x:Key="Expander.Disabled.Arrow.Stroke" Color="#FF707070"/>
            <SolidColorBrush x:Key="Expander.Static.Circle.Fill" Color="#FFFFFFFF"/>
            <SolidColorBrush x:Key="Expander.Static.Circle.Stroke" Color="#FF333333"/>
            <SolidColorBrush x:Key="Expander.Static.Arrow.Stroke" Color="#FF333333"/>
            <Style x:Key="ExpanderRightHeaderStyle" TargetType="{x:Type ToggleButton}">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type ToggleButton}">
                            <Border Padding="{TemplateBinding Padding}">
                                <Grid Background="Transparent" SnapsToDevicePixels="False">
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition Width="Auto" />
                                        <ColumnDefinition Width="*" />
                                    </Grid.ColumnDefinitions>
                                    <Grid>
                                        <Grid.LayoutTransform>
                                            <TransformGroup>
                                                <TransformGroup.Children>
                                                    <TransformCollection>
                                                        <RotateTransform Angle="-90"/>
                                                    </TransformCollection>
                                                </TransformGroup.Children>
                                            </TransformGroup>
                                        </Grid.LayoutTransform>
                                        <Ellipse x:Name="circle" Fill="{StaticResource Expander.Static.Circle.Fill}" HorizontalAlignment="Center" Height="19" Stroke="{StaticResource Expander.Static.Circle.Stroke}" VerticalAlignment="Center" Width="19"/>
                                        <Path x:Name="arrow" Data="M 1,1.5 L 4.5,5 L 8,1.5" HorizontalAlignment="Center" SnapsToDevicePixels="false" Stroke="{StaticResource Expander.Static.Arrow.Stroke}" StrokeThickness="2" VerticalAlignment="Center"/>
                                    </Grid>
                                    <ContentPresenter HorizontalAlignment="Center" Margin="4,4,0,0" Grid.Column="1" RecognizesAccessKey="True" SnapsToDevicePixels="True" VerticalAlignment="Center"/>
                                </Grid>
                            </Border>
                            <ControlTemplate.Triggers>
                                <Trigger Property="IsChecked" Value="true">
                                    <Setter Property="Data" TargetName="arrow" Value="M 1,4.5  L 4.5,1  L 8,4.5"/>
                                </Trigger>
                                <Trigger Property="IsMouseOver" Value="true">
                                    <Setter Property="Stroke" TargetName="circle" Value="{StaticResource Expander.MouseOver.Circle.Stroke}"/>
                                    <Setter Property="Fill" TargetName="circle" Value="{StaticResource Expander.MouseOver.Circle.Fill}"/>
                                    <Setter Property="Stroke" TargetName="arrow" Value="{StaticResource Expander.MouseOver.Arrow.Stroke}"/>
                                </Trigger>
                                <Trigger Property="IsPressed" Value="true">
                                    <Setter Property="Stroke" TargetName="circle" Value="{StaticResource Expander.Pressed.Circle.Stroke}"/>
                                    <Setter Property="StrokeThickness" TargetName="circle" Value="1.5"/>
                                    <Setter Property="Fill" TargetName="circle" Value="{StaticResource Expander.Pressed.Circle.Fill}"/>
                                    <Setter Property="Stroke" TargetName="arrow" Value="{StaticResource Expander.Pressed.Arrow.Stroke}"/>
                                </Trigger>
                                <Trigger Property="IsEnabled" Value="false">
                                    <Setter Property="Stroke" TargetName="circle" Value="{StaticResource Expander.Disabled.Circle.Stroke}"/>
                                    <Setter Property="Fill" TargetName="circle" Value="{StaticResource Expander.Disabled.Circle.Fill}"/>
                                    <Setter Property="Stroke" TargetName="arrow" Value="{StaticResource Expander.Disabled.Arrow.Stroke}"/>
                                </Trigger>
                            </ControlTemplate.Triggers>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </Expander.Resources>
        <Expander.Template>
            <ControlTemplate TargetType="{x:Type Expander}">
                <Border BorderBrush="{TemplateBinding BorderBrush}" 
                                BorderThickness="{TemplateBinding BorderThickness}" 
                                Background="{TemplateBinding Background}" 
                                CornerRadius="3" SnapsToDevicePixels="true">
                    <Grid>
                        <Grid.RowDefinitions>
                            <RowDefinition />
                            <RowDefinition Height="*" />
                        </Grid.RowDefinitions>
                        <ToggleButton x:Name="HeaderSite" ContentTemplate="{TemplateBinding HeaderTemplate}" ContentTemplateSelector="{TemplateBinding HeaderTemplateSelector}" Content="{TemplateBinding Header}" Foreground="{TemplateBinding Foreground}" FontWeight="{TemplateBinding FontWeight}" 
                                              FontStyle="{TemplateBinding FontStyle}" FontStretch="{TemplateBinding FontStretch}" 
                                              FontSize="{TemplateBinding FontSize}" FontFamily="{TemplateBinding FontFamily}" 
                                              HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" 
                                              IsChecked="{Binding IsExpanded, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" 
                                              Margin="1" MinWidth="0" MinHeight="0" Padding="{TemplateBinding Padding}" 
                                              Style="{StaticResource ExpanderRightHeaderStyle}" 
                                              VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
                                              HorizontalAlignment="Left">
                            <ToggleButton.FocusVisualStyle>
                                <Style>
                                    <Setter Property="Control.Template">
                                        <Setter.Value>
                                            <ControlTemplate>
                                                <Border>
                                                    <Rectangle Margin="0" SnapsToDevicePixels="true" Stroke="Black" StrokeThickness="1" StrokeDashArray="1 2"/>
                                                </Border>
                                            </ControlTemplate>
                                        </Setter.Value>
                                    </Setter>
                                </Style>
                            </ToggleButton.FocusVisualStyle>
                        </ToggleButton>
                        <ContentPresenter x:Name="ExpandSite" Grid.Row="1" Focusable="false" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" Visibility="Collapsed" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                    </Grid>
                </Border>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsExpanded" Value="true">
                        <Setter Property="Visibility" TargetName="ExpandSite" Value="Visible"/>
                    </Trigger>
                    <Trigger Property="IsEnabled" Value="false">
                        <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Expander.Template>
        <ListBox VerticalAlignment="Top" MaxHeight="400" Name="lbxTest"/>
    </Expander>
    

    And no, there is no easier way of doing this :)