Implementing a Paging Repeater
In this post we are going to talk about repeaters, but more specifically implementing paging to repeaters. Repeaters are a convenient way to display a list of data, while allowing for custom layout/formatting/styling of that list. We will use one of my current projects as an example. In the site there several posts, or articles, written by many different users. When a user searches for posts or clicks on a specific category, we want to show them a list of all the posts that are retrieved. Using a repeater you can show a formatted list of all the posts, but this can become a problem if you have a large number of posts available. This is where paging comes in – we want to maintain our dataset but only show a few records at a time, so we don’t overwhelm the user or implement a page that takes forever to load. Let’s start by first writing the markup for our repeater. A repeater can have up to 5 different templates defined, which you can read more about in this MSDN article. In this post we are only going to implement the ItemTemplate. Below is the basic markup for my repeater, as well as a label to display the current page, and prev/next buttons.
<![CDATA[ <asp:Repeater ID="rptPosts" runat="server" OnItemDataBound="rptPosts_OnItemDataBound" > <ItemTemplate> <table> <tr> <td> <asp:HyperLink ID="postTitle" runat="server"></asp:HyperLink> <br /> <br /> <asp:Label ID="moreInfo" runat="server" CssClass="moreInfo"></asp:Label> </td> <td> <a id="lnkUserPhoto" runat="server"><img id="userPhoto" runat="server" /></a> <div ><asp:HyperLink ID="lnkUser" runat="server"></asp:HyperLink></div> </td> </tr> </table> </ItemTemplate> </asp:Repeater> <br /> <br /> <div style="text-align:center;"> <asp:Label ID="lblCurrentPage" runat="server"></asp:Label> <br /> <asp:Button ID="btnPrev" runat="server" Text=" << " OnClick="btnPrev_Click" /> <asp:Button ID="btnNext" runat="server" Text=" >> " OnClick="btnNext_Click" /> </div> ]]>
Next we’ll need to bind the repeater to our data. This is where we are going to implement paging, by using a PagedDataSource. Think of it just the same way you would any other data source, but with the ability to separate your data (into pages). Below is part of the code-behind for my page; I am going to bind the data initially (not postback). I have also included a public int to more easily reference the current page of my data.
<![CDATA[ //The Current Page of the Data Source public int PostListCurrentPage { get { if (ViewState["_currentPage"] == null) { return 0; } else { return (int)ViewState["_currentPage"]; } } set { ViewState["_currentPage"] = value; } } protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { GetPosts(); } } private void GetPosts() { //get data DataTable postData = SelectAllPosts(); PagedDataSource pds = new PagedDataSource(); pds.DataSource = postData.DefaultView; DataView view = postData.DefaultView; //allow paging, set page size, and current page pds.AllowPaging = true; pds.PageSize = 3; pds.CurrentPageIndex = PostListCurrentPage; //show # of current page in label lblCurrentPage.Text = (PostListCurrentPage + 1).ToString() + " of " + pds.PageCount.ToString(); //disable prev/next buttons on the first/last pages btnPrev.Enabled = !pds.IsFirstPage; btnNext.Enabled = !pds.IsLastPage; //bind data rptPosts.DataSource = pds; rptPosts.DataBind(); } ]]>
Now we need to make sure that our data is linked to the proper controls, this is where OnItemDataBound comes into play:
<![CDATA[ protected void rptPosts_OnItemDataBound(object sender, RepeaterItemEventArgs e) { //find each control in the ItemTemplate HyperLink title = (HyperLink)e.Item.FindControl("storyTitle"); HyperLink lnkUser = (HyperLink)e.Item.FindControl("lnkUser"); Label moreInfo = (Label)e.Item.FindControl("moreInfo"); HtmlAnchor lnkUserPhoto = (HtmlAnchor)e.Item.FindControl("lnkUserPhoto"); HtmlImage userPhoto = (HtmlImage)e.Item.FindControl("userPhoto"); //define DataRowView DataRowView drv = e.Item.DataItem as DataRowView; //link up all controls w/ their data title.Text = drv.Row["Title"].ToString(); title.NavigateUrl = "Story.aspx?id=" + drv.Row["StoryID"].ToString(); lnkUser.Text = drv.Row["Username"].ToString(); lnkUser.NavigateUrl = "Profile.aspx?id=" + drv.Row["UserID"].ToString(); lnkUserPhoto.HRef = "Profile.aspx?id=" + drv.Row["UserID"].ToString(); userPhoto.Src = GetAvatarPath(drv.Row["UserID"].ToString()); moreInfo.Text = ((DateTime)(drv.Row["Date"])).ToShortDateString() + " | (" + drv.Row["numComments"].ToString() + ") comments"; moreInfo.Text += " | (" + drv.Row["numFavorites"].ToString() + ") favorites"; } ]]>
In your OnItemDataBound event you’ll have to keep in mind that your repeater items, well… repeat. Because of this, you won’t be able to reference them directly by ID and instead must use FindControl as I’ve shown above. Most of the references above are only relevant to my data, but you get the point as to what you need to do here.
Now, the only thing left to do is add the click events for our previous/next buttons:
<![CDATA[ protected void btnPrev_Click(object sender, System.EventArgs e) { PostListCurrentPage--; GetPosts(); } protected void btnNext_Click(object sender, System.EventArgs e) { PostListCurrentPage++; GetPosts(); } ]]>
Now we have our completed repeater! You can change the formatting/styling however you’d like. Here is a finished screenshot of my example:










