【问题标题】:Why can't I style a DataGridTextColumn?为什么我不能设置 DataGridTextColumn 的样式?
【发布时间】:2011-02-07 11:41:37
【问题描述】:

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

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

但是,Visual Studio 2010 用蓝线突出显示 {x:Type DataGridTextColumn} 并详细说明:Exception has been thrown by the target of an invocation.

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

【问题讨论】:

  • 看看这个链接,它就像一个用于设置数据网格样式的备忘单:blogs.msdn.microsoft.com/jaimer/2009/01/20/…
  • Ray 的解决方案对您有用吗?如果是这样,我很乐意看到一些示例代码。我在这里没有得到的部分是样式的 TargetType 是 FrameworkElement,那么您如何设置像 CanUserSort=False 这样的属性,例如?干杯

标签: wpf datagrid styles


【解决方案1】:

您无法设置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。这可能对您的情况没有影响,但我想我应该提一下。

【讨论】:

  • 这种情况下的触发器呢,它们会像往常一样工作吗?
【解决方案2】:

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

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

您最初可能会尝试直接在 DataGridTextColumn 元素中添加样式标记,但这是行不通的。但是,您可以在“DataGridTextColumn”元素内为“DataGridTextColumn.ElementStyle”和/或“DataGridTextColumn.EditingElementStyle”创建元素。这些元素标签中的每一个都可以在其中包含样式标签:

    <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(这让我一开始很感动!)。

【讨论】:

    【解决方案3】:

    这更像是对 Ray Burns 答案的补充。我一开始无法自己实现它,但在 mm8 (https://stackoverflow.com/a/46690951/5381620) 的帮助下,我让它运行起来了。工作得很好。对于遵循这种附加属性方法时遇到问题的其他人,完整的代码 sn-p 可能会有所帮助。

    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>
    

    【讨论】:

      【解决方案4】:

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

      要解决这个问题,我们需要获取所有 setter:

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

      【讨论】:

        【解决方案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 的样式,并将 DataGridTextColumn 的 ElementStyle 属性绑定到它。希望有帮助!

        【讨论】:

        • TextBlock 不是 System.Windows.Controls.Control,所以这不起作用。
        猜你喜欢
        • 2021-11-28
        • 1970-01-01
        • 2019-01-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多