分享
 
 
 

创建用于ASP.NET的分页控件(续)

王朝asp·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

数据绑定过程

DataBind 方法是所有 ASP.NET 控件公用的,对于数据绑定控件来说,它将触发用户界面的刷新以反映新数据。SqlPager 控件根据 SelectCommand 和 ConnectionString 属性的值,使用此方法启动数据检索操作。不言而喻,如果这些属性中的任何一个为空,该过程将终止。同样,如果合作者控件不存在,数据绑定过程将被取消。要查找合作者控件,DataBind 方法使用 Page 类中的 FindControl 函数。由此可见,合作者控件必须为主窗体的直接子控件。

进行分页显示的控件不能为任意的 ASP.NET 服务器控件。它必须为列表控件或基本数据列表控件。更一般来说,合作者控件必须具有 DataSource 属性并实现 DataBind 方法。可能进行分页的控件实际上只需要满足这些要求。Microsoft? .NET Framework 中所有继承 ListControl 或 BaseDataList 的控件都满足第一个要求;而所有 Web 控件通过设计都满足 DataBind 要求。使用当前的实现方法,无法使用 SqlPager 控件来对 Repeater 进行分页。Repeater 与合作者控件 DataList 和 DataGrid 不同,不继承 BaseDataList,也不提供列表控件的功能。下表列出了可以使用 SqlPager 进行分页的控件。

表 2:可以由 SqlPager 控件进行分页的数据绑定控件

控件 说明

CheckBoxList 从 ListControl 派生而来,显示为复选框列表。

DropDownList 从 ListControl 派生而来,显示为字符串下拉列表。

ListBox 从 ListControl 派生而来,显示为字符串可滚动列表。

RadioButtonList 从 ListControl 派生而来,显示为单选按钮列表。

DataList 从 BaseDataList 派生而来,显示为模板化数据项目列表。

DataGrid 从 BaseDataList 派生而来,显示为数据项目的表格网格。DataGrid 是唯一一个内置有功能强大的分页引擎的 ASP.NET 控件。

以下代码说明由 SqlPager 控件实现的数据绑定过程。

public override void DataBind()

{

// 启动数据绑定事件

base.DataBinding();

// 数据绑定后必须重新创建控件

ChildControlsCreated = false;

// 确保控件存在且为列表控件

_controlToPaginate = Page.FindControl(ControlToPaginate);

if (_controlToPaginate == null)

return;

if (!(_controlToPaginate is BaseDataList || _controlToPaginate is ListControl))

return;

// 确保具有足够的连接信息并指定查询

if (ConnectionString == "" || SelectCommand == "")

return;

// 获取数据

if (PagingMode == PagingMode.Cached)

FetchAllData();

else

FetchPageData();

// 将数据绑定到合作者控件

BaseDataList baseDataListControl = null;

ListControl listControl = null;

if (_controlToPaginate is BaseDataList)

{

baseDataListControl = (BaseDataList) _controlToPaginate;

baseDataListControl.DataSource = _dataSource;

baseDataListControl.DataBind();

return;

}

if (_controlToPaginate is ListControl)

{

listControl = (ListControl) _controlToPaginate;

listControl.Items.Clear();

listControl.DataSource = _dataSource;

listControl.DataBind();

return;

}

}

根据 PagingMode 属性的值调用不同的获取例程。在任何情况下,结果集都绑定到 PagedDataSource 类的实例上。此类提供了一些用来对数据进行分页的功能。特别是,当整个数据集都存储在缓存中时,该类将自动检索当前页面的记录并返回布尔值,来提供有关第一页和最后一页的信息。稍后将回来介绍此类的内部结构。在上述列表中,帮助程序的 PagedDataSource 对象是由 _dataSource 变量表示的。

然后,SqlPager 控件经过计算得出合作者控件的类型,并将 PagedDataSource 对象的内容绑定到合作者控件的 DataSource 属性。

有时,上述的 DataBind 方法还将 ChildControlsCreated 属性重新设置为 false。那么,为什么要这样做呢?

