Skip to content

Instantly share code, notes, and snippets.

@myaumyau
Last active October 12, 2015 00:37
Show Gist options
  • Save myaumyau/3944088 to your computer and use it in GitHub Desktop.
Save myaumyau/3944088 to your computer and use it in GitHub Desktop.
[C#]ASP.NET ページング用コントロール
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace WebControlLibrary
{
/// <summary>
/// ページング機能を提供します。
/// </summary>
[ToolboxData("<{0}:Pager runat=\"server\"></{0}:Pager>")]
public class Pager : WebControl, IPostBackEventHandler, INamingContainer
{
#region 定数
/// <summary>
/// IDのフォーマット文字列。
/// </summary>
private static readonly string IdFormat = "{0}_{1}";
#endregion
#region メンバ変数
/// <summary>
/// <see cref="Page.Command"/> イベントハンドラを保持します。
/// </summary>
private static readonly object EventCommand;
/// <summary>
/// 1ページあたりのデータ数を保持します。
/// </summary>
private int _pageSize;
/// <summary>
/// 現在表示されているのページの 1 から始まるインデックスを保持します。
/// </summary>
private int _currentPage;
/// <summary>
/// データの総合計数を保持します。
/// </summary>
private int _itemCount;
/// <summary>
/// 描画する開始ページ番号を保持します。
/// </summary>
private int _renderBeginPage;
/// <summary>
/// 描画する終了ページ番号を保持します。
/// </summary>
private int _renderEndPage;
#endregion
#region プロパティ
/// <summary>
/// 1ページあたりのデータ数を取得または設定します。
/// </summary>
[Browsable(true)]
[Category("Pager Behavior")]
[DefaultValue(10)]
[Description("1ページあたりのデータ数を取得または設定します。")]
public int PageSize
{
get { return this._pageSize; }
set { this._pageSize = value; }
}
/// <summary>
/// 現在表示されているのページの 1 から始まるページ番号を取得または設定します。
/// </summary>
[Browsable(false)]
public int CurrentPage
{
get { return this._currentPage; }
set { this._currentPage = value; }
}
/// <summary>
/// データの総合計数を取得または設定します。
/// </summary>
[Browsable(false)]
public int ItemCount
{
get { return this._itemCount; }
set { this._itemCount = value; }
}
/// <summary>
/// 総ページ数を取得します。
/// </summary>
[Browsable(false)]
public int PageCount
{
get { return (int)Math.Ceiling((double)this.ItemCount / this.PageSize); }
}
/// <summary>
/// 表示するリンクの最大数を取得または設定します。
/// </summary>
[Browsable(true)]
[Category("Pager Behavior")]
[DefaultValue(5)]
[Description("表示するリンクの最大数を取得または設定します。")]
public int MaxDisplayCount
{
get { return (int)(this.ViewState["MaxDisplayCount"] ?? 5); }
set { this.ViewState["MaxDisplayCount"] = value; }
}
/// <summary>
/// リンク押下時の動作をPostBackではなくURLによる制御とするかどうかを示す値を取得または設定します。
/// </summary>
[Browsable(true)]
[Category("Pager Behavior")]
[DefaultValue(false)]
[Description("リンク押下時の動作をPostBackではなくURLによる制御とするかどうかを示す値を取得または設定します。")]
public bool UseUrl
{
get { return (bool)(this.ViewState["UseUrl"] ?? false); }
set { this.ViewState["UseUrl"] = value; }
}
/// <summary>
/// リンクのURLのフォーマットを取得または設定します。
/// <see cref="Pager.UseUrl"/> プロパティが true の場合に使用されます。
/// </summary>
[Browsable(true)]
[Category("Pager Behavior")]
[DefaultValue("")]
[Description("リンクのURLのフォーマットを取得または設定します。")]
public string UrlFormat
{
get { return this.GetStringViewState("UrlFormat", ""); }
set { this.ViewState["UrlFormat"] = value; }
}
/// <summary>
/// タイトルを取得または設定します。
/// </summary>
[Browsable(true)]
[Category("Pager Appearance")]
[DefaultValue("Pages:")]
[Description("タイトルを取得または設定します。")]
public string Title
{
get { return this.GetStringViewState("Title", "Pages:", string.Empty); }
set { this.ViewState["Title"] = value; }
}
/// <summary>
/// リンクに適用するCSSクラス名を取得または設定します。
/// </summary>
[Browsable(true)]
[Category("Pager Appearance")]
[DefaultValue("pagerlink")]
[Description("リンクに適用するCSSクラス名を取得または設定します。")]
public string LinkCssClass
{
get { return this.GetStringViewState("LinkCssClass", "pagerlink"); }
set { this.ViewState["LinkCssClass"] = value; }
}
/// <summary>
/// 選択されたリンクに適用するCSSクラス名を取得または設定します。
/// </summary>
[Browsable(true)]
[Category("Pager Appearance")]
[DefaultValue("pagerlink_selected")]
[Description("選択されたリンクに適用するCSSクラス名を取得または設定します。")]
public string SelectedLinkCssClass
{
get { return this.GetStringViewState("SelectedLinkCssClass", "pagerlink_selected"); }
set { this.ViewState["SelectedLinkCssClass"] = value; }
}
/// <summary>
/// 最初と最後のページへ移動するリンクを表示するかどうか示す値を取得または設定します。
/// </summary>
[Browsable(true)]
[Category("Pager Appearance")]
[DefaultValue(true)]
[Description("最初と最後のページへ移動するリンクを表示するかどうか示す値を取得または設定します。")]
public bool ShowFirstAndLastLink
{
get { return (bool)(this.ViewState["ShowFirstAndLastLink"] ?? true); }
set { this.ViewState["ShowFirstAndLastLink"] = value; }
}
/// <summary>
/// 前と次のページへ移動するリンクを表示するかどうか示す値を取得または設定します。
/// </summary>
[Browsable(true)]
[Category("Pager Appearance")]
[DefaultValue(true)]
[Description("前と次のページへ移動するリンクを表示するかどうか示す値を取得または設定します。")]
public bool ShowPreviousAndNextLink
{
get { return (bool)(this.ViewState["ShowPreviousAndNextLink"] ?? true); }
set { this.ViewState["ShowPreviousAndNextLink"] = value; }
}
/// <summary>
/// 最初のページに移動するリンクの文字列を取得または設定します。
/// </summary>
[Browsable(true)]
[Category("Pager Appearance")]
[DefaultValue("First")]
[Description("最初のページに移動するリンクの文字列を取得または設定します。")]
public string FirstClause
{
get { return this.GetStringViewState("FirstClause", "First"); }
set { this.ViewState["FirstClause"] = value; }
}
/// <summary>
/// 最初のページに移動するリンクに適用するCSSクラス名を取得または設定します。
/// </summary>
[Browsable(true)]
[Category("Pager Appearance")]
[DefaultValue("pagerlink_first")]
[Description("最初のページに移動するリンクに適用するCSSクラス名を取得または設定します。")]
public string FirstLinkCssClass
{
get { return this.GetStringViewState("FirstLinkCssClass", "pagerlink_first"); }
set { this.ViewState["FirstLinkCssClass"] = value; }
}
/// <summary>
/// 前のページに移動するリンクの文字列を取得または設定します。
/// </summary>
[Browsable(true)]
[Category("Pager Appearance")]
[DefaultValue("Previous")]
[Description("前のページに移動するリンクの文字列を取得または設定します。")]
public string PreviousClause
{
get { return this.GetStringViewState("PreviousClause", "Previous"); }
set { this.ViewState["PreviousClause"] = value; }
}
/// <summary>
/// 前のページに移動するリンクに適用するCSSクラス名を取得または設定します。
/// </summary>
[Browsable(true)]
[Category("Pager Appearance")]
[DefaultValue("pagerlink_previous")]
[Description("前のページに移動するリンクに適用するCSSクラス名を取得または設定します。")]
public string PreviousLinkCssClass
{
get { return this.GetStringViewState("PreviousLinkCssClass", "pagerlink_previous"); }
set { this.ViewState["PreviousLinkCssClass"] = value; }
}
/// <summary>
/// 次のページに移動するリンクの文字列を取得または設定します。
/// </summary>
[Browsable(true)]
[Category("Pager Appearance")]
[DefaultValue("Next")]
[Description("次のページに移動するリンクの文字列を取得または設定します。")]
public string NextClause
{
get { return this.GetStringViewState("NextClause", "Next", "Next"); }
set { this.ViewState["NextClause"] = value; }
}
/// <summary>
/// 次のページに移動するリンクに適用するCSSクラス名を取得または設定します。
/// </summary>
[Browsable(true)]
[Category("Pager Appearance")]
[DefaultValue("pagerlink_next")]
[Description("次のページに移動するリンクに適用するCSSクラス名を取得または設定します。")]
public string NextLinkCssClass
{
get { return this.GetStringViewState("NextLinkCssClass", "pagerlink_next"); }
set { this.ViewState["NextLinkCssClass"] = value; }
}
/// <summary>
/// 最後のページに移動するリンクの文字列を取得または設定します。
/// </summary>
[Browsable(true)]
[Category("Pager Appearance")]
[DefaultValue("Last")]
[Description("最後のページに移動するリンクの文字列を取得または設定します。")]
public string LastClause
{
get { return this.GetStringViewState("LastClause", "Last", "Last"); }
set { this.ViewState["LastClause"] = value; }
}
/// <summary>
/// 最後のページに移動するリンクに適用するCSSクラス名を取得または設定します。
/// </summary>
[Browsable(true)]
[Category("Pager Appearance")]
[DefaultValue("pagerlink_last")]
[Description("最後のページに移動するリンクに適用するCSSクラス名を取得または設定します。")]
public string LastLinkCssClass
{
get { return this.GetStringViewState("LastLinkCssClass", "pagerlink_last"); }
set { this.ViewState["LastLinkCssClass"] = value; }
}
#endregion
#region イベント
/// <summary>
/// Pagerのリンクがクリックされたときに発生します。
/// </summary>
public event CommandEventHandler Command
{
add { this.Events.AddHandler(EventCommand, value); }
remove { this.Events.RemoveHandler(EventCommand, value); }
}
#endregion
#region コンストラクタ
/// <summary>
/// Pager クラスを初期化します。
/// </summary>
static Pager()
{
EventCommand = new object();
}
/// <summary>
/// Pager クラスの新しいインスタンスを初期化します。
/// </summary>
public Pager()
: base()
{
this.PageSize = 10;
this.CurrentPage = 1;
this.CssClass = "pager";
}
#endregion
#region IPostBackEventHandler メンバ
/// <summary>
/// フォームがサーバーにポストされたときに発生するイベントを
/// サーバー コントロールで処理できるようにします。
/// </summary>
/// <param name="eventArgument">
/// イベント ハンドラに渡される省略可能なイベント引数を表す <see cref="System.String"/>。
/// </param>
public void RaisePostBackEvent(string eventArgument)
{
this.OnCommand(new CommandEventArgs(this.UniqueID, eventArgument));
}
/// <summary>
/// フォームがサーバーにポストされたときに発生するイベントを
/// サーバー コントロールで処理できるようにします。
/// </summary>
/// <param name="eventArgument">
/// イベント ハンドラに渡される省略可能なイベント引数を表す <see cref="System.String"/>。
/// </param>
void IPostBackEventHandler.RaisePostBackEvent(string eventArgument)
{
this.RaisePostBackEvent(eventArgument);
}
#endregion
#region protected override メソッド
/// <summary>
/// <see cref="System.Web.UI.Control.Init"/> イベントを発生させます。
/// </summary>
/// <param name="e">
/// イベント データを格納している <see cref="System.EventArgs"/> オブジェクト。
/// </param>
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
// 常にViewStateに保存する状態情報が存在する場合は下記を実行し、
// SaveControlState, LoadControlStateメソッドを実装する。
Page.RegisterRequiresControlState(this);
}
/// <summary>
/// ページがサーバーにポスト バックされた時間以降に発生した、
/// サーバー コントロールの状態の変更を保存します。
/// </summary>
/// <returns>
/// サーバー コントロールの現在の状態を返します。
/// コントロールに関連付けられている状態がない場合、このメソッドは null を返します。
/// </returns>
protected override object SaveControlState()
{
////return base.SaveControlState();
object[] state = new object[2];
state[0] = this.PageSize;
state[1] = this.CurrentPage;
return state;
}
/// <summary>
/// System.Web.UI.Control.SaveControlState() メソッドによって
/// 保存された前回のページ要求からコントロールの状態情報を復元します。
/// </summary>
/// <param name="savedState">
/// 復元するコントロールの状態を表す <see cref="System.Object"/>。
/// </param>
protected override void LoadControlState(object savedState)
{
////base.LoadControlState(savedState);
object[] state = (object[])savedState;
this.PageSize = (int)state[0];
this.CurrentPage = (int)state[1];
}
/// <summary>
/// 指定された HTML ライタにコントロールを表示します。
/// </summary>
/// <param name="writer">
/// コントロールの内容を受け取る <see cref="HtmlTextWriter"/> オブジェクト。
/// </param>
protected override void Render(HtmlTextWriter writer)
{
base.Render(writer);
if (this.Page == null) Page.VerifyRenderingInServerForm(this);
// ページングなし
if (this.PageCount < 2) return;
//// <div class="pager">
//// <span>Pages:</span>
//// <a class="pagerlink_first" href="">First</a>
//// <a class="pagerlink_prev" href="">Previous</a>
//// <a class="pagerlink" href=""></a>
//// <a class="pagerlink" href=""></a>
//// <span class="pagerlink_selected"></span>
//// <a class="pagerlink" href=""></a>
//// <a class="pagerlink" href=""></a>
//// <a class="pagerlink_next" href="">Next</a>
//// <a class="pagerlink_last" href="">Last</a>
//// </div>
this.CalculateRenderPage();
this.RenderPagerBeginTag(writer);
this.RenderTitle(writer);
this.RenderFirstLink(writer);
this.RenderPreviousLink(writer);
int displayIdx = 0;
for (int pageNum = this._renderBeginPage; pageNum <= this._renderEndPage; pageNum++)
{
if (pageNum == this.CurrentPage)
{
this.RenderCurrent(writer, displayIdx);
}
else
{
this.RenderLink(writer, pageNum, displayIdx);
}
displayIdx++;
}
this.RenderNextLink(writer);
this.RenderLastLink(writer);
this.RenderPagerEndTag(writer);
}
#endregion
#region protected virtual メソッド
/// <summary>
/// <see cref="Pager.Command"/> イベントを発生させます。
/// </summary>
/// <param name="e">
/// イベント データを格納している <see cref="System.CommandEventArgs"/> オブジェクト。
/// </param>
protected virtual void OnCommand(CommandEventArgs e)
{
CommandEventHandler handler = this.Events[EventCommand] as CommandEventHandler;
if (handler == null) return;
handler(this, e);
}
#endregion
#region private メソッド
/// <summary>
/// <see cref="string"/> を格納するViewStateの値を取得します。
/// </summary>
/// <param name="key">キー。</param>
/// <param name="nullValue">nullの場合に返す値。</param>
/// <returns>格納された値。</returns>
private string GetStringViewState(string key, string nullValue)
{
return this.GetStringViewState(key, nullValue, nullValue);
}
/// <summary>
/// <see cref="string"/> を格納するViewStateの値を取得します。
/// </summary>
/// <param name="key">キー。</param>
/// <param name="nullValue">nullの場合に返す値。</param>
/// <param name="emptyValue">空の文字列の場合に返す値。</param>
/// <returns>格納された値。</returns>
private string GetStringViewState(string key, string nullValue, string emptyValue)
{
string val = (string)this.ViewState[key];
if (!string.IsNullOrEmpty(val)) return val;
return (val == null) ? nullValue : emptyValue;
}
/// <summary>
/// 書き出すページの範囲を計算します。
/// </summary>
private void CalculateRenderPage()
{
int maxPage = this.PageCount;
int displaMaxPage = this.MaxDisplayCount;
this._renderBeginPage = 1;
this._renderEndPage = maxPage;
if (this.PageCount <= displaMaxPage) return;
int halfMaxDisplayCount = displaMaxPage / 2;
int adjuster = displaMaxPage % 2 == 0 ? -1 : 0;
// 開始ページを暫定算出
if (this.CurrentPage > 2)
{
this._renderBeginPage = this.CurrentPage - halfMaxDisplayCount;
if (this._renderBeginPage < 1)
{
this._renderBeginPage = 1;
}
}
// 終了ページを暫定算出
this._renderEndPage = this.CurrentPage + halfMaxDisplayCount + adjuster;
// 開始ページが最小ページの場合は終了ページを補正
if (this._renderBeginPage == 1)
{
this._renderEndPage = displaMaxPage;
}
// 終了ページが最大ページを超える場合は補正
if (this._renderEndPage > maxPage)
{
this._renderEndPage = maxPage;
}
// 終了ページが最大ページと透過の場合は開始ページを補正
if (this._renderEndPage == maxPage)
{
this._renderBeginPage = this._renderEndPage - displaMaxPage + 1;
}
// 開始ページが範囲外の場合は補正
if (this._renderBeginPage < 1)
{
this._renderBeginPage = 1;
}
}
/// <summary>
/// Pager要素の開始タグを描画します。
/// </summary>
/// <param name="writer">
/// コントロールの内容を受け取る <see cref="HtmlTextWriter"/> オブジェクト。
/// </param>
private void RenderPagerBeginTag(HtmlTextWriter writer)
{
writer.AddAttribute(HtmlTextWriterAttribute.Id, this.ClientID);
if (!string.IsNullOrEmpty(this.CssClass))
{
writer.AddAttribute(HtmlTextWriterAttribute.Class, this.CssClass);
}
writer.RenderBeginTag(HtmlTextWriterTag.Div);
}
/// <summary>
/// Pager要素の終了タグを描画します。
/// </summary>
/// <param name="writer">
/// コントロールの内容を受け取る <see cref="HtmlTextWriter"/> オブジェクト。
/// </param>
private void RenderPagerEndTag(HtmlTextWriter writer)
{
writer.RenderEndTag();
}
/// <summary>
/// タイトルを描画します。
/// </summary>
/// <param name="writer">
/// コントロールの内容を受け取る <see cref="HtmlTextWriter"/> オブジェクト。
/// </param>
private void RenderTitle(HtmlTextWriter writer)
{
if (string.IsNullOrEmpty(this.Title)) return;
this.RenderSpanCore(writer, "title", this.Title, null);
}
/// <summary>
/// 最初のページに移動するリンクを描画します。
/// </summary>
/// <param name="writer">
/// コントロールの内容を受け取る <see cref="HtmlTextWriter"/> オブジェクト。
/// </param>
private void RenderFirstLink(HtmlTextWriter writer)
{
if (!this.ShowFirstAndLastLink) return;
if (this.CurrentPage == 1)
{
this.RenderSpanCore(writer, "first", this.FirstClause, this.FirstLinkCssClass);
}
else
{
this.RenderLinkCore(writer, 1, "first", this.FirstClause, this.FirstLinkCssClass);
}
}
/// <summary>
/// 最後のページに移動するリンクを描画します。
/// </summary>
/// <param name="writer">
/// コントロールの内容を受け取る <see cref="HtmlTextWriter"/> オブジェクト。
/// </param>
private void RenderLastLink(HtmlTextWriter writer)
{
if (!this.ShowFirstAndLastLink) return;
if (this.CurrentPage == this.PageCount)
{
this.RenderSpanCore(writer, "last", this.LastClause, this.LastLinkCssClass);
}
else
{
this.RenderLinkCore(
writer, this.PageCount, "last", this.LastClause, this.LastLinkCssClass);
}
}
/// <summary>
/// 前のページに移動するリンクを描画します。
/// </summary>
/// <param name="writer">
/// コントロールの内容を受け取る <see cref="HtmlTextWriter"/> オブジェクト。
/// </param>
private void RenderPreviousLink(HtmlTextWriter writer)
{
if (!this.ShowPreviousAndNextLink) return;
if (this.CurrentPage == 1)
{
this.RenderSpanCore(writer, "previous", this.PreviousClause, this.PreviousLinkCssClass);
}
else
{
this.RenderLinkCore(
writer, this.CurrentPage - 1, "previous", this.PreviousClause, this.PreviousLinkCssClass);
}
}
/// <summary>
/// 次のページに移動するリンクを描画します。
/// </summary>
/// <param name="writer">
/// コントロールの内容を受け取る <see cref="HtmlTextWriter"/> オブジェクト。
/// </param>
private void RenderNextLink(HtmlTextWriter writer)
{
if (!this.ShowPreviousAndNextLink) return;
if (this.CurrentPage == this.PageCount)
{
this.RenderSpanCore(writer, "next", this.NextClause, this.NextLinkCssClass);
}
else
{
this.RenderLinkCore(
writer, this.CurrentPage + 1, "next", this.NextClause, this.NextLinkCssClass);
}
}
/// <summary>
/// 現在のページを描画します。
/// </summary>
/// <param name="writer">
/// コントロールの内容を受け取る <see cref="HtmlTextWriter"/> オブジェクト。
/// </param>
/// <param name="displayIndex">表示時のインデックス。</param>
private void RenderCurrent(HtmlTextWriter writer, int displayIndex)
{
this.RenderSpanCore(
writer, "selected", this.CurrentPage.ToString(), this.SelectedLinkCssClass);
}
/// <summary>
/// 表示可能なリンクを描画します。
/// </summary>
/// <param name="writer">
/// コントロールの内容を受け取る <see cref="HtmlTextWriter"/> オブジェクト。
/// </param>
/// <param name="pageNum">ページ番号。</param>
/// <param name="displayIndex">表示時のインデックス。</param>
private void RenderLink(HtmlTextWriter writer, int pageNum, int displayIndex)
{
this.RenderLinkCore(
writer, pageNum, displayIndex.ToString(), pageNum.ToString(), this.LinkCssClass);
}
/// <summary>
/// リンク要素を描画します。
/// </summary>
/// <param name="writer">
/// コントロールの内容を受け取る <see cref="HtmlTextWriter"/> オブジェクト。
/// </param>
/// <param name="pageNum">ページ番号。</param>
/// <param name="idParam">IDのフォーマットの第2パラメータ。</param>
/// <param name="text">表示名。</param>
/// <param name="cssClass">適用するCSSクラス名。</param>
private void RenderLinkCore(
HtmlTextWriter writer,
int pageNum,
string idParam,
string text,
string cssClass)
{
if (!string.IsNullOrEmpty(idParam))
{
string id = string.Format(IdFormat, this.ClientID, idParam);
writer.AddAttribute(HtmlTextWriterAttribute.Id, id);
}
if (!string.IsNullOrEmpty(cssClass))
{
writer.AddAttribute(HtmlTextWriterAttribute.Class, cssClass);
}
string url = null;
if (this.UseUrl)
{
url = string.Format(this.UrlFormat, pageNum);
}
else
{
url = Page.ClientScript.GetPostBackClientHyperlink(this, pageNum.ToString());
}
writer.AddAttribute(HtmlTextWriterAttribute.Href, url);
writer.RenderBeginTag(HtmlTextWriterTag.A);
writer.Write(text);
writer.RenderEndTag();
writer.WriteLine();
}
/// <summary>
/// SPAN要素を描画します。
/// </summary>
/// <param name="writer">
/// コントロールの内容を受け取る <see cref="HtmlTextWriter"/> オブジェクト。
/// </param>
/// <param name="idParam">IDのフォーマットの第2パラメータ。</param>
/// <param name="text">InnerHTML。</param>
/// <param name="cssClass">適用するCSSクラス名。</param>
private void RenderSpanCore(
HtmlTextWriter writer,
string idParam,
string text,
string cssClass)
{
if (!string.IsNullOrEmpty(idParam))
{
string id = string.Format(IdFormat, this.ClientID, idParam);
writer.AddAttribute(HtmlTextWriterAttribute.Id, id);
}
if (!string.IsNullOrEmpty(cssClass))
{
writer.AddAttribute(HtmlTextWriterAttribute.Class, cssClass);
}
writer.RenderBeginTag(HtmlTextWriterTag.Span);
writer.Write(text);
writer.RenderEndTag();
}
#endregion
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment