无刷新分页

无刷新,利用客户端的XMLHttpRequest对象发起请求到服务器,服务器响应后发回数据再由客户端进行接收,由于XMLHttpRequest对象发起的请求与响应是异步方式,而不是产生中断,因而其数据处理过程能在页面中呈现出无刷新的效果。
进一步了解XMLHttpRequest对象,包括如下属性:
onreadystatechange:每次状态改变所触发事件的事件处理程序。
responseText:从服务器进程返回数据的字符串形式。
responseXML:从服务器进程返回的DOM兼容的文档数据对象。
status:从服务器返回的数字代码,比如常见的404(未找到)和200(已就绪)
status Text:伴随状态码的字符串信息。
readyState:状态值0、未初始化,对象已建立,但是尚未初始化(尚未调用open方法)
1、初始化,对象已建立,尚未调用send方法
2、发送数据,send方法已调用,但是当前的状态及http头未知
3、数据传送中,已接收部分数据,因为响应及http头不全,这时通过responseBody和responseText获取部分数据会出现错误
4、完成,数据接收完毕,此时可以通过通过responseXml和responseText获取完整的响应数据
先看一个简单的URL请求:
View Code
//创建XMLHttpRequest,不同浏览器调用的创建方法不一样
function XmlHttpRequest(){
var requestObj = null;
if(window.XMLHttpRequest){
requestObj
= new XMLHttpRequest();
}
else{
try{
requestObj
= new ActiveXObject('MSXML2.XMLHTTP.4.0');
}
catch(e){
try{
requestObj
= new ActiveXObject('MSXML2.XMLHTTP.3.0');
}
catch(e){
try{
requestObj
= new ActiveXObject('MSXML2.XMLHTTP.2.6');
}
catch(e){
try{
requestObj
= new ActiveXObject('MSXML2.XMLHTTP');
}
catch(e){
try{
requestObj
= new ActiveXObject('Microsoft.XMLHTTP');
}
catch(e){}
}
}
}
}
}

if(requestObj == null){
alert(
'The browser does not surport XMLHTTP.');
}

return requestObj;
}

//发起POST请求并接收响应数据
function GetData(){
var xmlhttp = XmlHttpRequest();
if(!xmlhttp){
alert(
'创建xmlhttp对象异常!');
return;
}

var url = 'AjaxHandler/ProcPager.aspx';
xmlhttp.open(
'POST', url, false);
xmlhttp.onreadystatechange
= function(){
if(xmlhttp.readyState == 4){
document.getElementById(
'divPager').innerHTML = '数据加载中,请稍候...';
if(xmlhttp.status == 200){
document.getElementById(
'divPager').innerHTML = xmlhttp.responseText;
}
}
}

xmlhttp.send();
}
 
看看C#的分页,C#中用于分页的承载控件有很多,DataList、GridView、Repeater等。DataList、GridView天然支持分页,Repeater只能承载数据,没有分页功能,但利用PagedDataSource对象可让其实现分页功能。
对于分页方式,可以通过Sql每次读取指定条数的记录,或者读取出所有记录,在内存中进行分页,下面是两种分页方式的实现: 
View Code
//分页存储过程
CREATE PROCEDURE [dbo].[PROC_PAGER]
@TABLE_NAME VARCHAR(
2048), --表名或视图表
@COLUMNS VARCHAR(
1024), --欲选择字段列表
@WHERE VARCHAR(
2000), --查询条件
@ORDER_BY VARCHAR(
1000), --排序表达式
@PAGE_INDEX INT,
--页号,从1开始
@PAGE_SIZE INT,
--页尺寸
@RECORD_COUNT INT OUTPUT
AS
BEGIN
IF @COLUMNS IS NULL OR LTRIM(RTRIM(@COLUMNS))
= ''
BEGIN
SET @COLUMNS
= '*'
END

DECLARE @SQL_RECORD NVARCHAR(
2000)
SET @SQL_RECORD
= 'SELECT @RECORD_COUNT = COUNT(0) FROM [' + @TABLE_NAME + '] WHERE 1 = 1 ' + @WHERE
EXEC SP_EXECUTESQL @SQL_RECORD, N
'@RECORD_COUNT INT OUTPUT', @RECORD_COUNT OUTPUT

SET @ORDER_BY
= ' ORDER BY ' + @ORDER_BY
IF @PAGE_INDEX
< 1
BEGIN
SET @PAGE_INDEX
= 1
END

IF @PAGE_SIZE
< 1
BEGIN
SET @PAGE_SIZE
= 10
END

DECLARE @SQL_TEXT VARCHAR(MAX)
SET @SQL_TEXT
= 'SELECT ' + @COLUMNS +', ROWNUMBER
FROM (
SELECT
' + @COLUMNS + ', ROW_NUMBER() OVER(' + @ORDER_BY + ') AS ROWNUMBER
FROM [
' + @TABLE_NAME + ']
WHERE
1 = 1 ' + @WHERE + '
) AS T
WHERE ROWNUMBER BETWEEN
' + STR(((@PAGE_INDEX - 1)* @PAGE_SIZE+1)) + ' AND ' + STR(@PAGE_INDEX * @PAGE_SIZE)

SET NOCOUNT ON
EXECUTE(@SQL_TEXT)
SET NOCOUNT OFF

RETURN @@RowCount
END

/// <summary>
/// DataTable分页
/// </summary>
public static DataTable DataTablePager(DataTable dt, int pageSize, int currentPage)
{
DataTable dtRet
= dt.Clone();
int records = dt.Rows.Count;
int start = pageSize * (currentPage - 1);
int end = pageSize * currentPage;
if (end > records)
{
end
= records;
}

DataRowCollection drc
= dt.Rows;
for (int i = start; i < end; i++)
{
dtRet.ImportRow(drc[i]);
}

return dtRet;
}
 
了解原理后,可以就分页功能进行实施,思路如下:
1、Web页中定义分页数据的承载区域,用于显示分页输出。
2、建立专用ASPX页,只输出分页内容及导航。
3、建立JS分页对象,用于发起Ajax请求(请求地址为专用的分页ASPX页面),将获取到的内容加载到承载区域进行呈现。

