[转载]RuGrid——仿AspxGridView(基于Jquery)

[转载]RuGrid——仿AspxGridView(基于Jquery) – 陈儒 – 博客园.

RuGrid JQuery插件的开发目的是为了克服AspxGridView性能低下的问题,AspxGridView的性能问题主要是由两方面构成:

1. 数据库的查询结果在应用层分页(主要问题)

2. 输出了过多的HTML以及js,在Internet环境中太过损耗带宽

同时AspxGridView控件本身的极度复杂性使得开发人员很难对其进行定制化的修改,即使AspxGridView已经提供了非常强大的定制 化功能得以覆盖大多数的需求。但我们永远也无法低估客户的淫荡程度,各种细枝末节上的调整会让你焦头烂额。或许某些小需求在纯html下可以轻松实现,但 使用了控件后复杂性就会疯狂提升。组件过度的封装使得开发人员压根儿无法得知如何设置暴露出来的属性才能render出自己想要的html代码(同时 render出的html代码又是复杂到几乎不可读的)。所以我们常常拿着文档反复试验着设置某个属性以期望达到预想的效果,这是一个极度乏味的工作,会 让你几乎有立即抛弃此控件用html从头写起的冲动。

而且就算拥有控件源代码也几乎无补于事,因为大多数的ASP.NET开发者都是组件的使用者,而不是组件的开发者。不熟悉组件的开发过程而去阅读代码只能是雾里看花。

所以,我希望拥有的是一个具有清晰可读的html和css的轻量级的数据表格控件、它可以在保证遵守约定的前提下随意定制render结果。

目前RuGrid实现AspxGridView的几个主要功能:纯Ajax的分页、排序、Filter、HeaderFilter。

效果图如下:

分页效果:

Filter:

HeaderFilter:

排序:

ASP.NET MVC3下的使用方式:

1. 建立一个View,名为List

引入如下文件:

<link href="@Url.Content("~/Content/Styles/RuGrid/style.css")" rel="stylesheet" type="text/css"/>
    <script type="text/javascript" src="@Url.Content("~/Scripts/jquery.ru-grid.js")"></script>

Body的内容:

<div id="dv-loading" class="data-loading">
    Loading...
</div>

<div id="dv-grid" style="width:600px;">
    @Html.Partial("_RuGrid")
</div>

_RuGrid是一个分部视图,里面的内容是关键:

@model IEnumerable<RuGridMvc.Personnel>
@{
    var pagingInfo = ViewBag.PagingInfo as RuGridMvc.Models.PagingInfo;
    var groupKey = ViewBag.GroupKey as IEnumerable<bool>;
}

<table class="data-grid">
    <tr class="header-row">
        <td data-field="ID" header-text="编号" sort="true"></td>
        <td data-field="Name" header-text="姓名" sort="true"></td>
        <td data-field="IsFemale" header-text="性别" sort="true" header-filter="true"></td>
        <td>Age </td>
    </tr>
    <tr class="filter-row">
        <td filter='true' data-field="ID"></td>
        <td filter='true' data-field="Name"></td>
        <td></td>
        <td></td>
    </tr>
    @foreach (var item in Model)
    {
        <tr class="data-row">
            <td>@item.ID</td>
            <td>@item.Name</td>
            <td>@(item.IsFemale ? "女" : "男")</td>
            <td>@item.Age</td>
        </tr>
    }
    <tr class="paging-row">
        <td colspan="4">@pagingInfo.PageLink()</td>
    </tr>
</table>

每个元素中的class属性同时也充当了约定内容,不允许更改,否则会失效。在header-row中的td元素中,通过自定义属性设置了属性名 称、显示内容,以及功能设定。比如如果想要启动排序效果,则设置sort=”true”属性,如要设置headerFilter功能,则设置 header-filter=”true”。class属性为filter-row的行是Filter行,如果不需要启动此功能,则去掉此行即可。否则需 要在需要启动过滤功能的列上指定filter=’true’以及data-field=’属性名称’即可。
需要注意的是,启用了header-filter功能后,需要手动输出下拉框的html模板,如下:

