3
votes

I have a page on my ASP.NET site that has a Repeater control to show posts from members of the site.

At the moment I am storing the data for the posts in an XML file and then caching it within the site inside custom objects.

So I have:

public class MemberPost
{
    public string Title { get; set; }
    public string Text { get; set; }
    public string Name { get; set; }
    public DateTime Date { get; set; }
    public List<string> Pictures { get; set; }
}

And:

public class MemberPosts : List<MemberPost>
{

}

I'm able to set the data source of the repeater to an instance of MemberPosts and it all works as expected, but I want to add paging for when more posts are added.

All the examples I find seem to involve having the data to be paged in a database - is there any way I can bind the repeater or another similar control to my in-memory MemberPosts collection and have it implement paging for me?

I'm using VS2010 / .NET 3.5, but can switch to 4.0 if necessary as I have that on my server.

Thanks.

3
+1 for pointing out that every ASP .NET example on the internet binds UI directly to DB. I really wish they'd stop promoting such a practice :) - David
it's common practice for CQRS usages - Tim Mahy

3 Answers

4
votes

http://msdn.microsoft.com/en-us/library/bb358985.aspx

http://msdn.microsoft.com/en-us/library/bb503062.aspx

those are the 2 methods in IEnumerable you need.

You got

yourList.Skip(5).Take(5)

to show the second page of 5 items.

3
votes

Using the idea of Skip and Take, I put together the following and it was pretty easy and works a treat thanks :-)

protected void Page_Load(object sender, EventArgs e)
{
    IEnumerable<MemberPost> posts = from post in MemberPostCacher.Instance.Posts
                                    orderby post.Date descending
                                    select post;

    memberPostsRepeater.DataSource = GetPageMemberPosts(posts);
    memberPostsRepeater.DataBind();
}

private IEnumerable<MemberPost> GetPageMemberPosts(IEnumerable<MemberPost> posts)
{
    int totalNumberOfPosts = posts.Count();
    int pageSize = int.Parse(ConfigurationManager.AppSettings["MemberPostsPageSize"]);
    int totalPages = GetTotalPages(totalNumberOfPosts, pageSize);
    int currentPage = 1;

    if (Request.QueryString["page"] != null)
    {
        int.TryParse(Request.QueryString["page"], out currentPage);
        if (currentPage < 1 || currentPage > totalPages)
        {
            currentPage = 1;
        }
    }

    SetIntroVisibility(currentPage);
    SetLinks(currentPage, totalPages);
    SetPageNumberText(currentPage, totalPages);

    return posts.Skip((currentPage - 1) * pageSize).Take(pageSize);
}

private int GetTotalPages(int numberOfPosts, int pageSize)
{
    return numberOfPosts % pageSize == 0
                ? numberOfPosts / pageSize
                : numberOfPosts / pageSize + 1;
}

private void SetIntroVisibility(int currentPage)
{
    membersIntro.Visible = currentPage == 1;
}

private void SetLinks(int currentPage, int totalPages)
{
    linkPrevious.Visible = currentPage != 1;
    linkNext.Visible = currentPage != totalPages;

    linkPrevious.NavigateUrl = FormatPageLink(currentPage - 1);
    linkNext.NavigateUrl = FormatPageLink(currentPage + 1);
}

private string FormatPageLink(int pageNumber)
{
    return string.Format("{0}{1}{2}",
                            Request.CurrentExecutionFilePath,
                            "?page=",
                            pageNumber);
}

private void SetPageNumberText(int currentPage, int totalPages)
{
    lblNumRecords.Text = string.Format("&nbsp;&nbsp;Page {0} of {1}&nbsp;&nbsp;", currentPage, totalPages);
    if(totalPages == 1)
    {
        lblNumRecords.Visible = false;
    }
}
0
votes

you can bind using an objectdatasource and implement paging very easily there! this way you can fully reuse the bindable controls logic.........