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; }
}
asked Jan 14, 2013 at 14:30
\$\endgroup\$
1
  • 1
    \$\begingroup\$ @Knownasilya. Thanks. I have accepted possible answers. 75% acceptance now \$\endgroup\$ 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.

answered Jan 23, 2013 at 17:18
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.