Keyvan Nayyeri

God breathing through me

Using DataPager and ListView Controls with a Custom Data Source in the ASP.NET

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>

Output

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.

Output

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.

Output

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.

Output

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.

38 Comments

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 :-)

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
#
Good Job Keep it up
tnx for this code

sonu
Sep 25, 2009 3:05 PM
#
Hi once agan tnx ,
this can be done through repater if yes plz email me on this

Ivan Pankov
Oct 01, 2009 1:43 PM
#
Awesome post! Works like a charm. Just change your profile picture -- current one is too serious :)

chartman
Oct 23, 2009 1:31 PM
#
Thanks very much. Solved the problem for me, and your explanation was organized and very clear.

moonkin
Oct 26, 2009 11:41 AM
#
tanx

stephen
Oct 27, 2009 10:46 PM
#
for those whose use this keep this in mind
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
#
@Stephen

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
#
Keyvan, thank you very much. I struggled with this issue for days and searched everywhere.

Ravinder
Nov 13, 2009 1:39 AM
#
I think this is the one for which i was searching for couple of days.
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
#
There is much simpler solution, which is not require to double-bind your data source, which isn't either pretty or effective.
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
#
@Bryan

Thanks for the point :-)

kourosh
Dec 25, 2009 5:48 PM
#
Zor Spas,
It solved my problem,
thnak you

Leave a Comment





Ads Powered by Lake Quincy Media Network