看看具体的实现,为Repeater实现分页功能,我们自己实现一个分页的自定义控件,主要功能就是根据总页数、当前页、每页大小计算出分页导航并输出,同时将PagedDataSource与页面中的Repeater进行绑定,再利用Ajax进行无刷新分页,原理简单,计算稍微麻烦,不多做解释,自己看代码: 
View Code
/// <summary>
/// Repeater分页控件
/// </summary>
[ToolboxData("<{0}:FMPager runat=\"server\" />")]
public class FMPager : WebControl, INamingContainer
{
protected static readonly string navigateClassFmt = "<div class=\"pageClass\">{0}</div>";
protected static readonly string navigatePageTotalFmt = "<div class=\"pagetotal\">总记录:<strong>{0}</strong>&nbsp;&nbsp;&nbsp;页码:<span>{1}</span>/{2}</div>";
protected static readonly string navigateAjaxlinkFmt = "<li><a href=\"javascript:;\" onclick=\"{0}('{1}');\" class=\"{3}\">{2}</a></li>";
protected static readonly string navigateDisableLinkFmt = "<li><a href=\"#\">{0}</a></li>";
protected static readonly string navigateGotoFmt = "<div class=\"fr\">转到第&nbsp;<input type=\"text\" style=\"width: 40px;\" value=\"{1}\" onblur=\"{0}(this.value);\">&nbsp;页</div>";
private PagedDataSource pagedDataSource = null;

public FMPager()
{
pagedDataSource
= new PagedDataSource();
}

public override void DataBind()
{
base.DataBind();
if (string.IsNullOrEmpty(repeaterId))
{
return;
}

Control repeater
= FindRepeater(Page);
if (repeater == null || !(repeater is Repeater))
{
return;
}

ChildControlsCreated
= false;

Repeater finalRepeater
= repeater as Repeater;
pagedDataSource.CurrentPageIndex
= currentPageIndex;
pagedDataSource.PageSize
= pageSize;
pagedDataSource.VirtualCount
= recordCount;
pagedDataSource.DataSource
= DataSource;
finalRepeater.DataSource
= pagedDataSource;
finalRepeater.DataBind();
}

/// <summary>
/// 把数据内容传递到客户端
/// </summary>
protected override void Render(HtmlTextWriter output)
{
if (Site != null && Site.DesignMode)
{
CreateChildControls();
}

output.Write(OutputNavigate());
base.Render(output);
}

/// <summary>
/// 输出导航
/// </summary>
protected virtual string OutputNavigate()
{
//总页码数
int pageCount = recordCount / pageSize;
if (recordCount % pageSize != 0)
{
pageCount
+= 1;
}

if (currentPageIndex > pageCount)
{
currentPageIndex
= pageCount;
}

StringBuilder sbNavigate
= new StringBuilder();

//输出跳转到
sbNavigate.AppendFormat(navigateGotoFmt, pagerJs, currentPageIndex);

//获取第一页、上一页
sbNavigate.Append("<ul>");
if (currentPageIndex > 1 && pageCount > 1)
{
sbNavigate.AppendFormat(navigateAjaxlinkFmt, pagerJs,
1, "第一页", "");
sbNavigate.AppendFormat(navigateAjaxlinkFmt, pagerJs, currentPageIndex
- 1, "上一页", "");
}
else
{
sbNavigate.Append(
string.Format(navigateDisableLinkFmt, "第一页"));
sbNavigate.Append(
string.Format(navigateDisableLinkFmt, "上一页"));
}

//获取数字页
int navigateCount = 10; //每10页进行导航
int navigateTotal = pageCount / navigateCount; //总计能生成多少个数字导航
int pageInNavigate = ((currentPageIndex - 1) / navigateCount) + 1; //当前在第几个数字导航中

//计算数字导航开始页序及结束页序
int startIndex = (pageInNavigate - 1) * navigateCount + 1; //数字导航开始页序
int endIndex = startIndex + navigateCount - 1; //数字导航结束页序
if (endIndex > pageCount)
{
endIndex
= pageCount;
}

string currentPageClass = "";
for (int i = startIndex; i <= endIndex; i++)
{
currentPageClass
= "";
if (i == currentPageIndex)
{
currentPageClass
= "pageactive";
}

sbNavigate.AppendFormat(navigateAjaxlinkFmt, pagerJs, i, i, currentPageClass);
}

//获取下一页、最后页
if (currentPageIndex != pageCount && pageCount > 1)
{
sbNavigate.AppendFormat(navigateAjaxlinkFmt, pagerJs, currentPageIndex
+ 1, "下一页", "");
sbNavigate.AppendFormat(navigateAjaxlinkFmt, pagerJs, pageCount,
"最后页", "");
}
else
{
sbNavigate.AppendFormat(navigateDisableLinkFmt,
"下一页");
sbNavigate.AppendFormat(navigateDisableLinkFmt,
"最后页");
}

sbNavigate.Append(
"</ul>");

//输出总页数、当前页
sbNavigate.AppendFormat(navigatePageTotalFmt, recordCount, currentPageIndex, pageCount);

return string.Format(navigateClassFmt, sbNavigate.ToString());
}

/// <summary>
/// 查找输出Repeater
/// </summary>
private Control FindRepeater(Control ctrl)
{
Control retCtrl
= ctrl.FindControl(repeaterId);
if (retCtrl != null)
{
return retCtrl;
}

foreach (Control childCtrl in Page.Controls)
{
retCtrl
= childCtrl.FindControl(repeaterId);
if (retCtrl != null)
{
return retCtrl;
}

FindRepeater(retCtrl);
}

return null;
}

/// <summary>
/// 绑定数据源
/// </summary>
public IEnumerable DataSource
{
get;
set;
}

[Description(
"当前页")]
public int CurrentPageIndex
{
get
{
currentPageIndex
= HTMLHelper.QueryStringInt("p");
if (currentPageIndex <= 0)
{
currentPageIndex
= 1;
}

return currentPageIndex;
}
set
{
currentPageIndex
= value;
}
}
private int currentPageIndex = 0;

[Description(
"分页控件ID")]
public string RepeaterId
{
get
{
return repeaterId;
}
set
{
repeaterId
= value;
}
}
private string repeaterId = "";

[Description(
"每页显示记录数")]
public int PageSize
{
get
{
return pageSize;
}
set
{
pageSize
= value;
}
}
private int pageSize = 20;

[Description(
"总记录数")]
public int RecordCount
{
get
{
return recordCount;
}
set
{
recordCount
= value;
}
}
private int recordCount = 0;

[Description(
"JS分页函数")]
public string PagerJs
{
get
{
return pagerJs;
}
set
{
pagerJs
= value;
}
}
private string pagerJs = "pagerObj.Page";
}
 
