Preventing a WPF Expander from expanding when header is clicked
Asked Answered
S

7

14

How can I prevent a WPF Expander from expanding when its header is clicked? I would like my Expander to expand or collapse only when the expand button itself is clicked.

I imagine the answer has something to do with canceling a bubbled event. If possible I would like to implement the solution in XAML while avoiding retemplating the entire Expander.

Skinned answered 8/9, 2009 at 20:24 Comment(3)
Me too! What did you end up doing?Bluhm
I did not find a solution, and left the expander as is.Skinned
The solution I used is to place a button inside the <Expander.Header> tags and then templating that button so that it has no mouseenter, click events and an alpha value of 1% (#02000000), Basically making it invisible but clickable. It will then eat the expander header click event.Spousal
B
3

The header is the button that creates the event, so you need to change the template for your Expander to just have the expander icon as the button.

Here is a post of how to change the expander.

Budweis answered 30/11, 2009 at 8:27 Comment(0)
H
18

There is actually a much simpler XAML solution than modifying templates. Simply DON'T use the Expander's header property in this case. Instead, cover the expander with your own styled TextBlock.

<Application.Resources>
    <Style x:Key="ExpanderHeader" TargetType="{x:Type TextBlock}">
        <Setter Property="Height" Value="22" />
        <Setter Property="Margin" Value="21,0,0,0" />
        <Setter Property="Padding" Value="9,3,0,0" />
        <Setter Property="HorizontalAlignment" Value="Stretch" />
        <Setter Property="VerticalAlignment" Value="Top" />
    </Style>
</Application.Resources>

<Grid>
    <Expander>
        <TextBlock Text="I am some content. I have disowned my default header." Margin="10,5" />
    </Expander>
    <TextBlock Text="I'm filling in for the default header. You'll like me better anyway."
               Style="{StaticResource ResourceKey=ExpanderHeader}"/>
</Grid>
Himyarite answered 19/11, 2012 at 18:51 Comment(2)
This is such a brilliant answer. I've tested it and it works like a charm. Using it in my own application now.Beverlee
FYI, you might place this XAML in a custom control that can be used throughout your project.Himyarite
B
3

The header is the button that creates the event, so you need to change the template for your Expander to just have the expander icon as the button.

Here is a post of how to change the expander.

Budweis answered 30/11, 2009 at 8:27 Comment(0)
L
1

I modified the default template for the Expander control as suggested by Simeon.

The following ControlTemplate defines an expander, which only reacts (expands/collapses) when the user clicks on the header icon.

This is quick and dirty so expect it to break.

<ControlTemplate x:Key="LazyExpanderTemplate" TargetType="Expander">
        <Border BorderThickness="{TemplateBinding Border.BorderThickness}" CornerRadius="3,3,3,3" BorderBrush="{TemplateBinding Border.BorderBrush}" Background="{TemplateBinding Panel.Background}" SnapsToDevicePixels="True">
            <DockPanel>
                <DockPanel DockPanel.Dock="Top" Name="HeaderSite">
                    <ToggleButton 
                        DockPanel.Dock="Left"
                        IsChecked="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsExpanded}"
                        Foreground="{TemplateBinding TextElement.Foreground}" 
                        FontFamily="{TemplateBinding TextElement.FontFamily}" 
                        FontSize="{TemplateBinding TextElement.FontSize}" 
                        FontStretch="{TemplateBinding TextElement.FontStretch}" 
                        FontStyle="{TemplateBinding TextElement.FontStyle}" 
                        FontWeight="{TemplateBinding TextElement.FontWeight}" 
                        HorizontalContentAlignment="{TemplateBinding Control.HorizontalContentAlignment}" 
                        VerticalContentAlignment="{TemplateBinding Control.VerticalContentAlignment}" 
                        Padding="{TemplateBinding Control.Padding}" 
                        MinWidth="0" 
                        MinHeight="0" 
                        Margin="1,1,1,1" 
                        >
                        <ToggleButton.Style>
                            <Style TargetType="ToggleButton">
                                <Setter Property="Control.Template">
                                    <Setter.Value>
                                        <ControlTemplate TargetType="ToggleButton">
                                            <Border Padding="{TemplateBinding Control.Padding}">
                                                <Grid Background="#00FFFFFF" SnapsToDevicePixels="False">
                                                    <Grid.ColumnDefinitions>
                                                        <ColumnDefinition Width="19" />
                                                        <ColumnDefinition Width="*" />
                                                    </Grid.ColumnDefinitions>
                                                    <Ellipse Stroke="#FFA9A9A9" Name="circle" Width="19" Height="19" HorizontalAlignment="Center" VerticalAlignment="Center" />
                                                    <Ellipse Name="shadow" Width="17" Height="17" HorizontalAlignment="Center" VerticalAlignment="Center" Visibility="Hidden" />
                                                    <Path Data="M1,1.5L4.5,5 8,1.5" Stroke="#FF666666" StrokeThickness="2" Name="arrow" HorizontalAlignment="Center" VerticalAlignment="Center" SnapsToDevicePixels="False" />
                                                </Grid>
                                            </Border>
                                            <ControlTemplate.Triggers>
                                                <Trigger Property="ToggleButton.IsChecked" Value="True" >
                                                    <Setter Property="Path.Data" TargetName="arrow">
                                                        <Setter.Value>
                                                            <StreamGeometry>M1,4.5L4.5,1 8,4.5</StreamGeometry>
                                                        </Setter.Value>
                                                    </Setter>
                                                </Trigger>
                                                <Trigger Property="UIElement.IsMouseOver" Value="True">
                                                    <Setter Property="Shape.Stroke" TargetName="circle">
                                                        <Setter.Value>
                                                            <SolidColorBrush>#FF666666</SolidColorBrush>
                                                        </Setter.Value>
                                                    </Setter>
                                                    <Setter Property="Shape.Stroke" TargetName="arrow">
                                                        <Setter.Value>
                                                            <SolidColorBrush>#FF222222</SolidColorBrush>
                                                        </Setter.Value>
                                                    </Setter>
                                                    <Setter Property="UIElement.Visibility" TargetName="shadow">
                                                        <Setter.Value>
                                                            <x:Static Member="Visibility.Visible" />
                                                        </Setter.Value>
                                                    </Setter>
                                                </Trigger>
                                            </ControlTemplate.Triggers>
                                        </ControlTemplate>
                                    </Setter.Value>
                                </Setter>
                            </Style>
                        </ToggleButton.Style>
                        <ToggleButton.FocusVisualStyle>
                            <Style TargetType="IFrameworkInputElement">
                                <Setter Property="Control.Template">
                                    <Setter.Value>
                                        <ControlTemplate>
                                            <Border>
                                                <Rectangle Stroke="#FF000000" StrokeThickness="1" StrokeDashArray="1 2" Margin="0,0,0,0" SnapsToDevicePixels="True" />
                                            </Border>
                                        </ControlTemplate>
                                    </Setter.Value>
                                </Setter>
                            </Style>
                        </ToggleButton.FocusVisualStyle>
                    </ToggleButton>
                    <ContentPresenter
                        RecognizesAccessKey="True"
                        Content="{TemplateBinding HeaderedContentControl.Header}" 
                        ContentTemplate="{TemplateBinding HeaderedContentControl.HeaderTemplate}"
                        ContentStringFormat="{TemplateBinding HeaderedContentControl.HeaderStringFormat}"
                        Margin="4,0,0,0"
                        HorizontalAlignment="Left"
                        VerticalAlignment="Center"
                        SnapsToDevicePixels="True"
                        />
                </DockPanel>
                <ContentPresenter Content="{TemplateBinding ContentControl.Content}" ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}" ContentStringFormat="{TemplateBinding ContentControl.ContentStringFormat}" Name="ExpandSite" Margin="{TemplateBinding Control.Padding}" HorizontalAlignment="{TemplateBinding Control.HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding Control.VerticalContentAlignment}" Visibility="Collapsed" Focusable="False" DockPanel.Dock="Bottom" />
            </DockPanel>
        </Border>
        <ControlTemplate.Triggers>
            <Trigger Property="Expander.IsExpanded">
                <Setter Property="UIElement.Visibility" TargetName="ExpandSite">
                    <Setter.Value>
                        <x:Static Member="Visibility.Visible" />
                    </Setter.Value>
                </Setter>
                <Trigger.Value>
                    <s:Boolean>True</s:Boolean>
                </Trigger.Value>
            </Trigger>
            <Trigger Property="Expander.ExpandDirection" Value="Right">
                <Setter Property="DockPanel.Dock" TargetName="ExpandSite" Value="Right"/>
                <Setter Property="DockPanel.Dock" TargetName="HeaderSite" Value="Left"/>
                <Setter Property="FrameworkElement.Style" TargetName="HeaderSite">
                    <Setter.Value>
                        <Style TargetType="ToggleButton">
                            <Setter Property="Control.Template">
                                <Setter.Value>
                                    <ControlTemplate TargetType="ToggleButton">
                                        <Border Padding="{TemplateBinding Control.Padding}">
                                            <Grid Background="#00FFFFFF" SnapsToDevicePixels="False">
                                                <Grid.RowDefinitions>
                                                    <RowDefinition Height="19" />
                                                    <RowDefinition Height="*" />
                                                </Grid.RowDefinitions>
                                                <Grid>
                                                    <Grid.LayoutTransform>
                                                        <TransformGroup>
                                                            <TransformGroup.Children>
                                                                <RotateTransform Angle="-90" />
                                                            </TransformGroup.Children>
                                                        </TransformGroup>
                                                    </Grid.LayoutTransform>
                                                    <Ellipse Stroke="#FFA9A9A9" Name="circle" Width="19" Height="19" HorizontalAlignment="Center" VerticalAlignment="Center" />
                                                    <Ellipse Name="shadow" Width="17" Height="17" HorizontalAlignment="Center" VerticalAlignment="Center" Visibility="Hidden" />
                                                    <Path Data="M1,1.5L4.5,5 8,1.5" Stroke="#FF666666" StrokeThickness="2" Name="arrow" HorizontalAlignment="Center" VerticalAlignment="Center" SnapsToDevicePixels="False" />
                                                </Grid>
                                                <ContentPresenter RecognizesAccessKey="True" Content="{TemplateBinding ContentControl.Content}" ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}" ContentStringFormat="{TemplateBinding ContentControl.ContentStringFormat}" Margin="0,4,0,0" HorizontalAlignment="Center" VerticalAlignment="Top" SnapsToDevicePixels="True" Grid.Row="1" />
                                            </Grid>
                                        </Border>
                                        <ControlTemplate.Triggers>
                                            <Trigger Property="ToggleButton.IsChecked">
                                                <Setter Property="Path.Data" TargetName="arrow">
                                                    <Setter.Value>
                                                        <StreamGeometry>M1,4.5L4.5,1 8,4.5</StreamGeometry>
                                                    </Setter.Value>
                                                </Setter>
                                                <Trigger.Value>
                                                    <s:Boolean>True</s:Boolean>
                                                </Trigger.Value>
                                            </Trigger>
                                            <Trigger Property="UIElement.IsMouseOver">
                                                <Setter Property="Shape.Stroke" TargetName="circle">
                                                    <Setter.Value>
                                                        <SolidColorBrush>#FF666666</SolidColorBrush>
                                                    </Setter.Value>
                                                </Setter>
                                                <Setter Property="Shape.Stroke" TargetName="arrow">
                                                    <Setter.Value>
                                                        <SolidColorBrush>#FF222222</SolidColorBrush>
                                                    </Setter.Value>
                                                </Setter>
                                                <Setter Property="UIElement.Visibility" TargetName="shadow">
                                                    <Setter.Value>
                                                        <x:Static Member="Visibility.Visible" />
                                                    </Setter.Value>
                                                </Setter>
                                                <Trigger.Value>
                                                    <s:Boolean>True</s:Boolean>
                                                </Trigger.Value>
                                            </Trigger>
                                        </ControlTemplate.Triggers>
                                    </ControlTemplate>
                                </Setter.Value>
                            </Setter>
                        </Style>
                    </Setter.Value>
                </Setter>
            </Trigger>
            <Trigger Property="Expander.ExpandDirection">
                <Setter Property="DockPanel.Dock" TargetName="ExpandSite" Value="Top"/>
                <Setter Property="DockPanel.Dock" TargetName="HeaderSite" Value="Bottom"/>
                <Setter Property="FrameworkElement.Style" TargetName="HeaderSite">
                    <Setter.Value>
                        <Style TargetType="ToggleButton">
                            <Setter Property="Control.Template">
                                <Setter.Value>
                                    <ControlTemplate TargetType="ToggleButton">
                                        <Border Padding="{TemplateBinding Control.Padding}">
                                            <Grid Background="#00FFFFFF" SnapsToDevicePixels="False">
                                                <Grid.ColumnDefinitions>
                                                    <ColumnDefinition Width="19" />
                                                    <ColumnDefinition Width="*" />
                                                </Grid.ColumnDefinitions>
                                                <Grid>
                                                    <Grid.LayoutTransform>
                                                        <TransformGroup>
                                                            <TransformGroup.Children>
                                                                <RotateTransform Angle="180" />
                                                            </TransformGroup.Children>
                                                        </TransformGroup>
                                                    </Grid.LayoutTransform>
                                                    <Ellipse Stroke="#FFA9A9A9" Name="circle" Width="19" Height="19" HorizontalAlignment="Center" VerticalAlignment="Center" />
                                                    <Ellipse Name="shadow" Width="17" Height="17" HorizontalAlignment="Center" VerticalAlignment="Center" Visibility="Hidden" />
                                                    <Path Data="M1,1.5L4.5,5 8,1.5" Stroke="#FF666666" StrokeThickness="2" Name="arrow" HorizontalAlignment="Center" VerticalAlignment="Center" SnapsToDevicePixels="False" />
                                                </Grid>
                                                <ContentPresenter RecognizesAccessKey="True" Content="{TemplateBinding ContentControl.Content}" ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}" ContentStringFormat="{TemplateBinding ContentControl.ContentStringFormat}" Margin="4,0,0,0" HorizontalAlignment="Left" VerticalAlignment="Center" SnapsToDevicePixels="True" Grid.Column="1" />
                                            </Grid>
                                        </Border>
                                        <ControlTemplate.Triggers>
                                            <Trigger Property="ToggleButton.IsChecked">
                                                <Setter Property="Path.Data" TargetName="arrow">
                                                    <Setter.Value>
                                                        <StreamGeometry>M1,4.5L4.5,1 8,4.5</StreamGeometry>
                                                    </Setter.Value>
                                                </Setter>
                                                <Trigger.Value>
                                                    <s:Boolean>True</s:Boolean>
                                                </Trigger.Value>
                                            </Trigger>
                                            <Trigger Property="UIElement.IsMouseOver">
                                                <Setter Property="Shape.Stroke" TargetName="circle">
                                                    <Setter.Value>
                                                        <SolidColorBrush>#FF666666</SolidColorBrush>
                                                    </Setter.Value>
                                                </Setter>
                                                <Setter Property="Shape.Stroke" TargetName="arrow">
                                                    <Setter.Value>
                                                        <SolidColorBrush>#FF222222</SolidColorBrush>
                                                    </Setter.Value>
                                                </Setter>
                                                <Setter Property="UIElement.Visibility" TargetName="shadow">
                                                    <Setter.Value>
                                                        <x:Static Member="Visibility.Visible" />
                                                    </Setter.Value>
                                                </Setter>
                                                <Trigger.Value>
                                                    <s:Boolean>True</s:Boolean>
                                                </Trigger.Value>
                                            </Trigger>
                                        </ControlTemplate.Triggers>
                                    </ControlTemplate>
                                </Setter.Value>
                            </Setter>
                        </Style>
                    </Setter.Value>
                </Setter>
                <Trigger.Value>
                    <x:Static Member="ExpandDirection.Up" />
                </Trigger.Value>
            </Trigger>
            <Trigger Property="Expander.ExpandDirection">
                <Setter Property="DockPanel.Dock" TargetName="ExpandSite">
                    <Setter.Value>
                        <x:Static Member="Dock.Left" />
                    </Setter.Value>
                </Setter>
                <Setter Property="DockPanel.Dock" TargetName="HeaderSite">
                    <Setter.Value>
                        <x:Static Member="Dock.Right" />
                    </Setter.Value>
                </Setter>
                <Setter Property="FrameworkElement.Style" TargetName="HeaderSite">
                    <Setter.Value>
                        <Style TargetType="ToggleButton">
                            <Setter Property="Control.Template">
                                <Setter.Value>
                                    <ControlTemplate TargetType="ToggleButton">
                                        <Border Padding="{TemplateBinding Control.Padding}">
                                            <Grid Background="#00FFFFFF" SnapsToDevicePixels="False">
                                                <Grid.RowDefinitions>
                                                    <RowDefinition Height="19" />
                                                    <RowDefinition Height="*" />
                                                </Grid.RowDefinitions>
                                                <Grid>
                                                    <Grid.LayoutTransform>
                                                        <TransformGroup>
                                                            <TransformGroup.Children>
                                                                <RotateTransform Angle="90" />
                                                            </TransformGroup.Children>
                                                        </TransformGroup>
                                                    </Grid.LayoutTransform>
                                                    <Ellipse Stroke="#FFA9A9A9" Name="circle" Width="19" Height="19" HorizontalAlignment="Center" VerticalAlignment="Center" />
                                                    <Ellipse Name="shadow" Width="17" Height="17" HorizontalAlignment="Center" VerticalAlignment="Center" Visibility="Hidden" />
                                                    <Path Data="M1,1.5L4.5,5 8,1.5" Stroke="#FF666666" StrokeThickness="2" Name="arrow" HorizontalAlignment="Center" VerticalAlignment="Center" SnapsToDevicePixels="False" />
                                                </Grid>
                                                <ContentPresenter RecognizesAccessKey="True" Content="{TemplateBinding ContentControl.Content}" ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}" ContentStringFormat="{TemplateBinding ContentControl.ContentStringFormat}" Margin="0,4,0,0" HorizontalAlignment="Center" VerticalAlignment="Top" SnapsToDevicePixels="True" Grid.Row="1" />
                                            </Grid>
                                        </Border>
                                        <ControlTemplate.Triggers>
                                            <Trigger Property="ToggleButton.IsChecked">
                                                <Setter Property="Path.Data" TargetName="arrow">
                                                    <Setter.Value>
                                                        <StreamGeometry>M1,4.5L4.5,1 8,4.5</StreamGeometry>
                                                    </Setter.Value>
                                                </Setter>
                                                <Trigger.Value>
                                                    <s:Boolean>True</s:Boolean>
                                                </Trigger.Value>
                                            </Trigger>
                                            <Trigger Property="UIElement.IsMouseOver">
                                                <Setter Property="Shape.Stroke" TargetName="circle">
                                                    <Setter.Value>
                                                        <SolidColorBrush>#FF666666</SolidColorBrush>
                                                    </Setter.Value>
                                                </Setter>
                                                <Setter Property="Shape.Stroke" TargetName="arrow">
                                                    <Setter.Value>
                                                        <SolidColorBrush>#FF222222</SolidColorBrush>
                                                    </Setter.Value>
                                                </Setter>
                                                <Setter Property="UIElement.Visibility" TargetName="shadow">
                                                    <Setter.Value>
                                                        <x:Static Member="Visibility.Visible" />
                                                    </Setter.Value>
                                                </Setter>
                                                <Trigger.Value>
                                                    <s:Boolean>True</s:Boolean>
                                                </Trigger.Value>
                                            </Trigger>
                                        </ControlTemplate.Triggers>
                                    </ControlTemplate>
                                </Setter.Value>
                            </Setter>
                        </Style>
                    </Setter.Value>
                </Setter>
                <Trigger.Value>
                    <x:Static Member="ExpandDirection.Left" />
                </Trigger.Value>
            </Trigger>
            <Trigger Property="UIElement.IsEnabled">
                <Setter Property="TextElement.Foreground">
                    <Setter.Value>
                        <DynamicResource ResourceKey="{x:Static SystemColors.GrayTextBrushKey}" />
                    </Setter.Value>
                </Setter>
                <Trigger.Value>
                    <s:Boolean>False</s:Boolean>
                </Trigger.Value>
            </Trigger>
        </ControlTemplate.Triggers>
    </ControlTemplate>
