使用绑定进行WPF DataGrid格式化的指南

目录

介绍

WPF DataGrid结构

WPF绑定基础

使用的业务数据

将DataGrid与业务数据连接

DataGrid格式

格式化列

格式化完整行

根据显示的值格式化单元格

根据业务逻辑数据格式化单元

ElementStyle与CellStyle


介绍

根据业务逻辑数据格式化WPF DataGrid内容太困难了,特别是因为MSDN没有告诉您任何有关它的信息。我花了几周时间想办法把它弄好。让我向您展示如何做到这一点,以节省您的时间并在Internet上进行无尽的搜索。

WPF DataGrid结构

DataGrid的容器层次结构是这样的:

DataGrid 
  DataGridRows
     DataGridCell
       TextBlock 

DataGrid包含DataGridRow, DataGridCell包含一个精确的TextBlock,如果它是一个TextColumn并且处于读取模式(编辑模式使用一个TextBox)。当然,可视化树有点复杂:

请注意,DataGridColumn不是视觉树的一部分。DataGridColumn定义中的任何内容都将应用于该列的所有单元格。

WPF绑定基础

绑定被分配给FrameworkElement属性,该属性构成绑定的目标。

WPF需要两个源信息才能进行绑定工作:

  • 来源:提供信息的对象
  • 路径:应使用源的哪个属性

通常,Source从父容器DataContext(通常是Window本身)继承。但是DataGridDataContext不能被用于行和单元格的结合,因为每一行需要绑定到不同的业务逻辑对象。

DataGridColumn使用DataGridColumn.Binding属性指定要显示在单元格中的值的绑定。在DataGrid运行时,为每一个TextBlock.Text创建绑定。不幸的是,DataGrid并不支持对的任何其他TextBlock属性的绑定。如果尝试为自己的TextBlock设置样式,则绑定很可能会失败,因为它不知道要使用ItemsSource中的哪个业务对象。

使用的业务数据

业务数据示例基于一些盘点数字。库存项如下所示:

public class StockItem {
  public string Name { get; set; }
  public int Quantity { get; set; }
  public bool IsObsolete { get; set; }
}

 

样本数据:

名称

数量

已过时

很多项目

100

false

足够的物品

10

false

短缺项目

1

false

有错误的项目

-1

false

过时的物品

200

true

DataGrid与业务数据连接

甚至将DataGrid与业务数据连接起来也不是一件容易的事。基本上,CollectionViewSource用来将DataGrid和业务数据联系起来:

CollectionViewSource执行实际的数据导航、排序、过滤等

<Window.Resources>
    <CollectionViewSource x:Key="ItemCollectionViewSource"  CollectionViewType="ListCollectionView"/>
</Window.Resources> 
<DataGrid
  DataContext="{StaticResource ItemCollectionViewSource}"
  ItemsSource="{Binding}"
  AutoGenerateColumns="False"
  CanUserAddRows="False">  
//create business data
var itemList = new List<stockitem>();
itemList.Add(new StockItem {Name= "Many items",      Quantity=100, IsObsolete=false});
itemList.Add(new StockItem {Name= "Enough items",    Quantity=10,  IsObsolete=false});
...
 
//link business data to CollectionViewSource
CollectionViewSource itemCollectionViewSource;
itemCollectionViewSource = (CollectionViewSource)(FindResource("ItemCollectionViewSource"));
itemCollectionViewSource.Source = itemList; 
  1. Windows.Resource中定义CollectionViewSource
  2. 需要注意的是,您必须设置CollectionViewType。如果您不这样做,GridView则将使用BindingListCollectionView,它不支持排序。当然,MSDN不会在任何地方解释这一点。
  3. 设定DataGridDataContextCollectionViewSource
  4. 在后面的代码中,找到CollectionViewSource并将您的业务数据分配给Source属性

在本文中,仅读取数据。如果用户应该能够编辑数据,请使用ObservableCollection

DataGrid格式

格式化列

格式化整个列很容易。只需直接在DataGridColumn中设置Fontweight属性即可:

<DataGridTextColumn Binding="{Binding Path=Name}" Header="Name" FontWeight="Bold"/>

 

此处的绑定不涉及格式设置,但指定单元格的内容(即TextBlockText属性)。

格式化完整行

格式化行是特殊的,因为将有很多行。DataGrid为此提供了RowStyle属性。这种样式将适用于每一个DataGridRow

<datagrid.rowstyle>
  <style targettype="DataGridRow">
    <Setter Property="Background" Value="{Binding RelativeSource={RelativeSource Self},
      Path=Item.Quantity, Converter={StaticResource QuantityToBackgroundConverter}}"/>
  </style>
