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

时间:2022-10-24 21:04:28

I tried to create a Style for DataGridTextColumn with the following code

我尝试用以下代码为DataGridTextColumn创建一个样式

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

However, Visual Studio 2010 highlights {x:Type DataGridTextColumn} with a blue line and elaborates: Exception has been thrown by the target of an invocation.

但是,Visual Studio 2010突出显示了{x:Type DataGridTextColumn},并使用了蓝色的线,并详细说明了:异常被调用的目标抛出。

Why does this happen and how do I fix it?

为什么会发生这种情况,我该如何修复呢?

6 个解决方案

#1


18  

You can't style the DataGridTextColumn because DataGridTextColumn does not derive from FrameworkElement (or FrameworkContentElement). Only FrameworkElement, etc supports styling.

您不能对DataGridTextColumn进行样式化,因为DataGridTextColumn不是由FrameworkElement(或FrameworkContentElement)派生的。只有FrameworkElement等支持样式。

When you attempt to create a style in XAML for any type that is not a FrameworkElement or FrameworkContentElement you get that error message.

当您试图在XAML中为非FrameworkElement或FrameworkContentElement的任何类型创建样式时,您将获得该错误消息。

How do you solve this? As with any problem, where there is a will there is a way. In this case I think the easiest solution is to create an attached property for DataGrid to assign a DataGridColumn style:

如何解决这个问题?和任何问题一样,有志者事竟成。在这种情况下,我认为最简单的解决方案是为DataGrid创建一个附加属性来分配DataGridColumn样式:

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

The implementation would be something along these lines:

执行工作将是这样的:

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);
  }
}

The way this works is, any time the attached property is changed, a handler is added for the Columns.CollectionChanged event on the grid. When the CollectionChanged event fires, all columns are updated with the style that was set.

它的工作方式是,每当附加的属性发生更改时,为列添加一个处理程序。网格上的集合更改事件。当集合更改事件触发时,所有列都用设置的样式更新。

Note that the above code does not handle the situation where a style is removed and re-added gracefully: Two event handlers are registered. For a really robust solution you would want to fix this by adding another attached property containing the event handler so the event handler could be unregistered, but for your purpose I think this is unimportant.

注意,上面的代码不处理删除样式并优雅地重新添加样式的情况:注册了两个事件处理程序。对于真正健壮的解决方案,您可能希望通过添加另一个包含事件处理程序的附加属性来修复这个问题,以便事件处理程序可以被取消注册,但是出于您的目的,我认为这并不重要。

Another caveat here is that the direct use of SetBinding and SetValue will cause the DependencyProperty to have a BaseValueSource of Local instead of DefaultStyle. This will probably make no difference in your case but I thought I should mention it.

这里的另一个警告是,直接使用SetBinding和SetValue将导致DependencyProperty具有一个本地的BaseValueSource,而不是DefaultStyle。这可能对你的情况没有影响,但我想我应该提一下。

#2


5  

The style tag has to go in the right place. Your datagrid may look something like this right now:

样式标签必须放在正确的位置。您的datagrid现在可能是这样的:

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

You might initially try to add the style tag directly within the DataGridTextColumn element which will not work. You can however create elements for "DataGridTextColumn.ElementStyle" and or "DataGridTextColumn.EditingElementStyle" just within the "DataGridTextColumn" element. Each of those element tags can then have style tags within them:

您可能首先尝试在DataGridTextColumn元素中直接添加样式标签,但它不能工作。但是,您可以为“DataGridTextColumn”创建元素。ElementStyle”,或“DataGridTextColumn。仅在“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>

One style will be applied to viewing and the other will be applied when the cell is in edit mode. Note that it changes from a TextBlock when viewing to a TextBox when editing (This got me at first!).

当单元格处于编辑模式时,将应用一种样式用于查看,另一种样式将被应用。请注意,它在编辑时从一个文本块变为一个文本框(这让我第一次得到这个!)

#3


2  

Take a look at this link, its like a cheat sheet for styling datagrids:

看看这个链接,它就像一个样式化datagrid的备忘单:

http://blogs.msdn.com/jaimer/archive/2009/01/20/styling-microsoft-s-wpf-datagrid.aspx

http://blogs.msdn.com/jaimer/archive/2009/01/20/styling-microsoft-s-wpf-datagrid.aspx

#4


1  

This is more an addition to Ray Burns answer. I first wasn't able to implement it on my own, but with help of mm8 (https://*.com/a/46690951/5381620) I got it running. Works really fine. For other people who have problems following this attached property approach maybe a full code snippet is helpful.