Lexielexigraphy answered 11/6, 2010 at 20:58 Comment(0)
I
1

My solution was to leave the header blank and just tag on a control that acts as the header (using a canvas for absolute positioning), that way only the icon expands the control:

<Grid Height="{Binding ElementName=exp, Path=ActualHeight}">
    <Canvas>
        <Expander Name="exp">
        ...
        </Expander>
        <Control Style="{StaticResource ExpanderHeaderStyle}" Margin="20,0,0,0" />
     </Canvas>
</Grid>
Inflationism answered 31/10, 2012 at 13:4 Comment(0)
E
1

A rather simple solution is to modify the toggle button templates that the Expander control tempate uses in the following way.

  • Set the background of the grid and border to {x:Null}
  • Set the content presenter IsHitTestVisible to False

            <ControlTemplate TargetType="{x:Type ToggleButton}">
            <Border Padding="{TemplateBinding Padding}" Background="{x:Null}">
                <Grid Background="{x:Null}" SnapsToDevicePixels="False">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="19"/>
                        <RowDefinition Height="*"/>
                    </Grid.RowDefinitions>
                    <Grid>
                        <Grid.LayoutTransform>
                            <TransformGroup>
                                <TransformGroup.Children>
                                    <TransformCollection>
                                        <RotateTransform Angle="-90"/>
                                    </TransformCollection>
                                </TransformGroup.Children>
                            </TransformGroup>
                        </Grid.LayoutTransform>
                        <Ellipse x:Name="circle" HorizontalAlignment="Center" Height="19" Stroke="DarkGray" 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="#666" StrokeThickness="2" VerticalAlignment="Center"/>
                    </Grid>
                    <ContentPresenter HorizontalAlignment="Center" Margin="0,4,0,0" Grid.Row="1" RecognizesAccessKey="True" SnapsToDevicePixels="True" VerticalAlignment="Top"IsHitTestVisible="False"/>
                </Grid>
            </Border>
        </ControlTemplate>
    
