神奇的 DataGrid

**_ 数据绑定 _ ** **_ _ **

语言: C# .

技术: DataGrid , 数据绑定

** 神奇的 ** ** ** DataGrid

揭示众多没有成文的可能用法的技巧

作者 Dino Esposito

即使你可能是新接触到 ASP.NET 编程和数据绑定,但你是完全有可能以数字图表的形式作出有价值的强大的 ** DataGrid ** 控件。简单说,它是把数据格式化为列表式、柱式的控件,它有着极端通用性和很高的可配置性。它自身就有强大的可编程的特征,虽然我已经有一年的使用经验,但我还没发现有不能放在 ** DataGrid ** Web 控件表上的元素。我想和大家一起分享这些小技巧;其中一些甚至可称为“卑鄙的用法”。这些技巧涉及到对 ** DataGrid ** 控件的外表、感觉和它是怎么把信息呈现给用户。

在深入之前,让我阐明可能会引起混淆的关键一点。 .NET 构架定义了两种风格迥异的 ** DataGrid ** 控件。它们有着相同的名字但属于不同的命名空间。更重要的是,它们除了名字相同之外没就有任何相同之处了。我在本文所讲的 ** DataGrid ** 控件是 ** DataGrid ** Web 控件,它是在 ** System.Web.UI.WebControl ** 命名空间中被定义的。另外一个 ** DataGrid ** 控件是在 Windonws 窗体中的 DataGrid 控件,它是在 ** System.Windows.Forms ** 命名空间中被定义的。它们在相当独立的两个方向发展,即使两者都试图提供一套共同的兼容能力和遵循相似编程模型。 Windows 窗体的 ** DataGrid ** 控件显示出许多 Web DataGrid 所不具有特性。同样的,你可以使用 Web 窗体的 DataGrid 控件做 Windows 窗体的 ** DataGrid ** 控件所不能的事。所以在阅读 MSDN 文档时,小心的查看你所阅读的是哪个控件。

在这篇文章中,我将会讨论并实现下列常遇到的情形的解决方案:

l 如何在最高一行上方建立两行标题,其中一行把相关的内容合在一起,使 DataGrid 成为更具描述性和信息性的表格。

对我们所要接触的 ** DataGrid ** 控件编程基础有 ** ItemCreated ** ** , BoundColumn ** 的 ** DataFormatString ** 属性和分页栏。

** 一个两行的标题 ** ** **

DataGrid 控件允许你给绑定到控件上的每一列分配一个标题。标题的文字是使用 column 类的 ** HeaderText ** 属性。所有的 column 类,从 ** BoundColumn ** 到 ** TemplateColumn ** 、从 ** HyperLinkColumn ** 到 ** ButtonColumn ** 都有一个 ** HeaderText ** 的属性。有这样一种情形,然而其中具有大量复杂的数据所以你需要用一个二级的标题显示。这个二级标题在列标题的上方。每一格把二个或更多的下面的列组合在一起。下面的 HTML 代码(图一)显示我的意思:

 1<table>
 2<tr>
 3<td colspan="2">Group 1</td> <td colspan="2">Group 2</td>
 4</tr>
 5<tr>
 6<td> Col  #1</td> <td> Col  #2</td>
 7<td> Col  #3</td> <td> Col  #4</td>
 8</tr>
 9<tr>
10<td><i>Contents of the table</i></td>
11</tr>
12</table>

图一:一个简单的两行标题的 HTML 表格。

在你有复杂的表格要显示时这个特性是相当重要的的,如:发票单,销售报告或统计单。如果你计划使用 HTML 或 ASP 来完成一个两行的标题是没有问题的。荒谬的是,当你是用 ASP.NET 来实现它时它变成了技巧了。为了能实现专业的报告,最和情理的方法是使用 ** DataGrid ** 控件。不幸的是, DataGrid 控件通过预定义的属性和委派,没有包含双标题的特性。在另一方面,使用 ** DataGrid ** 控件几乎是强制的,因为其它控件如: DataList 或 Repeater 没有提供分页和排序功能,但这两个功能对于 Web 报告来说是很重要的。然而,如果分页和排序对你来说并不是重要的特征的话, ** DataList ** 控件是你用来建立复杂标题的更简单的工具。

