Vertically aligning an expander button to the top of a DataGridRowHeader

804 views Asked by At

In a DataGrid I have added an Expander to the row headers, which works fine functionally, however when expanded it centers vertically.

I would like it to remain at the top of the header where it was clicked in the first place.

What am I missing here?

<DataGrid Name="dgAudit" 
          CanUserReorderColumns="False"
          CanUserAddRows="False"
          CanUserDeleteRows="False"
          CanUserResizeColumns="False"
          CanUserResizeRows="False"
          CanUserSortColumns="False"
          IsReadOnly="True"
          ItemsSource="{Binding GEOM_ASSET_OC_LIST}"
          HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"
          AutoGenerateColumns="False"
          HorizontalAlignment="Stretch"
          VerticalAlignment="Stretch"
          RowDetailsVisibilityMode="Collapsed"
          >
    <DataGrid.RowHeaderTemplate>
        <DataTemplate>
            <Expander Template="{StaticResource NewExpander}"
                      OverridesDefaultStyle="True"
                      Header=""
                      HorizontalAlignment="Right"
                      VerticalAlignment="Top"
                      Expanded="Expander_Expanded" Collapsed="Expander_Collapsed"
                      >
            </Expander>
        </DataTemplate>
    </DataGrid.RowHeaderTemplate>

    <DataGrid.Columns>...

Here is the main template:

<ControlTemplate x:Key="NewExpander" TargetType="{x:Type Expander}">
    <DockPanel>
        <ToggleButton x:Name="ExpanderButton" 
                      DockPanel.Dock="Top"
                      Template="{StaticResource AnimatedExpanderButtonTemp}"
                      Content="{TemplateBinding Header}"
                      IsChecked="{Binding Path=IsExpanded, RelativeSource={RelativeSource TemplatedParent}}"
                      OverridesDefaultStyle="True"
                      Padding="0.5,0">
        </ToggleButton>
    </DockPanel>
</ControlTemplate>

Here is the template for animation:

<ControlTemplate x:Key="AnimatedExpanderButtonTemp" TargetType="{x:Type ToggleButton}">
    <Border x:Name="ExpanderButtonBorder"
            Background="{TemplateBinding Background}"
            BorderBrush="{TemplateBinding BorderBrush}"
            BorderThickness="{TemplateBinding BorderThickness}"
            Padding="{TemplateBinding Padding}" >
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>
            <Rectangle Fill="Transparent"
                       Grid.ColumnSpan="2"/>
            <Ellipse Name="Circle"
                     Grid.Column="0"
                     Stroke="DarkGray"
                     Fill="LightGray"
                     Width="15"
                     Height="15"
                     HorizontalAlignment="Center"
                     VerticalAlignment="Center" />
            <Path x:Name="Arrow"
                  Grid.Column="0"
                  Data="M 1,1.5 L 4.5,5 8,1.5"
                  Stroke="#FF666666"
                  StrokeThickness="2"
                  HorizontalAlignment="Center"
                  VerticalAlignment="Center"
                  RenderTransformOrigin="0.5,0.5" >
                <Path.RenderTransform>
                    <RotateTransform Angle="0"/>
                </Path.RenderTransform>
            </Path>
        </Grid>
    </Border>
    <ControlTemplate.Triggers>
        <Trigger Property="IsChecked"
                 Value="True">
            <Trigger.EnterActions>
                <BeginStoryboard>
                    <Storyboard>
                        <DoubleAnimation Storyboard.TargetName="Arrow"
                                         Storyboard.TargetProperty="(Path.RenderTransform).(RotateTransform.Angle)"
                                         To="180"
                                         Duration="0:0:0.4"/>
                    </Storyboard>
                </BeginStoryboard>
            </Trigger.EnterActions>
            <Trigger.ExitActions>
                <BeginStoryboard>
                    <Storyboard>
                        <DoubleAnimation Storyboard.TargetName="Arrow"
                                         Storyboard.TargetProperty="(Path.RenderTransform).(RotateTransform.Angle)"
                                         To="0"
                                         Duration="0:0:0.4"/>
                    </Storyboard>
                </BeginStoryboard>
            </Trigger.ExitActions>
        </Trigger>

        <Trigger Property="IsMouseOver" Value="true">
            <Setter Property="Stroke" Value="#FF3C7FB1" TargetName="Circle"/>
            <Setter Property="Stroke" Value="#222" TargetName="Arrow"/>
        </Trigger>
        <Trigger Property="IsPressed" Value="true">
            <Setter Property="Stroke" Value="#FF526C7B" TargetName="Circle"/>
            <Setter Property="StrokeThickness"  Value="1.5" TargetName="Circle"/>
            <Setter Property="Stroke" Value="#FF003366" TargetName="Arrow"/>
        </Trigger>
    </ControlTemplate.Triggers>
