SharePoint Extended Content Query web part(ECQWP) Enabling Pagination

SharePoint out of the box Content Query web part(CQWP) is an essential component when it comes to deal with data from list. Some of the key point

  • Part of Enterprise Content Management functionality.
  • Aggregates and display list items within a site hierarchy
  • Offers caching capabilities and query optimization
  • Comes with a set of Item style templates for look and feel.
  • Although Content Search Web part is current trend and according to some sharepoint communities CQWP is dead.But in my opinion it is not true.Above all CSWP is not supported in O365 hence CQWP is the option for content roll up. 

Now the key question Why we need extended CQWP and why not to go for a custom visual web part?

WHY Extended CQWP?

CQWP comes with a good amount of out of the box capabilities,still sometime its essential to have component which serves CQWP features+Additional customization like

  • Pagination,one of the major limitation for OOTB CQWP is it does not allow pagination
  • Content roll up from multiple list, it is not possible to configure more than one list for a CQWP
  • Look and feel.
  • Additional filtering, OOTB CQWP supports at most 3 level of filtering etc.

Look and feel can be customized by using customized Item style ,but in this article I am not going to discuss on that.This article is to highlight on extending a OOTB CQWP and develop some feature like pagination in content roll up while levering the key features of a CQWP.

WHY NOT Custom Visual WEBPART?

  1. Developing a visual web part from scratch and implementing the content roll up will be more time consuming
  2. Its quite easy to tweak the content query for CQWP rather go with the Visual web part
  3. Another point to remember  app part vs web part
  4. Performance, pointless to compare the performance between a CQWP and Visual web part.

And the list can go on.Still we should not forget sometime we dont have any option rather go for a entirely custom visual web part.

Now the time for some coding.

How to develop an Extended Content Query Web part?

 I am going to land upon the coding part directly as I believe you are already aware to add the classes and code obviously we need a sharepont solution ;)

Step 1 Extending a CQWP: Add a class ECQWP inhering from ContentByQueryWebPart

   /// <summary>
    /// Extended Content query web part with additional feature.
    /// </summary>
    [ToolboxItemAttribute(false)]
    public class ECQWP : ContentByQueryWebPart
    {

Step 2 Customizing the tool part: Add a class to hold the tool part properties. For our convenience lets name it as ECQWPToolpart.

 public class ECQWPToolpart : ToolPart
    {

    }

For example we need to add the Item per page property in the tool pane for pagination.The complete tool part class for the same will be look like as given below

 

publicclassECQWPToolpart : ToolPart

    {

        #region Constructor

       public ECQWPToolpart(string itemPerPageValue)

        {

            ////Create the Text box

            this.ItemPerPage = newTextBox();

            if (!string.IsNullOrEmpty(itemPerPageValue))

            {

                this.ItemPerPage.Text = itemPerPageValue;

            }

        }

        #endregion Constructor

        #region Properties

        protectedLiteral ItemPerPageUIDisplay { get; set; }

        protectedTextBox ItemPerPage { get; set; }

        #endregion Properties

        #region Methods

        publicoverridevoid ApplyChanges()

        {

            base.ApplyChanges();

            ////Applies the custom settings to the content query web part

            ECQWP parentWebPart = (ECQWP)this.ParentToolPane.SelectedWebPart;

            if (parentWebPart != null)

            {

                ////Applies the control settings

                parentWebPart.ItemPerPageValue = Convert.ToInt32(this.ItemPerPage.Text);

  }

        }

       protectedoverridevoid CreateChildControls()

        {

            base.CreateChildControls();

            ////TODO:Localization can be done

            ////Title to the web part

            this.Title = "Custom Configurable Properties";

            ////Add the contorls so that they show up on the screen

            this.ItemPerPageUIDisplay = newLiteral();

            this.ItemPerPageUIDisplay.Text = "<br /><b>Item Per Page </b><br />";

            this.Controls.Add(this.ItemPerPageUIDisplay);

            this.Controls.Add(this.ItemPerPage);

  }

  #endregion Methods

    }

Step 3 Developing the ECQWP: Key Methods that we need to take care or override from the ContentByQueryWebPart

  • GetToolParts:-To customize and append custom tool pane properties.
  • CreateChildControls:-To add additional controls to the CQWP
  • OnInit:- To override the Query feature for the Component
  • ProcessItems:-To do any manipulation with the fetched data.

In our case we need to deal with the GetToolParts and ProcessItems mainly .

 

    [ToolboxItemAttribute(false)]

    publicclassECQWP : ContentByQueryWebPart

    {

        #region Properties

        publicint ItemPerPageValue { get; set; }

        #endregion

        #region Overriden Methods

        publicoverride ToolPart[] GetToolParts()

        {

            List<ToolPart> toolParts = newList<ToolPart>(base.GetToolParts());

            toolParts.Insert(

                0,

                newECQWPToolpart(

                this.ItemPerPageValue.ToString(CultureInfo.InvariantCulture));

            return toolParts.ToArray();

        }

       protectedoverridevoid OnInit(EventArgs e)

        {

            base.OnInit(e);

            this.ProcessDataDelegate += this.ProcessItems;

        }

        #endregion Overriden Methods

        #region Methods

       protectedDataTable ProcessItems(DataTable data)

        {

            if (this.ItemPerPageValue > 0)

            {

                int currentPage = 1;

                int numPages = (int)Math.Round(

                     (double)data.Rows.Count / this.ItemPerPageValue);

                DataTable dataTable = null;

                if (!string.IsNullOrEmpty(Constants.CurrentPageQuerystringKey) &&

                    !string.IsNullOrEmpty(

                    Page.Request.QueryString[Constants.CurrentPageQuerystringKey]) &&

                    int.TryParse(

                    Page.Request.QueryString[Constants.CurrentPageQuerystringKey],

                    out currentPage) &&

                    (currentPage < 1 || currentPage > numPages))

                {

                    currentPage = 1;

                }

                if (dataTable == null)

                {

                    int startItemNumber = (currentPage - 1) * this.ItemPerPageValue;

                    int endItemNumber = startItemNumber + (this.ItemPerPageValue - 1);

                    data.Columns.Add("Number", typeof(int));

                    for (int i = 0; i < data.Rows.Count; i++)

                    {

                        data.Rows[i]["Number"] = i;

                    }

                     data.DefaultView.RowFilter = string.Format(

                        CultureInfo.CurrentCulture,

  "Number >= {0} AND Number <= {1}",

                        startItemNumber,

                        endItemNumber);

                    dataTable = data.DefaultView.ToTable();

                }

                 data = dataTable;

   uint locale = Convert.ToUInt16(CultureInfo.CurrentCulture.LCID);

                if (numPages > 0 && currentPage != 1)

                {

                    string prevText = "prev";

                    this.AddPaggingControls(prevText, "prevButton");

                }

                for (int index = 1; index <= numPages; index++)

                {

                    this.AddPaggingControls(index.ToString(CultureInfo.InvariantCulture), "page_" + index.ToString(CultureInfo.InvariantCulture));

                }

                if (numPages > 0 && currentPage != numPages)

                {

                    string nextText= "next";

                    this.AddPaggingControls(nextText, "nextButton");

                }

            }

            return data;

        }

        privatevoid ChangePage(object sender, EventArgs e)

        {

            LinkButton button = sender asLinkButton;

            string prevPage = this.Page.Request.QueryString[Constants.CurrentPageQuerystringKey];

            string value = button.Text;

            string currentPage = this.GetCurrentPage(value, button.ID, Convert.ToInt32(prevPage, CultureInfo.InvariantCulture));

            string currentPageUrl = HttpContext.Current.Request.Url.ToString();

            string queryStringParam = Constants.CurrentPageQuerystringKey + "=" + currentPage;

            string postbackUrl = null;

            if (!string.IsNullOrEmpty(value))

            {

                if (!string.IsNullOrEmpty(prevPage))

                {

                    string oldQueryStringParam = Constants.CurrentPageQuerystringKey + "=" + prevPage;

  postbackUrl = currentPageUrl.Replace(oldQueryStringParam, queryStringParam);

                }

                else

                {

                    postbackUrl = currentPageUrl.Contains("?") ? currentPageUrl + "&" + queryStringParam : currentPageUrl + "?" + queryStringParam;

                }

                 Response.Redirect(postbackUrl);

     }

        }

       privatestring GetCurrentPage(string value, string buttonId, int prevPageNumber)

        {

            int currentPage = 1;

            if (buttonId == "prevButton")

            {

                currentPage = prevPageNumber - 1;

            }

            elseif (buttonId == "nextButton")

            {

                currentPage = prevPageNumber + 1;

       }

            else

            {

                currentPage = Convert.ToInt32(value);

            }

            return currentPage.ToString(CultureInfo.InvariantCulture);

        }

       privatevoid AddPaggingControls(string text, string id)

        {

            LinkButton button = newLinkButton();

            button.Text = text;

            button.ID = id;

            button.Click += this.ChangePage;

            this.pagerContainer.Controls.Add(button);

        }

        #endregion Methods

    }

That's all,we are done with an extended Content Query web part with pagination in which even we can configure the number of pages to be displayed per page.We can have several other things like Query overrride, additional server controls to leverage filtering capabilities and so on. We can look those part on some other article,till then keep doing a little bing it and HAPPY CODING.