3
\$\begingroup\$

I have following code for doing custom paging from an asp.net web application.

Points of interest

  1. It uses Link Buttons as suggested in https://stackoverflow.com/questions/14335067/passing-search-parameters-to-same-page-when-hyperlink-clicked
  2. The link buttons are added in Page_Load itself as listed in https://stackoverflow.com/questions/14364332/dynamic-controls-event-handlers-working
  3. It is made as user control for reuse

QUESTIONS

  1. Is there any pitfalls in this approach?
  2. Is there any improvement suggestions?

User Control Markup

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="PagingUserControl.ascx.cs"
Inherits="PagingTestWebApplication.PagingUserControl" %>
<div class="pagingSection" id="pagingSection" runat="server">
<asp:LinkButton ID="lnkPrevious" runat="server" CssClass='page-numbers prev' Visible="false" OnClick="LinkButton_Click">Prev</asp:LinkButton>
<asp:LinkButton ID="lnkFirst" runat="server" CssClass='page-numbers' Visible="false"
    OnClick="LinkButton_Click">1</asp:LinkButton>
<asp:Label runat="server" ID="lblFirstDots" CssClass="page-numbers prev" Visible="false"
    Text="..."></asp:Label>
<asp:PlaceHolder ID="plhDynamicLink" runat="server"></asp:PlaceHolder>
<asp:Label runat="server" ID="lblSecondDots" Visible="false" CssClass="page-numbers prev"
    Text="..."></asp:Label>
<asp:LinkButton ID="lnkLast" runat="server" CssClass='page-numbers' Visible="false"
    OnClick="LinkButton_Click">Last</asp:LinkButton>
<asp:LinkButton ID="lnkNext" runat="server" CssClass='page-numbers next' Visible="false" OnClick="LinkButton_Click">Next</asp:LinkButton>
 </div>

User Control Code Behind

public partial class PagingUserControl : System.Web.UI.UserControl
{
    protected void Page_Load(object sender, EventArgs e)
    {

    }

    public int PreviousIndex { get; set; }
    public int CurrentClickedIndex { get; set; }

    public event EventHandler PaginationLinkClicked;

    protected void LinkButton_Click(object sender, EventArgs e)
    {
        //Assumption: Text of the LinkButton will be same as index
        LinkButton clickedLinkButton = (LinkButton)sender;

        if (String.Equals(clickedLinkButton.Text, "Next"))
        {
            //Next Page index will be one greater than current
            //Note: If the current index is the last page, "Next" control will be in disabled state
            CurrentClickedIndex = PreviousIndex + 1;
        }
        else if (String.Equals(clickedLinkButton.Text, "Prev"))
        {
            //Previous Page index will be one less than current
            //Note: If the current index is the first page, "Prev" control will be in disabled state
            CurrentClickedIndex = PreviousIndex - 1;
        }
        else
        {
            CurrentClickedIndex = Convert.ToInt32(clickedLinkButton.Text, CultureInfo.InvariantCulture);
        }

        //Raise event
        if (this.PaginationLinkClicked != null)
        {
            this.PaginationLinkClicked(clickedLinkButton, e);
        }

    }

    public void PreAddAllLinks(int tableDataCount,int pageSize,  int currentIndex)
    {
        if (tableDataCount > 0)
        {
            PagingInfo info = PagingHelper.GetAllLinks(tableDataCount, pageSize, currentIndex);

            //Remove all controls from the placeholder
            plhDynamicLink.Controls.Clear();

            if (info.PaginationLinks != null)
            {
                foreach (LinkButton link in info.PaginationLinks)
                {
                    //Adding Event handler must be done inside Page_Laod /Page_Init
                    link.Click += new EventHandler(LinkButton_Click);

                    //Validation controls should be executed before link click.
                    link.ValidationGroup = "Search";
                    this.plhDynamicLink.Controls.Add(link);
                }
            }

        }
    }

