Paging in SPQuery

In order to retrieve SharePoint list items,  SPQuery is object of choice most of the times. Also for better performance, we may need to display limited items (PageSize) and  use paging to fetch next set of items.  Paging is not that straightforward task when you want to use it with SPQuery.

SPListItemCollectionPosition is the class which helps to achieve paging and sorting the results fetched by SPQuery.

SPQuery.ListItemCollectionPosition is a property of SPQuery that getssets an object of type SPListItemCollectionPosition that is used to obtain the next set of list item rows.

The ListItemCollectionPosition property is used together with the SPQuery.RowLimit property to define paging in a CAML query. The SPListItemCollectionPosition object is used to traverse through all the items in a collection n items at a time, where n is  row limit.

SPListItemCollectionPosition object needs to be initialized with correct PagingInfo string prior to use it with SPQuery. Below is the constructor signature

public SPListItemCollectionPosition(
         string PagingInfo
)

PagingInfo is the string containing paging information used to fetch the next page of list items. The PagingInfo string can have below formats depending upon how you need to traverse, forward or backward.

For traversing forward , PagingInfo string becomes :

Paged=TRUE&p_ID={l}

In case of paging forward p_ID is list item Id of last item (=l) on current page.

For traversing  backward , PagingInfo  string becomes

Paged=TRUE &PagedPrev=TRUE&p_ID={f}

In case of paging backward(previous) , p_ID is item Id of first item (=f) on current page.

Now, Let’s assume that we want to set page size of 5. For that, we first need to set SPQuery.RowLimit to 5 to return 5 list items at one time. When the page loads first time, you need not use SPListItemCollectionPosition .For the next page,The blow code retrieves next 5 items assuming last item in current page have Id=5 .

[Note: If items are deleted from lists, the 5thitem Id may not necessarily be 5.You will need correct list item id to go to next page. In below example ,itemCollec[itemCollec.Count - 1].ID gives correct item Id]

            SPQuery query = new SPQuery();
            query.RowLimit = 5;
            query.Query = "<IsNotNull><FieldRef Name='Title' /></IsNotNull>";
            query.ViewFields = "<FieldRef Name='Title' /><FieldRef Name='Id' />";
            SPListItemCollectionPosition position = new SPListItemCollectionPosition(“Paged=TRUE&p_ID=5”);
            query.ListItemCollectionPosition = position;
            itemCollec = oWeb.Lists["Tasks"].GetItems(query);
            return itemCollec;

If you are on Page 2 and from here if you want to go previous,the pagingInfo becomes Paged=TRUE &PagedPrev=TRUE&p_ID=6

            SPQuery query = new SPQuery();
            query.RowLimit = 5;
            query.Query = "<IsNotNull><FieldRef Name='Title' /></IsNotNull>";
            query.ViewFields = "<FieldRef Name='Title' /><FieldRef Name='Id' />";
            SPListItemCollectionPosition position = new SPListItemCollectionPosition(“Paged=TRUE &PagedPrev=TRUE&p_ID=6”);
            query.ListItemCollectionPosition = position;
            itemCollec = oWeb.Lists["Tasks"].GetItems(query);
            return itemCollec;

[Note: If items are deleted from lists, the 1st item Id on second page may not necessarily be 6 .You will need correct list item id to go to previous page. In below example ,itemCollec[0].ID gives correct item Id]

Using above logic you can create a webpart that uses  querystring to pass Page No and Item Id to construct PagingInfo string on fly. You can also use ViewState instead of Query String to persist the necessary information to construct the PagingInfo.

Check the below working code sample.

public class SPQueryPaging : WebPart
    {
        string prevPageUrl;
        string nextPageUrl;
        GridView GridView1=null;
        Label lblcurrPageItems;
        protected override void CreateChildControls()
        {
            GridView1 = new GridView();
            this.Controls.Add(GridView1);
            lblcurrPageItems = new Label();
            this.Controls.Add(lblcurrPageItems);
            //Fetch ‘page no’ and ‘item id’ from querystring to get items for current page.
            GetListItems(GetcurrPage("page"), GetCurrentItemId("itemid"));
        }
        private string GetQueryStringValue(string param)
        {
            if (!string.IsNullOrEmpty(Page.Request.QueryString[param]))
                return Page.Request.QueryString[param];
            return string.Empty;
        }
        private int GetcurrPage(string currPage)
        {
            int page = 1;
            try
            {
                page = Int32.Parse(GetQueryStringValue(currPage));
            }
            catch
            {
                page = 1;
            }
            return page;
        }
        private int GetCurrentItemId(string currItemId)
        {
            int currItem = -1;
            try
            {
                currItem = Int32.Parse(GetQueryStringValue(currItemId));
            }
            catch
            {
                currItem = -1;
            }
            return currItem;
        }
        protected override void Render(HtmlTextWriter writer)
        {
            lblcurrPageItems.RenderControl(writer);
            GridView1.RenderControl(writer);
            if (!string.IsNullOrEmpty(prevPageUrl))
                writer.Write("<a href='" + prevPageUrl + "'><< Prev</a>");
            writer.Write("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;");
            if (!string.IsNullOrEmpty(nextPageUrl))
                writer.Write("<a href='" + nextPageUrl + "'>Next >></a>");
        }
        private void GetListItems(int currPage, int itemId)
        {
            uint rowCount = 5;
            string pagingInfo = string.Empty;
            if (itemId != -1)
            {
                //Generate PagingInfo from current Page No and Item Id
                if (GetQueryStringValue("prev") == "1")
                    pagingInfo = string.Format("Paged=TRUE&PagedPrev=TRUE&p_ID={0}", itemId);
                else
                    pagingInfo = string.Format("Paged=TRUE&p_ID={0}", itemId);
            }
            SPListItemCollection itemCollec;
            itemCollec = GetListItems(pagingInfo, rowCount);
            DataTable objDataTable = itemCollec.GetDataTable();
            GridView1.DataSource = objDataTable;
            GridView1.DataBind();

            if (itemCollec.ListItemCollectionPosition != null)
            //Generate previous PageUrl with correct item id and Page No    
             nextPageUrl = Page.AppRelativeVirtualPath.Replace("~", string.Empty) + "?itemid=" + itemCollec[itemCollec.Count - 1].ID + "&page=" + (currPage + 1);
            else
                nextPageUrl = string.Empty;
            if (currPage > 1)

 //Generate next PageUrl with correct item id and Page No   
        prevPageUrl = Page.AppRelativeVirtualPath.Replace("~", string.Empty) + "?itemid=" + itemCollec[0].ID + "&page=" + (currPage - 1) + "&prev=1";
            else
                prevPageUrl = string.Empty;
            lblcurrPageItems.Text = ((currPage - 1) * rowCount) + 1 + " - " + currPage * rowCount;
        }
        public SPListItemCollection GetListItems(string pagingInfo, uint rowLimit)
        {
            SPWeb oWeb = SPContext.Current.Web;
            SPListItemCollection itemCollec;
            SPQuery query = new SPQuery();
            query.RowLimit = rowLimit;
            query.Query = "<IsNotNull><FieldRef Name='Title' /></IsNotNull>";
            query.ViewFields = "<FieldRef Name='Title' /><FieldRef Name='Id' />";
            if (!string.IsNullOrEmpty(pagingInfo))
            {
                SPListItemCollectionPosition position = new SPListItemCollectionPosition(pagingInfo);
                query.ListItemCollectionPosition = position;
            }
            itemCollec = oWeb.Lists["Tasks"].GetItems(query);
            return itemCollec;
        }
    }
6 Responses to “Paging in SPQuery”
  1. [...] and may fail on large lists. You should specify a RowLimit between 1 and 2000. You can also use Paging in SPQuery   if you want to retreive more then 2000 items at a time [...]

  2. Akash says:

    Great Article Amit…
    Just one question, what if I have to go directly on 6 page. Is it necessary in that case to get all the first 50 items (page size=10) to get the 5th page last item id which is required for PageInfo property.

    • Amit Kumawat says:

      Implementing paging with page numbers may be tricky to have with SPQuery. I recommend to use a next-previous pager so you can always grab the correct item id.

      If anyhow you need to do it, try coupling with PagedDataSource. For <500 list items, it should be ok.

      For items>500, a less intuitive approach would be to have a helper list.
      This list will have id mapping with target list item ids. So, you can directly grab the item using GetItemById(50) from the mapping list.Using this item, you can find the mapped item id and fire GetItemById(mappedID) on target list to grab actual item. You can create a simple console app to create a mapping list with the sort factor you need .

  3. Richard says:

    I have been working on a version of this which also includes a “first” & “Last ” page links

    case “FIRST”:
    pagestring = “Paged=TRUE”;

    case “LAST”:
    pagestring = “Paged=TRUE&PagedPrev=TRUE”;

    But the last case, always takes me to the penultimate page, I even tried a high value for p_id

    pagestring = “Paged=TRUE&PagedPrev=TRUE&p_ID=9999999999″;

    Any Suggestions?

    • Amit Kumawat says:

      The value of p_ID also depends on your page size and current SPListItemCollectionPosition. To go to the last page(say n) you will need to know the last item ID on second last page(n-1).

  4. yogesh says:

    This is userfull Paging on sharepoint list
    Thanks

  5. boyhxf says:

    How about sort the list by other column? Now, sort by id. what about sort other column?

Leave a Reply

Subscribe

Get every post delivered to your inbox via FeedBurner :