GridThemes
结合 BuildProvider 和 IExtenderProvider,为 ASP.NET GridViews 创建声明式的条件格式化框架。
引言
GridThemes 是一个为 ASP.NET 2.0 设计的类集合,它允许使用声明式结构来应用条件格式化到多个 GridView
控件。通常,开发人员会捕获 Grid 的 RowDataBound
事件来应用条件格式化。然而,有了 GridThemes,页面设计者可以通过设置一个属性来更改此 Grid 的格式。
改为这样。
或者这个
当需要逐个单元格进行条件格式化,或者在整个项目中一致地应用此类格式化时,此框架非常有用。当 AutoGenerateColumns
应用于 GridView
且否则无法进行列格式化时,它也很有用。
GridThemes 程序集结合了
- 一个自定义的
BuildProvider
;构建器识别 App_Code 文件夹中为 GridThemes 配置的文件。它解释这些文件中声明式的标签,这些标签指定了条件和格式化指令,并生成可以响应GridView
的RowDataBound
事件的编程方法。 - 一个自定义的
IExtenderProvider
实现;当扩展控件存在于 Web 窗体上时,它将GridTheme
属性添加到该窗体上的所有GridView
控件。 - 一个自定义的
UITypeEditor
子类;编辑器列出构建器构建的所有 GridThemes,以便分配给GridTheme
属性。
这些自定义类协同工作,使得以声明式和可重用的方式对 GridView
进行逐个单元格的条件格式化成为可能。
使用代码
以下列表总结了使用 GridThemes 的步骤
- 配置您的 ASP.NET 应用程序
- 在 App_Code 文件夹中创建并存储 GridTheme 定义文件
- 将
GridThemeExtender
控件添加到您的GridView
的 .aspx 页面 - 将所需的 Theme 名称分配给
GridView
新增的GridTheme
属性
配置
要配置应用程序以使用 GridThemes,请将程序集 GridThemes.dll 复制到您项目的 bin 目录。然后,通过将以下条目添加到 web.config 文件的 <buildProviders>
部分来激活构建提供程序
<system.web>
<compilation debug="false">
<buildProviders>
<add extension=".gt"
type="UNLV.IAP.GridThemes.GridThemesBuildProvider, GridThemes"/>
</buildProviders>
</compilation>
</system.web>
此条目将 GridThemes 自定义构建器与 App_Code 文件夹中具有 .gt 扩展名的文件相关联。您可以根据需要替换其他扩展名。
创建 GridTheme 定义文件
GridTheme 文件遵循一个简单的 XML 格式来定义条件和格式化指令。该文件(或文件)应保存在 App_Code 文件夹中,并使用上面配置的扩展名。
基础: <Theme>
, <Apply>
每个单独主题的父标签是 <Theme>
标签。一个条件主题文件可以包含一个或多个 <Theme>
实例,每个都需要一个唯一的 id
属性,并且可以选择性地包含一个 title
属性。如果提供了 title
,扩展器在列出主题时将使用它;否则,将使用 id
。
<Apply>
标签用于为 GridView
表内的单个单元格分配格式化。<Apply>
标签中的属性被解释为 TableCell
对象的属性,类似于在 .aspx 页面中以声明方式应用单元格格式化。在下面的示例中,单元格右对齐、加粗,并具有浅蓝色背景色。
<Theme id="exampleTheme" title="Example Theme">
<Apply horizontalAlign="right" font-bold="true" backColor="lightBlue" />
</Theme>
<Apply>
标签中的属性名也可以遵循 propertyExpression
的语法,其中 property 是一个有效的 TableCell
属性名。这些“表达式”属性被解释为项目语言语法的代码表达式,而不是字面值。在此示例中,<Apply>
指令通过为 Text
属性指定一个表达式,为给定单元格的文本添加行号。RowIndex
和 CellText
是特殊变量,将在文章稍后介绍。
<Theme id="exampleTheme" title="Example Theme">
<Apply textExpression='string.Format("{0}. {1}", RowIndex, CellText)' />
</Theme>
基于行类型的格式化: <DataRow>
, <Header>
, <Footer>
行类型标签包括 <DataRow>
、<Header>
和 <Footer>
。这些标签封装了给定行类型的条件和格式化指令。以下示例为标题行和数据行单元格指定了不同的格式。
<Theme id="exampleTheme" title="Example Theme">
<Header>
<Apply backColor="DarkRed" foreColor="White" />
</Header>
<DataRow>
<Apply backColor="White" foreColor="Black" />
</DataRow>
</Theme>
条件格式化: <If>
, <Else>
, <ElseApply>
要定义逐个单元格的条件,请使用 <If>
标签。<If>
需要一个 test
属性,该属性以项目语言的语法指定条件。嵌套的 <Apply>
标签将在条件为 true 时提供格式化指令。嵌套的 <ElseApply>
标签将在条件为 false 时提供格式化指令。也支持嵌套的 <Else>
标签,允许在条件为 false 时提供额外的 <If>
和 <Apply>
标签层级。
以下示例演示了使用 <If>
和嵌套标签的方法。第一个示例展示了一个简单的 <If>
,其中包含一个格式化指令,如果当前单元格值是否定数(IsNegative
是一个特殊变量,将在文章稍后介绍),则应用此指令。
<If test='IsNegative'>
<Apply ForeColor='Red' />
</If>
此示例将文本颜色设置为蓝色,如果单元格值不是负数。
<If test='IsNegative'>
<Apply ForeColor='Red' />
<ElseApply ForeColor='Blue' />
</If>
这显示了使用 <Else>
标签而非 <ElseApply>
的相同格式化指令。
<If test='IsNegative'>
<Apply ForeColor='Red' />
<Else>
<Apply ForeColor='Blue' />
</Else>
</If>
下一个示例确定单元格值是否为数字,如果是,则右对齐文本。数字单元格的宽度也设置为 90 像素。如果单元格值不是数字,宽度设置为 200 像素。如果是数字,嵌套的 <If>
然后确定单元格值是否为负数并相应地更改颜色。
<If test='IsNumeric'>
<Apply HorizontalAlign='Right' Width='90px' />
<If test='IsNegative'>
<Apply ForeColor='Red' />
<Else>
<Apply ForeColor='Blue' />
</Else>
</If>
<ElseApply Width='200px' />
</If>
可以使用项目语言的语法来表达更复杂的条件。例如,以下 C# 语法将交替列的背景色设置为灰色。
<If test='CellIndex % 2 == 1'>
<Apply BackColor='Gray' />
<ElseApply BackColor='White' />
</If>
在 VB.NET 项目中,相同的条件如下所示。
<If test='CellIndex mod 2 = 1'>
<Apply BackColor='Gray' />
<ElseApply BackColor='White' />
</If>
如果使用大于或小于符号,请确保使用 XML 友好的 <
和 >
实体引用。在此示例中,大于 100 的单元格值将格式化为粗体。
<If test='CellValue > 100'>
<Apply Font-Bold='True' />
</If>
这个最后的条件示例展示了在 <Theme>
的上下文中如何使用 <If>
标签。它还演示了嵌套 <If>
标签的用法。任何文本为“n/a”的非数字单元格在应用此主题后将显示为“0”。请注意,在此处使用了 ==
作为相等运算符,并假定项目语言为 C#。如果项目语言是 VB.NET,则在条件测试中,单个 =
号表示相等。
<Theme id="numerics" title="Example: numeric values">
<DataRow>
<If test='IsNumeric' >
<Apply horizontalAlign='Right' />
<Else>
<If test='CellText == "n/a" '>
<Apply Text="0" horizontalAlign='Right' />
<ElseApply horizontalAlign='Left' />
</If>
</Else>
</If>
</DataRow>
</Theme>
特殊变量
有许多预定义变量,可以在条件测试或属性表达式中使用。它们如下:
|
给定单元格的值,解释为 |
|
给定单元格的数值,解释为 |
|
当前单元格的 0 基索引值。 |
|
当前行的 0 基索引值。 |
|
如果单元格中的值是数字,则为 |
|
如果单元格中的值是数字且小于零,则为 |
|
如果单元格中的值是数字且大于零,则为 |
|
如果单元格中的值是数字且等于零,则为 |
|
如果单元格中的文本不是数字,则为 |
|
如果单元格中的值是数字且大于或等于零,则为 |
|
如果单元格中的值是数字且小于或等于零,则为 |
|
如果单元格中的值是数字且不等于零,则为 |
|
在 |
|
在 |
|
在 |
在此示例中,IsNumeric
、IsNegative
和 CellIndex
被用来创建一个主题,其中交替的列以不同的背景色突出显示,数字右对齐,负数以红色显示。假定项目语言为 C#。
<Theme id="ifs" title="Working with If conditions">
<DataRow>
<!-- display alternate columns with different background colors -->
<If test='CellIndex % 2 == 0'>
<Apply backColor='LightGray' />
<ElseApply backColor='White' />
</If>
<!-- apply numeric formatting -->
<If test='IsNumeric' >
<Apply horizontalAlign='Right' />
<If test='IsNegative' >
<Apply foreColor='Red' />
</If>
</If>
</DataRow>
</Theme>
类别分组: <Group>
, <AlternateFormat>
GridThemes 框架的一个附加功能是能够根据给定列 DataRow
s 的重复值将行的块解释为组。<Group>
标签可用于标识此类行的集合,其中必需的 column
属性提供类别的 0 基索引号。数据源应在数据绑定之前按此列排序,因为类别文本的变化会向构建器发出新组的信号。<Group>
标签可以有一个可选的 suppressRepeating
属性,如果存在且设置为 true
,则表示类别文本应只显示一次,即在给定组的第一行。
此示例显示了一个主题,其中第一个网格列中的重复值被抑制。
<Theme id="groups" title="Working with Groups">
<Group column='0' suppressRepeating='true' />
</Theme>
可以通过在 <Group>
中嵌套一个或多个 <AlternateFormat>
标签来为组定义一个或多个格式化块。顾名思义,每个组的格式化将在所有列出的 <AlternateFormat>
指令之间交替,并且每个 <AlternateFormat>
块可以包含任何 <Apply>
、<If>
、<Else>
和 <ElseApply>
标签的组合。在此示例中,每个组的背景在浅蓝色和浅绿色之间交替。
<Theme id="groups" title="Working with Groups">
<Group column='0' suppressRepeating='true'>
<AlternateFormat>
<Apply backColor='lightBlue' />
</AlternateFormat>
<AlternateFormat>
<Apply backColor='lightGreen' />
</AlternateFormat>
</Group>
</Theme>
特殊变量 GroupText
、GroupIndex
和 RowIndexWithinGroup
可用于 <Group>
标签中的条件和表达式。在前一个示例的基础上,以下代码将组编号添加到组类别文本中(假定为 C#)。
<Theme id="groups" title="Working with Groups">
<Group column='0' suppressRepeating='true'>
<AlternateFormat>
<Apply backColor='lightBlue' />
</AlternateFormat>
<AlternateFormat>
<Apply backColor='lightGreen' />
</AlternateFormat>
<If test='CellIndex == 0 && RowIndexWithinGroup == 0' >
<Apply textExpression='string.Format("{0}. {1}", GroupText)' />
</If>
</Group>
</Theme>
对于 VB.NET 用户,相同的 <If>
条件如下所示。
. . .
<If test='CellIndex = 0 And RowIndexWithinGroup = 0' >
<Apply textExpression='string.Format("{0}. {1}", GroupText)' />
</If>
. . .
本文附带的示例项目中还有几个其他主题定义示例。您可以使用文章顶部的链接下载示例项目。
将 GridThemeExtender 控件添加到您的 .aspx 页面
一旦 GridTheme 构建器在 web.config 中配置了相应的条目,并且在 App_Code 中定义并保存了一个或多个主题文件,您就可以重新构建项目。构建后,自定义的 GridThemesBuildProvider
将解释它找到的每个 <Theme>
中的条件和格式化指令,并以项目语言生成源代码方法。这些方法都遵循 GridViewRowEventHandler
委托定义的相应签名,使其能够响应 GridView.RowDataBound
事件。构建器还将 GridThemesAttribute
自定义属性添加到每个 GridTheme 方法。此属性有助于静态类 GridThemesManager
,该类负责列出可供选择的 GridThemes。
自定义的 GridThemeExtender
控件实现了 IExtenderProvider
,向 GridView
控件添加了一个名为 GridTheme
的属性。像处理其他控件一样,将 GridThemeExtender
控件添加到您的工具箱,然后将其实例拖到 Web 窗体上。窗体上的所有 GridView
控件都将获得新的 GridTheme
属性。
将 GridTheme 分配给 GridView 控件
GridThemes 程序集包含一个自定义的 GridThemesEditor
类,它是 UITypeEditor
的子类,允许在设计时选择 GridTheme。单击扩展的 GridTheme
属性的下拉菜单,将显示 GridThemesBuildProvider
生成的可用主题列表。
通过设置此属性,页面设计者实际上是将先前编译的 GridTheme 方法之一分配给该 Grid 的 RowDataBound
事件的处理器。
关注点
使此库最大的挑战之一是我选择使用 TableCell
属性名作为 <Apply>
标签的属性。我的目标是让 GridTheme
中的声明式格式化语法与常见 .aspx 页面中的声明式属性语法相似。在研究页面解析器如何通用地处理此类属性赋值时,我使用了 Lutz Roeder's .NET Reflector [^],并偶然发现了内部 System.Web.Compilation.CodeDomUtility
类中未公开的 GenerateExpressionForValue()
方法。此方法使用反射来确定如何将属性值赋值转换为 CodeDOM 表达式,并且 ASP.NET 在生成 Page
子类的代码时会使用它。我借用了此方法的数据结构来创建我自己的构建器方法 GetValueExpressionForAssignment()
,该方法用于在解释 <Apply>
标签的属性时具有类似目的。
摘要
GridThemes 程序集结合了自定义的 BuildProvider
、IExtenderProvider
和 UITypeEditor
,为页面设计者提供了一个框架,让他们可以通过设置单个 GridView
扩展属性来应用复杂的条件单元格格式化。放置在 App_Code 文件夹中的 GridTheme 文件包含 XML 标签形式的条件和格式化指令,并由自定义构建器编译为 RowDataBound
事件处理器。自定义扩展控件使 GridTheme
属性可用于页面上的所有 GridView
,而自定义编辑器则负责在属性窗口中按标题列出可用的 GridThemes。总而言之,这些类提供了一种声明式方法,用于在整个项目中一致地应用条件网格格式化。
历史
- 2007 年 2 月 25 日 - 更新以修复在某些情况下会导致 VB.NET 项目中主题的渲染代码引起编译问题的错误。C# 项目不会出现此问题。在下载文件中添加了新的 VB.NET 演示项目以及 C# 演示项目。还添加了新的 GridThemesManager 方法
AssignThemeToGridView
,该方法简化了以编程方式而不是通过扩展器以声明方式分配主题的过程。 - 2006 年 11 月 28 日 - 首次发布