【问题标题】:Sorting a ListView using an ObjectDataSource with a LinkButton and ViewState使用带有 LinkBut​​ton 和 ViewState 的 ObjectDataSource 对 ListView 进行排序
【发布时间】:2012-11-07 12:36:23
【问题描述】:

我正在使用 ListView 来显示分页数据:

<asp:ListView ID="listOfItems" runat="server" DataSourceID="ItemsDataSource" EnableModelValidation="True" InsertItemPosition="FirstItem" ItemPlaceholderID="ItemRowContainer">
    <LayoutTemplate>
        <div class="tablecontainer">
            <div class="pagination-top">
                <custom:TablePaginationControl ID="TablePaginationControl1" runat="server" ControlID="listOfItems" ShowPageSizeList="true" />
            </div>
            <table class="list-view">
                <tr>
                    <th class="first-column" width="350px">
                        <asp:LinkButton ID="SortByName" runat="server" CommandArgument="Name" CommandName="SortMainList" OnCommand="SortItems" Text="<%$ Resources:Name %>"></asp:LinkButton>
                    </th>
                    ...
                </tr>
                <tbody>
                    <tr runat="server" id="ItemRowContainer" />
                </tbody>
            </table>
        </div>
    </LayoutTemplate>
    ...
</asp:ListView>

数据源定义:

<asp:ObjectDataSource ID="ItemsDataSource" runat="server" EnablePaging="True" InsertMethod="AddItems" SelectCountMethod="SelectItemsCount" SelectMethod="SelectItems" TypeName="SHLCentral.TheLibrary.Web.View.DocumentManagementControl, ClientPortal.Web, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cd2852a10d692fb9" UpdateMethod="UpdateItems">
    ...
</asp:ObjectDataSource>

后面的相关代码是由这两种方法组成的:

public IEnumerable<ListDocumentsResult> SelectItems(
    int maximumRows,
    int startRowIndex)
{
    var results = Controller.ListDocuments(new ListDocumentsRequest());

    PropertyInfo sortProperty;
    try
    {
        sortProperty = typeof (ListDocumentsResult).GetProperty((string) ViewState["mainListSortColumn"]);
    }
    catch
    {
        sortProperty = null;
    }
    Func<ListDocumentsResult, object> sortFunction = sortProperty == null
                            ? (Func<ListDocumentsResult, object>) (ldr => ldr.LastUpdatedDate)
                            : (ldr => sortProperty.GetValue(ldr, new object[0]));

    return
        (sortProperty == null || !((bool) ViewState["mainListSortAsc"])
             ? results.OrderByDescending(sortFunction)
             : results.OrderBy(sortFunction))
            .Skip(startRowIndex)
            .Take(maximumRows);
}

protected void SortItems(object sender, CommandEventArgs e)
{
    if (e.CommandName == "SortMainList")
    {
        var sortColumn = (string) e.CommandArgument;
        if ((string)ViewState["mainListSortColumn"] == sortColumn)
        {
            ViewState["mainListSortAsc"] = !(bool)ViewState["mainListSortAsc"];
        }
        else
        {
            ViewState["mainListSortAsc"] = true;
            ViewState["mainListSortColumn"] = sortColumn;
        }
        DataBind();
    }
}

所以我的意图是:当用户单击“名称”列标题中包含的LinkButton 时(为了清楚起见,我除了一列之外都省略了),调用SortItems 方法:它设置排序列名称和排序顺序到ViewState,然后使用DataBind 方法重新加载ListView。在ObjectDataSource 的Select 方法中,我们读取了这个ViewState 值并使用它们对数据进行排序。

在所有这些方法上都设置断点,当我点击LinkButton时,我可以看到有这个调用序列:

  • OnLoad
  • SortItems
  • SelectItems

我遇到的问题是,当我到达SelectItems 方法时,ViewState 完全为空(它有 0 个键):如果我在页面的 Load 方法上设置断点,我会看到控件包含所有这些只加载一次。 DataBind 方法似乎没有触发控件的任何加载,它似乎只是触发了控件新实例的SelectItems 方法(这意味着如果我设置了一个实例,而不是使用ViewState SortItems方法中的字段,进入SelectItems方法时该字段为空)。

我确信 ViewState 在页面上处于活动状态(例如,我可以使用 Firefox 扩展程序在浏览器端找到 ViewState 键)。

