要实现Ext Grid的远程排序其实很简单,只要修改查询语句的排序关键字就可以了,但是,如果你的项目是使用Linq进行开发的,会发现动态修改排序关键字并不是那么容易的事,解决办法就是使用LINQ Dynamic Query Library。LINQ Dynamic Query Library是一个很实用很强大的库函数,通过该库,可以轻松实现一些需要通过动态参数实现的Linq查询。

本文将通过一个实例演示如何使用LINQ Dynamic Query Library实现Ext Grid的远程排序。
LINQ Dynamic Query Library可以在VS2008的例程里找到,也可以从以下链接下载: 
 本例子将使用SQL Server的“NORTHWND”样例数据库。Ext Grid显示的是Employees表的数据。
以下是客户端的完整代码:
  1. <html> 
  2. <head> 
  3.   <title></title> 
  4.   <meta http-equiv="Content-Type" content="text/html; charset=utf-8" xmlns="" /> 
  5.   <link rel="stylesheet" type="text/css" href="lib/ext/resources/css/ext-all.css" /> 
  6.   <link rel="stylesheet" type="text/css" href="css/application.css" /> 
  7. </head> 
  8.   <script type="text/javascript" src="lib/ext/ext-base.js"></script> 
  9.   <script type="text/javascript" src="lib/ext/ext-all.js"></script> 
  10.   <script type="text/javascript" src="lib/ext/locale/ext-lang-zh_CN.js"></script> 
  11. <body scroll="no"> 
  12.   <div id="loading-mask"></div> 
  13.   <div id="loading"> 
  14.     <div class="loading-indicator"><img alt="" src="lib/ext/resources/p_w_picpaths/default/shared/large-loading.gif" width="32" height="32" style="margin-right:8px;" align="absmiddle"/>正在加载...</div> 
  15.   </div> 
  16.   <script type="text/javascript"> 
  17.    
  18. var app={}  
  19.    
  20. Ext.onReady(function(){  
  21.        Ext.BLANK_IMAGE_URL='lib/ext/resources/p_w_picpaths/default/s.gif';  
  22.   Ext.QuickTips.init();  
  23.        Ext.form.Field.prototype.msgTarget = 'side';  
  24.        Ext.Msg.minWidth=300;  
  25.    
  26.    
  27.        app.store=new Ext.data.Store({  
  28.     url:'employees_action.ashx?act=list',  
  29.     baseParams:{},  
  30.     reader:new Ext.data.JsonReader({  
  31.            totalProperty: "results",  
  32.              root:"rows",  
  33.              id:"id"  
  34.               },[{name: 'id',type:'int'},{name:'lastname'},{name:'firstname'},  
  35.                      {name:'title'},{name:'titleofcourtesy'},{name:'city'},  
  36.                      {name:'address'},{name:'region'},{name:'postalcode'},{name:'homephone'},{name:'country'},  
  37.              {name:'birthdate',type: 'date',dateFormat:'Y-m-d'},  
  38.              {name:'hiredate',type: 'date',dateFormat:'Y-m-d'}  
  39.               ]),  
  40.     remoteSort: true  
  41.   }) //store  
  42.    
  43.   app.pageToolbar=new Ext.PagingToolbar({  
  44.       pageSize:3,displayInfo:true,store:app.store  
  45.       });  
  46.    
  47.   app.grid=new Ext.grid.GridPanel({layout:'fit',  
  48.       store:app.store, autoExpandColumn:2,tbar:app.pageToolbar,  
  49.     columns:  
  50.     [  
  51.       {id:'id',header: "ID",width:80,dataIndex:'id',sortable: true},  
  52.       {header: "FirstName",width:80, dataIndex:'firstname',sortable: true},  
  53.       {header: "LastName",width:80, dataIndex:'lastname',sortable: true},  
  54.       {header: "Title",width:80, dataIndex:'title',sortable: true},  
  55.       {header: "Title of Courtesy",width:80, dataIndex:'titleofcourtesy',sortable: true},  
  56.       {header: "City",width:80, dataIndex:'city',sortable: true},  
  57.       {header: "Region",width:80, dataIndex:'region',sortable: true},  
  58.       {header: "Country",width:80, dataIndex:'country',sortable: true},  
  59.       {header: "Postalcode",width:80, dataIndex:'postalcode',sortable: true},  
  60.       {header: "Homephone",width:80, dataIndex:'homephone',sortable: true},  
  61.       {header: "Birthdate", width: 120,dataIndex:'birthdate',sortable: true,renderer:Ext.util.Format.dateRenderer('Y-m-d')},  
  62.       {header: "Hiredate", width: 120,dataIndex:'hiredate',sortable: true,renderer:Ext.util.Format.dateRenderer('Y-m-d')}  
  63.     ]  
  64.   })  
  65.    
  66.    
  67.        var viewport = new Ext.Viewport({layout:'fit',items:[app.grid]});  
  68.    
  69.    
  70.        app.store.load();  
  71.         
  72.    
  73.        setTimeout(function(){  
  74.     Ext.get('loading').remove();  
  75.     Ext.get('loading-mask').fadeOut({remove:true});  
  76.   }, 250);  
  77.    
  78.    
  79. })//onReady  
  80. </script> 
  81. </body> 
  82. </html> 

