细说jquery ui和jqgrid的ASP.NET实现

前言

数据显示的方式可以通过很多控件来实现,例如服务端的原生GridView,第三方控件ComponentArt、Telerik等,客户端的flexgrid, extgrid, easyui, jqgrid等等。在这里我要讲解的是jqgrid,它也是我最近在项目中尝试用到的。

我选择使用jqgrid主要是因为它基于jquery ui,在没有美工、契合系统主题并且快速完成系统的前提下,我选择了可以定制theme的jquery ui.

这篇文章主要涉及到jquery ui和jqgrid的使用,我对每一步的讲解都尽量做到细化,结合详细的截图,以期能够通过step by step让初学者们熟悉一些原理和实现步骤。
文中涉及到一些其他知识点,也做了较为详细的说明,希望这些内容能够对web开发者们有较好的增益作用。
每一个页面的在线demo我还在配置中,一会儿放出来,可以让大家有个直观的感受。
本章概要:

在开始项目之前,需要做的一些准备我也会逐步在下面的内容中进行介绍,或者你可以直接在这里下载本文所有demo的源代码,源代码中已经为你准备好这些文件,并且这些都是可以运行的。

源代码下载:KFBlogDemo.zip
数据库:Northwind(2005,2008 Northwind下载

一、 Json标准格式和验证

由于自己在使用jqgrid的过程中曾经使用过不规范的json,导致数据jqgrid一直无法显示,所以在此有必要简单讲一下json的标准格式和验证方式。

虽然在js中字符串也可以用单引号来表示,但是json的标准格式是用双引号把属性和值给括起来的,注意是属性和值(当然数值类型的值可以省去双引号)。下列Json格式中除了最后一条是标准的,前面几条都是不标准的。

 

    { 'name' : 'keepfool' }  // 错误格式,proerty和value应用双引号
     { name: 'keepfool' }    // 错误格式,property应用双引号括起来,value应用双引号
     { name: "keepfool" }    // 错误格式,property应用双引号括起来
     { "name" : 'keepfool' } // 错误格式,value应用双引号
     { "name" : "keepfool" } // 标准格式

有时候手动生成json格式的字符串,可能会不符合标准,可以通过下面两个在线验证工具执行验证,我推荐使用第一个,因为它能够准确的定位出json串出错的地方,下面的几个例子也是通过第一个在线工具做验证的。

例子1(属性没带引号):

{ id: "1", "invdate": "2010-05-24", "name": "test", "note": "note", "tax": "10.00", "total": "2111.00" }

image

例子2(属性带单引号):

{ "id": "1", "invdate": '2010-05-24', "name": "test", "note": "note", "tax": "10.00", "total": "2111.00" }

image

例子3(正确):

{ "id": "1", "invdate": "2010-05-24", "name": "test", "note": "note", "tax": "10.00", "total": "2111.00" }

 image

二、jquery ui theme的使用

在文章的开篇我已经提到过jquery ui,它的使用方式我会在下面一步一步列出来,希望对那些还不太会使用jquery ui theme的童鞋们有所帮助。

1. 项目创建

在VS下创建一个Web网站或者Web应用程序,创建一个themes目录。如下图:

image

2. Base Theme的使用

先到http://jqueryui.com/themeroller/下载一个theme,就选那个最基本的灰色调的。
如果你已经熟悉下面的步骤并且对于下面的步骤不感兴趣,可以直接跳至关键的代码区域

image 

然后会看到下面这个画面,甭管左边有哪些组件,直接download.

image

把下载得到的jquery-ui-1.8.16.custom.zip文件解压出来,找到base目录(\jquery-ui-1.8.16.custom\development-bundle\themes\base),将base目录复制到themes文件夹下。

image

找到ui目录(\jquery-ui-1.8.16.custom\development-bundle\ui\),同样将其复制到themes目录下,ui目录包含三部分:
(1). jquery ui组件 
(2). 压缩后的jquery ui组件(在minified文件夹中)
(3). 本地化语言文件(i18n)

现在基本的准备都已经做好了,下面我们来创建第一个页面BaseTheme.html,并演示一个手风琴的效果(accordion组件),请看基础并且关键的步骤:

(1). 引用jquery ui css

<link rel="Stylesheet" type="text/css" href="themes/base/jquery.ui.all.css" />

(2). 引用jquery ui js

<script type="text/javascript" src="themes/ui/jquery.ui.core.js"></script>
<script type="text/javascript" src="themes/ui/jquery.ui.widget.js"></script>
<script type="text/javascript" src="themes/ui/jquery.ui.accordion.js"></script>

(3). 手风琴html代码

<div class="demo">
     <h1> Base Demo</h1>
     <div id="accordion">
     <h3><a href="#">Section 1</a></h3>
     <div>
     <p>A.</p>
     </div>
     <h3><a href="#">Section 2</a></h3>
     <div>
     <p> B. </p>
     </div>
     </div>
</div>

(4). 实现jquery 手风琴效果

<script type="text/javascript">
     $(function() {
     $("#accordion").accordion({
     collapsible: true
     });
     });
</script>

最终的效果如下图,当然完整的代码已经包含在下载文件中了,您也可以直接运行里面的代码。

SNAGHTML5f95aa

3. theme的使用方式

Base Theme的使用方式我们已经使用过了,但是这个灰色调的主题可能并不符合您网站的主题色调,不过幸运的是http://jqueryui.com/themeroller/已经为我们提供了很多种不同色调而且比较美观的主题,你可以自行去下载。当然如果你觉得某一款主题看起来不错,但某些样式还需要一些轻微的改动才能应用到自己的程序中,你可以使用Edit去进行编辑,然后生成自己需要的主题。

image

在第2步中已经列出了jquery ui的使用方式,如果你想换一个主题使用,方式和上面的代码步骤是相同的。
以lightness主题为例: 
image 

唯一不同之处是对css的引用

<link rel="Stylesheet" type="text/css" href="themes/theme_lightness/jquery.ui.all.css"/>
   
SNAGHTML5cf4ff[6]
这里演示的例子都是accordion的,如果你想尝试其他的jquery ui组件,可以自行更换。但关键的两行代码是必不可少的,它们是jquery ui组件的基础:
<script type="text/javascript" src="themes/ui/jquery.ui.core.js"></script>
<script type="text/javascript" src="themes/ui/jquery.ui.widget.js"></script>

4. 主题切换的实现

有了前面的基础,要实现主题切换已经很简单了,因为我们已经知道jquery theme的关键之处——引用jquery.ui.all.css,请看代码:

步骤I

<link rel="Stylesheet" type="text/css" href="themes/theme_lightness/jquery.ui.all.css" id="theme" >

步骤II

<div>
     请选择主题:
     <select id="theme_changer">
     <option value="themes/base/jquery.ui.all.css">Base Theme</option>
     <option value="themes/theme_lightness/jquery.ui.all.css" selected="selected">Lightness Theme</option>
     </select>
</div>

步骤III

<script type="text/javascript">
     $(document).ready(function() {
     $('#theme_changer').change(function() {
     var theme = $(this).find("option:selected").val();
     $('#theme').attr('href', theme);
     });
     });
</script>

效果图:

SNAGHTML5c904a

5. 单个页面多主题的实现

单个页面多主题的实现需要在http://jqueryui.com/download页面下载时多做一个步骤——指定css scope
点开Advanced Theme Settings,指定css scope,一般为css class的命名方式 
image

点击CSS Scope后面的image 按钮,你会发现css scope是专门针对多主题应用。

image

下面是完整的代码:

<!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>
     <title>多主题</title>
     <link rel="shortcut icon" href="/images/favicon.ico" />
     <!--应用base主题和lightness主题-->
     <link rel="Stylesheet" type="text/css" href="themes/base/jquery.ui.all.css" />
     <link rel="Stylesheet" type="text/css" href="themes/theme_lightness/jquery.ui.all.css" />
     <link rel="Stylesheet" type="text/css" href="themes/demos.css" />
     <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
     <script type="text/javascript" src="themes/ui/jquery.ui.core.js"></script>
     <script type="text/javascript" src="themes/ui/jquery.ui.widget.js"></script>
     <script type="text/javascript" src="themes/ui/jquery.ui.accordion.js"></script>
     <script type="text/javascript">
     $(function() {
     $(".accordion").accordion({
     collapsible: true
     });
     });
</script>
     </head>
     <body>
     <div class="demo">
     <h1> Base Demo</h1>
     <div class="accordion">
     <h3> <a href="#">Section 1</a></h3>
     <div>
     <p> A.</p>
     </div>
     <h3> <a href="#">Section 2</a></h3>
     <div>
     <p> B. </p>
     </div>
     </div>
     <div class="space"> </div>
     <h1> Lightness Demo</h1>
     <!--主题中如果包含css scope,需要指定css scope,才能让主题生效-->
     <div class="lightness">
     <div class="accordion">
     <h3> <a href="#">Section 1</a></h3>
     <div>
     <p> A.</p>
     </div>
     <h3> <a href="#">Section 2</a></h3>
     <div>
     <p> B. </p>
     </div>
     </div>
     </div>
     </div>
     </body>
     </html>
     

效果如下:

SNAGHTML586c4a

三、jqgrid的使用方式

上面的两大步都是为了使用jqgrid做准备,冗长的叙述和截图看起来很多,其实知道是jquery ui是怎么回事儿以后就会觉得很简单,步骤也不多。

jqgrid的项目地址:http://www.trirand.com/blog/,目前最新的版本是4.3的,也是我正在使用的版本。

1. jqgrid的local data使用方式

请看代码示例1

<script type="text/javascript">
     var mydata = [
     { id: "1", invdate: "2010-05-24", name: "test", note: "note", tax: "10.00", total: "2111.00" },
     { id: "2", invdate: "2010-05-25", name: "test2", note: "note2", tax: "20.00", total: "320.00" },
     { id: "3", invdate: "2007-09-01", name: "test3", note: "note3", tax: "30.00", total: "430.00" },
     { id: "4", invdate: "2007-10-04", name: "test", note: "note", tax: "10.00", total: "210.00" },
     { id: "5", invdate: "2007-10-05", name: "test2", note: "note2", tax: "20.00", total: "320.00" },
     { id: "6", invdate: "2007-09-06", name: "test3", note: "note3", tax: "30.00", total: "430.00" },
     { id: "7", invdate: "2007-10-04", name: "test", note: "note", tax: "10.00", total: "210.00" },
     { id: "8", invdate: "2007-10-03", name: "test2", note: "note2", amount: "300.00", tax: "21.00", total: "320.00" },
     { id: "9", invdate: "2007-09-01", name: "test3", note: "note3", amount: "400.00", tax: "30.00", total: "430.00" },
     { id: "11", invdate: "2007-10-01", name: "test", note: "note", amount: "200.00", tax: "10.00", total: "210.00" },
     { id: "12", invdate: "2007-10-02", name: "test2", note: "note2", amount: "300.00", tax: "20.00", total: "320.00" },
     { id: "13", invdate: "2007-09-01", name: "test3", note: "note3", amount: "400.00", tax: "30.00", total: "430.00" },
     { id: "14", invdate: "2007-10-04", name: "test", note: "note", amount: "200.00", tax: "10.00", total: "210.00" },
     { id: "15", invdate: "2007-10-05", name: "test2", note: "note2", amount: "300.00", tax: "20.00", total: "320.00" },
     { id: "16", invdate: "2007-09-06", name: "test3", note: "note3", amount: "400.00", tax: "30.00", total: "430.00" },
     { id: "17", invdate: "2007-10-04", name: "test", note: "note", amount: "200.00", tax: "10.00", total: "210.00" },
     { id: "18", invdate: "2007-10-03", name: "test2", note: "note2", amount: "300.00", tax: "20.00", total: "320.00" },
     { id: "19", invdate: "2007-09-01", name: "test3", note: "note3", amount: "400.00", tax: "30.00", total: "430.00" },
     { id: "21", invdate: "2007-10-01", name: "test", note: "note", amount: "200.00", tax: "10.00", total: "210.00" },
     { id: "22", invdate: "2007-10-02", name: "test2", note: "note2", amount: "300.00", tax: "20.00", total: "320.00" },
     { id: "23", invdate: "2007-09-01", name: "test3", note: "note3", amount: "400.00", tax: "30.00", total: "430.00" },
     { id: "24", invdate: "2007-10-04", name: "test", note: "note", amount: "200.00", tax: "10.00", total: "210.00" },
     { id: "25", invdate: "2007-10-05", name: "test2", note: "note2", amount: "300.00", tax: "20.00", total: "320.00" },
     { id: "26", invdate: "2007-09-06", name: "test3", note: "note3", amount: "400.00", tax: "30.00", total: "430.00" },
     { id: "27", invdate: "2007-10-04", name: "test", note: "note", amount: "200.00", tax: "10.00", total: "210.00" },
     { id: "28", invdate: "2007-10-03", name: "test2", note: "note2", amount: "300.00", tax: "20.00", total: "320.00" },
     { id: "29", invdate: "2007-09-01", name: "test3", note: "note3", amount: "400.00", tax: "30.00", total: "430.00"}];
     $(document).ready(function() {
     $("#gridTable").jqGrid({
     data: mydata,
     datatype: "local",
     height: 150,
     rowNum: 10,
     rowList: [10, 20, 30],
     colNames: ['Inv No', 'Date', 'Client', 'Amount', 'Tax', 'Total', 'Notes'],
     colModel: [
     { name: 'id', index: 'id', width: 60, sorttype: "int" },
     { name: 'invdate', index: 'invdate', width: 90, sorttype: "date", formatter: "date" },
     { name: 'name', index: 'name', width: 100 },
     { name: 'amount', index: 'amount', width: 80, align: "right", sorttype: "float", formatter: "number" },
     { name: 'tax', index: 'tax', width: 80, align: "right", sorttype: "float" },
     { name: 'total', index: 'total', width: 80, align: "right", sorttype: "float" },
     { name: 'note', index: 'note', width: 150, sortable: false }
     ],
     sortname: 'id',
     sortorder: 'desc',
     pager: "#gridPager",
     viewrecords: true,
     caption: "Manipulating Array Data"
     });
     });
</script>
<table id="gridTable">
</table>
<div id="gridPager">
</div>

jqgrid有几个较为关键的属性:

  • data:指定jqgrid的数据源
  • datatype:数据源的格式——local,javascript,function,json,jsonstring,xml。默认的数据源格式为xml
  • colNames:列名
  • colModels:指定列的属性,例如name用于关联数据源,index用于排序时的索引
  • sortname和sortorder分别指定jqgrid首次加载时的排序字段和排序方式
  • pager:指定分页容器。

上面这个例子的效果如下图:

SNAGHTML582493

2. jqgrid的server data之loadonce方式

实际的应用中数据都来源于后端,local方式基本不会使用。jqgrid中有一种loadonce的方式,当然后台提供所有的数据,分页、排序功能都由客户端自己去实现。数据少的时候用这种方式是最为快速的,毕竟只做一次请求后台除了输出数据外不需要再写其他的代码了。

请看代码示例:

 <script type="text/javascript">
     $(document).ready(function() {
     $("#gridTable").jqGrid({
     url : 'data/test.json',
     datatype: 'json',
     height: 150,
     rowNum: 10,
     rowList: [10, 20, 30],
     colNames: ['Inv No', 'Date', 'Client', 'Amount', 'Tax', 'Total', 'Notes'],
     colModel: [
     { name: 'id', index: 'id', width: 60, sorttype: "int" },
     { name: 'invdate', index: 'invdate', width: 90, sorttype: "date", formatter: "date" },
     { name: 'name', index: 'name', width: 100 },
     { name: 'amount', index: 'amount', width: 80, align: "right", sorttype: "float", formatter: "number" },
     { name: 'tax', index: 'tax', width: 80, align: "right", sorttype: "float" },
     { name: 'total', index: 'total', width: 80, align: "right", sorttype: "float" },
     { name: 'note', index: 'note', width: 150, sortable: false }
     ],
     loadonce: true,
     sortname: 'id',
     sortorder: 'desc',
     pager: "#gridPager",
     viewrecords: true,
     caption: "Manipulating Array Data"
     });
     });
</script>
<table id="gridTable">
</table>
<div id="gridPager">
</div>

这段代码和local data的形式中不同之处在于:

(1). url:指定了请求的数据源。(test.json文件的内容和第1个例子中的mydata 内容一样)
(2). datatype:将数据格式改为json,而不是local
(3). loadonce: true(默认情况下为false,显示指定只加载一次数据)

OK,编译运行,但是结果却没有出现例子1中的效果图,jqgrid里面完全没有数据显示。
页面LoadOnce.aspx效果:

SNAGHTML57cf5e

这时候jqgrid中一个更为关键的属性出现了,jsonReader。既然指定了数据格式为json,那么后端传过来的数据格式总不能我说什么就是什么吧,{"id":1, "name":"keepfool"}和{1,"keepfool"}都是正确的json数据,当传到前端时,前端怎么会知道要匹配哪一种形式?jsonReader顾名思义,就是用来读取json的,当然也要给它定个规矩。

让我们来看看http://www.trirand.com/jqgridwiki/doku.php?id=wiki:retrieving_data#json_data中默认的json格式是怎么样的吧

image 当没有设置jsonReader属性时,提供的json格式应该是下面这种形式的: 
image

它让你指定total,page,records属性(分别对应总页数、当前页、记录总数);rows属性,还必须指定id和cell属性,cell属性里面仅仅是单元格的值,还不能包括列名。如果按照默认的jsonReader来读取数据,你可能会写出如下构造json的代码:

public class MyOrder : IHttpHandler
     {
     public void ProcessRequest(HttpContext context)
     {
     context.Response.ContentType = "application/json";
     string sql = "select top 100 OrderID,CustomerID,ShipName,OrderDate from Orders ";
     DataTable dt = SqlHelper.ExecuteDataset(sql).Tables[0];
     IList<object> rowObjects = new List<object>();
     // 每页显示记录数
     int pageSize = 10;
     // 记录总数
     int rowCount = dt.Rows.Count;
     // 列数
     int columnCount = dt.Columns.Count;
     // 总页数
     int pageCount = rowCount%pageSize == 0 ? rowCount/pageSize : rowCount/pageSize + 1;
     int ID = 1;
     foreach (DataRow dr in dt.Rows)
     {
     var cellValue = new string[columnCount];
     for (var i = 0; i < cellValue.Length; i++)
     {
     cellValue[i] = dr[i].ToString();
     }
     // 创建row对象
     var rowObj = new
     {
     id = ID,
     cell = cellValue
     };
     rowObjects.Add(rowObj);
     ID++;
     }
     var resultObj = new
     {
     total = pageCount, // 总页数
     page = 1, // 由于客户端是loadonce的,page也可以不指定
     records = rowCount, // 总记录数
     rows = rowObjects // 数据
     };
     string json = JsonConvert.SerializeObject(resultObj);
     context.Response.Write(json);
     }
     public bool IsReusable
     {
     get
     {
     return false;
     }
     }
     }

请求页面LoadOnceOrders.aspx效果:

SNAGHTML55c96d

 

但是这里存在一个小bug,下面的分页按钮不起作用了,当点击排序或者选择每页显示记录数时,分页按钮又可以用了。这个bug仅在loadonce为true时会出现。

http://www.trirand.com/jqgridwiki/doku.php?id=wiki:options页面中也对loadonce属性有所说明

image

3. jqgrid的jsonReader

上面的例子中没有设置jsonReader属性,那么就得按照默认的jsonReader提供json数据。上面的json数据会让我们感觉有一些怪异,rows属性中的id和cell我们不需要,total应该表示为pagecount, records应该表示为total在语义上更易读通,而且row中的每一列都应当是键值对这种形式。

寻常情况下我们提供的json格式应该是这样的:

{
     "pagecount":10,
     "pageindex":1,
     "total":100,
     "rows":[
     {
     "id":1,
     "name":"keepfool"
     },
     {
     "id":2,
     "name":"pai"
     }
     ...
     ]
}

pagecount表示总页数,pageindex表示当前页,total表示总记录数,rows表示记录。既然这样,我们的jsonReader就得改写,而且后台也应当提供这样的数据。

LoadOnceOrder2.aspx页面的jsonReader(其他设置和LoadOnceOrders.aspx页面一致)

jsonReader: {
     root: "rows",
     page: "pageindex",
     total: "pagecount",
     records: "total",
     repeatitems: false,
     id: "0"
}

请注意这里的repeatitems属性,当repeatitems=false时,jqgrid会根据返回的json数据搜索列名,这个列名对应colModel中的名字
为LoadOnceOrder2.aspx页面提供Json数据的MyOrder2.ashx的http处理程序

public class MyOrder2 : IHttpHandler
     {
     public void ProcessRequest(HttpContext context)
     {
     context.Response.ContentType = "application/json";
     string sql = "select top 100 OrderID,CustomerID,ShipName,OrderDate from Orders ";
     DataTable dt = SqlHelper.ExecuteDataset(sql).Tables[0];
     // 每页显示记录数
     int pageSize = 10;
     // 记录总数
     int rowCount = dt.Rows.Count;
     // 总页数
     int pageCount = rowCount % pageSize == 0 ? rowCount / pageSize : rowCount / pageSize + 1;
     var resultObj = new DataResult
     {
     // 总页数
     PageCount = pageCount,
     // 当前页
     PageIndex = 1,
     // 总记录数
     Total = rowCount,
     // 数据
     Data = dt
     };
     string resultJson = JsonHelper.Serialize(resultObj);
     context.Response.Write(resultJson);
     }
     public bool IsReusable
     {
     get { return false; }
     }
     }

DataResult类的代码很简单:

      /// <summary>
     /// 数据结果
     /// </summary>
     public class DataResult
     {
     /// <summary>
     /// 总记录数
     /// </summary>
     public int Total { get; set; }
     /// <summary>
     /// 总页数
     /// </summary>
     public int PageCount { get; set; }
     /// <summary>
     /// 页码
     /// </summary>
     public int PageIndex { get; set; }
     /// <summary>
     /// 数据
     /// </summary>
     public DataTable Data { get; set; }
     }

JsonHelper类是通过JSON.NET实现对象序列化和反序列化的一个帮助类,这里我用它来的Serialize方法序列化DataResult对象,它的代码我就不贴出来了,在下载的源代码中已经包含了。

下面再介绍一种jsonReader用法,这种用法是函数形式的,也是我个人最推崇这种用法:

jsonReader: {
     repeatitems: false,
     root: function(obj) { return obj.rows; },
     page: function(obj) { return obj.pageindex; },
     total: function(obj) { return obj.pagecount; },
     records: function(obj) { return obj.total; }
}

观察一下上面所提供的寻常json写法,你会发现json数据对象里面包含rows, pageindex, pagecount,total属性,而后台输出到前端的function函数中得到的是obj对象,那么用obj.rows, obj.pageindex, obj.pagecount, obj.total就会觉得一目了然。

4.  jqgird的server data之最终解决方案

上面的分页和排序都因为loadonce=true属性而导致客户端帮我们做了大部分的事情,但是loadonce存在bug,当数据量很大时,一次性加载有所数据不仅速度太慢,如果传输的json数据超过4MB时会抛出异常(可以在web.config中设置大小)。

要解决这个问题,我们只有通过服务端来操控分页数据,而不是一次性丢给客户端所有的数据,也就是说客户端请求哪一页的数据服务端就提供那一页的几条数据给客户端。

到了这里,我要介绍最后一个jqgrid很重要的属性——prmNames

image

http://www.trirand.com/jqgridwiki/doku.php?id=wiki:options中对prmNames的介绍很详细,我们只用看标记部分,prmNames是发起请求时发送到服务端的参数数组。
我们现在只关心4个参数的设定,page,rows,sort和order,不用它的默认设置,我们自己将prmNames设置为如下的形式:

prmNames: {
     page: 'PageNumber',
     rows: 'PageSize',
     sort: 'OrderBy',
     order: 'Sort'
     }
那么一个发送到服务端的请求可能是这样的mydata.ashx?PageNumber=1&PageSize=10&OrderBy=OrderID&Sort=desc

注意这里的rows,由于我们每一页的分页数据都是向服务器发送请求取得的,所以这里的rows对应为PageSize

在此之前我们先看看我们最终的效果图:

SNAGHTML54fe9b

①.排序 ②.分页 ③. 记录这三个地方的功能都是正常的无误的。

我还是按照step by step的方式来介绍这个实现过程。

(1) 分页存储过程

分页数据通过执行存储过程来得到,以获取订单为例(数据库为Northwind):

create proc SP_GetOrdersByPage
(
     @PageSize int,
     @PageIndex int,
     @Where nvarchar(1000),
     @OrderBy nvarchar(50),
     @Sort varchar(4),
     @Total int output
)
     as
     declare @startRow int,@endRow int
     --起始行索引
     select @startRow = @PageSize*(@PageIndex -1) + 1
     --结束行索引
     select @endRow = @startRow + @PageSize - 1
     --查询结果集sql
     declare @query_sql nvarchar(2000)
     --查询记录数sql
     declare @count_sql nvarchar(2000)
     set @query_sql = 'select OrderID,CustomerID,ShipName,OrderDate from
     (select row_number() over(order by ' + @OrderBy + ' ' + @Sort +' ) as rowid,
     OrderID,CustomerID,ShipName,OrderDate from Orders
     where 1 = 1 ' + @Where + ') as Results
     where rowid >='+ str(@startRow) + ' and rowid <=' + str(@endRow)
     set @count_sql = 'select @Total = count(OrderID) from Orders where 1 = 1' + @Where
     print @query_sql
     exec sp_executesql @count_sql,N'@Total int output', @Total output
     exec sp_executesql @query_sql
     go
     declare @Total int
     exec SP_GetOrdersByPage 10,2,'','OrderID','desc',@Total output

(2) PagingParameters类

既然有了客户端提供的prmNames参数,那么后台总得有个东西来接收它们吧。那好,在这儿我们定义一个PagingParameters类,用来管理分页参数。现在PagingParameters类已经拥有4个属性了:PageNumber, PageSize, OrderBy, Sort。

这些参数还不够,存储过程中的@Where参数和@Total参数总需要吧,那么再加两个属性Where,Total。但是这些参数还不够,存储过程SP_GetOrdersByPage是对Order进行分页的,如果我要对Category分页,那是不是该提供一个SP_GetCategoriesByPage这种存储过程呢?所以这儿还需要一个参数ProcedureName。

稍作整理后,PagingParameters类的代码如下:

      /// <summary>
     /// 分页参数
     /// </summary>
     public class PagingParameters
     {
     /// <summary>
     /// 页码
     /// </summary>
     public int PageIndex { get; set; }
     /// <summary>
     /// 每页显示个数
     /// </summary>
     public int PageSize { get; set; }
     /// <summary>
     /// 排序字段
     /// </summary>
     public string OrderBy { get; set; }
     /// <summary>
     /// 排序方式
     /// </summary>
     public string Sort { get; set; }
     /// <summary>
     /// 查询条件
     /// </summary>
     public string Where { get; set; }
     /// <summary>
     /// 存储过程名称
     /// </summary>
     public string ProcedureName { get; set; }
     /// <summary>
     /// 总记录数,为输出参数
     /// </summary>
     public int Total
     {
     get
     {
     if (_parameters.Count > 0)
     {
     return (int)_parameters[5].Value;
     }
     return 0;
     }
     }
     private IList<SqlParameter> _parameters = new List<SqlParameter>();
     /// <summary>
     /// 存储过程参数
     /// </summary>
     /// <returns></returns>
     public SqlParameter[] ProcedureParameters
     {
     get
     {
     if(_parameters.Count == 0)
     {
     _parameters.Add(new SqlParameter("PageIndex", PageIndex));
     _parameters.Add(new SqlParameter("PageSize", PageSize));
     _parameters.Add(new SqlParameter("Where", Where));
     _parameters.Add(new SqlParameter("OrderBy", OrderBy));
     _parameters.Add(new SqlParameter("Sort", Sort));
     var outputTotal = new SqlParameter
     {
     Direction = ParameterDirection.Output,
     ParameterName = "Total",
     DbType = DbType.Int32,
     Size = 4
     };
     _parameters.Add(outputTotal);
     }
     return _parameters.ToArray();
     }
     }
     }

(3) 抽象的http 分页处理程序类

如果ajax分页用到的场合不止一处,就干脆写个抽象的http处理程序吧,代码我就不解释了,看注释应该很容易弄明白的。

 /// <summary>
     /// 抽象分页http处理程序类
     /// </summary>
     public abstract class PagingHandler : IHttpHandler
     {
     public virtual void ProcessRequest(HttpContext context)
     {
     var parameters = new PagingParameters
     {
     // 接收PageIndex参数
     PageIndex = int.Parse(context.Request["PageIndex"]),
     // 接收PageSize参数
     PageSize = int.Parse(context.Request["PageSize"]),
     // 接收OrderBy参数
     OrderBy = context.Request["OrderBy"],
     // 接收Sort参数
     Sort = context.Request["Sort"],
     // 存储过程名称
     ProcedureName = ProcName,
     // Where条件
     Where = string.Empty
     };
     this.PagingParameters = parameters;
     context.Response.ContentType = "application/json";
     context.Response.Write(GetJsonResult());
     }
     /// <summary>
     /// 获取输出到客户端的json
     /// </summary>
     public virtual string GetJsonResult()
     {
     DataSet ds = SqlHelper.ExecuteDataset(PagingParameters.ProcedureName, PagingParameters.ProcedureParameters);
     DataTable table = ds.Tables[0];
     // 存储过程已经执行完毕,可以获取到输出参数Total的值
     int total = PagingParameters.Total;
     // 计算总页数
     int pageCount = total%PagingParameters.PageSize == 0? total/PagingParameters.PageSize: total/PagingParameters.PageSize + 1;
     var dataResult = new DataResult
     {
     Data = table,
     PageIndex = PagingParameters.PageIndex,
     PageCount = pageCount,
     Total = total
     };
     // 序列化DataResult对象
     string json = JsonHelper.Serialize(dataResult);
     return json;
     }
     protected PagingParameters PagingParameters { get; set; }
     public bool IsReusable
     {
     get { return false; }
     }
     /// <summary>
     /// 必须初始化的属性:存储过程名称
     /// </summary>
     public abstract string ProcName { get; }
     }

(4) MyOrder3.ashx服务端http分页处理程序

创建一个MyOrder3.ashx http处理程序,让它继承PagingHandler类。现在要做的很简单了,PagingHandler类已经帮我们做了大部分的事情,我们只需要实现抽象属性ProcName就可以了。

public class MyOrder3 : PagingHandler
     {
     public override string ProcName
     {
     get { return "SP_GetOrdersByPage"; }
     }
     }

(5) 前端代码ServerPaging.aspx页面

<script type="text/javascript">
     $(document).ready(function() {
     $("#gridTable").jqGrid({
     url: 'handlers/myorder3.ashx',
     datatype: 'json',
     height: 250,
     rowNum: 10,
     rowList: [10, 20, 30],
     colNames: ['订单ID', '客户ID', '送货人', '下单时间'],
     colModel: [
     { name: 'OrderID', index: 'OrderID', width: 100 },
     { name: 'CustomerID', index: 'CustomerID', width: 100 },
     { name: 'ShipName', index: 'ShipName', width: 160 },
     { name: 'OrderDate', index: 'OrderDate', width: 160 }
     ],
     jsonReader: {
     repeatitems: false,
     root: function(obj) { return obj.rows; },
     page: function(obj) { return obj.pageindex; },
     total: function(obj) { return obj.pagecount; },
     records: function(obj) { return obj.total; }
     },
     prmNames: {
     page: 'PageIndex',
     rows: 'PageSize',
     sort: 'OrderBy',
     order: 'Sort'
     },
     loadonce: false,
     sortname: 'OrderID',
     sortorder: 'desc',
     pager: "#gridPager",
     viewrecords: true,
     caption: "Manipulating Array Data"
     });
     });
     </script>
     <table id="gridTable">
     </table>
     <div id="gridPager">
     </div>

(6) jqgrid最大高度+高度自适应的实现

在我的另外一篇文章Web布局中的几种宽高自适应中,我没有贴出实现jqgrid宽高自适应的代码,在这里一并贴出吧。

<script type="text/javascript">
     $(document).ready(function() {
     function content_resize() {
     var grid = $("#gridTable");
     var h = $(window).height() - grid.offset().top - 50;
     $('.ui-jqgrid-bdiv').css("height", h);
     }
     $(window).wresize(content_resize);
     content_resize();
     });
     </script>

加上这一段脚本后,最终的效果看起来就更为不错了。

SNAGHTML54b221

SNAGHTML548ab3

posted @ 2012-01-05 22:02  keepfool  阅读(8520)  评论(40编辑  收藏  举报