I am using a ListView to display paginated data:
<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>
The datasource definition:
<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>
The revelant code behind is made of these two methods:
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();
}
}
So my intention this: when the users clicks on the LinkButton contained in the “Name” column header (I left out all but one column for clarity), the SortItems method is called: it sets the sorted column name and sort order into the ViewState, then reloads the ListView using the DataBind method. In the Select method of the ObjectDataSource, we read this ViewState values and use them to order the data.
Putting breakpoints on all these methods, I can see there is this sequence of calls when I click the LinkButton:
OnLoadSortItemsSelectItems
The problem I have is that when I get to the SelectItems method, the ViewState is totally empty (it has 0 keys): if I set a breakpoint on the Load method of the page, I see the control containing all this is only loaded once. The DataBind method does not seem to trigger any loading of the control, it seems to be just triggering the SelectItems method of a new instance of the control (meaning that if, instead of using ViewState, I set an instance field in the SortItems method, the field is null when getting in the SelectItems method).
I am sure that the ViewState is active on the page (I can find the ViewState keys on the browser side using a Firefox extension for instance).
There is something not quite clear to me about the life cycle of the page/control. Could someone explain what it is to me?
There exist much, much simpler approach.
First, instead of a custom
CommandName, you put a built-in name into the sort link button. The name isSort.You have then
Then, on your
ObjectDataSourceyou add theSortParameterNameto be something likeOrderBy:Then you modify your data provider method to be:
The data source will provide the value automatically based on the command argument (
Name) and it will automatically appendDESCwhenever you click the command button for the second time (it is because theListViewpersists the state of sort order in its viewstate automatically, you don’t have to reinvent this!)Then, you don’t need this ugly delegates to order by strings for linq. Instead, download the Dynamic Linq library:
http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
find the
Dynamic.csfile, include it in your project and it will add a bunch of additional linq operators, including theOrderBywhich accepts strings and which automatically supportsDESC(!).You then just
This is just as simple!
Be warned though that there’s a small bug (or an inconvenience) in the dynamic linq – it throws an exception when the sort order is empty.
Find then this code (line 47 and down)
and change it manually to
Done.