Почему я не могу стилизовать DataGridTextColumn? - PullRequest
34 голосов
/ 13 апреля 2010

Я пытался создать стиль для DataGridTextColumn со следующим кодом

<Style TargetType="{x:Type DataGridTextColumn}">
           ...
</Style>

Однако Visual Studio 2010 выделяет {x:Type DataGridTextColumn} синей линией и уточняет: Exception has been thrown by the target of an invocation.

Почему это происходит и как я могу это исправить?

Ответы [ 5 ]

25 голосов
/ 15 апреля 2010

Вы не можете стилизовать DataGridTextColumn, потому что DataGridTextColumn не является производным от FrameworkElement (или FrameworkContentElement). Только FrameworkElement и т. Д. Поддерживает стилизацию.

Когда вы пытаетесь создать стиль в XAML для любого типа, который не является FrameworkElement или FrameworkContentElement, вы получаете это сообщение об ошибке.

Как вы решаете это? Как и в случае любой проблемы, там, где есть воля, есть способ. В этом случае я думаю, что самое простое решение - это создать присоединенное свойство для DataGrid, чтобы назначить стиль DataGridColumn:

<DataGrid ...>
  <local:MyDataGridHelper.TextColumnStyle>
    <Style TargetType="FrameworkElement">
      ... setters here ...
    </Style>
  </local:MyDataGridHelper.TextColumnStyle>
  ...

Реализация будет выглядеть примерно так:

public class MyDataGridHelper : DependencyObject
{
  // Use propa snipped to create attached TextColumnStyle with metadata:
  ... 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);
        }
    }
  }
  private void UpdateStyles(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, setter.Value);
        else
          column.SetValue(setter.Property, setter.Value);
  }
}

Способ, которым это работает, заключается в том, что каждый раз, когда присоединяемое свойство изменяется, добавляется обработчик для события Columns.CollectionChanged в сетке. При возникновении события CollectionChanged все столбцы обновляются в соответствии с установленным стилем.

Обратите внимание, что приведенный выше код не обрабатывает ситуацию, когда стиль удаляется и изящно добавляется: два обработчика событий зарегистрированы. Для действительно надежного решения вы бы хотели исправить это, добавив другое прикрепленное свойство, содержащее обработчик событий, чтобы обработчик событий мог быть незарегистрированным, но для вашей цели я думаю, что это неважно.

Еще одна оговорка: прямое использование SetBinding и SetValue приведет к тому, что свойство DependencyProperty будет иметь BaseValueSource Local вместо DefaultStyle. Это, вероятно, не будет иметь никакого значения в вашем случае, но я подумал, что должен упомянуть об этом.

5 голосов
/ 07 июня 2016

Тег стиля должен идти в правильном месте. Ваша сетка данных может выглядеть примерно так прямо сейчас:

    <DataGrid>
        <DataGrid.Columns>
            <DataGridTextColumn />
        </DataGrid.Columns>
    </DataGrid>

Сначала вы можете попытаться добавить тег стиля непосредственно в элемент DataGridTextColumn, который не будет работать. Однако вы можете создавать элементы для «DataGridTextColumn.ElementStyle» и «DataGridTextColumn.EditingElementStyle» только внутри элемента «DataGridTextColumn». Каждый из этих тегов элемента может иметь теги стиля внутри них:

    <DataGrid>
        <DataGrid.Columns>
            <DataGridTextColumn>
                <DataGridTextColumn.ElementStyle>
                    <Style TargetType="TextBlock">
                        <Setter Property="Background" Value="Green"></Setter>
                    </Style>
                </DataGridTextColumn.ElementStyle>
                <DataGridTextColumn.EditingElementStyle>
                    <Style TargetType="TextBox">
                        <Setter Property="Background" Value="Orange"></Setter>
                    </Style>
                </DataGridTextColumn.EditingElementStyle>
            </DataGridTextColumn>
        </DataGrid.Columns>
    </DataGrid>

Один стиль будет применяться к просмотру, а другой будет применяться, когда ячейка находится в режиме редактирования. Обратите внимание, что он изменяется от TextBlock при просмотре к TextBox при редактировании (Это меня сначала дало!).

1 голос
/ 16 октября 2017

Это больше дополнение к ответу Рэя Бернса. Сначала я не смог реализовать это самостоятельно, но с помощью mm8 (https://stackoverflow.com/a/46690951/5381620) я запустил его. Работает очень хорошо. Для других людей, у которых есть проблемы с этим подходом к присоединенному свойству, может быть полный фрагмент кода полезно.

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 origStyle = GetTextColumnStyle(grid);
        foreach (var column in grid.Columns.OfType<DataGridTextColumn>())
        {
            //may not add setters to a style which is already in use
            //therefore we need to create a new style merging
            //original style with setters from attached property
            var newStyle = new Style();
            newStyle.BasedOn = column.ElementStyle;
            newStyle.TargetType = origStyle.TargetType;

            foreach (var setter in origStyle.Setters.OfType<Setter>())
            {
                newStyle.Setters.Add(setter);
            }

            column.ElementStyle = newStyle;
        }
    }
}

XAML

<Grid>
    <DataGrid Name="MyDataGrid" ItemsSource="{Binding Lines}" AutoGenerateColumns="False" >
        <local:MyDataGridHelper.TextColumnStyle>
            <Style TargetType="TextBlock">
                <Setter Property="TextWrapping" Value="Wrap"/>
            </Style>
        </local:MyDataGridHelper.TextColumnStyle>
        <DataGrid.Columns>
            <DataGridTextColumn Header="ProductId1" Binding="{Binding Path=Result1}" />
            <DataGridTextColumn Header="ProductId2" Binding="{Binding Path=Result2}" />
        </DataGrid.Columns>
    </DataGrid>
</Grid>

Редактировать : При первом подходе я переписал весь стиль. В новой версии все еще возможно сохранить другие модификации стилей, подобные этой

<DataGridTextColumn.ElementStyle>
    <Style TargetType="{x:Type TextBlock}">
        <Setter Property="Foreground" Value="Red"/>
    </Style>
</DataGridTextColumn.ElementStyle>
0 голосов
/ 03 декабря 2015

Более просто:

<FontFamily x:Key="DefaultFont">Snap ITC</FontFamily>
<Style x:Key="ControlStyle" TargetType="Control">
    <Setter Property="FontFamily" Value="{StaticResource DefaultFont}"/>
</Style>
<Style TargetType="{x:Type DataGridCellsPresenter}" BasedOn="{StaticResource ControlStyle}">
</Style>
0 голосов
/ 23 апреля 2013

DataGridTextColumn - это не что иное, как столбец с TextBlock в нем. Напишите стиль с TargetType как TextBlock и привяжите к нему свойство ElementStyle объекта DataGridTextColumn. Надеюсь, это поможет!

...