Подтвердить что ты не робот

Почему я не могу создать стиль DataGridTextColumn?

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

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

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

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

4b9b3361

Ответ 1

Вы не можете создать стиль 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 будет иметь источник BaseValueS Local вместо DefaultStyle. Это, вероятно, не будет иметь никакого значения в вашем случае, но я думал, что должен упомянуть об этом.

Ответ 2

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

    <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 при редактировании (это меня сначала!).

Ответ 4

Это больше похоже на ответ Ray Burns. Я сначала не смог реализовать его самостоятельно, но с помощью mm8 (/questions/178394/style-all-datagridtextcolumns-via-attachedproperty/1000456#1000456) я начал работать. Работает очень хорошо. Для других людей, у которых есть проблемы, связанные с этим подключенным методом собственности, возможно, полезен полный фрагмент кода.

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>

Ответ 5

Проще всего:

<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>

Ответ 6

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