Guide to WPF DataGrid formatting using bindings

Peter Huber SG, 25 Nov 2013 CPOL

   4.83 (13 votes)

1

2

3

4

5
4.83/5 - 13 votes
μ 4.83, σa 0.93 [?]
 
Rate:
Add a reason or comment to your vote: x
Votes of 3 or less require a comment
 
Using Style and Binding to control WPF DataGridCell appearance.

Introduction

Formatting WPF DataGrid content depending on business logic data is way too difficult. Especially since MSDN is not telling you anything about it. I have spent weeks to figure out how to get the binding right. Let me show you how it is done to save you time and endless searches on the Internet.

WPF DataGrid  Structure

The container hierarchy of a DataGrid looks like this:

DataGrid
DataGridRows
DataGridCell
TextBlock

A DataGrid contains DataGridRows which contain DataGridCells which contain exactly one TextBlock, if it is a TextColumn and in read mode (editing mode uses a TextBox). Of course, the visual tree is a bit more complicated:

Note that DataGridColumn is not part of the visual tree. Whatever is defined in DataGridColumn will be applied to all cells of that column.

WPF Binding Basics

The binding gets assigned to a FrameworkElement property, which constitutes the target of the binding.

WPF needs two source information to make a binding work:

  • Source: Which object provides the information
  • Path: Which property of the source should be used

Usually, the Source gets inherited from the DataContext of a parent container, often the Window itself. But DataGrid's DataContext cannot be used for the binding of rows and cells, because each row needs to bind to a different business logic object.

DataGridColumn specifies the binding for the value to be displayed in the cell with the DataGridColumn.Binding property. The DataGrid creates during runtime a binding for every TextBlock.Text. Unfortunately, the DataGrid does not support binding for any other property of TextBlock. If you try to setup a style for the TextBlock yourself, the binding will most likely fail, because it wouldn't know which business object from the ItemsSource to use.

Business data used

The business data example is based on some stock taking figures. A stock item looks like this:

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

The sample data:

Name Quantity   IsObsolete  
Many items  100 false
Enough items 10 false
Shortage item 1 false
Item with error   -1 false
Obsolete item 200 true 

Connecting a DataGrid with business data

Even connecting a DataGrid with the business data is not trivial. Basically, a CollectionViewSource is used to connect the DataGrid with the business data:

The CollectionViewSource does the actual data navigation, sorting, filtering, etc.

    1. Define a CollectionViewSource in Windows.Resource
<Window.Resources>
<CollectionViewSource x:Key="ItemCollectionViewSource" CollectionViewType="ListCollectionView"/>
</Window.Resources>
      The gotcha here is that you

must

      set the

CollectionViewType

      . If you don't, the GridView will use

BindingListCollectionView

      , which does not support sorting. Of course, MSDN does not explain this anywhere.
    1. Set the DataContext of the DataGrid to the CollectionViewSource .
<DataGrid
DataContext="{StaticResource ItemCollectionViewSource}"
ItemsSource="{Binding}"
AutoGenerateColumns="False"
CanUserAddRows="False">
    1. In the code behind, find the CollectionViewSource and assign your business data to the Source property
//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;</stockitem>

In this article, data gets only read. If the user should be able to edit the data, use an ObservableCollection.

DataGrid Formatting

Formatting a column

Formatting a whole column is easy. Just set the property, like Fontweight directly in the DataGridColumn:

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

The binding here is not involved with the formatting, but specifies the content of the cell (i.e., Text property of TextBlock).

Formatting complete rows

Formatting the rows is special, because there will be many rows. The DataGrid offers for this purpose the RowStyle property. This style will be applied to every DataGridRow.

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

The DatGridRow has an Item property, which contains the business logic object for that row. The binding for the DataRow must therefore bind to itself ! The path is a bit surprising, because Item is of type Object and doesn't know any business data properties. But the WPF binding applies a bit of magic and finds the Quantity property of StockItem anyway.

In this example, the background of a row depends on the value of the Quantity property of the business object. If there are many items in stock, the background should be white, if only few are left, the background should be grey. The QuantityToBackgroundConverter performs the necessary calculation:

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

Formatting a cell based on the displayed value

Formatting just a cell instead of the whole row is a challenge. In a text column, the cell has a TextBlock which needs to be styled. To create a Style for TextBlocks is easy, but how can the TextBlock property be bound to the proper business object ? The DataGrid is binding already the Text property of the TextBlock. If the styling depends only on the cell value, we can simply use a self binding to this Text property.

Example: In our stock grid, the Quantity should always be greater than or equal zero. If a quantity is negative, it is an error and should be displayed in red:

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

Formatting a cell based on business logic data

The most complex case is if the cell format does not depend on the cell value, but some other business data. In our example, the quantity of an item should be displayed as strike through if it is obsolete. To achieve this, the TextDecorations property needs to be linked to the business object of that row. Meaning the TextBlock has to find the parent DataGridRow. Luckily, binding to a parent visual object can be done with a relative source:

<Setter Property="TextDecorations"
Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGridRow}},
Path =Item.IsObsolete,
Converter={StaticResource IsObsoleteToTextDecorationsConverter}}" />
public class IsObsoleteToTextDecorationsConverter: IValueConverter {

  public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
if (value is bool) {
if ((bool)value) {
TextDecorationCollection redStrikthroughTextDecoration =
TextDecorations.Strikethrough.CloneCurrentValue();
redStrikthroughTextDecoration[0].Pen = new Pen {Brush=Brushes.Red, Thickness = 3 };
return redStrikthroughTextDecoration;
}
}
return new TextDecorationCollection();
} public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
throw new NotImplementedException();
}
}

