Resetting the Page Index in a ListView
Resetting paging in a GridView is way easy. For a ListView, not so much. There's actually no such property as PageIndex with ListView, so we need to dig into the internals of the DataPager to perform this common task.
There are lots of situations in which you need to set a paged list of data back to the first page in code. For example, let's say you search your database by specifying some search criteria, and you get several pages of results. Using the supplied paging controls, you jump to different pages -- page 1... page 2... page 3... and then, you decide to alter the search criteria. You certainly don't want the new set of results to start on page 3! Of course, you'd want to see the new results starting again from the first page.
To provide a concrete example, let's use the Products table from the Northwind database. This sample database contains data about various food products, each of which belong to a certain category -- Beverages, Condiments, Confections, and so on. If we set up a simple page that lists product data by category, we might end up with something that looks like the following:
In the screenshot above, you can see we're filtering the products table by category. The category to search for is specified using a DropDownList. The results are displayed in a paged GridView below that. The PageSize property of the GridView is set to 5, meaning a maximum of 5 results will be displayed per page.
If we flip through the results to a page greater than 1, then set the categories dropdown to a new category, we'll get new results, but those results won't start on the first page as we might expect (unless there's only one page of data, of course). Whenever you cause the contents of the list to change, you have to reset the display back to the first page in code.
As you are no doubt aware, the PageIndex property of a GridView gets or sets the zero-based index of the currently displayed page. Resetting the GridView back to the first page of data is trivial -- you simply set PageIndex = 0. In this case, you'd do that whenever the SelectedIndex changes on the Categories dropdown.
protected void ddlCategories_SelectedIndexChanged(object sender, EventArgs e)
{
gvProducts.PageIndex = 0;
}
This is possible because the paging controls are integral to the GridView itself. But that isn't so with a ListView. The ListView control has no paging controls of its own, and thus no PageIndex property. The ListView actually requires support from a DataPager control to provide paging functionality. So, how do we create the same behavior using the ListView and DataPager?
Example 1: DataPager with NumericPagerField
Reproducing the results grid in a ListView is pretty standard stuff, so I won't show all of that here. Providing paging support through the DataPager is pretty easy as well. In our example above, the GridView provides paging functionality through the use of page number links. We can do the same with the DataPager by adding a NumericPagerField to the Fields collection of the DataPager. We'll set the PagedControlID to the ID of our ListView , and set its PageSize property to 5.
<asp:DataPager ID="dpgProducts" runat="server" PagedControlID="lvProducts"
PageSize="5">
<Fields>
<asp:NumericPagerField />
Fields>
asp:DataPager>
This results in the following page:
So far, we've recreated all the functionality of our original GridView -based page, except for resetting the paged results when the dropdown changes. Unfortunately, that's a bit more involved than just setting a property.
The key to pulling this off is to think about how we do it manually, and then recreate that functionality in code. In this case, to "manually" set the results of this ListView on the screen back to the first page, we would click the "1" LinkButton in the DataPager. So, we need to recreate that -- to somehow "click" the "1" LinkButton in the DataPager -- not manually of course, but with code.
The NumericPagerField class derives from the DataPagerField class and overrides its HandleEvent method. HandleEvent is a helper method that handles events that occur in a DataPagerField. In the case of the NumericPagerField, clicking a page number raises an event. This raised event has a CommandName value that is equal to one less than the number that was clicked (remember, the index is zero-based). For example, when you click on the number "1", a LinkButton OnClick event is raised with CommandName of 0. We can simulate this event in code by encapsulating the proper event data in a CommandEventArgs object, then passing it to the HandleEvent method. Once we do that, our application should behave exactly as though we clicked on the "1" button with the mouse.
We can do that as follows:
protected void ddlCategories_SelectedIndexChanged(object sender, EventArgs e)
{
CommandEventArgs commandEventArgs = new CommandEventArgs("0", "");
NumericPagerField numericPagerField = dpgProducts.Fields[0] as NumericPagerField;
if (numericPagerField != null)
{
numericPagerField.HandleEvent(commandEventArgs);
}
}
In the above code, we first create a CommandEventArgs object with a CommandName of 0. Then, we extract the NumericPagerField object from the DataPagerFieldCollection. Then assuming that worked, we pass the CommandEventArgs object we created to the HandleEvent method of the NumericPagerField. This sets the list of data back to the first page.
Example 2: DataPager with NextPreviousPagerField
Let's now assume you decide, instead of letting users click numbers to jump to any page, you only want to allow users to flip through one page at a time. You could do that with a NextPreviousPagerField:
<asp:DataPager ID="dpgProducts" runat="server" PagedControlID="lvProducts"
PageSize="5">
<Fields>
<asp:NextPreviousPagerField />
Fields>
asp:DataPager>
Resetting data paging here is almost the same as before. The only thing to remember is, with a NextPreviousPagerField, the CommandName value of the event that navigates to the first page is "First", not "0". (Technically, the CommandName value is actually DataControlCommands.FirstPageCommandArgument, which is merely a constant that represents the string "First"). Therefore, the logic is very similar:
protected void ddlCategories_SelectedIndexChanged(object sender, EventArgs e)
{
CommandEventArgs commandEventArgs = new CommandEventArgs("First", "");
NextPreviousPagerField nextPreviousPagerField = dpgProducts.Fields[0] as NextPreviousPagerField;
if (nextPreviousPagerField != null)
{
nextPreviousPagerField.HandleEvent(commandEventArgs);
}
}
The only difference here is the type of DataPagerField whose HandleEvent method we're invoking, and the CommandName value we're passing to it.
Example 3: DataPager with TemplateField
With the NumericPagerField and the NextPreviousPagerField, you can see that the actual work of paging itself is being handled automatically for us. We're not handling any button clicks, we're not examining any command event data, and we're not setting any start row indexes every time. We click, the list pages, and life is sweet.
However, the DataPager also lets us use a TemplateField to page data. The cool thing about the TemplateField is that you can really go wild with it, pretty much defining any kind of paging scheme you can imagine. The downside is that the TemplateField provides no automatic paging functionality at all. You've got to do it all on your own, including providing your own postback-enabled controls and CommandName values.
Take a look at the the following paging example:
Here we've set up custom LinkButtons that, instead of showing words like "First", "Previous", or "Next", show the record numbers. In the screenshot, we can see from the pager that records 6 through 10 of the results are bring displayed. There are buttons that let us go to records 1 - 5 or 11 - 12. There is also a "<<" button that lets us go back to the first page.
Below is the markup that renders the pager in shown above. Don't get bogged down in all the databinding going on here; all that's really important to note is the first LinkButton:
"dpgProducts" runat="server" PagedControlID="lvProducts"
PageSize="5">
"TemplatePagerField_OnPagerCommand">
"lnkbFirst" runat="server" CommandName="FirstPage"
Text="<<" Enabled='<%# Container.StartRowIndex > 0 %>' />
"lnkbPrevious" runat="server" CommandName="PreviousPage"
Text='<%# (Container.StartRowIndex - Container.PageSize + 1) + " - " + (Container.StartRowIndex) %>'
Visible='<%# Container.StartRowIndex > 0 %>' />
"lnkbNext" runat="server" CommandName="NextPage"
Text='<%# (Container.StartRowIndex + Container.PageSize + 1) + " - " + (Container.StartRowIndex + Container.PageSize*2 > Container.TotalRowCount ? Container.TotalRowCount : Container.StartRowIndex + Container.PageSize*2) %>'
Visible='<%# (Container.StartRowIndex + Container.PageSize) < Container.TotalRowCount %>' />
Note that the first LinkButton (with ID="lnkbFirst") has the CommandName = "FirstPage". That's the button we'll wire up to jump to the first page. Below is the code that does that, along with the code for the other buttons as well:
protected void TemplatePagerField_OnPagerCommand(object sender, DataPagerCommandEventArgs e)
{
int maximumRows = dpgProducts.MaximumRows;
switch (e.CommandName)
{
case "FirstPage":
e.NewStartRowIndex = 0;
e.NewMaximumRows = maximumRows;
break;
case "NextPage":
int newIndex = e.Item.Pager.StartRowIndex + maximumRows;
if (newIndex <= e.TotalRowCount)
{
e.NewStartRowIndex = newIndex;
e.NewMaximumRows = maximumRows;
}
break;
case "PreviousPage":
e.NewStartRowIndex = e.Item.Pager.StartRowIndex - maximumRows;
e.NewMaximumRows = maximumRows;
break;
}
}
In order to to accomplish template-driven paging on a ListView, you handle the OnPagerCommand event of the TemplateField. In this event handler, you have to determine which control was clicked (you do this via the CommandName) and then set the value of the NewStartRowIndex and NewMaximumRows properties of the DataPagerCommandEventArgs object accordingly.
We can now reset the paging as we did before, by passing the appropriate CommandName to the HandleEvent method.
protected void ddlCategories_SelectedIndexChanged(object sender, EventArgs e)
{
CommandEventArgs commandEventArgs = new CommandEventArgs("FirstPage", "");
TemplatePagerField templatePagerField = dpgProducts.Fields[0] as TemplatePagerField;
if (templatePagerField != null)
{
templatePagerField.HandleEvent(commandEventArgs);
}
}
Conclusion
To set a paged ListView back to the first page, just follow these basic steps:
- Create a handler for any event that causes the displayed data to change. This could be a search/filtering action as in our example, insertion/deletion of data, or whatever.
- Determine the CommandName value that brings you to the first page, based on the type of DataPagerField you're using.
- In the event handler (from step 1), create a CommandEventArgs object using the CommandName value (from step 2).
- From the Fields collection of the DataPager, get the field that fires the event that brings you to the first page.
- Pass the CommandEventArgs object (from step 3) to the HandleEvent method of the field (from step 4).
That's all folks. Happy paging!
Subscribe to this blog for more cool content like this!
You've been kicked (a good thing) - Trackback from DotNetKicks.com
Thank you for submitting this cool story - Trackback from DotNetShoutout
Pingback from Twitter Trackbacks for Resetting the Page Index in a ListView : LeeDumond.com [leedumond.com] on Topsy.com