定义一个输出控件,继承自FMPager,用于输出具体的分页的内容:
View Code
/// <summary>
/// 存储过程分页
/// </summary>
public class ProcPager : FMPager
{
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
FM.Business.ProcPager inf
= new Business.ProcPager();
int recordCount = 0;
DataSet ds
= inf.GetProcList("TEST", "ID, NAME, VAL", "", "ID", this.CurrentPageIndex, this.PageSize, ref recordCount);
this.RecordCount = recordCount;
this.DataSource = ds.Tables[0].DefaultView;
base.DataBind();
}
}
 
在专用ASPX页中调用,全部内容如下,没有HTML开始结束标识,只有输出内容:
View Code
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="ProcPager.aspx.cs" Inherits="AjaxHandler_ProcPager" %>
<asp:Repeater ID="rpList" runat="server">
<HeaderTemplate>
<table class="tb1" cellspacing="2">
<tr class="tr_bg">
<td style="width: 80%;">名称</td>
<td></td>
</tr>
</HeaderTemplate>
<ItemTemplate>
<tr class="tr_bg2">
<td><%# Eval("NAME") %></td>
<td><%# Eval("VAL") %></td>
</tr>
</ItemTemplate>
<FooterTemplate>
</table>
</FooterTemplate>
</asp:Repeater>
<ctrl:ProcPager ID="pagerTest" RepeaterId="rpList" PageSize="2" runat="server" />
 
最后是JS分页函数:
View Code
/*
JS分页对象
参数:
containerId:载体的容器ID
url:分页数据地址
callbackFn:分页数据加载完成后的回调函数
*/
function PagerObj(containerId, url, callbackFn){
var obj = new Object;
obj.Page
= page;

function page(currentIndex){
var finalUrl = url;
if(finalUrl.indexOf('?') == -1){
finalUrl
+= '?';
}

finalUrl
+= '&p=' + currentIndex;
finalUrl
+= '&clearBuffer=' + RandomKey(); //消除浏览器缓存
var xmlhttp = XmlHttpRequest();
if(!xmlhttp){
alert(
'创建xmlhttp对象异常!');
return;
}

xmlhttp.open(
'POST', finalUrl, false);
xmlhttp.onreadystatechange
= function(){
if(xmlhttp.readyState == 4){
document.getElementById(containerId).innerHTML
= '数据加载中,请稍候...';
if(xmlhttp.status == 200){
document.getElementById(containerId).innerHTML
= xmlhttp.responseText;
if(callbackFn != null){
callbackFn();
}
}
}
}

xmlhttp.send();
}

return obj;
}

Web页面的输出:
View Code
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Test.aspx.cs" Inherits="Test" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div id="divPager" style="float: left; width: 100%;">分页数据的承载区域</div>
</form>
<script type="text/javascript">
var pagerObj = null;

window.onload
= function(){
LoadInfo();
}

function LoadInfo(){
var url = 'AjaxHandler/ProcPager.aspx';
pagerObj
= new PagerObj('divPager', url, LoadInfoCallback);
pagerObj.Page(
1);
}

function LoadInfoCallback(){
// alert('测试用回调函数');
}