Code

See the Zip file for the complete source code of the sample.

&amp;lt;a href="http://ad.doubleclick.net/N6839/jump/lqm.codeproject.site/Platforms-Frameworks-Libraries/Windows-Presentation-Foundation/Controls;sz=300x250;ord=635503472155835127?"&amp;gt;&amp;lt;img src="http://ad.doubleclick.net/N6839/ad/lqm.codeproject.site/Platforms-Frameworks-Libraries/Windows-Presentation-Foundation/Controls;sz=300x250;ord=635503472155835127?" width="300px" height="250px" /&amp;gt;&amp;lt;/a&amp;gt;

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

WPF DataGrid显格式的更多相关文章

  1. WPF DataGrid常用属性记录

    WPF DataGrid常用属性记录 组件常用方法: BeginEdit:使DataGrid进入编辑状态. CancelEdit:取消DataGrid的编辑状态. CollapseRowGroup:闭 ...

  2. WPF DATAGRID - COMMITTING CHANGES CELL-BY-CELL

    In my recent codeproject article on the DataGrid I described a number of techniques for handling the ...

  3. WPF DataGrid某列使用多绑定后该列排序失效,列上加入 SortMemberPath 设置即可.

    WPF DataGrid某列使用多绑定后该列排序失效 2011-07-14 10:59hdongq | 浏览 1031 次  悬赏:20 在wpf的datagrid中某一列使用了多绑定,但是该列排序失 ...

  4. xceed wpf datagrid

    <!--*********************************************************************************** Extended ...

  5. 获取wpf datagrid当前被编辑单元格的内容

    原文 获取wpf datagrid当前被编辑单元格的内容 确认修改单元个的值, 使用到datagrid的两个事件 开始编辑事件 BeginningEdit="dataGrid_Beginni ...

  6. WPF DataGrid绑定一个组合列

    WPF DataGrid绑定一个组合列 前台: <Page.Resources>        <local:InfoConverter x:Key="converter& ...

  7. WPF DataGrid自定义样式

    微软的WPF DataGrid中有很多的属性和样式,你可以调整,以寻找合适的(如果你是一名设计师).下面,找到我的小抄造型的网格.它不是100%全面,但它可以让你走得很远,有一些非常有用的技巧和陷阱. ...

  8. WPF DataGrid Custommization using Style and Template

    WPF DataGrid Custommization using Style and Template 代码下载:http://download.csdn.net/detail/wujicai/81 ...

  9. 编写 WPF DataGrid 列模板,实现更好的用户体验

    Julie Lerman 下载代码示例 最近我在为一个客户做一些 Windows Presentation Foundation (WPF) 方面的工作. 虽然我提倡使用第三方工具,但有时也会避免使用 ...

随机推荐

  1. PHP初入,简易网页整理(布局&特效的使用)

    html><html> <head> <meta charset="UTF-8"> <title></title> ...

  2. JOptionPane弹框常用实例

    最近在做swing程序中遇到使用消息提示框的,JOptionPane类其中封装了很多的方法. 很方便的,于是就简单的整理了一下. 1.1 showMessageDialog 显示一个带有OK 按钮的模 ...

  3. Java 多线程(二) 线程的实现

    线程的实现 在Java中通过run方法为线程指明要完成的任务,有两种技术来为线程提供run方法: 1.继承Thread类并重写它的run方法.之后创建这个子类的对象并调用start()方法. 2.通过 ...

  4. 团队作业3--需求改进&系统设计

    小学生四则运算练习软件APP 一.需求&原型改进 1.给目标用户展现原型,与目标用户进一步沟通理解需求 我们的主要目标用户是小学生,次要目标用户是小学教师 场景一:小明一个三年级的学生,放学回 ...

  5. 英语词典Alpha版本发布说明

    Alpha版本发布说明 功能: ·简洁的应用界面,不被无良的广告弹窗影响  ·功能直接,在需要查词时及时出现,没有每日一句精选文章等杀了你的流量,在学习过程中更加专注! ·采用金山词霸API,提供发音 ...

  6. 201521123025《java程序设计》第七周学习总结

    1. 本周学习总结 2. 书面作业 Q1.ArrayList代码分析 1.1 解释ArrayList的contains源代码 public boolean contains(Object o) { r ...

  7. 201521123079《java程序设计》第7周学习总结

    1. 本周学习总结 以你喜欢的方式(思维导图或其他)归纳总结集合相关内容. 2. 书面作业 1.ArrayList代码分析 1.1 解释ArrayList的contains源代码 public boo ...

  8. 201521123087 《Java程序设计》第1周学习总结

    1.学习总结 初步了解面对对象编程思想 学会安装JDK和设置JAVA_HOME,PATH,CLASSPATH环境变量 简单了解java 2.书面作业 1.为什么java程序可以跨平台运行?执行java ...

  9. 201521123064 《Java程序设计》第13周学习总结

    本次作业参考文件 正则表达式参考资料 1. 本章学习总结 1.1 以你喜欢的方式(思维导图.OneNote或其他)归纳总结多网络相关内容. ① InetAddress(IP地址对应的类) InetAd ...

  10. 理解G1垃圾回收日志

    本篇文章主要介绍在-XX:+PrintGCDetails选项的情况下G1 GC log打印的信息.(注:本文假设读者对G1算法的基本原理已经有所了解) 下面是一段G1垃圾收集器相关的log信息 0.5 ...