Search code examples
c#wpfwpfdatagridattached-properties

Style all DataGridTextColumns via AttachedProperty


What I tried to do is create a Style to apply a WordWrap on all DataGridTextColumns in a Datagrid without explicitly setting it like this.

<DataGrid ItemsSource="{Binding Lines}">
    <DataGrid.Columns>
        <DataGridTextColumn Header="Column1" Binding="{Binding Path=Result1}">
            <DataGridTextColumn.ElementStyle>
                <Style TargetType="{x:Type TextBlock}">
                    <Setter Property="TextWrapping" Value="Wrap"/>
                </Style>
            </DataGridTextColumn.ElementStyle>
        </DataGridTextColumn>
    </DataGrid.Columns>
</DataGrid>

Unfortunately it is not possible to use some Style as below directly, because DataGridTextColumn isn't a FrameworkElement.

<Style TargetType="{x:Type TextBlock}" x:Key="WrapText">
    <Setter Property="TextWrapping" Value="Wrap"/>
</Style>

I found this workaround https://stackoverflow.com/a/2640862/5381620 by RayBurns and was trying to figure out how it's working. However, I'm new to attached properties and therefore don't understand why it is not working.

The c# code seems to be ok.

public class MyDataGridHelper : DependencyObject
{
    private static readonly DependencyProperty TextColumnStyleProperty = DependencyProperty.RegisterAttached("TextColumnStyle", typeof(Style), typeof(MyDataGridHelper), new PropertyMetadata
    {
        PropertyChangedCallback = (obj, e) =>
        {
            var grid = (DataGrid)obj;
            if (e.OldValue == null && e.NewValue != null)
                grid.Columns.CollectionChanged += (obj2, e2) =>
                {
                    UpdateColumnStyles(grid);
                };
        }
    });

    public static void SetTextColumnStyle(DependencyObject element, Style value)
    {
        element.SetValue(TextColumnStyleProperty, value);
    }
    public static Style GetTextColumnStyle(DependencyObject element)
    {
        return (Style)element.GetValue(TextColumnStyleProperty);
    }

    private static void UpdateColumnStyles(DataGrid grid)
    {
        var style = GetTextColumnStyle(grid);
        foreach (var column in grid.Columns.OfType<DataGridTextColumn>())
            foreach (var setter in style.Setters.OfType<Setter>())
                if (setter.Value is BindingBase)
                    BindingOperations.SetBinding(column, setter.Property, (BindingBase)setter.Value);
                else
                    column.SetValue(setter.Property, setter.Value);
    }
}

I got totally confused is when we get towards figuring out the style setter. Currently I'm trying it this way, which is obviously not working, but actually I don't have a clue what this targettype should really look like.

<local:MyDataGridHelper.TextColumnStyle>
    <Style TargetType="FrameworkElement">
        <Setter Property="TextBlock.TextWrapping" Value="Wrap"/>
    </Style>
</local:MyDataGridHelper.TextColumnStyle>

Solution

  • You should set the ElementStyle of the columns to the value of the attached property:

    public class MyDataGridHelper : DependencyObject
    {
        private static readonly DependencyProperty TextColumnStyleProperty = 
            DependencyProperty.RegisterAttached("TextColumnStyle", typeof(Style), typeof(MyDataGridHelper), new PropertyMetadata
        {
            PropertyChangedCallback = (obj, e) =>
            {
                var grid = (DataGrid)obj;
                if (e.OldValue == null && e.NewValue != null)
                    grid.Columns.CollectionChanged += (obj2, e2) =>
                    {
                        UpdateColumnStyles(grid);
                    };
            }
        });
    
        public static void SetTextColumnStyle(DependencyObject element, Style value)
        {
            element.SetValue(TextColumnStyleProperty, value);
        }
        public static Style GetTextColumnStyle(DependencyObject element)
        {
            return (Style)element.GetValue(TextColumnStyleProperty);
        }
    
        private static void UpdateColumnStyles(DataGrid grid)
        {
            var style = GetTextColumnStyle(grid);
            foreach (var column in grid.Columns.OfType<DataGridTextColumn>())
                column.ElementStyle = style;
        }
    }
    

    Usage:

    <DataGrid>
        <local:MyDataGridHelper.TextColumnStyle>
            <Style TargetType="TextBlock">
                <Setter Property="Foreground" Value="Red"/>
                <Setter Property="TextWrapping" Value="Wrap"/>
            </Style>
        </local:MyDataGridHelper.TextColumnStyle>
        ...
    </DataGrid>