<div class="grid-header-filter" data-field="IsFemale">
<table>
    <tr>
        <td class="filter-content">
            <table>
                <tr>
                    <td>(全部)</td>
                </tr>
                @foreach (var item in groupKey)
                {
                    <tr><td data_value='@item'>@(item?"女" : "男")</td></tr>
                }
            </table>
            
        </td>
        <td class="vertical-shadow"></td>
    </tr>
    <tr>
        <td class="horizontal-shadow" colspan="2"></td>
    </tr>
</table>
</div>

指定了最外侧的div的class属性以及data-field,控件就会自动进行关联。

然后是服务端的代码,创建PersonnelController,在其中创建List Action,写上关键逻辑:

        public ActionResult List(int page = 1, string order = null, string direction = null, Models.PersonnelFilterInfo filterInfo = null)
        {
            System.Threading.Thread.Sleep(1000);
            DbDataContext db = new DbDataContext();

            var expr = PredicateBuilder.True<Personnel>();
            if (null != filterInfo)
            {
                expr = expr.And(p => (filterInfo.ID == null || p.ID == filterInfo.ID)).And(p => (string.IsNullOrWhiteSpace(filterInfo.Name) || p.Name.Contains(filterInfo.Name))).And(p => (filterInfo.IsFemale == null || p.IsFemale == filterInfo.IsFemale));
            }

            Models.PagingInfo pagingInfo = new Models.PagingInfo(db.Personnel.Count(expr), 10, page);
            var model = Order(db.Personnel.Where(expr), order, direction).Skip((pagingInfo.CurrentPage - 1) * 10).Take(10);

            ViewBag.PagingInfo = pagingInfo;
            ViewBag.GroupKey = from x in db.Personnel group x by x.IsFemale into g select g.Key;

            if (Request.IsAjaxRequest())
            {
                return PartialView("_RuGrid", model);
            }
            return View(model);
        }

里面使用了动态Linq,详细可见源码。

最后在List视图中进行控件调用:

    <script type="text/javascript">
        $(function () {
            $('#dv-grid').rugrid({
                loadingElementId: 'dv-loading',
                url: '@Url.Action("List")'
            });
        });
    </script>

我们只需指定loading元素id以及请求url即可。

在ASP.NET WebForm下的使用方式:

在webForm下的使用比较ugly,我们需要创建一个httphandler来处理请求。然后使用web用户控件(.ascx)充当分部页的角色,最后将用户控件的html通过httphandler输出即可。

第一步:创建Grid.ascx用户控件

<%@ Control Language="C#" AutoEventWireup="true" CodeFile="Grid.ascx.cs" Inherits="Grid"%>

<table class="data-grid">
<asp:Repeater ID="Repeater1" runat="server">
    <HeaderTemplate>
        <tr class="header-row">
            <td data-field="ID" header-text="编号" sort="true">
            </td>
            <td data-field="Name" header-text="姓名" sort="true">
            </td>
            <td data-field="IsFemale" header-text="性别" sort="true" header-filter="true">
            </td>
            <td>
                Age
            </td>
        </tr>
        <tr class="filter-row">
            <td filter='true' data-field="ID"></td>
            <td filter='true' data-field="Name">
            </td>
            <td></td>
            <td></td>
        </tr>
    </HeaderTemplate>
    <ItemTemplate>
        <tr class="data-row">
            <td><%# Eval("ID") %></td>
            <td><%# Eval("Name") %></td>
            <td><%# (bool)Eval("IsFemale")? "" : ""%></td>
            <td><%# Eval("Age")%></td>
        </tr>
    </ItemTemplate>
</asp:Repeater>
    <tr class="paging-row">
        <td colspan="4">
            <%= this.PagingInfo.PageLink()%>
        </td>
    </tr>
</table>