现在你将会看到如何使用 SQL Server2000 的 Northwind 库中的 employee 表来建立一个报告,并且它是个人信息和工作相关信息清楚分开的报告。图二显示示例中的 ** DataGrid ** 控件怎样申明列的。

1<columns>
2<asp:templatecolumn headertext="Name">
3<itemtemplate>

"<b>" + ((DataRowView)Container.DataItem)

["lastname"] + "</b>, " +

((DataRowView)Container.DataItem)["firstname"]

1
2</itemtemplate>
3</asp:templatecolumn>
4<asp:boundcolumn datafield="birthdate" dataformatstring="{0:d}" headertext="Born"></asp:boundcolumn>
5<asp:boundcolumn datafield="country" headertext="Country"></asp:boundcolumn>
6<asp:boundcolumn datafield="title" headertext="Title"></asp:boundcolumn>
7<asp:boundcolumn datafield="hiredate" dataformatstring="{0:d}" headertext="Hired"></asp:boundcolumn>
8</columns>

图二 : 示例中 DataGrid 的列

开始的三列——姓名,生日和籍贯——将会被组织在 ** Personal ** 超级标题下。剩下两个,名称和雇佣时间,被放到 ** Job ** 超级标题下。

** DataGrid ** 控件自动提供一行标题的表格。标题也可以使用绘制样式定义的 HeaderStyle 属性。困难就是 ** DataGrid ** 控件不让你创建标题行。虽然你能定义一个 ** ItemCreated ** 事件的事件处理器,但它只能在标题行的 HTML 代码已经产生之后才让你有机会去操作它。作为选择,空件的编程界面允许你得到代表标题行的 ** TableRow ** 对象,不过你不能从它得到可靠的父控件:

TableRow rowHeader = (TableRow) e.Item;

你需要一个 ** Table ** 对象——它是 ASP.NET 控件用于生成表的一个活实例——来添加一个新行。我试了 ** TableRow ** 的 ** Parent ** 属性,但它总是返回了 null.

我尝试的另一种失败的方法是把 ** DataGrid ** 控件封装在一个 ** asp:table ** 控件中。在这个方法中,结果是两个完全分离的表。它更不可能把表的最上行分为包含两个或更多列的行。

神秘的事物就想它看起来那样神秘,解决这个问题的关键竟是分页栏。如果你 ** DataGrid ** 文档小心的检查分页栏的话,你不会在三个不同位置中把分页栏放错。控件默认把它放在栅格栏下。然而,他还可以放在栅格栏之下,当然也可以上下都有。在我追踪 ** ItemCreated ** 事件处理器, 我找出这个仅仅是靠着运气。你可以通过 ** Position ** 属性来控制分页的位置。

grid.PagerStyle.Position = PagerPosition.TopAndBottom;

就像其它 ASP.NET 控件所做的那样, ** DataGrid ** 首先准备输出为一串,接着触发 ** PreRender ** 事件,最后生成 HTML 代码。 HTML 代码是依照控件属性和在 ** ItemGreated ** 中建立的。我注意到 ** ItemCreated ** 为分页栏调用了两次,最后栅格定义了分页栏作为生成表格第一行和最后一行。当它开始实际地生成时,这些行中的一行或两行将依照分页栏位置和可见性的设置被删除掉。从这里学到了什么呢?如果你设置分页的位置为 ** TopAndBottom ** , 控件将会显示两个同一的更能相似的分页栏:一个在栅格的上方,一个在它的下方(图三)。

图三:两个分页栏的 DataGrid 控件 .