</ControlTemplate>

Here are the expand collapse events:

private void Expander_Expanded(object sender, RoutedEventArgs e) 
{ 
    for (var vis = sender as Visual; vis != null; vis = VisualTreeHelper.GetParent(vis) as Visual) 
    if (vis is DataGridRow) 
    { 
        var row = (DataGridRow)vis; 
        row.DetailsVisibility = row.DetailsVisibility == Visibility.Visible ? Visibility.Collapsed : Visibility.Visible; 
        break; 
    } 
} 

private void Expander_Collapsed(object sender, RoutedEventArgs e) 
{ 
    for (var vis = sender as Visual; vis != null; vis = VisualTreeHelper.GetParent(vis) as Visual) 
    if (vis is DataGridRow) 
    { 
        var row = (DataGridRow)vis; 
        row.DetailsVisibility = row.DetailsVisibility == Visibility.Visible ? Visibility.Collapsed : Visibility.Visible; 
        break; 
    } 
}
1

There are 1 answers

1
mm8 On BEST ANSWER

The ContentPresenter in the default DataGridRowHeader template has a hard-coded VericalAlignment of Center. You need to modify the template to be able to fix this:

<DataGrid.Resources>
    <BooleanToVisibilityConverter x:Key="bool2VisibilityConverter"/>
    <Style x:Key="RowHeaderGripperStyle" TargetType="{x:Type Thumb}">
        <Setter Property="Height" Value="8"/>
        <Setter Property="Background" Value="Transparent"/>
        <Setter Property="Cursor" Value="SizeNS"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Thumb}">
                    <Border Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}"/>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    <Style TargetType="{x:Type DataGridRowHeader}"
                       xmlns:Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero2">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type DataGridRowHeader}">
                    <Grid>
                        <Themes:DataGridHeaderBorder BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" IsPressed="{TemplateBinding IsPressed}" IsHovered="{TemplateBinding IsMouseOver}" IsSelected="{TemplateBinding IsRowSelected}" Orientation="Horizontal" Padding="{TemplateBinding Padding}" SeparatorBrush="{TemplateBinding SeparatorBrush}" SeparatorVisibility="{TemplateBinding SeparatorVisibility}">
                            <StackPanel Orientation="Horizontal">
                                <ContentPresenter RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="Stretch"/>
                                <Control SnapsToDevicePixels="false" Template="{Binding ValidationErrorTemplate, RelativeSource={RelativeSource AncestorType={x:Type DataGridRow}}}" Visibility="{Binding (Validation.HasError), Converter={StaticResource bool2VisibilityConverter}, RelativeSource={RelativeSource AncestorType={x:Type DataGridRow}}}"/>
                            </StackPanel>
                        </Themes:DataGridHeaderBorder>
                        <Thumb x:Name="PART_TopHeaderGripper" Style="{StaticResource RowHeaderGripperStyle}" VerticalAlignment="Top"/>
                        <Thumb x:Name="PART_BottomHeaderGripper" Style="{StaticResource RowHeaderGripperStyle}" VerticalAlignment="Bottom"/>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</DataGrid.Resources>

Don't fortget to add a reference to PresentationFramework.Aero2.dll.