    public void AddPageLinks(int tableDataCount, int pageSize, int index)
    {

        if (tableDataCount > 0)
        {
            pagingSection.Visible = true;
            PagingInfo info = PagingHelper.GetPageLinks(tableDataCount, pageSize, index);

            //Remove all controls from the placeholder
            plhDynamicLink.Controls.Clear();

            if (info.PaginationLinks != null)
            {
                lnkPrevious.Visible = info.PaginationLinks.Count > 0 ? true : false;
                lnkNext.Visible = info.PaginationLinks.Count > 0 ? true : false;

                foreach (LinkButton link in info.PaginationLinks)
                {
                    //Validation controls should be executed before link click.
                    link.ValidationGroup = "Search";
                    this.plhDynamicLink.Controls.Add(link);
                }
            }


            //Dots visiblity
            if (info.IsEndDotsVisible != null)
            {
                lblSecondDots.Visible = Convert.ToBoolean(info.IsEndDotsVisible, CultureInfo.InvariantCulture);
            }
            else
            {
                lblSecondDots.Visible = false;
            }

            if (info.IsStartDotsVisible != null)
            {
                lblFirstDots.Visible = Convert.ToBoolean(info.IsStartDotsVisible, CultureInfo.InvariantCulture);
            }
            else
            {
                lblFirstDots.Visible = false;
            }

            //First and Last Links
            if (info.IsFirstLinkVisible != null)
            {
                lnkFirst.Visible = Convert.ToBoolean(info.IsFirstLinkVisible, CultureInfo.InvariantCulture);
            }
            else
            {
                lnkFirst.Visible = false;
            }

            if (info.IsLastLinkVisible != null)
            {
                lnkLast.Visible = Convert.ToBoolean(info.IsLastLinkVisible, CultureInfo.InvariantCulture);
                lnkLast.Text = info.NumberOfPagesRequired.ToString(CultureInfo.InvariantCulture);
            }
            else
            {
                lnkLast.Visible = false;
            }


            //For first page, there is no previous
            if (index != 1 && info.NumberOfPagesRequired != 1)
            {
                lnkPrevious.Enabled = true;
            }
            else
            {
                lnkPrevious.Enabled = false;
            }


            //For last page there is no Next
            if (index != info.NumberOfPagesRequired && info.NumberOfPagesRequired != 1)
            {
                lnkNext.Enabled = true;
            }
            else
            {
                lnkNext.Enabled = false;
            }
        }
        else
        {
            pagingSection.Visible = false;
        }

    }

 }

DTO

public class PagingInfo
{
    public Collection<LinkButton> PaginationLinks { get; set; }
    public bool? IsEndDotsVisible { get; set; }
    public bool? IsStartDotsVisible { get; set; }
    public bool? IsFirstLinkVisible { get; set; }
    public bool? IsLastLinkVisible { get; set; }
    public int NumberOfPagesRequired { get; set; }

}

Helper