//创建XMLHttpRequest,不同浏览器调用的创建方法不一样
function XmlHttpRequest(){
var requestObj = null;
if(window.XMLHttpRequest){
requestObj
= new XMLHttpRequest();
}
else{
try{
requestObj
= new ActiveXObject('MSXML2.XMLHTTP.4.0');
}
catch(e){
try{
requestObj
= new ActiveXObject('MSXML2.XMLHTTP.3.0');
}
catch(e){
try{
requestObj
= new ActiveXObject('MSXML2.XMLHTTP.2.6');
}
catch(e){
try{
requestObj
= new ActiveXObject('MSXML2.XMLHTTP');
}
catch(e){
try{
requestObj
= new ActiveXObject('Microsoft.XMLHTTP');
}
catch(e){}
}
}
}
}
}

if(requestObj == null){
alert(
'The browser does not surport XMLHTTP.');
}

return requestObj;
}

function PagerObj(containerId, url, callbackFn){
var obj = new Object;
obj.Page
= page;

function page(currentIndex){
var finalUrl = url;
if(finalUrl.indexOf('?') == -1){
finalUrl
+= '?';
}

finalUrl
+= '&p=' + currentIndex;
finalUrl
+= '&clearBuffer=' + RandomKey(); //消除浏览器缓存
var xmlhttp = XmlHttpRequest();
if(!xmlhttp){
alert(
'创建xmlhttp对象异常!');
return;
}

xmlhttp.open(
'POST', finalUrl, false);
xmlhttp.onreadystatechange
= function(){
if(xmlhttp.readyState == 4){
document.getElementById(containerId).innerHTML
= '数据加载中,请稍候...';
if(xmlhttp.status == 200){
document.getElementById(containerId).innerHTML
= xmlhttp.responseText;
if(callbackFn != null){
callbackFn();
}
}
}
}

xmlhttp.send();
}

return obj;
}

//生成随机数,用于消除页面缓存
function RandomKey(){
var hex = new Array('0','1','2','3','4','5','6','7','8', '9','a','b','c','d','e','f');
var t = '';
for(var i = 0; i<32; i++){
t
+= hex[Math.floor(Math.random() * 16)];
}

return t.toUpperCase();
}
</script>
</body>
</html>

整个功能实施完毕,最后推荐使用jQuery,它封装了Ajax相关方法,我们不再需要自己实现XmlHttpRequest对象,只需利用jQuery进行异步请求即可,下面是利用jQuery实现的JS分页对象:
function PagerObj(op){
var obj = new Object;
obj.Page
= page;

var settings = $.extend({
containerId:
'divPager', //容器编号
url: '', //分页请求URL地址
extendParams: '', //URL地址扩展参数
callbackFn: null //加载完毕后的回调函数
}, op);

function page(currentIndex){
var finalUrl = settings.url;
if(finalUrl.indexOf('?') == -1){
finalUrl
+= '?';
}

finalUrl
+= '&p=' + currentIndex;
finalUrl
+= '&clearBuffer=' + RandomKey(); //消除浏览器缓存
$.ajax({
type:
"POST",
url: finalUrl,
data: settings.extendParams,
success:
function(html){
pageCallback(html);
}
});
}

function pageCallback(html){
$(
'#' + settings.containerId).html(html);
if(settings.callbackFn != null){
settings.callbackFn();
}
}

return obj;
}

调用方法:
function LoadInfo(){
var url = 'AjaxHandler/ProcPager.aspx';
pagerObj
= new PagerObj({
url: url,
callbackFn: LoadInfoCallback
});
pagerObj.Page(
1);
}

顺带一提,利用上面的JS分页对象可以实现同一页中的多个分页列表:
View Code
var pagerObj = null;
var pagerTestObj = null;

$(
function(){
LoadInfo();
LoadTestInfo();
})

function LoadInfo(){
var url = 'AjaxHandler/ProcPager.aspx';
pagerObj
= new PagerObj({
url: url
});
pagerObj.Page(
1);
}

function LoadTestInfo(){
var url = 'AjaxHandler/TestPager.aspx';
pagerTestObj
= new PagerObj({
containerId:
'divTestPager',
url: url
});
pagerTestObj.Page(
1);
}


最终效果:


放出本文源码:
Pager_Demo.rar
posted @ 2011-06-12 20:07  flysoul  阅读(4774)  评论(8编辑  收藏