扩展 ASP.NET 控件:可滚动 GridView





5.00/5 (18投票s)
扩展标准的 ASP.NET GridView 控件以在网格中添加垂直滚动条
引言
尽管 ASP.NET 提供了大量可自定义的 Web 控件,但在某些情况下,您可能希望扩展 Web 控件的功能和行为。幸运的是,ASP.NET 控件有许多扩展点:
- 可自定义属性
- 样式和 CSS 类
- 模板化
- 重写
虽然前三个选项可以在运行时配置,但第四个选项需要一些编码。当您希望更改控件的行为或布局时,需要进行重写。在这种情况下,通常会为 Web 控件创建一个子类,并在其中实现重写的方法。
本文提供了一个示例,用于扩展标准的 ASP.NET GridView
控件,使其可以选择显示滚动条(也称为带固定页眉的可滚动表),而不是使用标准的“分页”,从而允许一次显示所有列表项。此解决方案已在 Internet Explorer、Firefox 和 Chrome 中进行过测试。
背景
Web 标准规定在浏览大型列表时要具有“分页”功能。其思想是大型列表需要“很长时间”才能下载,因此在使用分页时,用户只需下载包含少量行的单个页面。
标准的 ASP.NET GridView
默认支持分页。奇怪的是,GridView
在优化加载时间方面并没有多大作用,因为默认情况下绑定到 gridview
的列表存储在 ViewState
中,因此仍然会生成“大型”页面,导致下载时间更长。
因此,您也可以选择忽略分页,而一次显示整个列表。这样做的好处是,每次用户在网格中选择不同页面时,都不需要重新加载页面。
显然,列表可能会变得太大而无法容纳在单个网页中,因此使用滚动条将是一个足够好的解决方案。这在桌面应用程序中很常见。但是我发现,向 GridView
添加滚动条并非易事:目标是只让列表项可滚动,同时保持 gridview
页眉固定。
我曾寻找解决此问题的方法,但一些提出的解决方案仅兼容某些浏览器(例如,基于 CSS 的),其他一些则相当复杂或仅在特定情况下有效。据我所知,对于 GridView
控件,没有可用的标准解决方案。本文提出的解决方案与各种常用浏览器兼容,并且可以轻松地集成到任何 ASP.NET 解决方案中。此外,最终渲染的 HTML 尽可能紧凑。
解决方案
解决方案的方法并不新颖,例如,请参阅此处的一个示例:此处。基本上,页眉行显示在页眉 DIV
中的一个表中,而正文行显示在单独的可滚动正文 DIV
元素中。此方法会带来以下需要解决的问题:
从 GridView 中提取页眉行并在单独的 DIV 和 TABLE 中渲染它
这需要一些调整才能使其正常工作。它需要重写 GridView
控件的默认 RenderChildren
方法。RenderChildren
方法负责生成写入响应对象的输出。我重排并插入了我自己的某些 HTML 元素,而不是生成默认的 GridView
html 元素。
基本上,我使用的是以下步骤:
- 创建一个新的页眉
DIV
html 元素。为其分配一个特殊的 css-class,以便 JQuery 可以访问它。System.Web.UI.HtmlControls.HtmlGenericControl divhead = new System.Web.UI.HtmlControls.HtmlGenericControl("DIV"); divhead.Attributes["class"] = "GridViewHeaderContainer"; // Assign a css-style // accessible by jquery divhead.Style.Add("overflow", "hidden"); // Make sure the inner table // will not overflow if (this.Width != Unit.Empty) divhead.Style.Add("width", this.Width.ToString()); // Assign new width, // overrule stylesheet. // Use width defined // in the control
- 创建一个新的
Table
对象并将其插入到页眉DIV
中。确保应用了正确的样式。Table tablehead = new Table(); tablehead.ApplyStyle(this.HeaderStyle); tablehead.Style.Add("width", "100%"); tablehead.ID = ctrl.UniqueID + "$Header"; divhead.Controls.Add(tablehead);
- 选择
GridView
中的页眉行并将其插入到新创建的表中(请注意,这将从原始表中删除该行!)。WebControl ctrl = (WebControl)this.Controls[0]; // Reference the table Control ctrlrow = ctrl.Controls[0]; // Reference the first row: // table->row[0] tablehead.Controls.Add(ctrlrow); // This will automatically // bind the row to the new // parent control (and remove // itself from the previous container)
- 渲染页眉
DIV
(这将自动渲染其子元素)。divhead.RenderControl(writer); // will render the DIV // including the table // containing only the header.
- 创建正文
DIV
。System.Web.UI.HtmlControls.HtmlGenericControl divbody = new System.Web.UI.HtmlControls.HtmlGenericControl("DIV");
- 设置
DIV
的style
属性,并确保它是可滚动的。divbody.Attributes["class"] = "GridViewBodyContainer"; // style is hardcoded // for jquery if (this.Width != Unit.Empty) divbody.Style.Add("width", this.Width.ToString()); // Assign new width. if (this.Height != Unit.Empty) // Note: if no height // is set, // scrollbars will not be // visible since all rows // will be displayed then divbody.Style.Add("height", this.Height.ToString()); // Assign new height. divbody.Style.Add("margin", "0px"); divbody.Style.Add("overflow", "hidden"); divbody.Style.Add("overflow-y", "auto");
- 将页眉行回插入到原始表中的位置 0(顶部)。
ctrl.Controls.AddAt(0, ctrlrow); // This will automatically bind the row // to the parent table (and remove itself // from previous container)
- 将
GridView
表插入到正文DIV
中并渲染正文DIV
。divbody.Controls.AddAt(0, ctrl); // Bind the table to the body DIV // (and remove itself from previous container) divbody.RenderControl(writer); // will render the DIV and the included table
- 将表回插入到
GridView
中。这是一个关键步骤,因为它会将GridView
恢复到其原始状态。this.Controls.AddAt(0, ctrl); // Restore to previous container, // this way the control will not break
- 渲染
gridview
中包含的任何其他控件(例如页脚)for (int i = 1; i < this.Controls.Count; i++) { ctrl = (WebControl)this.Controls[i]; ctrl.RenderControl(writer); }
使页眉行的列与正文表中的列对齐
为了确保两个表的列对齐,会使用一些 JavaScript/JQuery。该脚本包含一个名为 UpdateGrid()
的函数,该函数在 gridview
完全渲染*之后*被调用。此时,列宽度已知,并在运行时重新对齐。它将根据 css-class 查找 DIV
,并遍历所有行和列以确定宽度。然后,它将显式设置每个表单元格的宽度。
完成后,它将使正文中的第一行(即原始页眉行)不可见。由于这是在渲染之后完成的,因此有时可能会短暂显示两个页眉。这是此方法唯一的缺点。
在 UpdatePanel 中使用 Grid
为了支持 Grid
在 UpdatePanel
中的更新,需要额外的 JavaScript 代码来确保 Grid
在每次(部分)回发到服务器后都能正确刷新和重新对齐。每次 UpdatePanel
更新其内容时,都必须调用 UpdateGrid()
JavaScript 函数。为此,必须在 ScriptManager
声明之后立即在网页中声明以下处理程序:
<asp:ScriptManager ID="ScriptManager1" runat="server" />
<script type="text/javascript">
Sys.Application.add_load(function () { UpdateGrid(); });
</script>
这个巧妙的小技巧将使网格与 UpdatePanel
同步。
Using the Code
提供的解决方案包含 2 个文件:
- ScrollableGridView.cs
- ScrollableGridView.js
您可以将这些文件添加到自己的 ASP.NET 解决方案中,或者直接从 ControlExtensions
组件中使用 ScrollableGridView
。您需要从 Web 项目向该组件添加引用。
将以下几行添加到您的网页中:
<%@ Register TagPrefix="ce" Namespace="Grids" Assembly="ControlExtensions" %>
<!-- add the following js scripts -->;
<script type="text/javascript" src="Scripts/jquery-1.4.1.min.js"></script>
<script type="text/javascript" src="Scripts/ScrollableGridView.js"></script>
...
<!-- insert the scrollable gridview control on the page at the desired position
make sure to set the Height property of the control to make sure scrollbars are displayed-->
<ce:ScrollableGridView id="ScrollableGridView1" runat="server" height="360px" width="100%">
<HeaderStyle BackColor="Orange" />
</ce:ScrollableGridView>
...
这足以支持 GridView
中的滚动。在此处包含的示例应用程序中,为了填充列表,会生成一些随机行。这仅出于测试目的而在页面的代码隐藏中完成。
此外,还添加了一个示例网页,用于演示如何在 UpdatePanel
控件的组合中使用此网格。
关注点
我很高兴地发现,通过重写 RenderChildren
方法,GridView
可以以非常不同的方式进行渲染。这使得现有 ASP.NET 控件易于重用和扩展。
历史
- 2013 年 4 月 5 日:发布 1.0 版本
- 2013 年 4 月 17 日:发布 1.1 版本