当包含分页程序的页面返回时,所有控件都要重新创建;分页程序也不例外。通常情况下,所有控件及其子控件都是在准备显示页面之前创建的。在每个控件接收到 OnPreRender 通知之前的一瞬间,protected EnsureChildControls 方法将被调用,这样,每个控件都可以生成自己的控件树。此事件发生后,数据绑定过程完成,新数据已存储到缓存中。

但是,当由于单击分页程序的一个组成控件而使页面返回时(即用户单击以更改页面),就会生成分页程序的控件树,这时远未达到显示阶段。特别是,当处理相关的服务器端事件时,就必须生成控件树,因而是在数据绑定开始之前生成控件树。困难在于,数据绑定将修改页面索引,而这必须反映在用户界面中。如果不采取某些对策的话,当用户切换到另一页时,分页程序中的页面索引将不会被刷新。

有各种方法可以解决此问题,但重要的是要弄清楚问题及其真正的原因。您可以避免生成控件树,并在 Render 方法中生成所有输出。另外,您还可以修改树中受数据绑定更改影响的部分。本文选择了第三种方法,这种方法需要较少的代码,而且,不管控件的用户界面的哪个部分受到数据绑定更改的影响,都能够解决问题。通过将 ChildControlsCreated 属性设置为 false,可以使以前创建的任何控件树无效。这样,在显示之前将重新创建控件树。

分页引擎

SqlPager 控件支持两种检索数据的方法 - 缓存和非缓存。如果采用前者,选择命令原样执行,整个结果集将绑定到在内部进行分页的数据源对象上。PagedDataSource 对象将自动返回适合特定页面的记录。PagedDataSource 类也是在 DataGrid 默认分页机制背后运行的系统组件。

检索所有记录但只显示适合页面的几个记录,这通常不是一种明智的方法。由于 Web 应用程序的无状态特性,事实上,每次请求页面时都可能运行大量的查询。要使操作有效,采用缓存的数据检索方法必须依赖于某种缓存对象,ASP.NET 的 Cache 对象就是一个很好的候选对象。缓存技术的使用加快了应用程序的运行速度,但其提供的数据快照不能反映最新的更改。另外,它需要使用 Web 服务器上的较多内存。而且荒谬的是,如果大量的数据按会话缓存的话,甚至可能造成很大的问题。Cache 容器对于应用程序来说是全局的;如果数据按会话存储在其中,还需生成会话特有的项目名称。

Cache 对象的上面是完全支持过期策略的。换句话说,存储在缓存中的数据在一段时间后可以自动被释放。以下代码说明 SqlPager 类中用来获取数据并将其存储在缓存中的一个 private 方法。

private void FetchAllData()

{

// 在 ASP.NET Cache 中查找数据

DataTable data;

data = (DataTable) Page.Cache[CacheKeyName];

if (data == null)

{

// 使用 order-by 信息修改 SelectCommand

AdjustSelectCommand(true);

// 如果数据过期或从未被获取,则转到数据库

SqlDataAdapter adapter = new SqlDataAdapter(SelectCommand, ConnectionString);

data = new DataTable();

adapter.Fill(data);

Page.Cache.Insert(CacheKeyName, data, null,

DateTime.Now.AddSeconds(CacheDuration),

System.Web.Caching.Cache.NoSlidingExpiration);

}

// 配置分页的数据源组件

if (_dataSource == null)

_dataSource = new PagedDataSource();

_dataSource.DataSource = data.DefaultView;

_dataSource.AllowPaging = true;

_dataSource.PageSize = ItemsPerPage;

TotalPages = _dataSource.PageCount;

// 确保页面索引有效

ValidatePageIndex();

if (CurrentPageIndex == -1)

{

_dataSource = null;

return;

}

// 选择要查看的页面

_dataSource.CurrentPageIndex = CurrentPageIndex;

}

控件和请求的缓存项目名称是唯一的。它包括页面的 URL 和控件的 ID。在指定的时间(以秒计算)内,数据被绑定到缓存。要使项目过期,必须使用 Cache.Insert 方法。以下较简单的代码将项目添加到缓存,但不包括任何过期策略。

Page.Cache[CacheKeyName] = data;