两个分页栏都是栅格表的重要部分,并且都能在 ** ItermCteated ** 事件中被挂起。唯一需要告诫的是你应该分清哪个是第一哪个是第二。如果你的代码挂起了第一个分页栏,你可能想要清除所有的子控件,并且再增加适合的单元格来放置超级标题。如果分页栏截取的是第二个,所有你必须做的就是应用任何你需要的自定义连接。

你怎样指导哪一个分页栏是 ** ItemCreated ** 处理的?你还记得老式的有效的编程工具——全局变量吗?一个老的全局布尔变量的方法如: ** m_bFirstTime ** ** , ** 很容易得可以追踪到分页栏是否是第一次被创建。在图四的示例中, ** ItemCreated ** 判断分页栏再次对话中是否是第一次被创建,如果是,在第一个(且是唯一一个)行的单元格中移除所有的控件。注意 ** ItemCreated ** 总是调用两次分页栏,并不考虑分配给 Position 的值。这个组合分页格(现在是第一个超级标题格)能从 ** Header ** 中继承某些样式(如:颜色、字体和边框等)并且重写某些部分。 ** MergeStyle ** 提供了这个方法。

private bool m_bFirstTime = true;

public void ItemCreated(Object sender,

DataGridItemEventArgs e)

{

ListItemType elemType = e.Item.ItemType;

if (elemType == ListItemType.Pager)

{

if (m_bFirstTime)

{

// Personal header

TableCell cell0 = (TableCell) e.Item.Controls[0];

cell0.Controls.Clear();

cell0.MergeStyle(grid.HeaderStyle);

cell0.BackColor = Color.Navy;

cell0.ForeColor = Color.Yellow;

cell0.ColumnSpan = 3;

cell0.HorizontalAlign = HorizontalAlign.Center;

cell0.Controls.Add(new LiteralControl("Personal"));

// Job header

TableCell cell1 = new TableCell();

cell1.MergeStyle(grid.HeaderStyle);

cell1.BackColor = Color.Navy;

cell1.ForeColor = Color.Yellow;

cell1.ColumnSpan = 2;

cell1.HorizontalAlign = HorizontalAlign.Center;

cell1.Controls.Add(new LiteralControl("Job"));

e.Item.Controls.Add(cell1);

m_bFirstTime = false;

}

else

{ TableCell pager = (TableCell) e.Item.Controls[0];

// Loop through the pager buttons skipping

// over blanks

// (Blanks are treated as LiteralControl(s)

for (int i=0; i<pager.Controls.Count; i+=2)

{

Object o = pager.Controls[i];

if (o is LinkButton)

{

LinkButton h = (LinkButton) o;

h.Text = "[ " + h.Text + " ]";

}

else

{

Label l = (Label) o;

l.Text = "Page " + l.Text;

}

}

m_bFirstTime = true;

}

}

图四: ItemCreated 事件处理使分页栏加到了标题行里。

超级标题格的 ** ColumSpan ** 属性必须被设置为期望组合在一起的列的数目,最终,单元格通过文字控件被附上文字。当你截取分页栏时,它只包含一格。因此,如果你需要更多的第一级标题的话,新格必须被创建。这些值的总数被分配给 ** ColumnSpan ** 属性,所有单元格必须和栅格中的列匹配。当你这样做时,不要忘记设置 ** m_bFirstTime ** 变量为 False 。同样的,不要忘记当你重新建立第二个分页时把这个全局变量重置为 True 。如果你忽略了这一步,你将会在页间移动时遇到标题的问题。图五显示了有两个标题栏的 ** DataGrid ** 控件。

图五:使用重写最上的分页栏产生的标题栏。

** 结论 ** ** **

** DataGrid ** 控件是一个非常复杂的控件。它是一个有着丰富特征和可能性的一个宝库,包括已列为文档的和没有列为文档的,被发现的和没有被发现的。为了寻找一些实用的代码技巧,我希望我已经在控件内在使用上给出了一些提示。

Published At
Categories with Web编程
Tagged with
comments powered by Disqus