I'm Keyvan Nayyeri, a 25 years old Ph.D. student at
the Computer Science department of
the University of Texas at San Antonio.
I'm also
a Software Architect and Developer and previously held a B.Sc.
degree in Applied Mathematics.
This is my blog where I publish content about various topics specifically Programming Languages and Compilers, Software
Engineering and Programming.
DataPager is a new server control in ASP.NET 3.5 that makes the data paging easier as a built-in feature. The necessity for a customizable data pager control has been a request by developers for some years and ASP.NET 3.5 comes with this solution.
Even though DataPager control is limited to be used with ListView server control and there are also some limitations in customizing its behavior but it’s still a great way to accomplish data paging in a short and easy way.
As many other data controls in the ASP.NET, you can use ListView with different data source controls. There isn’t any special point about this in general except that you may stick with using these DataPager and ListView controls in conjunction with a custom data source programmatically.
In this post I want to repeat the known story about using DataPager and ListView with a built-in .NET data source and then talk about an issue that will come into the play when you want to use a custom data source.
First let me show you how to bind a ListView to a built-in data source control like XmlDataSource and use DataPager to enable paging among its data. To do this, I create a web form and add three controls to the page: an XmlDataSource that uses my RSS feed as its data file, a ListView control that uses this XmlDataSource as its data source and displays my most recent post titles and a DataPager control that pages among data in the ListView. This control uses a QueryStringField attribute to link to pages using query string parameter rather than internal paging. It also has a PageSize attribute that is set to 5 in order to display this number of items in a page.
DataPager has three types of <Field /> child elements that you can use to customize the behavior of paging and I factor them here. There are many resources about DataPager control to learn about its details.
So here is the ASPX code for this simple data binding scenario:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="DataPagerSample._Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Using DataPager and ListView Controls with a Custom Data Source</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:DataPager ID="dataPager" runat="server" PagedControlID="listItems" QueryStringField="page"
PageSize="5">
<Fields>
<asp:NumericPagerField />
</Fields>
</asp:DataPager>
<br />
<asp:ListView ID="listItems" runat="server" DataSourceID="xmlDataSource">
<LayoutTemplate>
<asp:PlaceHolder runat="server" ID="itemPlaceholder" />
</LayoutTemplate>
<ItemTemplate>
<h3>
<a href="<%# XPath("guid") %>">
<%# XPath("title") %></h3>
</a> on
<%# XPath("pubDate")%>
</ItemTemplate>
<ItemSeparatorTemplate>
<hr />
</ItemSeparatorTemplate>
</asp:ListView>
<asp:XmlDataSource ID="xmlDataSource" runat="server" DataFile="http://feeds.nayyeri.net/keyvan"
XPath="rss/channel/item"></asp:XmlDataSource>
</div>
</form>
</body>
</html>
_3.png)
Now let me use a custom data source to bind my ListView programmatically. Before implementing this part, I need some mechanisms to fetch data from my feed and make them suitable for my data binding.
First I create a class called FeedFetcher that contains a single GetFeedItems function and it retrieves most recent blog posts from my feed using WCF Syndication framework. It returns a list of SyndicationItem objects that I’ll use as a data source for my ListView.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ServiceModel.Syndication;
using System.Xml;
namespace DataPagerSample.Components
{
public static class FeedFetcher
{
public static List<SyndicationItem> GetFeedItems(string feedUrl)
{
var rssReader = XmlReader.Create(feedUrl);
var rssFeed = SyndicationFeed.Load(rssReader);
return rssFeed.Items.ToList<SyndicationItem>();
}
}
}
Now I go back and create a new page with a ListView and a DataPager but this time I set the data source for my ListView control programmatically and use this list of SyndicationItem objects as my data source.
The ASPX code for this page looks like this:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default2.aspx.cs" Inherits="DataPagerSample.Default2" %>
<%@ Import Namespace="System.ServiceModel.Syndication" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title>Using DataPager and ListView Controls with a Custom Data Source</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:DataPager ID="dataPager" runat="server" PagedControlID="listItems" QueryStringField="page"
PageSize="5">
<Fields>
<asp:NumericPagerField />
</Fields>
</asp:DataPager>
<br />
<asp:ListView ID="listItems" runat="server">
<LayoutTemplate>
<asp:PlaceHolder runat="server" ID="itemPlaceholder" />
</LayoutTemplate>
<ItemTemplate>
<h3>
<a href="<%# Eval("Id") %>">
<%# ((TextSyndicationContent)Eval("Title")).Text %>
</a>
</h3>
on
<%# Eval("PublishDate")%>
</ItemTemplate>
<ItemSeparatorTemplate>
<hr />
</ItemSeparatorTemplate>
</asp:ListView>
</div>
</form>
</body>
</html>
And the code-behind is as simple as the following code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using DataPagerSample.Components;
using System.ServiceModel.Syndication;
namespace DataPagerSample
{
public partial class Default2 : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
BindData();
}
private void BindData()
{
var items = FeedFetcher.GetFeedItems("http://feeds.nayyeri.net/keyvan");
this.listItems.DataSource = items;
this.listItems.DataBind();
}
}
}
This gives me an output similar to the output with the XmlDataSource.
_3.png)
This is fine but the problem appears when you navigate to other pages such as the second page because you still see data items for the first page.
_3.png)
This is weird! Most likely you expected to see the correct output from this control since I didn’t do anything except changing my data source. I wrestled with this issue for half an hour last week and couldn’t guess any solution. After some searches, I finally could find a forum post on ASP.NET forums that was talking about the issue with a not straightforward solution. I tried to work around and develop it to a very simple solution that I present here.
All you need to do is, adding a PageProeprtiesChanging event handler for your ListView control and there you should set the new page properties for your ListView and rebind your data.
Thus I can update my ASPX code with adding a new OnPagePropertiesChanging event for ListView control as follows:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default2.aspx.cs" Inherits="DataPagerSample.Default2" %>
<%@ Import Namespace="System.ServiceModel.Syndication" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title>Using DataPager and ListView Controls with a Custom Data Source</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:DataPager ID="dataPager" runat="server" PagedControlID="listItems" QueryStringField="page"
PageSize="5">
<Fields>
<asp:NumericPagerField />
</Fields>
</asp:DataPager>
<br />
<asp:ListView ID="listItems" runat="server" OnPagePropertiesChanging="listItems_PagePropertiesChanging">
<LayoutTemplate>
<asp:PlaceHolder runat="server" ID="itemPlaceholder" />
</LayoutTemplate>
<ItemTemplate>
<h3>
<a href="<%# Eval("Id") %>">
<%# ((TextSyndicationContent)Eval("Title")).Text %>
</a>
</h3>
on
<%# Eval("PublishDate")%>
</ItemTemplate>
<ItemSeparatorTemplate>
<hr />
</ItemSeparatorTemplate>
</asp:ListView>
</div>
</form>
</body>
</html>
And the code-behind should be updated to handle this event as well.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using DataPagerSample.Components;
using System.ServiceModel.Syndication;
namespace DataPagerSample
{
public partial class Default2 : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
BindData();
}
private void BindData()
{
var items = FeedFetcher.GetFeedItems("http://feeds.nayyeri.net/keyvan");
this.listItems.DataSource = items;
this.listItems.DataBind();
}
protected void listItems_PagePropertiesChanging(object sender, PagePropertiesChangingEventArgs e)
{
this.dataPager.SetPageProperties(e.StartRowIndex, e.MaximumRows, false);
BindData();
}
}
}
This can simply solve the issue and show the correct data for each page.
_3.png)
I don’t know if this can be considered as an issue for DataPager and ListView controls or not but I’m pretty sure this could be as an internal part for these controls somehow. Having the first behavior in mind, this behavior is not expected for anyone, though!
Finally you can download the source code sample for this post.
Tim Laughlin
Aug 25, 2008 1:29 PM
#
You find the best stuff to cover. These types of articles are great! I don't need to this today. But I am sure I will, so off the bookmarks for now. But in advance, thanks for the great info.
Keyvan Nayyeri
Aug 25, 2008 1:32 PM
#
@Tim:
Thank you, Tim! You are very welcome and I hope this post can help you one day :-)
Dew Drop - August 26, 2008 | Alvin Ashcraft's Morning Dew
Aug 26, 2008 7:32 AM
#
Pingback from Dew Drop - August 26, 2008 | Alvin Ashcraft's Morning Dew
dek
Aug 28, 2008 5:55 PM
#
oh, thank you so much, i has been searching for this solution long time d, is working for me.....
Mansoor
Sep 12, 2008 12:10 AM
#
Hey great work and great stuff.....Thanks for the examples provided..Appreciable work buddy...Keep it up..
Zorro2006
Sep 15, 2008 5:21 PM
#
Thanks for your helping, It's useful for me. And can you answer for me this question? How to get the total of page of datapager?
Eric Fan
Oct 05, 2008 1:48 AM
#
Hi Mate.
Your article is great. I'm just thinking to replace DataPager control by repeater control because I can't figure out how to use DataPager at codebehind. Luckly I find this. It helps me a lot
Eric
Vinoth Kannan
Oct 10, 2008 7:03 AM
#
Thanks a lot.
I am confused with is problem more than 2 days.
It is very help to me.
Mesut Demir
Nov 12, 2008 8:24 AM
#
Nice example... and here is another way....
The nice thing belwo is it is within your Listview... Looks good when it's shown on the screen...
you can put tha dapager control in the listview self in the page (in the layoutemplate) like this...
<div class="pager">
<asp:DataPager ID="pagerBottom" runat="server" PagedControlID="listviewControlName" QueryStringField="page" PageSize="10" >
<Fields>
<asp:NextPreviousPagerField
ButtonCssClass="command"
FirstPageText="«" PreviousPageText="‹"
RenderDisabledButtonsAsLabels="true"
ShowFirstPageButton="true" ShowPreviousPageButton="true"
ShowLastPageButton="false" ShowNextPageButton="false"
/>
<asp:NumericPagerField
ButtonCount="7" NumericButtonCssClass="command" CurrentPageLabelCssClass="current" NextPreviousButtonCssClass="command"
/>
<asp:NextPreviousPagerField
ButtonCssClass="command"
LastPageText="»" NextPageText="›"
RenderDisabledButtonsAsLabels="true"
ShowFirstPageButton="false" ShowPreviousPageButton="false"
ShowLastPageButton="true" ShowNextPageButton="true"
/>
</Fields>
</asp:DataPager>
and in your code-behind in PagePropertiesChaningEventhandler:
==========================================
protected void listViewControlName_PagePropertiesChanging(object sender, PagePropertiesChangingEventArgs e)
{
DataPager dpBottom;
dpBottom = ((ListView)sender).FindControl("pagerBottom") as DataPager;
dpBottom.SetPageProperties(e.StartRowIndex, e.MaximumRows, false);
lvPallets.DataBind();
FillData();
}
#endregion
cheers,
mesut demir
Benedict
Dec 07, 2008 7:10 PM
#
Hi Keyvan
Your code example solved my problem. You are heaven sent!
Tesekkur Ederim!
Selam.
My Best Blog Posts in 2008
Dec 31, 2008 1:41 PM
#
In the past 3.5 years of blogging, I haven’t had such best pick up collections in the end of the year, but now that everybody is writing one, why shouldn’t I write my own?! Collecting this list, I could realize some interesting facts that completely changed
Bazzz
Mar 11, 2009 10:51 AM
#
Perfect! I just came across the exact same problem. Your solution made it work! Thank you
Calvin
Mar 11, 2009 8:14 PM
#
Thank you soooooo much!! This solved my custom data source paging problem!!
Christopher
Mar 23, 2009 5:24 AM
#
Nice dude - was looking for the same thing
When using querystring field - I would have to apply the same but searching for that querystring and then setting the PageProperties
Vinney
Mar 26, 2009 5:09 PM
#
Finding that post was a major blessing! I was stuck on the part where the datapager was only displaying the first set of data but your post fixed me right up! Thank you!
Nuaj
Apr 27, 2009 4:46 AM
#
Thanks!
Alexandre
Apr 30, 2009 6:15 AM
#
You're the best man. Many, many thanks!!!!!!!!!
Madi
May 19, 2009 2:57 PM
#
Hey guys.
This is beautiful but I’m having hard time doing the code in VB. Can anyone help me?
Many thxs
Madi
Jenn
Jun 09, 2009 4:59 PM
#
Hey guys.
I need code in VB for this problem, I just started using ASP.net
Thank
Jenn
Sudhir
Jun 24, 2009 6:09 AM
#
Hi Keyvan
Your code example solved my problem. You are excellent Frd
Thanks
Sudhir
Goran
Jul 11, 2009 2:26 PM
#
This post solved my problem too.
Anyway, I'd find solution eventually, but it's the clean and concise way you demonstrated the solution that made me write this comment.
Very nice, thanks!
Dilum
Jul 14, 2009 11:18 AM
#
Thank you very much...!! U saved me loads of time.
Dilum
max
Jul 18, 2009 5:27 PM
#
Hi, thank you very much!
Is it possible to retrieve only the rows of the selected page instead of all the rows? Datapager has a TotalRowCount property, but it is readonly..
Jakub
Sep 01, 2009 5:41 AM
#
Thanks a lot for this solution!
Raju
Sep 05, 2009 4:46 AM
#
Thanks buddy..
Its what i was search for..
good work.
God bless you
Sonu
Sep 25, 2009 3:02 PM
#
tnx for this code
sonu
Sep 25, 2009 3:05 PM
#
this can be done through repater if yes plz email me on this
Ivan Pankov
Oct 01, 2009 1:43 PM
#
chartman
Oct 23, 2009 1:31 PM
#
moonkin
Oct 26, 2009 11:41 AM
#
stephen
Oct 27, 2009 10:46 PM
#
it only works fine when you have small amount of data
when u have tons of rows, it gonna kill ur efficiency
even u r displaying 10 rows on each page, it still binding 100000... rows on each request
Keyvan Nayyeri
Oct 27, 2009 11:02 PM
#
With a little creativity you can modify this code to load data on demand. I put this simpler code to make it easier to understand.
Nebbercracker
Nov 12, 2009 1:13 PM
#
Ravinder
Nov 13, 2009 1:39 AM
#
Matter of fact i m not able to download the source code.
Can u mail to me My id:
Ravinder.Nathan@gmail.com
Thanks in Advance
Mark P
Dec 03, 2009 4:43 AM
#
All you need to do is just calling your BindData method on Page_PreRender.
Bryan Rite
Dec 15, 2009 5:12 PM
#
FYI, this particular combination of 3.5 controls (Listview, DataPager, QueryString Parameter) has a Microsoft accepted bug (http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=355348) and will cause your Select function to be called twice... this is obviously a very important consideration.
Keyvan Nayyeri
Dec 15, 2009 6:00 PM
#
Thanks for the point :-)
kourosh
Dec 25, 2009 5:48 PM
#
It solved my problem,
thnak you
rahul
Feb 22, 2010 12:34 AM
#
Leave a Comment