代码很简单,定义了一个Store、PagetoolBar和Grid。因为Employees表数据只有9条,所以设置了每页3条数据。在Store定义中将remoteSort设置为true,说明数据要实现远程排序。Grid的每一列都将sortable属性设置为true,说明都可以通过单击Grid的列标题实现排序。 

以下是服务器端的完整代码:

  1. <%@ WebHandler Language="C#" Class="employees_action" Debug="true" %> 
  2.    
  3. using System;  
  4. using System.Web;  
  5. using System.Linq;  
  6. using System.Linq.Dynamic;  
  7. using System.Collections;  
  8. using System.Collections.Generic;  
  9. using System.Web.Security;  
  10. using LitJson;  
  11.    
  12.    
  13. public class employees_action : IHttpHandler  
  14. {  
  15.      
  16.   public void ProcessRequest (HttpContext context) {  
  17.     string action = context.Request.Params["act"];  
  18.     string outputStr = "";  
  19.     if (action == null) action = "";  
  20.     switch (action.ToLower())  
  21.     {  
  22.       case "list":  
  23.         outputStr = List(context);  
  24.         break;  
  25.       default:  
  26.         outputStr = HDQ.Functions.WriteJsonResult(false, "错误的操作类型!");  
  27.         break;  
  28.     }  
  29.     context.Response.ContentType = "text/javascript";  
  30.     context.Response.Write(outputStr);  
  31.   }  
  32.    
  33.   public bool IsReusable {  
  34.     get {  
  35.         return false;  
  36.     }  
  37.   }  
  38.    
  39.   private string List(HttpContext context)  
  40.   {  
  41.     int limit=0;  
  42.     int.TryParse(context.Request.Params["limit"], out limit);  
  43.     if (limit == 0) limit = 3;  
  44.     int start=0;  
  45.     int.TryParse(context.Request.Params["start"], out start);  
  46.     string orderColumn = context.Request.Params["sort"];  
  47.     string orderBy = context.Request.Params["dir"] == "ASC" ? "" : "descending";  
  48.     switch (orderColumn)  
  49.     {  
  50.       case "id":  
  51.         orderColumn = "EmployeeID";  
  52.         break;  
  53.       case "lastname":  
  54.         orderColumn = "LastName";  
  55.         break;  
  56.       case "firstname":  
  57.         orderColumn = "FirstName";  
  58.         break;  
  59.       case "title":  
  60.         orderColumn = "Title";  
  61.         break;  
  62.       case "titleofcourtesy":  
  63.         orderColumn = "TitleOfCourtesy";  
  64.         break;  
  65.       case "birthdate":  
  66.         orderColumn = "BirthDate";  
  67.         break;  
  68.       case "hiredate":  
  69.         orderColumn = "HireDate";  
  70.         break;  
  71.       case "address":  
  72.         orderColumn = "Address";  
  73.         break;  
  74.       case "city":  
  75.         orderColumn = "City";  
  76.         break;  
  77.       case "region":  
  78.         orderColumn = "Region";  
  79.         break;  
  80.       case "postalcode":  
  81.         orderColumn = "PostalCode";  
  82.         break;  
  83.       case "country":  
  84.         orderColumn = "Country";  
  85.         break;  
  86.       case "homephone":  
  87.         orderColumn = "HomePhone";  
  88.         break;  
  89.       default:  
  90.         orderColumn = "EmployeeID";  
  91.         break;  
  92.     }  
  93.     DBDemosDataContext dc = new DBDemosDataContext();  
  94.     int recordCount=0;  
  95.     JsonWriter jw = new JsonWriter();  
  96.     jw.WriteObjectStart();  
  97.     jw.WritePropertyName("rows");  
  98.     jw.WriteArrayStart();  
  99.     recordCount = dc.Employees.Count();  
  100.     if (start > recordCount) start = 0;  
  101.     var q=dc.Employees.OrderBy(orderColumn + " " + orderBy).Skip(start).Take(limit);  
  102.     foreach (var c in q)  
  103.     {  
  104.       jw.WriteObjectStart();  
  105.       jw.WritePropertyName("id");  
  106.       jw.Write(c.EmployeeID);  
  107.       jw.WritePropertyName("firstname");  
  108.       jw.Write(c.FirstName);  
  109.       jw.WritePropertyName("lastname");  
  110.       jw.Write(c.LastName);  
  111.       jw.WritePropertyName("title");  
  112.       jw.Write(c.Title);  
  113.       jw.WritePropertyName("titleofcourtesy");  
  114.       jw.Write(c.TitleOfCourtesy);  
  115.       jw.WritePropertyName("address");  
  116.       jw.Write(c.Address);  
  117.       jw.WritePropertyName("city");  
  118.       jw.Write(c.City);  
  119.       jw.WritePropertyName("region");  
  120.       jw.Write(c.Region);  
  121.       jw.WritePropertyName("country");  
  122.       jw.Write(c.Country);  
  123.       jw.WritePropertyName("postalcode");  
  124.       jw.Write(c.PostalCode);  
  125.       jw.WritePropertyName("homephone");  
  126.       jw.Write(c.HomePhone);  
  127.       jw.WritePropertyName("birthdate");  
  128.       jw.Write(c.BirthDate == null ? "" : Convert.ToDateTime(c.BirthDate).ToString("yyyy-MM-dd"));  
  129.       jw.WritePropertyName("hiredate");  
  130.       jw.Write(c.HireDate == null ? "" : Convert.ToDateTime(c.HireDate).ToString("yyyy-MM-dd"));  
  131.       jw.WriteObjectEnd();  
  132.     }  
  133.      
  134.     jw.WriteArrayEnd();  
  135.     jw.WritePropertyName("results");  
  136.     jw.Write(recordCount.ToString());  
  137.     jw.WriteObjectEnd();  
  138.     return jw.ToString();  
  139.   }  
  140.    
  141.    
  142.    
  143. }  