</datagrid.rowstyle>

 

DatGridRow有一个Item属性,它包含了该行的业务逻辑对象。因此,DataRow的绑定必须绑定到自身!该路径有点令人惊讶,因为Item是类型Object的并且不知道任何业务数据属性。但是WPF绑定应用了一些魔术,并且仍然找到了StockItemQuantity属性。

在此示例中,行的背景取决于业务对象的Quantity属性的值。如果库存中有很多物品,则背景应为白色,如果只剩少量,则背景应为灰色。QuantityToBackgroundConverter执行必要的计算:

 

class QuantityToBackgroundConverter: IValueConverter {
  public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
    if (value is int) {
      int quantity = (int)value;
      if (quantity>=100) return Brushes.White;
      if (quantity>=10) return Brushes.WhiteSmoke;
      if (quantity>=0) return Brushes.LightGray;
      return Brushes.White; //quantity should not be below 0
    }
    //value is not an integer. Do not throw an exception
    // in the converter, but return something that is obviously wrong
    return Brushes.Yellow;
  }
 
  public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
    throw new NotImplementedException();
   }
}  

 

注意

  1. 只能将某些属性设置为DataGridRow,例如Background颜色。其他属性,如Fonts,在DataGridCell中被设定,即TextBlock
  2. DataGridCellTextBlock等在DataGridRow上绘制。如果两者都设置为Background,则DataGridRowBackground将会被隐藏。

根据显示的值格式化单元格

仅格式化一个单元格而不是整个行是一个挑战。在文本列中,单元格具有需要设置样式的TextBlock元素。为TextBlock创建Style的操作很容易,但是如何将TextBlock属性绑定到适当的业务对象呢?DataGrid已经绑定TextBlockText属性。如果样式仅取决于单元格值,则可以简单地对该Text属性使用自绑定。

示例:在我们的股票表格中,该Quantity值应始终大于或等于零。如果数量为负,则为错误,应以红色显示:

<Setter Property="Foreground" 
  Value="{Binding 
    RelativeSource={RelativeSource Self}, 
    Path=Text, 
    Converter={StaticResource QuantityToForegroundConverter}}" /> 

 

根据业务逻辑数据格式化单元

最复杂的情​​况是,单元格格式不取决于单元格值,而是取决于其他一些业务数据。在我们的示例中,如果一个项目的数量已过时,则应显示为删除线。为此,需要将TextDecorations属性链接到该行的业务对象。意思是TextBlock必须找到父级DataGridRow。幸运的是,可以使用相对源来绑定到父级可视化对象:

 

<Window.Resources>
  <Style x:Key="QuantityStyle" TargetType="TextBlock">
    ...
    <Setter Property="TextDecorations"
      Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGridRow}},
      Path =Item.IsObsolete,
      Converter={StaticResource IsObsoleteToTextDecorationsConverter}}" />
  </Style>
</Window.Resources>
...
<DataGrid ...>
  ...
  <DataGrid.Columns>
   ...
   <DataGridTextColumn Binding="{Binding Path=Quantity}" Header="Quantity" 
     ElementStyle="{StaticResource QuantityStyle}"/>
  </DataGrid.Columns>
 </DataGrid>

ElementStyleCellStyle

DataGridTextColumnDataGridBoundColumn继承,而从DataGridColumn继承。

DataGridBoundColumn它继承属性ElementStyle。此样式将应用于TextBlock控件。

DataGridColumn它继承属性Cell。此样式将应用于DataGridCell控件。

请注意,在上面的可视树中,DataGridCell包含一个TextBlockDataGridCell首先被绘制,然后TextBlock在上面。

因为TextBlockDataGridCellControl继承,都具有背景、边框、字体、前景、填充和内容对齐的属性。意思是可以在ElementStyleCellStyle中设置Background颜色。

什么时候应该使用?我们在这里使用ElementStyle,因为它还支持TextBlock设置诸如TextDecorations的特定属性。对于某些属性,如BackgroundCellStyle应被使用,因为它覆盖了所有单元格,而ElementStyle设置了仅文本块的背景,而文本块仅覆盖了单元格的一部分。

也可以同时设置两种样式。如果他们用不同的值设置相同的属性,则ElementStyle获胜。

https://www.codeproject.com/Articles/683429/Guide-to-WPF-DataGrid-Formatting-Using-Bindings

  • 0
    点赞
  • 0
    收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:酷酷鲨 设计师:CSDN官方博客 返回首页
评论
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值