polfosol polfosol - 2 months ago 26
C# Question

WPF- How to place the Expander icon at the center of its header?

Please take a look at the code below:

<Window x:Class="WpfTest.Test2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Test2" Height="300" Width="400">
<Grid>
<DockPanel LastChildFill="true">
<Expander DockPanel.Dock="Left" Header="" Background="#E2FFD6"
HorizontalAlignment="Left" VerticalAlignment="Stretch"
ExpandDirection="Left" IsExpanded="True" Height="Auto">
<StackPanel>
<Button Content=" open some tabs and show a messagebox "/>
<Button Content="do something else"/>
</StackPanel>
</Expander>
<TabControl HorizontalAlignment="Stretch">
<!-- some tabs here -->
</TabControl>
</DockPanel>
</Grid>
</Window>


which results in this:

enter image description here

As you see in the picture, this doesn't look nice and I want to move the expander icon to the center of the header. How can I do this? I tried changing the
HeaderTemplate
, but it didn't affect the icon placement.

Answer

This behaviour is hard-coded in the Template. When we edit a copy:

Rightclick your element in the designer window (not in the Xaml-Code), then "Edit Template..." and "Edit a Copy..."

We find the relevant code in the ExpanderLeftHeaderStyle

<Style x:Key="ExpanderLeftHeaderStyle" TargetType="{x:Type ToggleButton}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ToggleButton}">
                <Border Padding="{TemplateBinding Padding}">
                    <Grid Background="Transparent" SnapsToDevicePixels="False">
                        <Grid.RowDefinitions>
                            <RowDefinition Height="19"/>
                            <RowDefinition Height="*"/>
                        </Grid.RowDefinitions>
                        <Grid>
                            <Grid.LayoutTransform>
                                ...
                            </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="0,4,0,0" Grid.Row="1" RecognizesAccessKey="True" SnapsToDevicePixels="True" VerticalAlignment="Top"/>
                    </Grid>
                </Border>
                <ControlTemplate.Triggers>
                    ...
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

The RowDefinitions determine the location of the Icon (= the inner Grid), so we need to change them and the Row assignments for the inner Grid and ContentPresenter accordingly:

<Border Padding="{TemplateBinding Padding}">
    <Grid Background="Transparent" SnapsToDevicePixels="False">
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="19"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <Grid Grid.Row="1">
            <Grid.LayoutTransform>
                ...
            </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 Grid.Row="2" HorizontalAlignment="Center" Margin="0,4,0,0" RecognizesAccessKey="True" SnapsToDevicePixels="True" VerticalAlignment="Top"/>
    </Grid>
</Border>