WinForm輕鬆實現自定義分頁
以前都是做web開發,最近接觸了下WinForm,發現WinForm分頁控制元件好像都沒有,網上搜索了一下,發現有很多網友寫的分頁控制元件,分頁效果應該都能實現吧,只是其風格都不是很符合我想要的。做web的時候,我習慣了Extjs的Grid分頁效果,所以也想在WinForm中做個類似的效果,所以咬咬牙,做個山寨版本的吧,雖然自己寫費時費力,在專案進度考慮中不是很可取,但是還是特別想山寨一回,做自己喜歡的風格。
按照慣例,還是先看看實現效果圖吧(有圖有真像,才好繼續下文呀)
應用效果:(效果有點難看,因為我是剛裝的
xp系統,還是經典主題,如果換成Win7系統或其他主題,效果還是會很不錯的)
我們要做的就是上圖顯示的一個自定義控制元件,這個效果參考自我做
web開發使用的Extjs之Grid的分頁效果(如下圖)
Extjs的動畫效果我們暫時就不實現了,這裡只做個外觀看起來想像即可,完全一樣就脫離“山寨”概念了,總要比人家差點吧,誰讓咱是模仿呢!
言歸正傳,我們現在就看看具體怎麼實現吧:
第一步:先佈局
注:我們建立的是使用者自定義控制元件,而不是WinForm窗體
就是先做出個顯示效果,這個佈局很簡單,在這就不多說,重點就是“首頁、前一頁、後一頁、末頁”圖示,每個圖示分兩種,一是能點選的高亮效果,一個是灰色不不能點選。以下是套圖:(大家如果不喜歡,可以去做成自己喜歡的風格圖片)
第二步:編寫分頁程式碼
佈局好了,那麼第二步我們就要程式碼實現正確顯示文字資訊,分頁事件,每頁條數選擇事件,公開屬性和事件。以下是完整程式碼:
/// <summary> /// 宣告委託 /// </summary> /// <param name="e"></param> public delegate void EventPagingHandler(EventArgs e); public partial class Paging : UserControl { public Paging() { InitializeComponent(); } public event EventPagingHandler EventPaging; #region 公開屬性 private int _pageSize = 50; /// <summary> /// 每頁顯示記錄數(預設50) /// </summary> public int PageSize { get { return _pageSize; } set { if (value > 0) { _pageSize = value; } else { _pageSize = 50; } this.comboPageSize.Text = _pageSize.ToString(); } } private int _currentPage = 1; /// <summary> /// 當前頁 /// </summary> public int CurrentPage { get { return _currentPage; } set { if (value > 0) { _currentPage = value; } else { _currentPage = 1; } } } private int _totalCount = 0; /// <summary> /// 總記錄數 /// </summary> public int TotalCount { get { return _totalCount; } set { if (value>=0) { _totalCount = value; } else { _totalCount = 0; } this.lblTotalCount.Text = this._totalCount.ToString(); CalculatePageCount(); this.lblRecordRegion.Text = GetRecordRegion(); } } private int _pageCount = 0; /// <summary> /// 頁數 /// </summary> public int PageCount { get { return _pageCount; } set { if (value>=0) { _pageCount = value; } else { _pageCount = 0; } this.lblPageCount.Text = _pageCount + ""; } } #endregion /// <summary> /// 計算頁數 /// </summary> private void CalculatePageCount() { if (this.TotalCount>0) { this.PageCount = Convert.ToInt32(Math.Ceiling(Convert.ToDouble(this.TotalCount) / Convert.ToDouble(this.PageSize))); } else { this.PageCount = 0; } } /// <summary> /// 獲取顯示記錄區間(格式如:1-50) /// </summary> /// <returns></returns> private string GetRecordRegion() { if (this.PageCount == 1) //只有一頁 { return "1-" + this.TotalCount.ToString(); } else //有多頁 { if(this.CurrentPage==1) //當前顯示為第一頁 { return "1-"+this.PageSize; } else if(this.CurrentPage==this.PageCount) //當前顯示為最後一頁 { return ((this.CurrentPage-1)*this.PageSize+1) +"-"+this.TotalCount; } else //中間頁 { return ((this.CurrentPage-1) * this.PageSize+1) + "-" + this.CurrentPage * this.PageSize; } } } /// <summary> /// 資料繫結 /// </summary> public void Bind() { if (this.EventPaging != null) { this.EventPaging(new EventArgs()); } if (this.CurrentPage>this.PageCount) { this.CurrentPage = this.PageCount; } this.txtBoxCurPage.Text = this.CurrentPage+""; this.lblTotalCount.Text = this.TotalCount+""; this.lblPageCount.Text = this.PageCount+""; this.lblRecordRegion.Text = GetRecordRegion(); if (this.CurrentPage==1) { this.btnFirst.Enabled = false; this.btnPrev.Enabled = false; this.btnFirst.Image = global::CHVM.Properties.Resources.page_first_disabled; this.btnPrev.Image = global::CHVM.Properties.Resources.page_prev_disabled; } else { this.btnFirst.Enabled = true; this.btnPrev.Enabled = true; this.btnFirst.Image = global::CHVM.Properties.Resources.page_first; this.btnPrev.Image = global::CHVM.Properties.Resources.page_prev; } if (this.CurrentPage == this.PageCount) { this.btnNext.Enabled = false; this.btnLast.Enabled = false; this.btnNext.Image = global::CHVM.Properties.Resources.page_next_disabled; this.btnLast.Image = global::CHVM.Properties.Resources.page_last_disabled; } else { this.btnNext.Enabled = true; this.btnLast.Enabled = true; this.btnNext.Image = global::CHVM.Properties.Resources.page_next; this.btnLast.Image = global::CHVM.Properties.Resources.page_last; } if (this.TotalCount==0) { this.btnFirst.Enabled = false; this.btnPrev.Enabled = false; this.btnNext.Enabled = false; this.btnLast.Enabled = false; this.btnFirst.Image = global::CHVM.Properties.Resources.page_first_disabled; this.btnPrev.Image = global::CHVM.Properties.Resources.page_prev_disabled; this.btnNext.Image = global::CHVM.Properties.Resources.page_next_disabled; this.btnLast.Image = global::CHVM.Properties.Resources.page_last_disabled; } } private void btnFirst_Click(object sender, EventArgs e) { this.CurrentPage = 1; this.Bind(); } private void btnPrev_Click(object sender, EventArgs e) { this.CurrentPage -= 1; this.Bind(); } private void btnNext_Click(object sender, EventArgs e) { this.CurrentPage += 1; this.Bind(); } private void btnLast_Click(object sender, EventArgs e) { this.CurrentPage = this.PageCount; this.Bind(); } /// <summary> /// 改變每頁條數 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void comboPageSize_SelectedIndexChanged(object sender, EventArgs e) { this.PageSize = Convert.ToInt32(comboPageSize.Text); this.Bind(); } } 這裡重點提兩點:一是圖片切換: this.btnFirst.Image = global::CHVM.Properties.Resources.page_first_disabled; Image物件是在Properties.Resource.resx中自動生成的,程式碼如下: internal static System.Drawing.Bitmap page_first { get { object obj = ResourceManager.GetObject("page-first", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } internal static System.Drawing.Bitmap page_first_disabled { get { object obj = ResourceManager.GetObject("page_first_disabled", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } 二是應用了委託事件:我們在這定義了一個分頁事件 public event EventPagingHandler EventPaging; 在資料繫結方法中實現它: /// <summary> /// 資料繫結 /// </summary> public void Bind() { if (this.EventPaging != null) { this.EventPaging(new EventArgs()); } //… 以下省略 } 這裡需要大家對C#的委託和事件有一定的瞭解,不清楚的可以直接使用,或者先去查閱相關參考資料,這裡我們就不談委託機制了。 第三步:應用 值得一提的是,WinForm並不能直接把使用者自定控制元件往Windows窗體中拖拽,而自動生成例項(ASP.NET是可以直接拖拽的)。那麼如果我們需要在應用中使用,只能自己修改Desginer.cs程式碼了。 先宣告: private CHVM.PagingControl.Paging paging1; 然後在InitializeComponent()方法中例項化: this.paging1 = new CHVM.PagingControl.Paging(); // // paging1 // this.paging1.CurrentPage = 1; this.paging1.Location = new System.Drawing.Point(3, 347); this.paging1.Name = "paging1"; this.paging1.PageCount = 0; this.paging1.PageSize = 50; this.paging1.Size = new System.Drawing.Size(512, 30); this.paging1.TabIndex = 8; this.paging1.TotalCount = 0; //在這裡註冊事件 this.paging1.EventPaging += new CHVM.PagingControl.EventPagingHandler(this.paging1_EventPaging);
加完後就能看到效果了,相當於託了一個分頁控制元件的效果:(如下圖所示)
最後在事件中加入分頁事件需要執行的程式碼:
/// <summary>
/// 分頁事件
/// </summary>
/// <param name="e"></param>
private void paging1_EventPaging(EventArgs e)
{
GvDataBind(); //DataGridView資料繫結
}
/// <summary>
/// 查詢
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnQuery_Click(object sender, EventArgs e)
{
paging1_EventPaging(e);
}
/// <summary>
/// gvOperateLogList 資料邦定
/// </summary>
private void GvDataBind()
{
PagingCondition paging = new PagingCondition()
{
startIndex=paging1.CurrentPage,
pageSize = paging1.PageSize
};
MultiCondition condition = new MultiCondition();
condition.DateSign="FOperateTime";
condition.BeginDate = dtBegin.Value;
condition.EndDate = dtEnd.Value;
if (comboOperator.Text != "")
{
condition.Dict.Add("FOperator", comboOperator.Text);
}
if (comboType.Text != "")
{
condition.Dict.Add("FType", comboType.Text);
}
if (comboObject.Text != "")
{
condition.Dict.Add("FOptObject", comboObject.Text);
}
if (txtBoxContent.Text != "")
{
condition.Dict.Add("FContent", txtBoxContent.Text);
}
DataTable dt = GetByCondition(paging, condition);
paging1.TotalCount = Convert.ToInt32(dt.TableName);
gvOperateLogList.DataSource = dt;
gvOperateLogList.Columns.Clear();
var dict = GetGvColumnsDict();
DataGridViewHelp.DisplayColList(gvOperateLogList, dict);
}
注:MultiCondition、PagingCondition是我專門針對分頁綜合查詢定義的兩個類,興趣的話可以去了解一下:
Extjs+LINQ輕鬆實現高階綜合查詢:
其他:
/// <summary>
/// gv顯示列設定
/// </summary>
/// <returns></returns>
public Dictionary<string, string> GetGvColumnsDict()
{
Dictionary<string, string> dict = new Dictionary<string, string>();
dict.Add("FTYPE", "操作型別");
dict.Add("FOPTOBJECT", "操作物件");
dict.Add("FCONTENT", "操作內容");
dict.Add("FOperator", "操作人員");
return dict;
}
DataGridViewHelp.DisplayColList是一個靜態方法,為一個輔助類:
/// <summary>
/// 替換列表
/// </summary>
/// <param name="dgv">類表名稱</param>
/// <param name="dic">資料</param>
/// <param name="isRM">是否顯示序列號</param>
public static void DisplayColList(DataGridView dgv, Dictionary<string, string> dic)//, bool isRM
{
_dgv = dgv;
dgv.RowsDefaultCellStyle.BackColor = Color.FromArgb(255, 255, 255);//第一行
dgv.AlternatingRowsDefaultCellStyle.BackColor = Color.FromArgb(231, 232, 239);//第二行
dgv.GridColor = Color.FromArgb(207, 208, 216);//
dgv.RowTemplate.Height = 25;//列寬
dgv.AllowUserToAddRows=false;//無空行
dgv.CellBorderStyle = DataGridViewCellBorderStyle.SingleVertical;
dgv.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
dgv.AllowUserToOrderColumns = true;
dgv.RowPostPaint += new DataGridViewRowPostPaintEventHandler(dgv_RowPostPaint);
dgv.CellPainting += new DataGridViewCellPaintingEventHandler(dgv_CellPainting);//列頭樣式
dgv.CellFormatting += new DataGridViewCellFormattingEventHandler(dgv_CellFormatting);//選中行樣式
foreach (KeyValuePair<string, string> cl in dic)
{
dgv.AutoGenerateColumns = false;
DataGridViewTextBoxColumn obj = new DataGridViewTextBoxColumn();
obj.DataPropertyName = cl.Key;
obj.HeaderText = cl.Value;
obj.Name = cl.Key;
obj.Width = 100;
//obj.DefaultCellStyle.Padding.All = 10;
obj.Resizable = DataGridViewTriState.True;
dgv.Columns.AddRange(new DataGridViewColumn[] { obj });
}
}
到此實現就全部完成了,執行效果後就是前面所示的效果!也可以動態修改每頁條數。
說在最後,改功能簡單是簡單,但是涉及到很多知識點,委託、事件、
DataGridView資料動態繫結,綜合查詢,我這裡用的是Oracle資料庫,如果用LINQ語法的話查詢資料會比較方便,寫起程式碼也會顯得很優雅。
/// <summary> /// 獲取條件查詢資料 /// </summary> /// <param name="paging"></param> /// <param name="conditon"></param> /// <returns></returns> private DataTable GetByCondition(PagingCondition paging, MultiCondition conditon) { string strSql = "select * from TOperateLog "; string strSqlGetCount = "select count(1) from TOperateLog "; string strWhere = " where 1=1 "; if (conditon != null) { if (conditon.DateSign == "FOperateTime") //操作日期 { if (conditon.BeginDate != DateTime.MinValue) { strWhere += string.Format(" and FOperateTime>='{0}'", conditon.BeginDate.ToString("yyyy-MM-dd HH:mm:ss")); } if (conditon.EndDate != DateTime.MaxValue) { strWhere += string.Format(" and FOperateTime<='{0}'", conditon.EndDate.AddDays(1).ToString("yyyy-MM-dd HH:mm:ss")); } } var dict = conditon.Dict; if (dict != null) { foreach (var key in dict.Keys) { if (key.Equals("FType")) //操作型別 { strWhere += string.Format(" and FType='{0}'", dict[key]); } if (key.Equals("FOperator")) //操作人員 { strWhere += string.Format(" and FOperator='{0}'", dict[key]); } else if (key.Equals("FOptObject")) //操作物件 { strWhere += string.Format(" and FOptObject='{0}'", dict[key]); } else if (key.Equals("FContent")) //操作內容 { strWhere += string.Format(" and FContent like '%{0}%'", dict[key]); } } } } strWhere += " order by FOperateTime "; strSql += strWhere; strSqlGetCount += strWhere; if (paging != null) { if (paging.needPaging) { //strSql = string.Format("select * from ( {0} ) where ROWNUM>={1} and ROWNUM<={2}", strSql, paging.startIndex, paging.startIndex + paging.pageSize-1); strSql = string.Format("select * from (select T.*,RowNum RN from ({0})T where ROWNUM <={1}) where RN>={2} ",strSql, paging.startIndex + paging.pageSize - 1,paging.startIndex); } } DataTable dt = DataCon.Query(strSql).Tables[0]; dt.TableName = DataCon.GetSingle(strSqlGetCount)+""; return dt; }
來自:http://www.cnblogs.com/markli/p/3863173.html