PagedDataSource 对象通过其 DataSource 属性获取要进行分页的数据。值得注意的是,PagedDataSource 类的 DataSource 属性只接受 IEnumerable 对象。DataTable 不满足此要求,这就是为什么采取 DefaultView 属性的原因。

SelectCommand 属性确定在 SQL Server 数据库上运行的查询。此字符串最好为 SELECT-FROM-WHERE 形式。不支持 ORDER BY 子句,如果指定了该子句,将被删除。这正是 AdjustSelectCommand 方法所做的。使用 SortField 属性可以指定任何排序信息。AdjustSelectCommand 方法本身将根据 SortField 的值添加一个正确的 ORDER BY 子句。这样做有什么原因吗?

当分页程序以 NonCached 模式工作时,原始的查询将被修改以确保只检索当前页面的记录。在 SQL Server 上执行的实际查询文本将采取以下形式。

SELECT * FROM

(SELECT TOP ItemsPerPage * FROM

(SELECT TOP ItemsPerPage*CurrentPageIndex * FROM

(SelectCommand) AS t0

ORDER BY SortField ASC) AS t1

ORDER BY SortField DESC) AS t2

ORDER BY SortField

该查询弥补了 SQL Server 2000 中 ROWNUM 子句的缺陷,并且对记录进行重新排序,使得只有 x 项目的“第 n 个”块经过正确排序后返回。您可以指定基础查询,分页程序将它分解为多个较小的页面。只有适合某个页面的记录被返回。正如您看到的那样,除了查询命令以外,上述查询需要处理排序字段。这就是为什么另外添加了 SortField 属性。此代码的唯一缺陷是默认情况为升序排序。通过使 ASC/DESC 关键字参数化,可以使此代码真的非常完美:

private void FetchPageData()

{

// 需要经过验证的页面索引来获取数据。

// 还需要实际的页数来验证页面索引。

AdjustSelectCommand(false);

VirtualRecordCount countInfo = CalculateVirtualRecordCount();

TotalPages = countInfo.PageCount;

// 验证页码(确保 CurrentPageIndex 有效或为“-1”)

ValidatePageIndex();

if (CurrentPageIndex == -1)

return;

// 准备并运行命令

SqlCommand cmd = PrepareCommand(countInfo);

if (cmd == null)

return;

SqlDataAdapter adapter = new SqlDataAdapter(cmd);

DataTable data = new DataTable();

adapter.Fill(data);

// 配置分页的数据源组件

if (_dataSource == null)

_dataSource = new PagedDataSource();

_dataSource.AllowCustomPaging = true;

_dataSource.AllowPaging = true;

_dataSource.CurrentPageIndex = 0;

_dataSource.PageSize = ItemsPerPage;

_dataSource.VirtualCount = countInfo.RecordCount;

_dataSource.DataSource = data.DefaultView;

}

在 NonCached 模式中,PagedDataSource 对象并不提供整个数据源,因此不能计算出要进行分页的总页数。进而必须对 AllowCustomPaging 属性进行标记,并提供数据源中的实际记录数量。通常,实际数量是使用 SELECT COUNT(*) 查询进行检索的。此模型与 DataGrid 的自定义分页几乎相同。此外,PagedDataSource 对象中选择的当前页面索引通常为 0,因为实际上已经存储了一页记录。

SqlPager 控件的实现方法就介绍到这里,下面我们介绍一下它的使用方法。

使用 SqlPager 控件

假设存在一个包含 ListBox 控件的示例页面。要使用分页程序,请确保 .aspx 页面正确地注册了该控件的程序集。

<%@ Register TagPrefix="expo" Namespace="DevCenter" Assembly="SqlPager" %>

控件的标记取决于实际设置的属性。以下标记是一个合理的示例:

<asp:listbox runat="server" id="ListBox1" Width="300px" Height="168px"

DataTextField="companyname" />

<br>

<expo:SqlPager runat="server" id="SqlPager1" Width="300px"

ControlToPaginate="ListBox1"

SelectCommand="SELECT customerid, companyname FROM customers"

ConnectionString="SERVER=localhost;DATABASE=northwind;UID=..."

SortKeyField="companyname" />

<br>

<asp:button runat="server" id="LoadFirst1" Text="加载第一页" />

除了分页程序以外,页面还包含一个列表框和一个按钮。列表框将显示每个页面的内容;按钮只用于首次填充列表框。该按钮具有一个单击事件处理程序,定义如下。

private void LoadFirst1_Click(object sender, EventArgs e) {

SqlPager1.CurrentPageIndex = 0;

SqlPager1.DataBind();

}

图 2 显示操作中的页面。

图 2:与 ListBox 控件协同工作的 SqlPager 控件。

使用 DataList 控件可以生成一个更有意思的示例。目标是使用分页程序浏览每个 Northwind 职员的个人记录。该 DataList 如以下列表所示。

<asp:datalist runat="server" id="DataList1" Width="300px"

Font-Names="Verdana" Font-Size="8pt">

<ItemTemplate>

<table bgcolor="#f0f0f0" style="font-family:verdana;font-size:8pt;">

<tr><td valign="top">

<b><%# DataBinder.Eval(Container.DataItem, "LastName") + ", " +

DataBinder.Eval(Container.DataItem, "firstname") %></b>

</td></tr>

<tr><td>

<span style="color:blue;"><i>

<%# DataBinder.Eval(Container.DataItem, "Title")%></i></span>

<p><img style="float:right;" src='image.aspx?id=<%# DataBinder.Eval(Container.DataItem, "employeeid")%>' />

<%# DataBinder.Eval(Container.DataItem, "Notes") %>

</td></tr>

</table>

</ItemTemplate>

</asp:datalist>

表格的第一行显示职员的姓名和职务,然后是相片,相片周围是注释。相片是使用特定的 .aspx 页面检索的,返回从数据库中获取的 JPEG 数据。

分页程序可以放置在页面中的任何位置。本例中将它放置在合作者 DataList 控件上方并紧挨着合作者控件。

图 3:SqlPager 对 DataList 控件进行分页

将 SqlPager 控件与 DataGrid 控件一起使用有意义吗?这要视情况而定。DataGrid 已经与一个基于本文中使用的 PagedDataSource 对象的嵌入式分页引擎一起工作。因此,如果您需要对以表格格式显示的单个记录集合进行分页时,就无需使用 SqlPager。但是,对于重要/复杂的方案,将这两个控件一起使用并不是一种牵强的想法。例如,如果您要向前一个屏幕快照添加一个 DataGrid 来显示由职员管理的订单,则可以在同一格页面上放置两个相关的分页引擎,一个要对职员进行分页,另一个用于滚动相关订单。

小结

不管您要生成哪种类型的应用程序(Web 应用程序、Microsoft? Windows? 应用程序或 Web 服务),您都很难下载和缓存要显示的整个数据源。有时,测试环境会使您相信这种解决方案真是太棒了,非常可取。但是测试环境也会误导。数据源的大小非常关键,应用程序的规模越大,数据源的大小越关键。

在 ASP.NET 中,只有 DataGrid 控件具有内置的分页功能。但是,分页引擎是由相当样板化的代码实现的,只要进行少量的处理,就可以进行推广并用于多个不同的控件。本文介绍的 SqlPager 控件就实现了此目标。它关注下载数据,将数据分成多个页面并通过合作者控件显示。该控件可以检索并缓存整个数据集,或者仅在 SQL Server 中查询要在选定的页面中显示的几个记录。我说的是 SQL Server,这是另一个重点。SqlPager 只能用于 SQL Server,不能用于通过 OLE DB 或 ODBC 来检索数据。也不能使用它访问 Oracle 或 DB2 存档。

要生成真正的通用 SQL 分页程序组件,应该形成数据访问层,并生成一种能够使用相应的数据提供程序创建连接、命令和适配器的工厂类。此外,要注意为各种 SQL 源设置一个分页引擎是最糟糕的做法。这里介绍的方法只适用于 SQL Server 7.0 和更高版本。TOP 子句在不同的环境下具有不同的特性。使用服务器游标和临时表格,它可适用于较大范围的 DBMS 系统。但那将使代码更为复杂。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有