Repeater控件是較為干凈的服務端數據控件,它不像GridView已經包含了分頁和排序功能,這兩個小功能都要咱們自己去實現。由於分頁的功能很容易實現,我也沒什么好講的;下文中我提供了三種排序方式,除了傳統方式以外,另外兩種都較為簡便靈活。
在線Demo:
本篇文章已經同步至我的個人博客站點:積累吧 | Repeater排序的三種實現方式(附在線Demo),這里有更好的博客視覺體驗。
1. 數據准備
數據庫Test,表名Books,下圖是表結構,在Books表中我准備了100條測試數據。
在頁面上除了Id以外其它字段都會被顯示。Repeater的數據源由NHibernate下的Repository模式提供,由於只有一個表而且給出了表圖,Book.cs和Book.hbm.xml代碼我不會貼出來。
在將要進行的幾種Demo中都包含下面4個代碼片段,如果大家對這4個片段不感興趣,可以直接略過去,直接跳轉到:
片段1:獲取數據源,緩存
#region Properties /// <summary> /// Book倉儲器 /// </summary> private IBookRepository BookRepository { get { return new BookRepository(); } } /// <summary> /// Cache緩存數據源 /// </summary> private IList<Book> DataSource { get { return Cache["_data"] as IList<Book>; } set { if (Cache["_data"] == null) { Cache["_data"] = value; } } } #endregion
片段2:綁定Repeater
#region Private Methods /// <summary> /// 綁定Repeater /// </summary> private void BindRepeater() { if (DataSource == null) { IList<Book> books = BookRepository.GetAll().ToList(); DataSource = books; } // 首次加載時綁定數據源 if(ViewState["SortOrder"] == null) { this.Repeater1.DataSource = DataSource; this.Repeater1.DataBind(); } } #endregion
片段3:排序設置
/// <summary> /// 設置排序字段、排序方式、排序文本 /// </summary> private void SetSorting(RepeaterCommandEventArgs e) { var sort = (LinkButton)e.Item.FindControl(e.CommandName.Trim()); if (e.Item.ItemType == ListItemType.Header) { string sortText = string.Empty; // 存儲排序字段、排序方式、排序文本 if (ViewState["SortOrder"] == null || ViewState["SortBy"].ToString() != e.CommandName) { ViewState["SortOrder"] = "ASC"; ViewState["SortBy"] = e.CommandName; sortText = sort.Text + "▲"; } else { ViewState["SortOrder"] = ViewState["SortOrder"].ToString() == "ASC" ? "DESC" : "ASC"; // 排序時更改文本 if (sort.Text.IndexOf("▲") == -1) { if (sort.Text.IndexOf("▼") == -1) sortText = sort.Text + "▲"; else sortText = sort.Text.Replace("▼", "▲"); } else { sortText = sort.Text.Replace("▲", "▼"); } } ViewState["SortText"] = sortText; } }
片段4:前端數據源解析
<table> <asp:Repeater runat="server" ID="Repeater1" OnItemCommand="Repeater1_ItemCommand" OnItemDataBound="Repeater1_ItemDataBound"> <HeaderTemplate> <tr> <th width="100"> <asp:LinkButton ID="ISBN" runat="server" Text="ISBN" CommandName="ISBN" /> </th> <th width="200"> <asp:LinkButton ID="Title" runat="server" Text="Title" CommandName="Title" /> </th> <th width="100"> Image </th> <th width="100"> <asp:LinkButton ID="Category" runat="server" Text="Category" CommandName="Category" /> </th> <th width="100"> <asp:LinkButton ID="Author" runat="server" Text="Author" CommandName="Author" /> </th> <th width="100"> <asp:LinkButton ID="Price" runat="server" Text="Price" CommandName="Price" /> </th> <th width="100"> <asp:LinkButton ID="Package" runat="server" Text="Package" CommandName="Package" /> </th> <th width="100"> <asp:LinkButton ID="Pages" runat="server" Text="Pages" CommandName="Pages" /> </th> </tr> <tr> <td colspan="8" style="border-bottom: 1px solid #465c71"> </td> </tr> </HeaderTemplate> <ItemTemplate> <tr> <td width="100"> <%# Eval("ISBN") %> </td> <td width="200"> <%# Eval("Title") %> </td> <td width="100"> <img src="<%# Eval("ImageUrl") %>" width="50" /> </td> <td width="100"> <%# Eval("CategoryName") %> </td> <td width="100" > <%# Eval("Author") %> </td> <td width="100" > <%# Eval("Price") %> </td> <td width="100" > <%# Eval("Package") %> </td> <td width="100" > <%# Eval("Pages") %> </td> </tr> <tr> <td colspan="8" style="border-bottom: 1px solid #465c71"> </td> </tr> </ItemTemplate> </asp:Repeater> </table>
2. 傳統排序方式
在線Demo瀏覽:Repeater傳統排序方式
protected void Repeater1_ItemCommand(object source, RepeaterCommandEventArgs e) { // 設置排序 SetSorting(e); if (ViewState["SortOrder"] != null) { string sortOrder = ViewState["SortOrder"].ToString(); switch (ViewState["SortBy"].ToString()) { case "ISBN": Repeater1.DataSource = sortOrder == "ASC" ? DataSource.OrderBy(b => b.ISBN) : DataSource.OrderByDescending(b => b.ISBN); break; case "Title": Repeater1.DataSource = sortOrder == "ASC" ? DataSource.OrderBy(b => b.Title) : DataSource.OrderByDescending(b => b.Title); break; case "Category": Repeater1.DataSource = sortOrder == "ASC" ? DataSource.OrderBy(b => b.CategoryName) : DataSource.OrderByDescending(b => b.CategoryName); break; case "Author": Repeater1.DataSource = sortOrder == "ASC" ? DataSource.OrderBy(b => b.Author) : DataSource.OrderByDescending(b => b.Author); break; case "Package": Repeater1.DataSource = sortOrder == "ASC" ? DataSource.OrderBy(b => b.Package) : DataSource.OrderByDescending(b => b.Package); break; case "Pages": Repeater1.DataSource = sortOrder == "ASC" ? DataSource.OrderBy(b => b.Pages) : DataSource.OrderByDescending(b => b.Pages); break; } Repeater1.DataBind(); } }
這段代碼是不是看起來很悲催?這么多的Switch Case語句,如果Book類的顯示字段增加了,並且都要求排序,那豈不是又得來更改這個方法 。我們想要的方式是只要知道字段的名稱(e.CommandName),就能通過幾行代碼實現排序。反射聽起來是個不錯的方式。
3. 使用反射獲取排序字段
在線Demo:Repeater 反射排序方式
通過反射動態獲取Book對象的屬性的值,可以將上面的方法精簡很多代碼。
protected void Repeater1_ItemCommand(object source, RepeaterCommandEventArgs e) { // 設置排序 SetSorting(e); if (ViewState["SortOrder"] != null) { Repeater1.DataSource = ViewState["SortOrder"].ToString() == "ASC" ? DataSource.OrderBy( o => TypeHelper.GetPropertyValue(o, ViewState["SortBy"].ToString())) : DataSource.OrderByDescending( o => TypeHelper.GetPropertyValue(o, ViewState["SortBy"].ToString())); Repeater1.DataBind(); } }
Type類代碼:
public static class TypeHelper { public static object GetPropertyValue(object obj, string name) { return obj == null ? null : obj.GetType() .GetProperty(name) .GetValue(obj, null); } }
4. 通過擴展方法讓Linq支持字符串排序
在線Demo:Repeater Linq 擴展方法排序方式
Linq本身不支持字符串排序的,在Linq下我們並不能像這樣去寫代碼:
Repeater1.DataSource = DataSource.OrderBy(e.CommandName).ToList();
VS會提示語法錯誤,因為e.CommandName是string類型,而它需要的是<TSource, TKey>參數。
但是我們可以通過擴展方法去達到這一目的。
在項目下添加一個LinqExtension類:
public static class LinqExtension { public static IEnumerable<T> Sort<T>(this IEnumerable<T> source, string sortExpression) { string[] sortParts = sortExpression.Split(' '); var param = Expression.Parameter(typeof (T), string.Empty); try { var property = Expression.Property(param, sortParts[0]); var sortLambda = Expression.Lambda<Func<T, object>>(Expression.Convert(property, typeof (object)), param); if (sortParts.Length > 1 && sortParts[1].Equals("desc", StringComparison.OrdinalIgnoreCase)) { return source.AsQueryable<T>().OrderByDescending<T, object>(sortLambda); } return source.AsQueryable<T>().OrderBy<T, object>(sortLambda); } catch (ArgumentException) { return source; } } }
sortExpression同時包含了排序字段和排序方式,傳入參數時像”Name desc”這種形式。調用的方式很簡單,僅一行代碼:
protected void Repeater1_ItemCommand(object source, RepeaterCommandEventArgs e) { // 設置排序 SetSorting(e); if (ViewState["SortOrder"] != null) { Repeater1.DataSource = DataSource.Sort(string.Concat(e.CommandName, " ", ViewState["SortOrder"].ToString())); Repeater1.DataBind(); } }
5. 總結
本文借Repeater排序這個小功能點,介紹了傳統的實現方式,但並不靈活,然后介紹了在此基礎上改進的反射排序方式,最后又提供了擴展方法實現排序的方式。
一個很小的功能可以有很多方式去實現,選擇最高效最簡便的方式是我們努力的方向,正如文章開頭提供的3個Demo一樣,它們的結果一模一樣,但后面的實現方式卻各不相同。一個粗俗的比喻:最開始我們給自己的代碼穿上了很多衣服,甚至棉衣也穿上了,但是夏天快到了,天氣較熱,又是在家里,我只想為代碼穿一條內褲,即使內褲里面可能篩了很多東西,但是我看到的就是一條內褲。