Repeater排序的三種實現方式(附在線Demo)


Repeater控件是較為干凈的服務端數據控件,它不像GridView已經包含了分頁和排序功能,這兩個小功能都要咱們自己去實現。由於分頁的功能很容易實現,我也沒什么好講的;下文中我提供了三種排序方式,除了傳統方式以外,另外兩種都較為簡便靈活。

在線Demo:

  1. Demo - Repeater 傳統排序
  2. Demo - Repeater 反射排序方式
  3. Demo - Repeater Linq 擴展方法排序方式

本篇文章已經同步至我的個人博客站點:積累吧 | Repeater排序的三種實現方式(附在線Demo),這里有更好的博客視覺體驗。

1. 數據准備

數據庫Test,表名Books,下圖是表結構,在Books表中我准備了100條測試數據。

image

在頁面上除了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一樣,它們的結果一模一樣,但后面的實現方式卻各不相同。一個粗俗的比喻:最開始我們給自己的代碼穿上了很多衣服,甚至棉衣也穿上了,但是夏天快到了,天氣較熱,又是在家里,我只想為代碼穿一條內褲,即使內褲里面可能篩了很多東西,但是我看到的就是一條內褲。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM