转载至http://xuzhihong1987.blog.163.com/blog/static/267315872011315114240140/
以前都是做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 = ; /// <summary> /// 每页显示记录数(默认50) /// </summary> public int PageSize { get { return _pageSize; } set { if (value > ) { _pageSize = value; } else { _pageSize = ; } this.comboPageSize.Text = _pageSize.ToString(); } } private int _currentPage = ; /// <summary> /// 当前页 /// </summary> public int CurrentPage { get { return _currentPage; } set { if (value > ) { _currentPage = value; } else { _currentPage = ; } } } private int _totalCount = ; /// <summary> /// 总记录数 /// </summary> public int TotalCount { get { return _totalCount; } set { if (value>=) { _totalCount = value; } else { _totalCount = ; } this.lblTotalCount.Text = this._totalCount.ToString(); CalculatePageCount(); this.lblRecordRegion.Text = GetRecordRegion(); } } private int _pageCount = ; /// <summary> /// 页数 /// </summary> public int PageCount { get { return _pageCount; } set { if (value>=) { _pageCount = value; } else { _pageCount = ; } this.lblPageCount.Text = _pageCount + ""; } } #endregion /// <summary> /// 计算页数 /// </summary> private void CalculatePageCount() { if (this.TotalCount>) { this.PageCount = Convert.ToInt32(Math.Ceiling(Convert.ToDouble(this.TotalCount) / Convert.ToDouble(this.PageSize))); } else { this.PageCount = ; } } /// <summary> /// 获取显示记录区间(格式如:1-50) /// </summary> /// <returns></returns> private string GetRecordRegion() { if (this.PageCount == ) //只有一页 { return "1-" + this.TotalCount.ToString(); } else //有多页 { if(this.CurrentPage==) //当前显示为第一页 { return "1-"+this.PageSize; } else if(this.CurrentPage==this.PageCount) //当前显示为最后一页 { return ((this.CurrentPage-)*this.PageSize+) +"-"+this.TotalCount; } else //中间页 { return ((this.CurrentPage-) * this.PageSize+) + "-" + 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==) { 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==) { 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 = ; this.Bind(); } private void btnPrev_Click(object sender, EventArgs e) { this.CurrentPage -= ; this.Bind(); } private void btnNext_Click(object sender, EventArgs e) { this.CurrentPage += ; 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 = ; this.paging1.Location = new System.Drawing.Point(, ); this.paging1.Name = "paging1"; this.paging1.PageCount = ; this.paging1.PageSize = ; this.paging1.Size = new System.Drawing.Size(, ); this.paging1.TabIndex = ; this.paging1.TotalCount = ; //在这里注册事件 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是我专门针对分页综合查询定义的两个类,兴趣的话可以去了解一下:
查询条件就统一定义在MultiCondition中(详见:http://xuzhihong1987.blog.163.com/blog/static/267315872011294150763 ),
PagingCondition是分页条件(详见: http://xuzhihong1987.blog.163.com/blog/static/2673158720112941950801 ),
Extjs+LINQ轻松实现高级综合查询:
http://xuzhihong1987.blog.163.com/blog/static/2673158720112943356111/
其他:
/// <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(, , );//第一行 dgv.AlternatingRowsDefaultCellStyle.BackColor = Color.FromArgb(, , );//第二行 dgv.GridColor = Color.FromArgb(, , );// dgv.RowTemplate.Height = ;//列宽 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 = ; //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; }