首页 > 代码库 > WinForm轻松实现自定义分页 (转载)
WinForm轻松实现自定义分页 (转载)
转载至http://xuzhihong1987.blog.163.com/blog/static/267315872011315114240140/
以前都是做web开发,最近接触了下WinForm,发现WinForm分页控件好像都没有,网上搜索了一下,发现有很多网友写的分页控件,分页效果应该都能实现吧,只是其风格都不是很符合我想要的。做web的时候,我习惯了Extjs的Grid分页效果,所以也想在WinForm中做个类似的效果,所以咬咬牙,做个山寨版本的吧,虽然自己写费时费力,在项目进度考虑中不是很可取,但是还是特别想山寨一回,做自己喜欢的风格。
按照惯例,还是先看看实现效果图吧(有图有真像,才好继续下文呀)
应用效果:(效果有点难看,因为我是刚装的
xp系统,还是经典主题,如果换成Win7系统或其他主题,效果还是会很不错的)
我们要做的就是上图显示的一个自定义控件,这个效果参考自我做
web开发使用的Extjs之Grid的分页效果(如下图)
Extjs的动画效果我们暂时就不实现了,这里只做个外观看起来想像即可,完全一样就脱离“山寨”概念了,总要比人家差点吧,谁让咱是模仿呢!
言归正传,我们现在就看看具体怎么实现吧:
第一步:先布局
注:我们创建的是用户自定义控件,而不是WinForm窗体
就是先做出个显示效果,这个布局很简单,在这就不多说,重点就是“首页、前一页、后一页、末页”图标,每个图标分两种,一是能点击的高亮效果,一个是灰色不不能点击。以下是套图:(大家如果不喜欢,可以去做成自己喜欢的风格图片)
第二步:编写分页代码
布局好了,那么第二步我们就要代码实现正确显示文字信息,分页事件,每页条数选择事件,公开属性和事件。以下是完整代码:
1 /// <summary> 2 3 /// 声明委托 4 5 /// </summary> 6 7 /// <param name="e"></param> 8 9 public delegate void EventPagingHandler(EventArgs e); 10 11 12 13 public partial class Paging : UserControl 14 15 { 16 17 18 19 20 21 public Paging() 22 23 { 24 25 InitializeComponent(); 26 27 } 28 29 30 31 public event EventPagingHandler EventPaging; 32 33 34 35 #region 公开属性 36 37 38 39 40 41 private int _pageSize = 50; 42 43 /// <summary> 44 45 /// 每页显示记录数(默认50) 46 47 /// </summary> 48 49 public int PageSize 50 51 { 52 53 get 54 55 { 56 57 return _pageSize; 58 59 } 60 61 set 62 63 { 64 65 if (value > 0) 66 67 { 68 69 _pageSize = value; 70 71 } 72 73 else 74 75 { 76 77 _pageSize = 50; 78 79 } 80 81 this.comboPageSize.Text = _pageSize.ToString(); 82 83 } 84 85 } 86 87 private int _currentPage = 1; 88 89 /// <summary> 90 91 /// 当前页 92 93 /// </summary> 94 95 public int CurrentPage 96 97 { 98 99 get 100 101 {102 103 return _currentPage;104 105 }106 107 set 108 109 {110 111 if (value > 0)112 113 {114 115 _currentPage = value;116 117 }118 119 else120 121 {122 123 _currentPage = 1;124 125 }126 127 128 129 }130 131 }132 133 private int _totalCount = 0;134 135 /// <summary>136 137 /// 总记录数138 139 /// </summary>140 141 public int TotalCount142 143 {144 145 get146 147 {148 149 return _totalCount;150 151 }152 153 set 154 155 {156 157 if (value>=0)158 159 {160 161 _totalCount = value;162 163 } 164 165 else166 167 {168 169 _totalCount = 0;170 171 }172 173 this.lblTotalCount.Text = this._totalCount.ToString();174 175 CalculatePageCount();176 177 this.lblRecordRegion.Text = GetRecordRegion();178 179 }180 181 }182 183 184 185 private int _pageCount = 0;186 187 /// <summary>188 189 /// 页数190 191 /// </summary>192 193 public int PageCount194 195 {196 197 get198 199 {200 201 return _pageCount;202 203 }204 205 set 206 207 {208 209 if (value>=0)210 211 {212 213 _pageCount = value;214 215 } 216 217 else218 219 {220 221 _pageCount = 0;222 223 }224 225 this.lblPageCount.Text = _pageCount + "";226 227 }228 229 }230 231 232 233 #endregion234 235 236 237 /// <summary>238 239 /// 计算页数240 241 /// </summary>242 243 private void CalculatePageCount()244 245 {246 247 if (this.TotalCount>0)248 249 {250 251 this.PageCount = Convert.ToInt32(Math.Ceiling(Convert.ToDouble(this.TotalCount) / Convert.ToDouble(this.PageSize)));252 253 }254 255 else256 257 {258 259 this.PageCount = 0;260 261 }262 263 }264 265 266 267 /// <summary>268 269 /// 获取显示记录区间(格式如:1-50)270 271 /// </summary>272 273 /// <returns></returns>274 275 private string GetRecordRegion()276 277 {278 279 if (this.PageCount == 1) //只有一页280 281 {282 283 return "1-" + this.TotalCount.ToString();284 285 }286 287 else //有多页288 289 {290 291 if(this.CurrentPage==1) //当前显示为第一页292 293 {294 295 return "1-"+this.PageSize;296 297 }298 299 else if(this.CurrentPage==this.PageCount) //当前显示为最后一页300 301 {302 303 return ((this.CurrentPage-1)*this.PageSize+1) +"-"+this.TotalCount;304 305 }306 307 else //中间页308 309 {310 311 return ((this.CurrentPage-1) * this.PageSize+1) + "-" + this.CurrentPage * this.PageSize;312 313 }314 315 316 }317 318 }319 320 321 322 323 324 /// <summary>325 326 /// 数据绑定327 328 /// </summary>329 330 public void Bind()331 332 {333 334 if (this.EventPaging != null)335 336 {337 338 this.EventPaging(new EventArgs());339 340 }341 342 if (this.CurrentPage>this.PageCount)343 344 {345 346 this.CurrentPage = this.PageCount;347 348 }349 350 this.txtBoxCurPage.Text = this.CurrentPage+"";351 352 this.lblTotalCount.Text = this.TotalCount+"";353 354 this.lblPageCount.Text = this.PageCount+"";355 356 this.lblRecordRegion.Text = GetRecordRegion();357 358 if (this.CurrentPage==1)359 360 {361 362 this.btnFirst.Enabled = false;363 364 this.btnPrev.Enabled = false;365 366 this.btnFirst.Image = global::CHVM.Properties.Resources.page_first_disabled;367 368 this.btnPrev.Image = global::CHVM.Properties.Resources.page_prev_disabled;369 370 }371 372 else373 374 {375 376 this.btnFirst.Enabled = true;377 378 this.btnPrev.Enabled = true;379 380 this.btnFirst.Image = global::CHVM.Properties.Resources.page_first;381 382 this.btnPrev.Image = global::CHVM.Properties.Resources.page_prev;383 384 }385 386 if (this.CurrentPage == this.PageCount)387 388 {389 390 this.btnNext.Enabled = false;391 392 this.btnLast.Enabled = false;393 394 this.btnNext.Image = global::CHVM.Properties.Resources.page_next_disabled;395 396 this.btnLast.Image = global::CHVM.Properties.Resources.page_last_disabled;397 398 } 399 400 else401 402 {403 404 this.btnNext.Enabled = true;405 406 this.btnLast.Enabled = true;407 408 this.btnNext.Image = global::CHVM.Properties.Resources.page_next;409 410 this.btnLast.Image = global::CHVM.Properties.Resources.page_last;411 412 }413 414 if (this.TotalCount==0)415 416 {417 418 this.btnFirst.Enabled = false;419 420 this.btnPrev.Enabled = false;421 422 this.btnNext.Enabled = false;423 424 this.btnLast.Enabled = false;425 426 this.btnFirst.Image = global::CHVM.Properties.Resources.page_first_disabled;427 428 this.btnPrev.Image = global::CHVM.Properties.Resources.page_prev_disabled;429 430 this.btnNext.Image = global::CHVM.Properties.Resources.page_next_disabled;431 432 this.btnLast.Image = global::CHVM.Properties.Resources.page_last_disabled;433 434 }435 436 437 438 }439 440 441 442 private void btnFirst_Click(object sender, EventArgs e)443 444 {445 446 this.CurrentPage = 1;447 448 this.Bind();449 450 }451 452 453 454 private void btnPrev_Click(object sender, EventArgs e)455 456 {457 458 this.CurrentPage -= 1; 459 460 this.Bind();461 462 }463 464 465 466 private void btnNext_Click(object sender, EventArgs e)467 468 {469 470 this.CurrentPage += 1;471 472 this.Bind();473 474 }475 476 477 478 private void btnLast_Click(object sender, EventArgs e)479 480 {481 482 this.CurrentPage = this.PageCount;483 484 this.Bind();485 486 }487 488 489 490 /// <summary>491 492 /// 改变每页条数493 494 /// </summary>495 496 /// <param name="sender"></param>497 498 /// <param name="e"></param>499 500 private void comboPageSize_SelectedIndexChanged(object sender, EventArgs e)501 502 {503 504 this.PageSize = Convert.ToInt32(comboPageSize.Text);505 506 this.Bind();507 508 }509 510 511 512 }513 514 515 516 这里重点提两点:一是图片切换:517 518 this.btnFirst.Image = global::CHVM.Properties.Resources.page_first_disabled;519 520 Image对象是在Properties.Resource.resx中自动生成的,代码如下:521 522 internal static System.Drawing.Bitmap page_first {523 524 get {525 526 object obj = ResourceManager.GetObject("page-first", resourceCulture);527 528 return ((System.Drawing.Bitmap)(obj));529 530 }531 532 }533 534 535 536 internal static System.Drawing.Bitmap page_first_disabled {537 538 get {539 540 object obj = ResourceManager.GetObject("page_first_disabled", resourceCulture);541 542 return ((System.Drawing.Bitmap)(obj));543 544 }545 546 }547 548 二是应用了委托事件:我们在这定义了一个分页事件549 550 public event EventPagingHandler EventPaging;551 552 在数据绑定方法中实现它:553 554 /// <summary>555 556 /// 数据绑定557 558 /// </summary>559 560 public void Bind()561 562 {563 564 if (this.EventPaging != null)565 566 {567 568 this.EventPaging(new EventArgs());569 570 }571 572 //… 以下省略573 574 }575 576 这里需要大家对C#的委托和事件有一定的了解,不清楚的可以直接使用,或者先去查阅相关参考资料,这里我们就不谈委托机制了。577 578 579 580 第三步:应用581 582 值得一提的是,WinForm并不能直接把用户自定控件往Windows窗体中拖拽,而自动生成实例(ASP.NET是可以直接拖拽的)。那么如果我们需要在应用中使用,只能自己修改Desginer.cs代码了。583 584 先声明:585 586 private CHVM.PagingControl.Paging paging1;587 588 然后在InitializeComponent()方法中实例化:589 590 this.paging1 = new CHVM.PagingControl.Paging();591 592 // 593 594 // paging1595 596 // 597 598 this.paging1.CurrentPage = 1;599 600 this.paging1.Location = new System.Drawing.Point(3, 347);601 602 this.paging1.Name = "paging1";603 604 this.paging1.PageCount = 0;605 606 this.paging1.PageSize = 50;607 608 this.paging1.Size = new System.Drawing.Size(512, 30);609 610 this.paging1.TabIndex = 8;611 612 this.paging1.TotalCount = 0; 613 614 //在这里注册事件615 616 this.paging1.EventPaging += new CHVM.PagingControl.EventPagingHandler(this.paging1_EventPaging);
加完后就能看到效果了,相当于托了一个分页控件的效果:(如下图所示)
最后在事件中加入分页事件需要执行的代码:
1 /// <summary> 2 3 /// 分页事件 4 5 /// </summary> 6 7 /// <param name="e"></param> 8 9 private void paging1_EventPaging(EventArgs e) 10 11 { 12 13 GvDataBind(); //DataGridView数据绑定 14 15 } 16 17 /// <summary> 18 19 /// 查询 20 21 /// </summary> 22 23 /// <param name="sender"></param> 24 25 /// <param name="e"></param> 26 27 private void btnQuery_Click(object sender, EventArgs e) 28 29 { 30 31 paging1_EventPaging(e); 32 33 } 34 35 /// <summary> 36 37 /// gvOperateLogList 数据邦定 38 39 /// </summary> 40 41 private void GvDataBind() 42 43 { 44 45 PagingCondition paging = new PagingCondition() 46 47 { 48 49 startIndex=paging1.CurrentPage, 50 51 pageSize = paging1.PageSize 52 53 }; 54 55 MultiCondition condition = new MultiCondition(); 56 57 condition.DateSign="FOperateTime"; 58 59 condition.BeginDate = dtBegin.Value; 60 61 condition.EndDate = dtEnd.Value; 62 63 if (comboOperator.Text != "") 64 65 { 66 67 condition.Dict.Add("FOperator", comboOperator.Text); 68 69 } 70 71 if (comboType.Text != "") 72 73 { 74 75 condition.Dict.Add("FType", comboType.Text); 76 77 } 78 79 if (comboObject.Text != "") 80 81 { 82 83 condition.Dict.Add("FOptObject", comboObject.Text); 84 85 } 86 87 if (txtBoxContent.Text != "") 88 89 { 90 91 condition.Dict.Add("FContent", txtBoxContent.Text); 92 93 } 94 95 DataTable dt = GetByCondition(paging, condition); 96 97 paging1.TotalCount = Convert.ToInt32(dt.TableName); 98 99 gvOperateLogList.DataSource = dt;100 101 gvOperateLogList.Columns.Clear();102 103 var dict = GetGvColumnsDict();104 105 DataGridViewHelp.DisplayColList(gvOperateLogList, dict);106 107 }
注: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/
其他:
1 /// <summary> 2 3 /// gv显示列设置 4 5 /// </summary> 6 7 /// <returns></returns> 8 9 public Dictionary<string, string> GetGvColumnsDict()10 11 {12 13 Dictionary<string, string> dict = new Dictionary<string, string>();14 15 dict.Add("FTYPE", "操作类型");16 17 dict.Add("FOPTOBJECT", "操作对象");18 19 dict.Add("FCONTENT", "操作内容");20 21 dict.Add("FOperator", "操作人员");22 23 return dict;24 25 }26 27 28 29 DataGridViewHelp.DisplayColList是一个静态方法,为一个辅助类:30 31 /// <summary>32 33 /// 替换列表34 35 /// </summary>36 37 /// <param name="dgv">类表名称</param>38 39 /// <param name="dic">数据</param>40 41 /// <param name="isRM">是否显示序列号</param>42 43 public static void DisplayColList(DataGridView dgv, Dictionary<string, string> dic)//, bool isRM44 45 {46 47 _dgv = dgv;48 49 dgv.RowsDefaultCellStyle.BackColor = Color.FromArgb(255, 255, 255);//第一行 50 51 dgv.AlternatingRowsDefaultCellStyle.BackColor = Color.FromArgb(231, 232, 239);//第二行 52 53 dgv.GridColor = Color.FromArgb(207, 208, 216);//54 55 dgv.RowTemplate.Height = 25;//列宽56 57 dgv.AllowUserToAddRows=false;//无空行58 59 dgv.CellBorderStyle = DataGridViewCellBorderStyle.SingleVertical;60 61 dgv.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;62 63 dgv.AllowUserToOrderColumns = true;64 65 dgv.RowPostPaint += new DataGridViewRowPostPaintEventHandler(dgv_RowPostPaint);66 67 dgv.CellPainting += new DataGridViewCellPaintingEventHandler(dgv_CellPainting);//列头样式68 69 dgv.CellFormatting += new DataGridViewCellFormattingEventHandler(dgv_CellFormatting);//选中行样式70 71 72 73 foreach (KeyValuePair<string, string> cl in dic)74 75 {76 77 dgv.AutoGenerateColumns = false;78 79 DataGridViewTextBoxColumn obj = new DataGridViewTextBoxColumn();80 81 obj.DataPropertyName = cl.Key;82 83 obj.HeaderText = cl.Value;84 85 obj.Name = cl.Key;86 87 obj.Width = 100;88 89 //obj.DefaultCellStyle.Padding.All = 10;90 91 obj.Resizable = DataGridViewTriState.True;92 93 dgv.Columns.AddRange(new DataGridViewColumn[] { obj });94 95 }96 97 }98 99
到此实现就全部完成了,运行效果后就是前面所示的效果!也可以动态修改每页条数。
说在最后,改功能简单是简单,但是涉及到很多知识点,委托、事件、
DataGridView数据动态绑定,综合查询,我这里用的是Oracle数据库,如果用LINQ语法的话查询数据会比较方便,写起代码也会显得很优雅。
1 /// <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; }