Сортировка 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:
Найти 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
в существующий контрольный экземпляр.