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
:
OnLoad
SortItems
SelectItems
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 is Sort
.
You have then
<asp:LinkButton ID="SortByName" runat="server" CommandArgument="Name" CommandName="Sort" />
Then, on your ObjectDataSource
you add the SortParameterName
to be something like OrderBy
:
<ObjectDataSource .... SortParameterName="OrderBy" />
Then you modify your data provider method to be:
public IEnumerable<ListDocumentsResult> SelectItems(
string OrderBy,
int maximumRows,
int startRowIndex)
The data source will provide the value automatically based on the command argument ( Name
) and it will automatically append DESC
whenever you click the command button for the second time (it is because the ListView
persists 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:
find the Dynamic.cs
file, include it in your project and it will add a bunch of additional linq operators, including the OrderBy
which accepts strings and which automatically supports DESC
(!).
You then just
public IEnumerable<ListDocumentsResult> SelectItems(
string OrderBy,
int maximumRows,
int startRowIndex)
{
Controller.ListDocuments(new ListDocumentsRequest())
.OrderBy(OrderBy)
.Skip(startRowIndex)
.Take(maximumRows);
}
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)
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");
and change it manually to
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");
Done.
If the SelectMethod
is not static, the ObjectDataSource
control will create a new instance of the type specified in TypeName
and call the method on that instance.
You either need to add a parameter for the sort expression to your select method and set the SortParameterName
property on the ObjectDataSource
, or you need to handle the ObjectCreating
event and set the ObjectInstance
to the existing control instance.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.