Search code examples
c#wpfdatatabledatagriddatagridcolumnheader

WPF- How to apply LayoutTransform to a specific DataGridColumnHeader


I have a DataGrid whose ItemsSource is bound to a System.Data.DataTable. This DataTable is filled at runtime with some text columns and some boolean columns. As expected, the text columns are displayed as DataGridTextColumn and the boolean ones are displayed as DataGridCheckBoxColumn. No problem so far. Now I want the column headers of the checkbox columns to be displayed as vertical. So I defined a style like this:

<Style x:Key="ColumnHeaderStyle" TargetType="{x:Type DataGridColumnHeader}">
    <Style.Triggers>
        <!--<Trigger Property=??? Value=???>
            <Setter Property="LayoutTransform">
                <Setter.Value>
                    <RotateTransform Angle="270"/>
                </Setter.Value>
            </Setter>
        </Trigger>-->
    </Style.Triggers>
</Style>

The problem is -as commented in the code, I don't know what kind of trigger I should use for the style in order to be applied only to the DataGridCheckBoxColumns and not other types of columns. Any ideas?


Solution

  • To fulfill both requisites:

    • Timing : Flip Headers after ItemSource is set
    • Filter : The Style should only be applied to CheckBoxColumns

    You could resort to Attached Behaviour:

    DataGridColumnsBehavior.cs

    public static class DataGridColumnsBehavior
    {
        public static readonly DependencyProperty
            FlipHeaderProperty =
                DependencyProperty.RegisterAttached("FlipHeader",
                    typeof(bool), typeof(DataGridColumnsBehavior),
                        new PropertyMetadata(FlipHeaderChanged));
    
        public static bool GetFlipHeader(DependencyObject obj)
        {
            return (bool)obj.GetValue(FlipHeaderProperty);
        }
        public static void SetFlipHeader(DependencyObject obj, bool value)
        {
            obj.SetValue(FlipHeaderProperty, value);
        }
    
        private static void FlipHeaderChanged(DependencyObject d,
            DependencyPropertyChangedEventArgs args)
        {
            var grid = d as DataGrid;
            var flip = (bool)grid.GetValue(FlipHeaderProperty);
            if (grid == null
             || grid.Columns.Count == 0
             || flip == false) return;
    
            foreach (var column in grid.Columns)
            {
                if (column.GetType() == typeof(DataGridCheckBoxColumn))
                    column.HeaderStyle = 
                        (Style)grid.FindResource("CheckBoxColumnHeaderStyle");
            }
        }
    }
    

    XAML

    <DataGrid ItemsSource="{Binding Collection}" 
              funk:DataGridColumnsBehavior.FlipHeader="{Binding Flip}">
        <DataGrid.Resources>
            <Style x:Key="CheckBoxColumnHeaderStyle" 
                   TargetType="{x:Type DataGridColumnHeader}">
                <Setter Property="LayoutTransform">
                    <Setter.Value>
                        <RotateTransform Angle="270"/>
                    </Setter.Value>
                </Setter>
            </Style>
        </DataGrid.Resources>
    </DataGrid>
    

    The timing is controlled by a Flip property. Every time Collection is changed, set Flip to false and then to true again. The Columns are iterated and the Headers of the DataGridCheckBoxColumns are flipped. Note the way this is implemented setting Flip to false does nothing, so the Headers aren't flipped back.

    EDIT

    Just learned about a shorter method, using the AutoGeneratingColumn event:

    <DataGrid ItemsSource="{Binding Collection}" 
              AutoGeneratingColumn="dataGrid_AutoGeneratingColumn">
        <DataGrid.Resources>
            <Style x:Key="CheckBoxColumnHeaderStyle" 
                   TargetType="{x:Type DataGridColumnHeader}">
                <Setter Property="LayoutTransform">
                    <Setter.Value>
                        <RotateTransform Angle="270"/>
                    </Setter.Value>
                </Setter>
            </Style>
        </DataGrid.Resources>
    </DataGrid>
    

    Handler in partial class

    private void dataGrid_AutoGeneratingColumn(object sender,
        DataGridAutoGeneratingColumnEventArgs e)
    {
        DataGrid grid = sender as DataGrid;
        // Only DataGridCheckBoxColumns
        if (e.PropertyType == typeof(bool))
            e.Column.HeaderStyle =
                (Style)grid.FindResource("CheckBoxColumnHeaderStyle");
    }