Сортировка ListView с использованием ObjectDataSource с LinkButton и ViewState

Я использую 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, затем перезагружает ListView с использованием DataBind метод. В методе Выбрать ObjectDataSourceмы читаем это ViewState значения и использовать их для упорядочения данных.

Помещая точки останова на все эти методы, я вижу, что при нажатии кнопки LinkButton:

  • OnLoad
  • SortItems
  • SelectItems

Проблема в том, что когда я добираюсь до SelectItems метод, ViewState абсолютно пустой (имеет 0 ключей): если я установил точку останова в методе Load страницы, я вижу, что элемент управления, содержащий все это, загружается только один раз. DataBind Кажется, что метод не запускает какую-либо загрузку элемента управления, а просто вызывает SelectItems метод нового экземпляра элемента управления (это означает, что если вместо ViewStateЯ установил поле экземпляра в SortItems метод, поле равно нулю при получении в SelectItems метод).

Я уверен, что ViewState активен на странице (могу найти ViewState ключи на стороне браузера с использованием расширения Firefox, например).

Что-то не совсем понятное для меня в жизненном цикле страницы / элемента управления. Может кто-нибудь объяснить, что это для меня?

2 ответа

Решение

Существует намного более простой подход.

Во-первых, вместо обычая 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 сохраняет состояние порядка сортировки в его viewstate автоматически, вам не нужно это заново изобретать!)

Тогда вам не нужны эти уродливые делегаты для упорядочивания по строкам для 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, в том числе OrderBy который принимает строки и который автоматически поддерживает DESC (!).

Вы тогда просто

 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");

Готово.

Если SelectMethod не является статичным, ObjectDataSource Элемент управления создаст новый экземпляр типа, указанного в TypeName и вызвать метод в этом экземпляре.

Вам либо нужно добавить параметр для выражения сортировки в ваш метод выбора и установить SortParameterName собственность на ObjectDataSourceили вам нужно справиться с ObjectCreating событие и установить ObjectInstance в существующий контрольный экземпляр.

Другие вопросы по тегам