Egotism answered 9/5, 2018 at 13:44 Comment(0)
P
0

You could just set IsEnabled to false but in case you need to determine if you can expand or collapse when the header is clicked:

public class MyExpander : Expander
{
    private bool CanExpand() => true;

    private bool CanCollapse() => true;

    private bool rollback;

    protected override void OnExpanded()
    {
        if (rollback) return;
        if (CanExpand())
        {
            base.OnExpanded();
        }
        else
        {
            rollback = true;
            SetCurrentValue(IsExpandedProperty, false);
            rollback = false;
        }
    }

    protected override void OnCollapsed()
    {
        if (rollback) return;
        if (CanCollapse())
        {
            base.OnCollapsed();
        }
        else
        {
            rollback = true;
            SetCurrentValue(IsExpandedProperty, true);
            rollback = false;
        }
    }
}
Protease answered 12/5, 2018 at 12:50 Comment(0)
I
0

As I've just checked, the header is named "HeaderSite". You can use method like the one described in this post in order to find child by name. When you have the child you can just change visibility of it. Also make sure the expander is loaded, otherwise you wont see the child.

expander.Loaded += (s, e) => {
    var hv = expander.findChildByName("HeaderSite");
    if (hv == null) return;
    hv.Visibility = Visibility.Collapsed;
};

I guess it's not guaranteed the name won't change and for me it's just cosmetic problem.

Inviolate answered 22/1, 2020 at 19:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.