WPF DataGrid 通过自定义表头模拟首行固定
独立观察员 2021 年 9 月 25 日
最近工作中要在 WPF 中做个表格,自然首选就是 DataGrid 控件了。问题是,UI 设计的表格是在首行有一个新增按钮,那一行样式和其它数据行是一样的,就在表头下面那行。
一开始,参照了其它界面,这一行还是通过数据行来做,只不过绑定的数据项中有一个特殊的属性来表明这是个特殊行,界面上通过数据模板选择器来自动展现出特别的内容(新增按钮及相关文字,并把其它字段内容隐藏)。做好之后,工作地很好,可惜好景不长,同事提醒说数据多的话,出现滚动条,这一行要固定住,不过有个属性可以直接设置。后来发现,他记错了,属性是有,不过是用来设置前几列固定的(FrozenColumnCount),而关于行固定,则没有提供任何相关功能。
经过搜索解决方案,发现没有能直接方便地使用的,有个国外大佬在问答网站上提供了尾行固定的方案,并且说了一句,首行固定更简单,只需要自定义表头就行了。那就听他的吧,我们来看看怎么通过自定义表头,来达到模拟首行固定的效果。
先来看看效果:
就是说,有个加号的那行,实际上是属于表头的,这个通过滚动条的范围也能看出(这里滚动条把表头遮住了,这个也可以改掉,之后再说吧)。
界面代码结构如下:
可以看到资源里有一个普通表头样式、一个用于特殊列的特殊表头样式,还有行样式、单元格样式等,还有个包含了新增按钮的控件模板的样式。最后就是表格控件 DataGrid 了,使用了上面这些资源,默认使用的是普通表头样式,所以普通列就不用特地指定样式了。另外,表格通过 ItemsSource 绑定了数据,通过 SelectedItem 绑定了选中项。
先来看看普通表头样式,这里实际是设置表头中每一格的样式。主要就是在原来表头的基础上新增了一行,第一行还是放原来的表头内容(基本就是标题文本),然后第二行就是空出来,给有需要的特殊列留好空间,或者说是与特殊列统一,具体见下图:
特殊表头样式继承于普通表头样式,所以只需要对控件模板进行设置即可。同样是分为两行,并保留了普通表头的框架及样式,然后把内容占位元素 ContentPresenter 移到外面,并让它占据两行覆盖在上面(具体内容则由使用的列来设置),如下图:
顺便来看看新增操作的控件样式,也就是使用 Border 做了个加号,并把 MouseDown 事件转换成相关命令,整体应用于某个内容控件 ContentControl,如下图。当然,使用 Button 来做也是可以的。
最后来看 DataGrid 表格的列集合,每列都是 DataGridTemplateColumn 类型。前面也说过 DataGrid 指定了普通表头样式作为默认的表头样式,所以普通列就不用额外设置了,而且由于内容简单,所以直接使用 Header 属性设置表头内容(列标题)。单元格的数据内容,都是设置了数据模板 DataTemplate,普通列是绑定了类的某个属性,特殊列这里是一个删除按钮。关键的是特殊列的表头,首先是指定了表头样式,然后通过 Header 标签来设置内容,内容同样是分为两行,第一行就是列标题内容,第二行通过一个内容控件 ContentControl 将那个加号加载进来。整个表头内容占据的就是特殊表头样式中那个同样跨了两行的 ContentPresenter,只需要设置内容,不需要设置框架和样式,因为已经在特殊列表头样式中设置好了。
当然,方法有很多,具体细节每个人写的可能也不一样。本文只是设置了一列特殊列,大家可以根据具体业务需求自行发挥。下面给出源码地址,主要看本文介绍的内容,其它操作逻辑暂时比较简陋。主项目是 .NET 5 的,然后,克隆下载源码需注意下载子模块,可以参考《通过 GitExtensions 来使用 Git 子模块功能》。
源码地址:https://gitee.com/dlgcy/DLGCY_WPFPractice/tree/Blog20210925
好了,该去吃饭了,祝大家生活愉快!