I created my own grid with a load of customizations; it inherits from GridView. It renders its own pager below and/or above the grid. This is done by overriding the Render(HtmlTextWriter writer) method of your grid and doing something like this:
// Render our custom pager if we are configured to use it.
if(CustomPagerConfig.Enable && (CustomPagerConfig.Location == VerticalLocation.Top || CustomPagerConfig.Location == VerticalLocation.Both))
RenderCustomPager(writer);
...
base.Render(writer);
...
// Render our custom pager if we are configured to use it.
if(CustomPagerConfig.Enable && (CustomPagerConfig.Location == VerticalLocation.Bottom || CustomPagerConfig.Location == VerticalLocation.Both))
RenderCustomPager(writer);
The call to RenderCustomPager() makes use of an object that holds configuration and style settings. This object is an inner property of my custom grid (i.e. PersistenceMode.InnerProperty) and is defined as such:
public class CustomPagerSettings
{
#region Members:
private string _linkDelimiter = " ";
private string _ellipses = "...";
private int _maxLinks = 15;
private int _ellipsesThreshold = 2;
private string _currentPageCssClass = string.Empty;
private string _alternatePageCssClass = string.Empty;
#endregion
#region Properties:
///
///
///
[Browsable(true)]
[DefaultValue(false)]
public bool Enable { get; set; }
///
///
///
[Browsable(true)]
[DefaultValue(VerticalLocation.Bottom)]
public VerticalLocation Location { get; set; }
///
///
///
[Browsable(true)]
[DefaultValue(15)]
public int MaxLinks { get { return _maxLinks; } set { _maxLinks = value; } }
///
///
///
[Browsable(true)]
[DefaultValue(2)]
public int EllipsesThreshold { get { return _ellipsesThreshold; } set { _ellipsesThreshold = value; } }
///
///
///
[Browsable(true)]
[DefaultValue(" ")]
public string LinkDelimiter { get { return _linkDelimiter; } set { _linkDelimiter = value; } }
///
///
///
[Browsable(true)]
[DefaultValue("...")]
public string Ellipses { get { return _ellipses; } set { _ellipses = value; } }
///
///
///
[Browsable(true)]
[PersistenceMode(PersistenceMode.InnerProperty)]
public Style PagerStyle { get; set; }
///
///
///
[Browsable(true)]
[DefaultValue("")]
public string CurrentPageCssClass { get { return _currentPageCssClass; } set { _currentPageCssClass = value; } }
///
///
///
[Browsable(true)]
[DefaultValue("")]
public string AlternatePageCssClass { get { return _alternatePageCssClass; } set { _alternatePageCssClass = value; } }
#endregion
} // End class.
The page that uses/hosts the grid contains Markup as such to instantiate this configuration object:
<CustomPagerConfig Enable="true"
Location="Bottom"
MaxLinks="10"
Ellipses="..."
EllipsesThreshold="2"
CurrentPageCssClass="pagerCurrentPage"
AlternatePageCssClass="pagerAlternatePage" >
<PagerStyle
BackColor="#eeeeee"
ForeColor="#4444cc"
BorderColor="#999999"
BorderStyle="Solid"
BorderWidth="1px"
Font-Size="10pt"
Font-Names="Verdana" />
</CustomPagerConfig>
Then the code that uses this configuration information to actually render a custom pager is shown below:
private void RenderCustomPager(HtmlTextWriter writer)
{
// If we don't have multiple pages, then don't render thte pager.
if(PageCount <= 1)
return;
if(CustomPagerConfig.PagerStyle != null)
{
if(CustomPagerConfig.PagerStyle.Width != null && !CustomPagerConfig.PagerStyle.Width.IsEmpty)
writer.AddStyleAttribute(HtmlTextWriterStyle.Width, CustomPagerConfig.PagerStyle.Width.ToString());
else
writer.AddStyleAttribute(HtmlTextWriterStyle.Width, FrameStyle.Width.ToString());
if(CustomPagerConfig.PagerStyle.Height != null)
writer.AddStyleAttribute(HtmlTextWriterStyle.Height, CustomPagerConfig.PagerStyle.Height.ToString());
if(CustomPagerConfig.PagerStyle.BorderStyle != BorderStyle.NotSet)
writer.AddStyleAttribute(HtmlTextWriterStyle.BorderStyle, CustomPagerConfig.PagerStyle.BorderStyle.ToString());
if(CustomPagerConfig.PagerStyle.BorderWidth != null)
writer.AddStyleAttribute(HtmlTextWriterStyle.BorderWidth, CustomPagerConfig.PagerStyle.BorderWidth.ToString());
if(CustomPagerConfig.PagerStyle.BorderColor != null && !CustomPagerConfig.PagerStyle.BorderColor.IsEmpty)
writer.AddStyleAttribute(HtmlTextWriterStyle.BorderColor, CustomPagerConfig.PagerStyle.BorderColor.IsKnownColor ? CustomPagerConfig.PagerStyle.BorderColor.Name : ("#" + CustomPagerConfig.PagerStyle.BorderColor.Name.Substring(2)));
if(CustomPagerConfig.PagerStyle.BackColor != null && !CustomPagerConfig.PagerStyle.BackColor.IsEmpty)
writer.AddStyleAttribute(HtmlTextWriterStyle.BackgroundColor, CustomPagerConfig.PagerStyle.BackColor.IsKnownColor ? CustomPagerConfig.PagerStyle.BackColor.Name : ("#" + CustomPagerConfig.PagerStyle.BackColor.Name.Substring(2)));
if(CustomPagerConfig.PagerStyle.ForeColor != null && !CustomPagerConfig.PagerStyle.ForeColor.IsEmpty)
writer.AddStyleAttribute(HtmlTextWriterStyle.Color, CustomPagerConfig.PagerStyle.ForeColor.IsKnownColor ? CustomPagerConfig.PagerStyle.ForeColor.Name : ("#" + CustomPagerConfig.PagerStyle.ForeColor.Name.Substring(2)));
if(CustomPagerConfig.PagerStyle.Font != null && CustomPagerConfig.PagerStyle.Font.Name != null)
writer.AddStyleAttribute(HtmlTextWriterStyle.FontFamily, CustomPagerConfig.PagerStyle.Font.Name);
if(CustomPagerConfig.PagerStyle.Font != null && CustomPagerConfig.PagerStyle.Font.Size != null)
writer.AddStyleAttribute(HtmlTextWriterStyle.FontSize, CustomPagerConfig.PagerStyle.Font.Size.ToString());
if(CustomPagerConfig.PagerStyle.CssClass != null)
writer.AddAttribute("class", CustomPagerConfig.PagerStyle.CssClass);
}
// These are variables that are used to determine if ellipses and links are displayed for NEXT and PREV.
int totalPages = PageCount;
int maxPages = CustomPagerConfig.MaxLinks;
int currentPageIndex = PageIndex;
int firstDisplayedPageIndex = _firstDisplayedPageIndex;
int lastDisplayedPageIndex = (_firstDisplayedPageIndex + maxPages - 1) >= totalPages ? (totalPages - 1) : (_firstDisplayedPageIndex + maxPages - 1);
// Determine if various links should be rendered...
bool renderNext = false;
bool renderPrev = false;
bool renderEllipsesNext = false;
bool renderEllipsesPrev = false;
// Determine if NextLink should be rendered.
if(lastDisplayedPageIndex != (totalPages - 1))
{
renderNext = true;
renderEllipsesNext = true;
}
else if(lastDisplayedPageIndex == (totalPages - 1) && lastDisplayedPageIndex == currentPageIndex)
{
renderNext = false;
renderEllipsesNext = false;
}
else if(lastDisplayedPageIndex == (totalPages - 1) && lastDisplayedPageIndex != currentPageIndex)
{
renderNext = true;
renderEllipsesNext = false;
}
// Determine if PrevLink should be rendered.
if(firstDisplayedPageIndex > 0)
{
renderPrev = true;
renderEllipsesPrev = true;
}
else if(firstDisplayedPageIndex == 0 && currentPageIndex != 0)
{
renderPrev = true;
renderEllipsesPrev = false;
}
else if(firstDisplayedPageIndex == 0 && currentPageIndex == 0)
{
renderPrev = false;
renderEllipsesPrev = false;
}
writer.RenderBeginTag(HtmlTextWriterTag.Table);
writer.RenderBeginTag(HtmlTextWriterTag.Tr);
writer.AddStyleAttribute("text-align", "left");
writer.AddStyleAttribute("width", "10%");
writer.RenderBeginTag(HtmlTextWriterTag.Td);
if(renderPrev)
{
int linkTo = PageIndex - 1;
// linkTo is an index, we must write the link with the page number.
linkTo++;
HyperLink link = new HyperLink();
link.Text = "Previous";
link.NavigateUrl = string.Format("javascript:__doPostBack('{0}','Page${1}')", this.ClientID.Replace('_', '$'), linkTo.ToString());
if(!string.IsNullOrEmpty(CustomPagerConfig.AlternatePageCssClass))
link.CssClass = CustomPagerConfig.AlternatePageCssClass;
link.ToolTip = string.Format("Go to page {0}", linkTo.ToString());
link.RenderControl(writer);
}
writer.RenderEndTag(); // End the first Cell/Td.
writer.AddStyleAttribute("text-align", "center");
writer.RenderBeginTag(HtmlTextWriterTag.Td);
RenderPagerPageLinks(writer, renderEllipsesPrev, renderEllipsesNext);
writer.RenderEndTag(); // End the second Cell/Td.
writer.AddStyleAttribute("text-align", "right");
writer.AddStyleAttribute("width", "10%");
writer.RenderBeginTag(HtmlTextWriterTag.Td);
if(renderNext)
{
int linkTo = PageIndex + 1;
// linkTo is an index, we must write the link with the page number.
linkTo++;
HyperLink link = new HyperLink();
link.Text = "Next";
link.NavigateUrl = string.Format("javascript:__doPostBack('{0}','Page${1}')", this.ClientID.Replace('_', '$'), linkTo.ToString());
if(!string.IsNullOrEmpty(CustomPagerConfig.AlternatePageCssClass))
link.CssClass = CustomPagerConfig.AlternatePageCssClass;
link.ToolTip = string.Format("Go to page {0}", linkTo.ToString());
link.RenderControl(writer);
}
writer.RenderEndTag(); // End the third Cell/Td.
writer.RenderEndTag(); // End row.
writer.RenderEndTag(); // End table.
}
private void RenderPagerPageLinks(HtmlTextWriter writer, bool renderPrevEllipse, bool renderNextEllipse)
{
if(renderPrevEllipse)
{
HyperLink link = new HyperLink();
link.Text = (1).ToString();
link.NavigateUrl = string.Format("javascript:__doPostBack('{0}','Page${1}')", this.ClientID.Replace('_', '$'), link.Text);
if(!string.IsNullOrEmpty(CustomPagerConfig.AlternatePageCssClass))
link.CssClass = CustomPagerConfig.AlternatePageCssClass;
link.ToolTip = string.Format("Go to page {0}", link.Text);
link.RenderControl(writer);
writer.Write(CustomPagerConfig.LinkDelimiter);
int linkTo = PageIndex - CustomPagerConfig.MaxLinks;
if(linkTo < 0)
linkTo = 0;
// linkTo is an index, we must write the link with the page number.
linkTo++;
link = new HyperLink();
link.Text = CustomPagerConfig.Ellipses;
link.NavigateUrl = string.Format("javascript:__doPostBack('{0}','Page${1}')", this.ClientID.Replace('_', '$'), linkTo.ToString());
if(!string.IsNullOrEmpty(CustomPagerConfig.AlternatePageCssClass))
link.CssClass = CustomPagerConfig.AlternatePageCssClass;
link.ToolTip = string.Format("Go to page {0}", linkTo.ToString());
link.RenderControl(writer);
writer.Write(CustomPagerConfig.LinkDelimiter);
}
int pageLinksToRender = Math.Min(CustomPagerConfig.MaxLinks, PageCount);
for(int pageIndex = _firstDisplayedPageIndex; pageIndex < (_firstDisplayedPageIndex + pageLinksToRender); pageIndex++)
{
// Current index should be rendered NOT as a link. And can have its own style.
if(pageIndex == this.PageIndex)
{
if(!string.IsNullOrEmpty(CustomPagerConfig.CurrentPageCssClass))
writer.AddAttribute("class", CustomPagerConfig.CurrentPageCssClass);
writer.RenderBeginTag(HtmlTextWriterTag.Span);
writer.Write((pageIndex + 1).ToString());
writer.RenderEndTag();
// If this is not the last link to render, then output a delimiter after it.
writer.Write(CustomPagerConfig.LinkDelimiter);
}
else
{
HyperLink link = new HyperLink();
link.Text = (pageIndex + 1).ToString();
link.NavigateUrl = string.Format("javascript:__doPostBack('{0}','Page${1}')", this.ClientID.Replace('_', '$'), link.Text);
if(!string.IsNullOrEmpty(CustomPagerConfig.AlternatePageCssClass))
link.CssClass = CustomPagerConfig.AlternatePageCssClass;
link.ToolTip = string.Format("Go to page {0}", link.Text);
link.RenderControl(writer);
// If this is not the last link to render, then output a delimiter after it.
writer.Write(CustomPagerConfig.LinkDelimiter);
}
}
if(renderNextEllipse)
{
int linkTo = PageIndex + CustomPagerConfig.MaxLinks;
if(linkTo > PageCount - 1)
linkTo = PageCount - 1;
// linkTo is an index, we must write the link with the page number.
linkTo++;
HyperLink link = new HyperLink();
link.Text = CustomPagerConfig.Ellipses;
link.NavigateUrl = string.Format("javascript:__doPostBack('{0}','Page${1}')", this.ClientID.Replace('_', '$'), linkTo.ToString());
if(!string.IsNullOrEmpty(CustomPagerConfig.AlternatePageCssClass))
link.CssClass = CustomPagerConfig.AlternatePageCssClass;
link.ToolTip = string.Format("Go to page {0}", linkTo.ToString());
link.RenderControl(writer);
writer.Write(CustomPagerConfig.LinkDelimiter);
link = new HyperLink();
link.Text = (PageCount).ToString();
link.NavigateUrl = string.Format("javascript:__doPostBack('{0}','Page${1}')", this.ClientID.Replace('_', '$'), link.Text);
if(!string.IsNullOrEmpty(CustomPagerConfig.AlternatePageCssClass))
link.CssClass = CustomPagerConfig.AlternatePageCssClass;
link.ToolTip = string.Format("Go to page {0}", link.Text);
link.RenderControl(writer);
}
}
Screenshot 1:
Screenshot 2 (configured to show a limited number of page links):
4c9519a4-33d2-4e5d-ae5b-864954fc41ae|0|.0