代码中ProcessRequest方法根据提交的参数action执行对应的方法。本文主要是执行List方法。

在List方法的开头首先获取了客户端提交的几个参数,参数对应的说明请看下表:

参数
说明
limit
每页总数,本例子是3
start
提取数据开始位置
sort
要排序的列
dir
排序顺序

获取数据后需要对排序的列名和顺序做一下转换,以下语句就是实现排序顺序的转换:
string orderBy = context.Request.Params["dir"] == "ASC" ? "" : "descending";
 
列名的转换则通过switch语句实现。如果在客户端定义的列名与数据库的真实列名相同,也可以不实施转换。不过,出于安全考虑,建议无论如何,还是要实行转换。
转换完成后,就可以定义查询语句了,相当的简单:
var q=dc.Employees.OrderBy(orderColumn + " " + orderBy).Skip(start).Take(limit);
 
将列名变量和顺序变量组合成字符串作为OrderBy方法的参数就可以了。LINQ Dynamic Query Library会自动重新生成Linq语句执行。
后面的代码就是将查询结果组合成Json格式数据输出。
 
如果不使用LINQ Dynamic Query Library,远程排序的实现最直接的方法就是使用switch语句,根据提交的列和排序顺序写不同的Linq语句,就不如本例的代码那么简洁了。
 
以下是本例程的代码下载地址: