• [转载]ASPNET MVC表格呈现利器:MvcContrib.UI.Grid


    ASPNET MVC框架现在日趋流行,最近我也刚完成一个用ASPNET MVC框架做的项目,现在想做一些阶段性的总结。

    很多 原来在WinForm下面的流行控件例如GridView,ListView,Repeater等表格呈现控件在ASPNET MVC下已经不能使用了,官方并没有提供现成的控件以供使用,因此我们也许经常会aspx页面中使用例如 <% For Or While语句 %>来呈现我们的表格了。但是一旦这样的页面多了,我们会发现这样还是比较繁琐且费时的。

    在老赵的ASPNET MVC课程里面介绍过一个很有名气的类库MvcContrib。里面就有一个比较强大的类Grid来专门呈现这样强类型的数据的,在我的项目中,我也大量的采用了Grid的呈现方式,发现其还是非常好用的,而且扩展起来也是非常的方便的。

    其实MvcContrib类库是一个很强大的类库,提供了诸如Ioc(控制反转)、ModelBinder、Filter、各种Input“控件”以及Grid,有兴趣的朋友可以去http://mvccontrib.codeplex.com/去获取更多信息。

    从Grid使用说起

    我 们首先从Grid(MvcContrib 下的Grid类,再次简称Grid,下同)的使用说起,做过Web开发的人都知道,表格式由行和列组成,而行又通常区分为标题行(Head)、数据行和尾 行三部分。Grid是通过一个名叫ColumnBuilder的类来实现IGridColumn列的累加,而IGridColumn接口则包含了控制单元 格属性的诸多功能。我们从一段简单代码说起

    <%= Html.Grid(Model)
    .Columns(c =>
    {
    c.For(x => x.CompanyName).Named("公司名称").Attributes(@class => "w10");
    c.For(x => x.City).Named("城市").Attributes(@class => "w8");
    c.For(x => x.ContactName).Named("联系名称").Attributes(@class => "w8");
    }
    )
    .Empty("暂无数据").RowStart(c => string.Format("<tr class='{0}'>", c.IsAlternate ? "alt" : ""))
    %>
    这是一段较为简单的代码,比较容易看出来呈现的是三列,首先我对代码做一个简单说明。

    Grid(Model) : HtmlHelper的扩展方法,实例化Grid类,包装数据,返回IGrid<T>接口,IGrid<T>接口继承了IGridWithOptions<T>接口。

    Columns(…) : IGridWithOptions<T>接口提供的方法,作用是构建表格的列。

    For(…) : ColumnBuilder<T>方法,返回IGridColumn<T>接口,作用是构建列,从而为后续的方法提供链式支持。

    Name(…) : IGridColumn<T>方法,指定列名显示内容。

    Attributes(…) : IGridColumn<T>方法,指定单元格的属性

    Empty(…) : IGridWithOptions<T>接口提供的方法,作用是当列表为空时显示的内容(提示)。它和Columns方法一样同为链式方法(即返回IGridWithOptions<T>自身)。

    RowStart(…) : HtmlHelper针对IGridWithOptions<T>的扩展方法,作用是自定义行的开头部分(即Tr)。

    显示效果:

    basegridDome

    源码分析

    上面只是一个简单的演示,其实提供的方法远比这多得多,要想更好的使用Grid,我们需对其源码进行分析。

    Grid在设计上采取的面向接口编程,大大降低了耦合性,经过HtmlHelper进行扩展的方法主要是针对两个接口进行的,一类是 IGridColumn<T>,另一类是IGridWithOptions<T> 。我们在使用过程中通过VS2008的智能提示留意一下他们各自的返回值,因为返回的也是同一个接口,这样也就很好的提供了链式的方法,下面我久别分别说 说这两类接口。

    IGridColumn<T>主要是针对特定列以及列下单元格的控制,接口的方法如下(为了便于理解,我添加了注释):

    Action<RenderingContext> CustomHeaderRenderer { get; set; }
    Action<RenderingContext, T> CustomItemRenderer { get; set; }

    //定义单元格附加属性
    IGridColumn<T> Attributes(Func<GridRowViewData<T>, IDictionary<string, object>> attributes);
    //定义一个委托,指定单元格内容是否显示
    IGridColumn<T> CellCondition(Func<T, bool> func);
    //是否对单元格内容进行HTML编码
    IGridColumn<T> DoNotEncode();
    //对列名称进行分离(老外的名字一般由两部分组成)
    IGridColumn<T> DoNotSplit();
    //格式化单元格内容,示例:.Format("{0:C}")
    IGridColumn<T> Format(string format);
    //定义头部(单元格)附加属性
    IGridColumn<T> HeaderAttributes(IDictionary<string, object> attributes);
    //定义列名
    IGridColumn<T> Named(string name);
    //指定该列是否显示
    IGridColumn<T> Visible(bool isVisible);

    后面我添加过注释的方法相对来说比较容易,我就不再赘述,这里我们需要留意的是前两个Custom方法,分别是用来定义列头和单元格的,在HtmlHelper扩展方法中对应的是HeaderAttributes和Attributes方法,值得一提的它这里用到了名曰Hash的一个类,我们来看看其扩展方法签名:

    public static IGridColumn<T> HeaderAttributes<T>(this IGridColumn<T> column, params Func<object, object>[] hash)
    {
    return column.HeaderAttributes(new Hash(hash));
    }
    public static IGridColumn<T> Attributes<T>(this IGridColumn<T> column, params Func<object, object>[] hash)
    {
    return column.Attributes(x => new Hash(hash));
    }

    再来看看Hash 类的写法

    public class Hash : Hash<object>
    {
    public Hash(params Func<object, object>[] hash) : base(hash)
    {
    }
    }

    public class Hash<TValue> : Dictionary<string, TValue>
    {
    public Hash(params Func<object, TValue>[] hash)
    : base(hash == null ? 0 : hash.Length, StringComparer.OrdinalIgnoreCase)
    {
    if (hash != null)
    {
    foreach (var func in hash)
    {
    Add(func.Method.GetParameters()[0].Name, func(null));
    }
    }
    }

    public static Dictionary<string, TValue> Empty
    {
    get { return new Dictionary<string, TValue>(0, StringComparer.OrdinalIgnoreCase); }
    }
    }

    这里的Hash类继承了Hash<object> 类,而Hash<object> 类又继承了Dictionary<string, TValue> 类,再看看Hash<object>添加字典项的方法,留意这一句:

    Add(func.Method.GetParameters()[0].Name, func(null));

    它是遍历Func<object, TValue>数组后用反射的方法获取匿名方法的参数名称作为字典的Key(这里是string型),而Value则是通过执行匿名泛型函数func(null)得到的。所以这就是前面示例中Attributes(@class => "w10")中的写法,它获取方法的参数名称“Class”作为Key,而方法的返回值“w10”即是字典的Value了,是不是设计的很巧妙呢。

    有了这个Hash类,我们在写IDictionary<string, object>属性的时候就方便多了,下面是几个示例:

    <%-- 指定单元格css为grid_cell,id为gridcell_id,合并2列 --%>
    .Attributes(new Hash(@class => "grid_cell", id => "gridcell_id", colspan => "2"))
    <%-- 指定Grid表格css为grid,单元格间隙为2,间距为0 --%>
    .Attributes(new Hash(@class => "grid").Add<int>("cellpadding", 2).Add<int>("cellspacing", 0))
    <%-- 定义表头列的样式为 居中,加粗13像素 Verdana字体,点击后触发排序事件 --%>
    .HeaderAttributes(new Hash(Style => "text-align:center;font:bold 13px/20px Verdana;", onclick => "javascript:sort()"))
     

    另一类接口IGridWithOptions<T> 则是对Grid的外围如行属性,表格属性的扩展,同样我也做了注释。

    [EditorBrowsable(EditorBrowsableState.Never)]
    IGridModel<T> Model { get; }

    //定义Grid属性
    IGridWithOptions<T> Attributes(IDictionary<string, object> attributes);
    //构建ColumnBuilder,对列(IGridColumn<T>)进行累加
    IGridWithOptions<T> Columns(Action<ColumnBuilder<T>> columnBuilder);
    //空数据时显示的文字内容
    IGridWithOptions<T> Empty(string emptyText);
    //定义头行属性,这里与html元素对应的应该是<tr>而不是<td>
    IGridWithOptions<T> HeaderRowAttributes(IDictionary<string, object> attributes);
    void Render();
    //定义采取哪一种呈现器(IGridRenderer<T> )进行呈现
    IGridWithOptions<T> RenderUsing(IGridRenderer<T> renderer);
    //定义行属性,与HeaderRowAttributes的区别在于不是头行
    IGridWithOptions<T> RowAttributes(Func<GridRowViewData<T>, IDictionary<string, object>> attributes);

    转自:http://joyaspx.com/post/2009/07/27/MVCe8a1a8e6a0bce59188e78eb0e588a9e599a8MvcContrib_Gridefbc88e4b880efbc89.aspx
  • 相关阅读:
    [BZOJ4876][ZJOI2017]线段树
    [FJOI2016]建筑师(斯特林数)
    WC2018伪题解
    [BZOJ3514]CodeChef MARCH14 GERALD07加强版(LCT+主席树)
    [BZOJ2594][WC2006]水管局长加强版(LCT+Kruskal)
    [洛谷3796]【模板】AC自动机(加强版)
    [洛谷3808]【模板】AC自动机(简单版)
    [BZOJ3261]最大异或和
    [BZOJ3439]Kpm的MC密码
    [POI2006]Periods of Words
  • 原文地址:https://www.cnblogs.com/zsea/p/1591761.html
Copyright © 2020-2023  润新知