为什么我不能设置DataGridTextColumn的样式?

时间:2010-04-13 14:25:19

标签: wpf datagrid styles

我尝试使用以下代码为<{1}}创建样式

DataGridTextColumn

但是,Visual Studio 2010使用蓝线突出显示<Style TargetType="{x:Type DataGridTextColumn}"> ... </Style> 并详细说明:{x:Type DataGridTextColumn}

为什么会发生这种情况,我该如何解决?

6 个答案:

答案 0 :(得分:25)

您无法设置DataGridTextColumn的样式,因为DataGridTextColumn不是来自FrameworkElement(或FrameworkContentElement)。只有FrameworkElement等支持样式。

当您尝试在XAML中为任何不是FrameworkElementFrameworkContentElement的类型创建样式时,您会收到该错误消息。

你是如何解决这个问题的?与任何问题一样,只要有遗嘱就有办法。在这种情况下,我认为最简单的解决方案是为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。这可能对你的情况没有影响,但我想我应该提一下。

答案 1 :(得分:5)

样式标签必须在正确的位置。您的数据网格现在可能看起来像这样:

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

您最初可能会尝试直接在DataGridTextColumn元素中添加样式标记,这将无效。但是,您可以为&#34; DataGridTextColumn.ElementStyle&#34;创建元素。和/或&#34; DataGridTextColumn.EditingElementStyle&#34;就在&#34; DataGridTextColumn&#34;元件。然后,每个元素标记中都可以包含样式标记:

    <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(这首先让我了!)。

答案 2 :(得分:1)

这是雷伯恩斯回答的补充。我第一次无法自己实现它,但在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>

答案 3 :(得分:0)

对pedrito答案的一小部分补充。一切正常,但是如果origStyle具有基本样式,则基本样式的设置器将被丢弃。

要解决此问题,我们需要获取所有设置器:

 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;

            var baseSetters = GetBaseSetters(origStyle);
            var allSetters = baseSetters.Concat(origStyle.Setters.OfType<Setter>());

            foreach (var setter in allSetters)
            {
                newStyle.Setters.Add(setter);
            }

            column.ElementStyle = newStyle;
        }
    }

    private static IEnumerable<Setter> GetBaseSetters(Style style)
    {
        return style.BasedOn?.Setters.OfType<Setter>().Concat(GetBaseSetters(style.BasedOn)??new Setter[0]);
    }

答案 4 :(得分:-1)

更简单:

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

答案 5 :(得分:-5)

DataGridTextColumn只是一个包含TextBlock的列。使用TargetType作为TextBlock编写样式,并将DataGridTextColumn的ElementStyle属性绑定到它。希望有所帮助!