关于页面/控件的生命周期,我不太清楚。有人能解释一下它对我来说是什么吗?

【问题讨论】:

    标签: c# asp.net listview viewstate objectdatasource


    【解决方案1】:

    存在很多非常简单的方法。

    首先,您将内置名称放入排序链接按钮中,而不是自定义CommandName。名字是Sort

    那么你有

       <asp:LinkButton ID="SortByName" runat="server" CommandArgument="Name" CommandName="Sort" />
    

    然后,在您的 ObjectDataSource 上添加 SortParameterName 类似于 OrderBy

       <ObjectDataSource .... SortParameterName="OrderBy" />
    

    然后你修改你的数据提供者方法为:

     public IEnumerable<ListDocumentsResult> SelectItems(
       string OrderBy,
       int maximumRows,
       int startRowIndex)
    

    数据源将根据命令参数(Name)自动提供值,并在您第二次单击命令按钮时自动附加DESC(这是因为ListView保持状态自动在其视图状态中排序,您不必重新发明它!)

    然后,您不需要这个丑陋的委托来按字符串为 linq 排序。而是下载 Dynamic Linq 库:

    http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx

    找到 Dynamic.cs 文件,将其包含在您的项目中,它会添加一堆额外的 linq 运算符,包括接受字符串并自动支持 DESC (!) 的 OrderBy

    那么你就

     public IEnumerable<ListDocumentsResult> SelectItems(
       string OrderBy,
       int maximumRows,
       int startRowIndex)
     {
    
         Controller.ListDocuments(new ListDocumentsRequest())
            .OrderBy(OrderBy)
            .Skip(startRowIndex)
            .Take(maximumRows);
     }
    

    就这么简单!

    请注意,动态 linq 中存在一个小错误(或不便) - 当排序顺序为空时,它会引发异常。

    然后找到这段代码(第 47 行及以下)

        public static IQueryable OrderBy(this IQueryable source, string ordering, params object[] values) {
            if (source == null) throw new ArgumentNullException("source");
            if (ordering == null) throw new ArgumentNullException("ordering");
    

    并手动将其更改为

        public static IQueryable OrderBy(this IQueryable source, string ordering, params object[] values) {
            if ( string.IsNullOrEmpty( ordering ) ) return source;
            if (source == null) throw new ArgumentNullException("source");
            if (ordering == null) throw new ArgumentNullException("ordering");
    

    完成。

    【讨论】:

    • 感谢您的广泛回答。出于某种我不记得的原因,我认为 SortParameterName 属性仅在 ObjectDataSource 与 GridView(而不是 ListView)一起使用时使用。但是不,它确实像你说的那样有效。我还没有研究 Dynamic.cs,所以我当然仍然需要调整我的代码来处理“Name DESC”的情况,这并没有让我的代码更漂亮......但它确实有效。
    • 你不必适应任何事情。正如我所说,“名称”和“名称 DESC”将由列表视图自动传递,动态 linq 也会自动处理。
    • 是的,我知道,我只是想说我还没有研究过动态 linq,所以我只是检查了我的排序问题的解决方案是否正常,所以我可以将响应设置为答案。我确信动态 linq 最终会让它更漂亮。
    • 有没有办法让它按多列排序?当我尝试它时,在降序排序时,它只对第二列进行降序排序,并在第一列保持升序排序(我正在对姓氏、名字进行排序)
    • 从来没有想过它,快速的答案是:不一定。想不出一种简单的方法来让这个方法适用于多个列。
    【解决方案2】:

    如果SelectMethod 不是静态的,ObjectDataSource 控件将创建TypeName 中指定类型的新实例并调用该实例上的方法。

    您要么需要将排序表达式的参数添加到您的选择方法并在ObjectDataSource 上设置SortParameterName property,要么您需要处理ObjectCreating event 并将ObjectInstance 设置为现有的控件实例.

    【讨论】:

    • 感谢您对我对控件实例生命周期的实际误解的解释。我决定使用 SortParameterName,处理 ObjectCreating 事件似乎有点矫枉过正,而且很难向下一个阅读我的代码的开发人员解释。
    猜你喜欢
    • 1970-01-01
    • 2015-06-11
    • 1970-01-01
    • 2012-10-13
    • 2023-03-12
    • 2010-11-03
    • 2010-11-03
    • 1970-01-01
    • 2017-09-22
    相关资源
    最近更新 更多