<div class="grid-header-filter" data-field="IsFemale">
<table>
    <tr>
        <td class="filter-content">
            <table>
                <tr>
                    <td>(全部)</td>
                </tr>
                <asp:Repeater ID="Repeater2" runat="server">
                    <ItemTemplate>
                        <tr><td data_value='<%# Eval("IsFemale") %>'><%# (bool)Eval("IsFemale") ? "" : ""%></td></tr>
                    </ItemTemplate>
                </asp:Repeater>
            </table>
            
        </td>
        <td class="vertical-shadow"></td>
    </tr>
    <tr>
        <td class="horizontal-shadow" colspan="2"></td>
    </tr>
</table>
</div>

CodeBehind关键代码:

protected void Page_Load(object sender, EventArgs e)
    {
        DbDataContext db = new DbDataContext();
        var expr = PredicateBuilder.True<Personnel>();
        if (null != Model)
        {
            expr = expr.And(p => (Model.ID == null || p.ID == Model.ID));
            expr = expr.And(p => (string.IsNullOrWhiteSpace(Model.Name) || p.Name.Contains(Model.Name)));
            expr = expr.And(p => (Model.IsFemale == null || p.IsFemale == Model.IsFemale));
        }

        _PagingInfo = new PagingInfo(db.Personnel.Count(expr), _PageSize, _CurrentPage);

        Repeater1.DataSource = Order(db.Personnel.Where(expr))
            .Skip(_PageSize * (_PagingInfo.CurrentPage - 1)).Take(_PageSize);
        Repeater1.DataBind();

        Repeater2.DataSource = from x in db.Personnel group x by x.IsFemale into g select new { IsFemale = g.Key };
        Repeater2.DataBind();
    }

HttpHandler用于处理请求:

public void ProcessRequest (HttpContext context) {
        context.Response.ContentType = "text/plain";
        int page = 1;
        string order = string.Empty;
        string direction = string.Empty;
        try
        {
            page = Convert.ToInt32(context.Request.Params["page"]);
        }
        catch { }

        order = context.Request.Params["order"];
        direction = context.Request.Params["direction"];
        PersonnelViewModel model = new PersonnelViewModel
        {
            Name = context.Request.Params["Name"]
        };
        try
        {
            model.ID = string.IsNullOrWhiteSpace(context.Request.Params["ID"]) ? (int?)null : Convert.ToInt32(context.Request.Params["ID"]);
            model.IsFemale = string.IsNullOrWhiteSpace(context.Request.Params["IsFemale"]) ? (bool?)null : Convert.ToBoolean(context.Request.Params["IsFemale"]);
        }
        catch { }
        
        
        Page webPage = new Page();
        Control grid = webPage.LoadControl("~/Grid.ascx");
        Type type = grid.GetType();
        type.GetProperty("CurrentPage").SetValue(grid, page, null);
        type.GetProperty("OrderColumn").SetValue(grid, order, null);
        type.GetProperty("Direction").SetValue(grid, direction, null);
        type.GetProperty("Model").SetValue(grid, model, null);
        
        webPage.Controls.Add(grid);
        
        
        
        using (System.IO.StringWriter writer = new System.IO.StringWriter())
        {
            HttpContext.Current.Server.Execute(webPage, writer, false);
            context.Response.Write(writer.ToString());
        }
        
    }

要注意的是我们无法已强类型的方式访问用户控件,只能通过反射进行属性赋值。然后用HttpContext.Current.Server.Execute方法进行html输出。
最后我们在aspx页面中使用即可:

<!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>Good Morning</title>
<link type="text/css" href="Styles/DataGrid/style.css" rel="Stylesheet"/>
    <script type="text/javascript" src="Script/jquery-1.5.1.min.js"></script>
    <script type="text/javascript" src="Script/jquery.ru-grid.js"></script>
    <script type="text/javascript">
        $(function () {
            $('#dv-grid').rugrid({ loadingElementId: 'dv-loading', url: 'Grid.ashx' });
        });
    </script>
</head>
<body>
    <div>
    
<div id="dv-loading" class="data-loading">
    Loading...
</div>

    <form id="form1" runat="server">
        <div id="dv-grid" style="width:600px;">
            <uc1:Grid ID="Grid1" runat="server"/>
        </div>
    </form>
    </div>
</body>
</html>

最后的最后是源码:点击下载

赞(0) 打赏
分享到: 更多 (0)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