在前几天发布的ArchOne CTP 1版中,我们所有的最佳实践案例中使用的列表控件都是经过我们重新封装并实现了自定义排序及自定义分页的控件,但在实际的项目中,经常会为了要实现某个高级功能而使用外部控件的情况,那么,在这样的场景中,我们是如何能做到快速实现自定义排序及自定义分页功能呢?
为此,我们需要请出专门为这类扩展而开发的自定义排序及自定义分页控件(由于ArchOne内部使用了统一的处理机制,因此我们提供的自定义排序及自定义分页控件的实现原理与我们对GridView及Repeater控件的封装原理是完全一样的,只不过将这部分代码独立出来形成更通用的控件)。下面我们将以04.Enterprise.Demo(Mvp)这个最佳实践案例为基础,对其中包含的WebForm1.aspx文件进行改造,来看看我们通用的自定义排序及自定义分页控件的使用方法。
改造前使用ArchOne中已封装的控件的前端HTML 1 < Framework:Repeater ID ="rptUsers" runat ="server" AllowPaging ="true" AllowSorting ="true" DefaultSortExpression ="LoginID" > 2 < HeaderTemplate > 3 < table class ="dg_borderstyle" cellspacing ="0" cellpadding ="0" rules ="all" border ="0" style ="border-width:0px;border-collapse:collapse;" > 4 < tr class ="dg_headerstyle" > 5 < th scope ="col" > 用户名 </ th > 6 < th scope ="col" >< Framework:SortableHeader ID ="shLoginID" runat ="server" SortExpression ="LoginID" > 登录ID </ Framework:SortableHeader ></ th > 7 < th scope ="col" >< Framework:SortableHeader ID ="shEmail" runat ="server" SortExpression ="Email" > 电子邮箱 </ Framework:SortableHeader ></ th > 8 < th scope ="col" >< Framework:SortableHeader ID ="shDepartment" runat ="server" > 所在部门 </ Framework:SortableHeader ></ th > 9 </ tr > 10 </ HeaderTemplate > 11 < ItemTemplate > 12 < tr class ="dg_itemstyle" > 13 < td align ="center" style ="width:120px;" >< span >< a href ="#;" title ='<%#Eval("Name") % > '> <% # Eval ( " Name " ) %> </ a ></ span ></ td > 14 < td align ="center" style ="width:120px;" > <% # Eval ( " LoginID " ) %> </ td > 15 < td align ="left" >< a href ='mailto:<%#Eval("EMail") % > '> <% # Eval ( " EMail " ) %> </ a ></ td > 16 < td align ="center" style ="width:160px;" > <% # Eval ( " DeptName " ) %> </ td > 17 </ tr > 18 </ ItemTemplate > 19 < AlternatingItemTemplate > 20 < tr class ="dg_alternatingitemstyle" style ="border-style:None;" > 21 < td align ="center" style ="width:120px;" >< span >< a href ="#;" title ='<%#Eval("Name") % > '> <% # Eval ( " Name " ) %> </ a ></ span ></ td > 22 < td align ="center" style ="width:120px;" > <% # Eval ( " LoginID " ) %> </ td > 23 < td align ="left" >< a href ='mailto:<%#Eval("EMail") % > '> <% # Eval ( " EMail " ) %> </ a ></ td > 24 < td align ="center" style ="width:160px;" > <% # Eval ( " DeptName " ) %> </ td > 25 </ tr > 26 </ AlternatingItemTemplate > 27 < PagerTemplate > 28 < div class ="dg_pagerstyle" > 29 < span class ="pageinfo" > 30 第 < asp:Label ID ="lblCurrentPage" runat ="server" /> 页/ 31 共 < asp:Label ID ="lblTotalPage" runat ="server" /> 页 32 </ span > 33 < span class ="pagejump" > 34 跳到第 < asp:TextBox ID ="txtGoto" runat ="server" ></ asp:TextBox > 页 < asp:LinkButton CommandName ="CustomPage" CommandArgument ="GoTo" ID ="btnGoTo" runat ="server" > Go </ asp:LinkButton > 35 < Framework:DropDownPager ID ="ddlPageList" runat ="server" /> 36 </ span > 37 < span class ="pagelink" > 38 < asp:LinkButton CommandName ="CustomPage" CommandArgument ="First" ID ="btnFirst" SkinID ="none" runat ="server" CausesValidation ="false" > 首页 </ asp:LinkButton > 39 < asp:LinkButton CommandName ="CustomPage" CommandArgument ="Prev" ID ="btnPrevious" SkinID ="none" runat ="server" CausesValidation ="false" > 上一页 </ asp:LinkButton > 40 < asp:LinkButton CommandName ="CustomPage" CommandArgument ="Next" ID ="btnNext" SkinID ="none" runat ="server" CausesValidation ="false" > 下一页 </ asp:LinkButton > 41 < asp:LinkButton CommandName ="CustomPage" CommandArgument ="Last" ID ="btnLast" SkinID ="none" runat ="server" CausesValidation ="false" > 尾页 </ asp:LinkButton > 42 </ span > 43 </ div > 44 </ PagerTemplate > 45 < FooterTemplate > 46 </ table > 47 </ FooterTemplate > 48 </ Framework:Repeater > 在上面的代码中我们可以看到,使用ArchOne重新封装的Repeater控件,代码相对简洁,自定义排序及自定义分页功能均完整的包含在Repeater控件内部,我们仅需要设置Repeater控件的AllowSorting、AllowPaging等属性即可控制自定义排序及自定义分页功能的开启与关闭。但是如果使用的是.Net Framework中自带的Repeater控件,我们又如何快速进行自定义排序及自定义分页呢?下面的代码演示如何使用自定义排序控件(SortContainer)及自定义分页控件(CustomPager)对未进行封装的Repeater控件进行自定义排序及自定义分页(此通用控件可适应于任何列表控件)。
改造后使用自定义排序及分页控件的前端HTML 1 < Framework:SortContainer ID ="sortContainer" runat ="server" DefaultSortExpression ="LoginID" > 2 < table class ="dg_borderstyle" cellspacing ="0" cellpadding ="0" rules ="all" border ="0" style ="border-width:0px;border-collapse:collapse;" > 3 < tr class ="dg_headerstyle" > 4 < th scope ="col" > 用户名 </ th > 5 < th scope ="col" >< Framework:SortableHeader ID ="shLoginID" runat ="server" SortExpression ="LoginID" > 登录ID </ Framework:SortableHeader ></ th > 6 < th scope ="col" >< Framework:SortableHeader ID ="shEmail" runat ="server" SortExpression ="Email" > 电子邮箱 </ Framework:SortableHeader ></ th > 7 < th scope ="col" >< Framework:SortableHeader ID ="shDepartment" runat ="server" > 所在部门 </ Framework:SortableHeader ></ th > 8 </ tr > 9 < asp:Repeater ID ="rptUsers" runat ="server" > 10 < ItemTemplate > 11 < tr class ="dg_itemstyle" > 12 < td align ="center" style ="width:120px;" >< span >< a href ="#;" title ='<%#Eval("Name") % > '> <% # Eval ( " Name " ) %> </ a ></ span ></ td > 13 < td align ="center" style ="width:120px;" > <% # Eval ( " LoginID " ) %> </ td > 14 < td align ="left" >< a href ='mailto:<%#Eval("EMail") % > '> <% # Eval ( " EMail " ) %> </ a ></ td > 15 < td align ="center" style ="width:160px;" > <% # Eval ( " DeptName " ) %> </ td > 16 </ tr > 17 </ ItemTemplate > 18 < AlternatingItemTemplate > 19 < tr class ="dg_alternatingitemstyle" style ="border-style:None;" > 20 < td align ="center" style ="width:120px;" >< span >< a href ="#;" title ='<%#Eval("Name") % > '> <% # Eval ( " Name " ) %> </ a ></ span ></ td > 21 < td align ="center" style ="width:120px;" > <% # Eval ( " LoginID " ) %> </ td > 22 < td align ="left" >< a href ='mailto:<%#Eval("EMail") % > '> <% # Eval ( " EMail " ) %> </ a ></ td > 23 < td align ="center" style ="width:160px;" > <% # Eval ( " DeptName " ) %> </ td > 24 </ tr > 25 </ AlternatingItemTemplate > 26 </ asp:Repeater > 27 </ table > 28 </ Framework:SortContainer > 29 < Framework:CustomPager ID ="customPager" runat ="server" PageSize ="10" CssClass ="dg_pagerstyle" > 30 < LayoutTemplate > 31 < span class ="pageinfo" > 32 第 < asp:Label ID ="lblCurrentPage" runat ="server" /> 页/ 33 共 < asp:Label ID ="lblTotalPage" runat ="server" /> 页 34 </ span > 35 < span class ="pagejump" > 36 跳到第 < asp:TextBox ID ="txtGoto" runat ="server" ></ asp:TextBox > 页 < asp:LinkButton CommandName ="CustomPage" CommandArgument ="GoTo" ID ="btnGoTo" runat ="server" > Go </ asp:LinkButton > 37 < Framework:DropDownPager ID ="ddlPageList" runat ="server" /> 38 </ span > 39 < span class ="pagelink" > 40 < asp:LinkButton CommandName ="CustomPage" CommandArgument ="First" ID ="btnFirst" SkinID ="none" runat ="server" CausesValidation ="false" > 首页 </ asp:LinkButton > 41 < asp:LinkButton CommandName ="CustomPage" CommandArgument ="Prev" ID ="btnPrevious" SkinID ="none" runat ="server" CausesValidation ="false" > 上一页 </ asp:LinkButton > 42 < asp:LinkButton CommandName ="CustomPage" CommandArgument ="Next" ID ="btnNext" SkinID ="none" runat ="server" CausesValidation ="false" > 下一页 </ asp:LinkButton > 43 < asp:LinkButton CommandName ="CustomPage" CommandArgument ="Last" ID ="btnLast" SkinID ="none" runat ="server" CausesValidation ="false" > 尾页 </ asp:LinkButton > 44 </ span > 45 </ LayoutTemplate > 46 </ Framework:CustomPager > 通过对比改造前及改造后的前端HTML代码,我们可以清楚的了解到:
1、自定义排序控件SortContainer是作为一个排序容器的概念出现,如果一个列表控件需要实现自定义排序功能,则你必须将其排序列头控件(SortableHeader)置入SortContainer容器中(其实原理与使用ArchOne中已封装的Repeater控件一致,因此ArchOne中的Repeater控件与SortContainer一样均实现了Bingosoft.Enterprise.UI.ISortable接口,都是以一种排序容器控件的形式出现);
2、将原包含在ArchOne中的Repeater控件PagerTemplate中的分页代码独立出来,置入CustomPager控件的LayoutTemplate内联模板中(实际的开项目中,可以在skin文件中对CustomPager控件进行统一定义,这里仅作演示)。
在对前端HTML代码改造完成后,我们需要对其后端代码进行一点小改动,主要是重写基类中的 PagerControl 及 SortControl 属性,代码如下:
改造后的后端代码(差异) 1 /// <summary> 2 /// 获取分页控件。 3 /// </summary> 4 /// <value> 分页控件。 </value> 5 protected override IPagerable PagerControl { 6 get { 7 return this .customPager; 8 } 9 } 10 /// <summary> 11 /// 获取排序控件。 12 /// </summary> 13 /// <value> 排序控件。 </value> 14 protected override ISortable SortControl { 15 get { 16 return this .sortContainer; 17 } 18 }
通过以上的几个小改动,我们即可实现对其它外部列表控件进行自定义排序及自定义分页功能的支持。