public static class PagingHelper
{
    public static PagingInfo GetAllLinks(int totalRecordsInTable, int pageSize, int previousIndex)
    {

        string LinkButtonIDPrefix = "lnK";
        PagingInfo pagingInfo = new PagingInfo();
        pagingInfo.PaginationLinks = new Collection<LinkButton>();

        if (totalRecordsInTable > 0)
        {
            int itemsBeforePage = 4;
            int itemsAfterPage = 2;
            int dynamicDisplayCount = itemsBeforePage + 1 + itemsAfterPage;

            Double numberOfPagesRequired = Convert.ToDouble(totalRecordsInTable / pageSize);
            if (totalRecordsInTable % pageSize != 0)
            {
                numberOfPagesRequired = numberOfPagesRequired + 1;
            }

            if (numberOfPagesRequired == 0)
            {
                numberOfPagesRequired = 1;
            }


            //Note: This function adds only the probable Links that the user can click (based on previous click).
            //This is needed sice dynamic controls need to be added while Page_Load itself for event handlers to work
            //In case of any bug, easiest way is add all links from 1 to numberOfPagesRequired
            //Following is an optimized way

            int endOfLeftPart = dynamicDisplayCount;
            //User may click "1". So the first 7 items may be required for display. Hence add them for event handler purpose
            for (int i = 1; i <= endOfLeftPart; i++)
            {
                //Create dynamic Links 
                LinkButton lnk = new LinkButton();
                lnk.ID = LinkButtonIDPrefix + i.ToString(CultureInfo.InvariantCulture);
                lnk.Text = i.ToString(CultureInfo.InvariantCulture);
                pagingInfo.PaginationLinks.Add(lnk);
            }


            int startOfRighPart = Convert.ToInt32(numberOfPagesRequired) - dynamicDisplayCount + 1;

            //User may click the last link. So the last 7 items may be required for display. Hence add them for event handler purpose
            for (int i = startOfRighPart; i <= Convert.ToInt32(numberOfPagesRequired); i++)
            {
                //Links already added should not be added again
                if (i > endOfLeftPart)
                {
                    //Create dynamic Links 
                    LinkButton lnk = new LinkButton();
                    lnk.ID = LinkButtonIDPrefix + i.ToString(CultureInfo.InvariantCulture);
                    lnk.Text = i.ToString(CultureInfo.InvariantCulture);
                    pagingInfo.PaginationLinks.Add(lnk);
                }
            }

            //User may click on 4 items before current index as well as 2 items after current index
            for (int i = (previousIndex - itemsBeforePage); i <= (previousIndex + itemsAfterPage); i++)
            {
                //Links already added should not be added again
                if (i > endOfLeftPart && i < startOfRighPart)
                {
                    //Create dynamic Links 
                    LinkButton lnk = new LinkButton();
                    lnk.ID = LinkButtonIDPrefix + i.ToString(CultureInfo.InvariantCulture);
                    lnk.Text = i.ToString(CultureInfo.InvariantCulture);
                    pagingInfo.PaginationLinks.Add(lnk);
                }
            }



        }
        return pagingInfo;
    }
    public static PagingInfo GetPageLinks(int totalRecordsInTable, int pageSize, int currentIndex)
    {
        string LinkButtonIDPrefix = "lnK";
        PagingInfo pagingInfo = new PagingInfo();
        pagingInfo.PaginationLinks = new Collection<LinkButton>();

        if (totalRecordsInTable > 0)
        {

            int itemsBeforePage = 4;
            int itemsAfterPage = 2;
            int dynamicDisplayCount = itemsBeforePage + 1 + itemsAfterPage;

            Double numberOfPagesRequired = Convert.ToDouble(totalRecordsInTable / pageSize);
            if (totalRecordsInTable % pageSize != 0)
            {
                numberOfPagesRequired = numberOfPagesRequired + 1;
            }

            if (numberOfPagesRequired == 0)
            {
                numberOfPagesRequired = 1;
            }

            //Generate dynamic paging 
            int start;
            if (currentIndex <= (itemsBeforePage + 1))
            {
                start = 1;
            }
            else
            {
                start = currentIndex - itemsBeforePage;
            }

            int lastAddedLinkIndex = 0;
            int? firtsAddedLinkIndex = null;

            for (int i = start; i < start + dynamicDisplayCount; i++)
            {

                if (i > numberOfPagesRequired)
                {
                    break;
                }

                //Create dynamic Links 
                LinkButton lnk = new LinkButton();
                lnk.ID = LinkButtonIDPrefix + i.ToString(CultureInfo.InvariantCulture);
                lnk.Text = i.ToString(CultureInfo.InvariantCulture);
                lastAddedLinkIndex = i;

                if (firtsAddedLinkIndex == null)
                {
                    firtsAddedLinkIndex = i;
                }


                //Check whetehr current page
                if (i == currentIndex)
                {
                    lnk.CssClass = "page-numbers current";
                }
                else
                {
                    lnk.CssClass = "page-numbers";
                }

                pagingInfo.PaginationLinks.Add(lnk);
            }

            if (numberOfPagesRequired > dynamicDisplayCount)
            {
                //Set dots (ellipse) visibility
                pagingInfo.IsEndDotsVisible = lastAddedLinkIndex == numberOfPagesRequired ? false : true;
                pagingInfo.IsStartDotsVisible = firtsAddedLinkIndex <= 2 ? false : true;

                //First and Last Page Links
                pagingInfo.IsLastLinkVisible = lastAddedLinkIndex == numberOfPagesRequired ? false : true;
                pagingInfo.IsFirstLinkVisible = firtsAddedLinkIndex == 1 ? false : true;

            }

            pagingInfo.NumberOfPagesRequired = Convert.ToInt32(numberOfPagesRequired);

        }
        return pagingInfo;

    }
 }

ASPX Page

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="LijosTest.aspx.cs" Inherits="PagingTestWebApplication.LijosTest" %>

<%@ Register TagPrefix="CP" TagName="LijosPager" Src="~/PagingUserControl.ascx" %>

<!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></title>
 <style type="text/css">
    .page-numbers
    {
        border: 1px solid #CCC;
        color: #808185;
        display: block;
        float: left;
        font-size: 9pt;
        margin-right: 3px;
        padding: 4px 4px 3px;
        text-decoration: none;
    }

    .page-numbers.current
    {
        background-color: #808185;
        border: 1px solid #808185;
        color: white;
        font-weight: bold;
    }

    .page-numbers.next, .page-numbers.prev
    {
        border: 1px solid white;
        font-size: 12pt;
    }
 </style>
 </head>
 <body>
 <form id="form1" runat="server">
 <asp:TextBox ID="txtEmpName" runat="server"></asp:TextBox>
 <asp:Button ID="btnSearch" runat="server" Text="Button" ValidationGroup="Search"
    OnClick="btnSearch_Click" />
 <div>
    <asp:GridView ID="grdEmployee" runat="server">
    </asp:GridView>
 </div>
 <CP:LijosPager ID="uclPager" runat="server" />
 <asp:HiddenField ID="hdnCurrentIndex" runat="server" Value="Blank Value" />
 </form>
 </body>
 </html>

ASPX Code Behind

public partial class LijosTest : System.Web.UI.Page
{

    private int pageSize = 25;
    private int pageIndex = 1;

    protected void Page_Load(object sender, EventArgs e)
    {
        //Register event handler for user control event
        this.uclPager.PaginationLinkClicked += new EventHandler(paginationLink_Click);

        if (Page.IsPostBack)
        {
            //During all postbacks - Add the pagination links to the page
            int tableDataCount = DatabaseSimulator.GetEmployeesCount(txtEmpName.Text);

            string defaultInitialValueForHiddenControl = "Blank Value";
            int indexFromPreviousDataRetrieval = 1;
            if (!String.Equals(hdnCurrentIndex.Value, defaultInitialValueForHiddenControl))
            {
                indexFromPreviousDataRetrieval = Convert.ToInt32(hdnCurrentIndex.Value, CultureInfo.InvariantCulture);
            }

            //Set property of user control
            uclPager.PreviousIndex = indexFromPreviousDataRetrieval;

            //Call method in user control
            uclPager.PreAddAllLinks(tableDataCount, pageSize, indexFromPreviousDataRetrieval);
        }

    }

    protected void btnSearch_Click(object sender, EventArgs e)
    {
        //When Search is clicked, reset the index to 1 (first page)
        pageIndex = 1;
        BindBusinessProcessActivitiesData();
    }

    protected void paginationLink_Click(object sender, EventArgs e)
    {
        //When link is clicked, set the pageIndex from user control property
        pageIndex = uclPager.CurrentClickedIndex;
        BindBusinessProcessActivitiesData();
    }

    private void BindBusinessProcessActivitiesData()
    {

        string name = txtEmpName.Text; 
        List<Employee> searchResult = DatabaseSimulator.GetData(name, pageSize, pageIndex).ToList();

        grdEmployee.DataSource = searchResult;
        grdEmployee.DataBind();

        //Index
        hdnCurrentIndex.Value = pageIndex.ToString(CultureInfo.InvariantCulture);

        //Get total number of records
        int tableDataCount = DatabaseSimulator.GetEmployeesCount(name);

        uclPager.AddPageLinks(tableDataCount, pageSize, pageIndex);

    }


}

Database Part

public static class DatabaseSimulator
{

    public static IEnumerable<Employee> GetData(string name, int pageSize,int index)
    {
        IEnumerable<Employee> employeesSource = SearchEmployees(name);
        int skipUpto = ((index-1) * pageSize);

        IEnumerable<Employee> searchResult = employeesSource.Skip(skipUpto).Take(pageSize);
        return searchResult;

    }

    public static int GetEmployeesCount(string name)
    {
        List<Employee> employees = GetEmployees();
        int employeesCount = 0;

        if (String.IsNullOrEmpty(name))
        {
            employeesCount = employees.Count;
        }
        else
        {
            List<Employee> selectedEmployees = employees.Where(r => r.Name == name).ToList();
            employeesCount =  selectedEmployees.Count;
        }

        return employeesCount;
    }

    private static IEnumerable<Employee> SearchEmployees(string name)
    {
        List<Employee> employees = GetEmployees();
        if (String.IsNullOrEmpty(name))
        {
            return employees;
        }

        return employees.Where(r => r.Name == name);
    }

    private static List<Employee> GetEmployees()
    {
        List<Employee> employees = new List<Employee>();

        for (int i = 1; i <= 400; i++)
        {
            Employee emp = new Employee();
            emp.EmpID = i;
            if (i % 2 == 0)
            {
                emp.Name = "Divisible by 2";
            }
            else if  (i % 3 == 0)
            {
                emp.Name = "Divisible by 3";
            }
            else if (i % 5 == 0)
            {
                emp.Name = "Divisible by 5";
            }
            else if (i % 7 == 0)
            {
                emp.Name = "Divisible by 7";
            }
            else 
            {
                emp.Name = "Other -- "+ i.ToString();
            }

            employees.Add(emp);
        }

        return employees;
    }

}

public class Employee
{
    public int EmpID { get; set; }
    public string Name { get; set; }
}
\$\endgroup\$
1
  • 1
    \$\begingroup\$ @Knownasilya. Thanks. I have accepted possible answers. 75% acceptance now \$\endgroup\$
    – LCJ
    Commented Jan 21, 2013 at 15:06

1 Answer 1

1
\$\begingroup\$

I have come across a scenario that will fail: Suppose there is an image button control in the same page along with our button. Our code for pre-adding links will be called in all postbacks.

Assume that the value in textbox is supposed to be an integer. We have added a validation that will fire when button is clicked. But when image button is clicked this validation should not be fired [The image button is redirecting to another page. Hence no validation should be there].

In the above scenario, when image button is clicked (with a non-integer value in textbox), a postback will happen and it will cause an exception.

To handle this scnario, we need to know the control's ID that caused the postback. I hope the answer in https://stackoverflow.com/questions/3175513/on-postback-how-can-i-check-which-control-cause-postback-in-page-init-event will be helpful for this purpose.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.