这是对雷·伯恩斯的补充回答。我首先不能自己实现它,但是在mm8 (https://*.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

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>

Edit: In first approach I did overwrite the whole style. In new version it is still possible to maintain other styles modifications like this one

编辑:在第一种方法中,我重写了整个样式。在新版本中,仍然可以保留其他样式的修改,比如这个。

<DataGridTextColumn.ElementStyle>
    <Style TargetType="{x:Type TextBlock}">
        <Setter Property="Foreground" Value="Red"/>
    </Style>
</DataGridTextColumn.ElementStyle>

#5


-1  

More simple:

更简单:

<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


-5  

A DataGridTextColumn is nothing but a column with a TextBlock in it. Write a style with the TargetType as TextBlock and bind the ElementStyle property of the DataGridTextColumn to it. Hope that helps!

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

#1


18  

You can't style the DataGridTextColumn because DataGridTextColumn does not derive from FrameworkElement (or FrameworkContentElement). Only FrameworkElement, etc supports styling.

您不能对DataGridTextColumn进行样式化,因为DataGridTextColumn不是由FrameworkElement(或FrameworkContentElement)派生的。只有FrameworkElement等支持样式。

When you attempt to create a style in XAML for any type that is not a FrameworkElement or FrameworkContentElement you get that error message.

当您试图在XAML中为非FrameworkElement或FrameworkContentElement的任何类型创建样式时,您将获得该错误消息。

How do you solve this? As with any problem, where there is a will there is a way. In this case I think the easiest solution is to create an attached property for DataGrid to assign a DataGridColumn style:

如何解决这个问题?和任何问题一样,有志者事竟成。在这种情况下,我认为最简单的解决方案是为DataGrid创建一个附加属性来分配DataGridColumn样式:

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

The implementation would be something along these lines:

执行工作将是这样的:

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);
  }
}

The way this works is, any time the attached property is changed, a handler is added for the Columns.CollectionChanged event on the grid. When the CollectionChanged event fires, all columns are updated with the style that was set.

它的工作方式是,每当附加的属性发生更改时,为列添加一个处理程序。网格上的集合更改事件。当集合更改事件触发时,所有列都用设置的样式更新。

Note that the above code does not handle the situation where a style is removed and re-added gracefully: Two event handlers are registered. For a really robust solution you would want to fix this by adding another attached property containing the event handler so the event handler could be unregistered, but for your purpose I think this is unimportant.

注意,上面的代码不处理删除样式并优雅地重新添加样式的情况:注册了两个事件处理程序。对于真正健壮的解决方案,您可能希望通过添加另一个包含事件处理程序的附加属性来修复这个问题,以便事件处理程序可以被取消注册,但是出于您的目的,我认为这并不重要。

Another caveat here is that the direct use of SetBinding and SetValue will cause the DependencyProperty to have a BaseValueSource of Local instead of DefaultStyle. This will probably make no difference in your case but I thought I should mention it.

这里的另一个警告是,直接使用SetBinding和SetValue将导致DependencyProperty具有一个本地的BaseValueSource,而不是DefaultStyle。这可能对你的情况没有影响,但我想我应该提一下。

#2


5  

The style tag has to go in the right place. Your datagrid may look something like this right now:

样式标签必须放在正确的位置。您的datagrid现在可能是这样的:

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

You might initially try to add the style tag directly within the DataGridTextColumn element which will not work. You can however create elements for "DataGridTextColumn.ElementStyle" and or "DataGridTextColumn.EditingElementStyle" just within the "DataGridTextColumn" element. Each of those element tags can then have style tags within them:

您可能首先尝试在DataGridTextColumn元素中直接添加样式标签,但它不能工作。但是,您可以为“DataGridTextColumn”创建元素。ElementStyle”,或“DataGridTextColumn。仅在“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>

One style will be applied to viewing and the other will be applied when the cell is in edit mode. Note that it changes from a TextBlock when viewing to a TextBox when editing (This got me at first!).

当单元格处于编辑模式时,将应用一种样式用于查看,另一种样式将被应用。请注意,它在编辑时从一个文本块变为一个文本框(这让我第一次得到这个!)

#3


2  

Take a look at this link, its like a cheat sheet for styling datagrids:

看看这个链接,它就像一个样式化datagrid的备忘单:

http://blogs.msdn.com/jaimer/archive/2009/01/20/styling-microsoft-s-wpf-datagrid.aspx

http://blogs.msdn.com/jaimer/archive/2009/01/20/styling-microsoft-s-wpf-datagrid.aspx

#4


1  

This is more an addition to Ray Burns answer. I first wasn't able to implement it on my own, but with help of mm8 (https://*.com/a/46690951/5381620) I got it running. Works really fine. For other people who have problems following this attached property approach maybe a full code snippet is helpful.

这是对雷·伯恩斯的补充回答。我首先不能自己实现它,但是在mm8 (https://*.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

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>

Edit: In first approach I did overwrite the whole style. In new version it is still possible to maintain other styles modifications like this one

编辑:在第一种方法中,我重写了整个样式。在新版本中,仍然可以保留其他样式的修改,比如这个。

<DataGridTextColumn.ElementStyle>
    <Style TargetType="{x:Type TextBlock}">
        <Setter Property="Foreground" Value="Red"/>
    </Style>
</DataGridTextColumn.ElementStyle>

#5


-1  

More simple:

更简单:

<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


-5  

A DataGridTextColumn is nothing but a column with a TextBlock in it. Write a style with the TargetType as TextBlock and bind the ElementStyle property of the DataGridTextColumn to it. Hope that helps!

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