WinForm中DataGridView折叠控件【超好看】
程序员文章站
2023-12-17 20:47:34
刚到一家新公司,领导下发任务要用cs系统做一个表格折叠显示,这真是把我难倒了,自己工作6年一直以来都是做bs的系统。这如果在bs里面那太简单了,jqgrid默认都自带,可是...
刚到一家新公司,领导下发任务要用cs系统做一个表格折叠显示,这真是把我难倒了,自己工作6年一直以来都是做bs的系统。这如果在bs里面那太简单了,jqgrid默认都自带,可是datagridview不支持折叠啊。自己一点经验没有,怎么办呢?于是上网搜了相关视频,资料,开始学习起来。最后借鉴源码封了这么一个东西,发出来分享下,也能让自己加深印象。
首先不多说,上图。如果大家感谢还不错,请继续往下阅读:
大概的效果就是这样。
上代码。
1、首先重写datagridview,代码如下:
public class mastercontrol : datagridview { #region 字段 private list<int> rowcurrent = new list<int>(); internal static int rowdefaultheight = ; internal static int rowexpandedheight = ; internal static int rowdefaultdivider = ; internal static int rowexpandeddivider = - ; internal static int rowdividermargin = ; internal static bool collapserow; //detailcontrol变量作为一个容器用来保存子表格 public detailcontrol childview = new detailcontrol() { visible = false }; // vbconversions note: initial value cannot be assigned here since it is non-static. assignment has been moved to the class constructors. // internal system.windows.forms.imagelist rowheadericonlist; private system.componentmodel.container components = null; // dataset _cdataset; string _foreignkey; string _primarykey; string _filterformat; private controltype econtroltype; public int expandrowindex = ; #endregion #region 构造函数 /// <summary> /// 通过传递过来的枚举判断是两级还是三级展开,表的对应关系通过relations来读取 /// 所以调用此构造函数的时候必须要讲relations设置正确,才能正确显示层级关系。 /// odataset.relations.add("", odataset.tables["t"].columns["menu_id"], odataset.tables["t"].columns["menu_id"]); /// odataset.relations.add("", odataset.tables["t"].columns["menu_name"], odataset.tables["t"].columns["menu_name"]); /// 这两次add的顺序不能颠倒,必须先添加一、二级的表关联,再添加二、三级的表关联 /// </summary> /// <param name="cdataset">数据源dataset,里面还有各个表的对应关系</param> /// <param name="econtroltype">枚举类型</param> public mastercontrol(dataset cdataset, controltype econtroltype) { setmastercontrol(cdataset, econtroltype); } /// <summary> /// 第二种使用方法 /// </summary> /// <param name="lstdata">折叠控件第一层的集合</param> /// <param name="lstdata">折叠控件第二层的集合</param> /// <param name="lstdata">折叠控件第三层的集合</param> /// <param name="dicrelatekey">第一二层之间对应主外键</param> /// <param name="dicrelatekey">第二三层之间对应主外键</param> /// <param name="econtroltype">枚举类型</param> public mastercontrol(object lstdata, object lstdata, object lstdata, dictionary<string, string> dicrelatekey, dictionary<string ,string>dicrelatekey, controltype econtroltype) { var odataset = new dataset(); try { var otable = new datatable(); otable = fill(lstdata); otable.tablename = "t"; var otable = fill(lstdata); otable.tablename = "t"; if (lstdata == null || dicrelatekey == null || dicrelatekey.keys.count <= ) { odataset.tables.addrange(new datatable[] { otable, otable }); odataset.relations.add("", odataset.tables["t"].columns[dicrelatekey.keys.firstordefault()], odataset.tables["t"].columns[dicrelatekey.values.firstordefault()]); } else { var otable = fill(lstdata); otable.tablename = "t"; odataset.tables.addrange(new datatable[] { otable, otable, otable }); //这是对应关系的时候主键必须唯一 odataset.relations.add("", odataset.tables["t"].columns[dicrelatekey.keys.firstordefault()], odataset.tables["t"].columns[dicrelatekey.values.firstordefault()]); odataset.relations.add("", odataset.tables["t"].columns[dicrelatekey.keys.firstordefault()], odataset.tables["t"].columns[dicrelatekey.values.firstordefault()]); } } catch { odataset = new dataset(); } setmastercontrol(odataset, econtroltype); } /// <summary> /// 控件初始化 /// </summary> private void initializecomponent() { this.components = new system.componentmodel.container(); base.rowheadermouseclick += new system.windows.forms.datagridviewcellmouseeventhandler(mastercontrol_rowheadermouseclick); base.rowpostpaint += new system.windows.forms.datagridviewrowpostpainteventhandler(mastercontrol_rowpostpaint); base.scroll += new system.windows.forms.scrolleventhandler(mastercontrol_scroll); base.selectionchanged += new system.eventhandler(mastercontrol_selectionchanged); system.componentmodel.componentresourcemanager resources = new system.componentmodel.componentresourcemanager(typeof(mastercontrol)); this.rowheadericonlist = new system.windows.forms.imagelist(this.components); ((system.componentmodel.isupportinitialize)this).begininit(); this.suspendlayout(); // //rowheadericonlist // this.rowheadericonlist.imagestream = (system.windows.forms.imageliststreamer)(resources.getobject("rowheadericonlist.imagestream")); this.rowheadericonlist.transparentcolor = system.drawing.color.transparent; this.rowheadericonlist.images.setkeyname(, "expand.png"); this.rowheadericonlist.images.setkeyname(, "collapse.png"); // //mastercontrol // ((system.componentmodel.isupportinitialize)this).endinit(); this.resumelayout(false); } #endregion #region 数据绑定 /// <summary> /// 设置表之间的主外键关联 /// </summary> /// <param name="tablename">datatable的表名称</param> /// <param name="foreignkey">外键</param> public void setparentsource(string tablename, string primarykey, string foreignkey) { this.datasource = new dataview(_cdataset.tables[tablename]); cmodule.setgridrowheader(this); _foreignkey = foreignkey; _primarykey = primarykey; if (_cdataset.tables[tablename].columns[primarykey].gettype().tostring() == typeof(int).tostring() || _cdataset.tables[tablename].columns[primarykey].gettype().tostring() == typeof(double).tostring() || _cdataset.tables[tablename].columns[primarykey].gettype().tostring() == typeof(decimal).tostring()) { _filterformat = foreignkey + "={}"; } else { _filterformat = foreignkey + "=\'{}\'"; } } #endregion #region 事件 //控件的行头点击事件 private void mastercontrol_rowheadermouseclick(object sender, datagridviewcellmouseeventargs e) { try { rectangle rect = new rectangle(system.convert.toint((double)(rowdefaultheight - ) / ), system.convert.toint((double)(rowdefaultheight - ) / ), , ); if (rect.contains(e.location)) { //缩起 if (rowcurrent.contains(e.rowindex)) { rowcurrent.clear(); this.rows[e.rowindex].height = rowdefaultheight; this.rows[e.rowindex].dividerheight = rowdefaultdivider; this.clearselection(); collapserow = true; this.rows[e.rowindex].selected = true; if (econtroltype == controltype.middle) { var oparent = ((mastercontrol)this.parent.parent); oparent.rows[oparent.expandrowindex].height = rowdefaultheight * (this.rows.count + ); oparent.rows[oparent.expandrowindex].dividerheight = rowdefaultheight * (this.rows.count + ); if (oparent.rows[oparent.expandrowindex].height > ) { oparent.rows[oparent.expandrowindex].height = ; oparent.rows[oparent.expandrowindex].height = ; } } } //展开 else { if (!(rowcurrent.count == )) { var erow = rowcurrent[]; rowcurrent.clear(); this.rows[erow].height = rowdefaultheight; this.rows[erow].dividerheight = rowdefaultdivider; this.clearselection(); collapserow = true; this.rows[erow].selected = true; } rowcurrent.add(e.rowindex); this.clearselection(); collapserow = true; this.rows[e.rowindex].selected = true; this.expandrowindex = e.rowindex; this.rows[e.rowindex].height = + rowdefaultheight * (((dataview)(childview.childgrid[].datasource)).count + ); this.rows[e.rowindex].dividerheight = + rowdefaultheight * (((dataview)(childview.childgrid[].datasource)).count); //设置一个最大高度 if (this.rows[e.rowindex].height > ) { this.rows[e.rowindex].height = ; this.rows[e.rowindex].dividerheight = ; } if (econtroltype == controltype.middle) { if (this.parent.parent.gettype() != typeof(mastercontrol)) return; var oparent = ((mastercontrol)this.parent.parent); oparent.rows[oparent.expandrowindex].height = this.rows[e.rowindex].height + rowdefaultheight * (this.rows.count + ); oparent.rows[oparent.expandrowindex].dividerheight = this.rows[e.rowindex].dividerheight + rowdefaultheight * (this.rows.count + ); if (oparent.rows[oparent.expandrowindex].height > ) { oparent.rows[oparent.expandrowindex].height = ; oparent.rows[oparent.expandrowindex].height = ; } } //if (econtroltype == controltype.outside) //{ // //setcontrol(this); //} //this.rows[e.rowindex].height = rowexpandedheight; //this.rows[e.rowindex].dividerheight = rowexpandeddivider; } //this.clearselection(); //collapserow = true; //this.rows[e.rowindex].selected = true; } else { collapserow = false; } } catch (exception ex) { } } //控件的行重绘事件 private void mastercontrol_rowpostpaint(object obj_sender, datagridviewrowpostpainteventargs e) { try { var sender = (datagridview)obj_sender; //set childview control var rect = new rectangle((int)(e.rowbounds.x + ((double)(rowdefaultheight - ) / )), (int)(e.rowbounds.y + ((double)(rowdefaultheight - ) / )), , ); if (collapserow) { if (this.rowcurrent.contains(e.rowindex)) { #region 更改点开后背景色 刘金龙 var rect = new rectangle(e.rowbounds.x, e.rowbounds.y + rowdefaultheight, e.rowbounds.width, e.rowbounds.height - rowdefaultheight); using (brush b = new solidbrush(color.fromargb(, , ))) { e.graphics.fillrectangle(b, rect); } #endregion sender.rows[e.rowindex].dividerheight = sender.rows[e.rowindex].height - rowdefaultheight; e.graphics.drawimage(rowheadericonlist.images[(int)rowheadericons.collapse], rect); childview.location = new point(e.rowbounds.left + sender.rowheaderswidth, e.rowbounds.top + rowdefaultheight + ); childview.width = e.rowbounds.right - sender.rowheaderswidth; childview.height = system.convert.toint(sender.rows[e.rowindex].dividerheight - ); childview.visible = true; } else { childview.visible = false; e.graphics.drawimage(rowheadericonlist.images[(int)rowheadericons.expand], rect); } collapserow = false; } else { if (this.rowcurrent.contains(e.rowindex)) { #region 更改点开后背景色 刘金龙 var rect = new rectangle(e.rowbounds.x, e.rowbounds.y + rowdefaultheight, e.rowbounds.width, e.rowbounds.height - rowdefaultheight); using (brush b = new solidbrush(color.fromargb(,,))) { e.graphics.fillrectangle(b, rect); } #endregion sender.rows[e.rowindex].dividerheight = sender.rows[e.rowindex].height - rowdefaultheight; e.graphics.drawimage(rowheadericonlist.images[(int)rowheadericons.collapse], rect); childview.location = new point(e.rowbounds.left + sender.rowheaderswidth, e.rowbounds.top + rowdefaultheight + ); childview.width = e.rowbounds.right - sender.rowheaderswidth; childview.height = system.convert.toint(sender.rows[e.rowindex].dividerheight - ); childview.visible = true; } else { childview.visible = false; e.graphics.drawimage(rowheadericonlist.images[(int)rowheadericons.expand], rect); } } cmodule.rowpostpaint_headercount(sender, e); } catch { } } //控件的滚动条滚动事件 private void mastercontrol_scroll(object sender, scrolleventargs e) { try { if (!(rowcurrent.count == )) { collapserow = true; this.clearselection(); this.rows[rowcurrent[]].selected = true; } } catch { } } //控件的单元格选择事件 private void mastercontrol_selectionchanged(object sender, eventargs e) { try { if (!(this.rowcount == )) { if (rowcurrent.contains(this.currentrow.index)) { foreach (datagridview cgrid in childview.childgrid) { ((dataview)cgrid.datasource).rowfilter = string.format(_filterformat, this[_primarykey, this.currentrow.index].value); } } } } catch { } } #endregion #region private //设置构造函数的参数 private void setmastercontrol(dataset cdataset, controltype econtroltype) { //.控件初始化赋值 this.controls.add(childview); initializecomponent(); _cdataset = cdataset; childview._cdataset = cdataset; cmodule.applygridtheme(this); dock = dockstyle.fill; econtroltype = econtroltype; this.allowusertoaddrows = false; //.通过读取dataset里面的relations得到表的关联关系 if (cdataset.relations.count <= ) { return; } datarelation orelates; if (econtroltype == controltype.outside) { orelates = cdataset.relations[]; childview.add(orelates.parenttable.tablename, orelates.parentcolumns[].columnname, orelates.childcolumns[].columnname); } else if (econtroltype == controltype.middle) { orelates = cdataset.relations[cdataset.relations.count - ]; childview.add(orelates.childtable.tablename); } //.设置主外键对应关系 orelates = cdataset.relations[]; //主表里面的值,副表里面的过滤字段 setparentsource(orelates.parenttable.tablename,orelates.parentcolumns[].columnname, orelates.childcolumns[].columnname); } private void setcontrol(mastercontrol ogrid) { ogrid.childview.removecontrol(); //ogrid.childview.controls.removebykey("childrenmaster"); // //var orelates = _cdataset.relations[]; //ogrid.childview.add(orelates.parenttable.tablename, orelates.childcolumns[].columnname); //foreach (var ogridcontrol in ogrid.controls) //{ // if (ogridcontrol.gettype() != typeof(detailcontrol)) // { // continue; // } // var detailcontrol =(detailcontrol)ogridcontrol; // foreach (var odetailcontrol in detailcontrol.controls) // { // if (odetailcontrol.gettype() != typeof(mastercontrol)) // { // continue; // } // var omastercontrol = (mastercontrol)odetailcontrol; // foreach (var omastercontrol in omastercontrol.controls) // { // if (omastercontrol.gettype() == typeof(detailcontrol)) // { // ((detailcontrol)omastercontrol).visible = false; // return; // } // } // } //} } //将list集合转换成datatable private datatable fill(object obj) { if(!(obj is ilist)) { return null; } var objlist = obj as ilist; if (objlist == null || objlist.count <= ) { return null; } var ttype = objlist[]; datatable dt = new datatable(ttype.gettype().name); datacolumn column; datarow row; system.reflection.propertyinfo[] mypropertyinfo = ttype.gettype().getproperties(bindingflags.public | bindingflags.instance); foreach (var t in objlist) { if (t == null) { continue; } row = dt.newrow(); for (int i = , j = mypropertyinfo.length; i < j; i++) { system.reflection.propertyinfo pi = mypropertyinfo[i]; string name = pi.name; if (dt.columns[name] == null) { column = new datacolumn(name, pi.propertytype); dt.columns.add(column); } row[name] = pi.getvalue(t, null); } dt.rows.add(row); } return dt; } #endregion }
2、detailcontrol变量作为一个容器用来保存子表格
代码如下:
public class detailcontrol : ewin.client.frame.controls.ewinpanel { #region 字段 public list<datagridview> childgrid = new list<datagridview>(); public dataset _cdataset; #endregion #region 方法 public void add(string tablename, string strprimarykey, string strforeignkey) { //tabpage tpage = new tabpage() { text = pagecaption }; //this.controls.add(tpage); var newgrid = new mastercontrol(_cdataset, controltype.middle) { dock = dockstyle.fill, datasource = new dataview(_cdataset.tables[tablename]) }; newgrid.setparentsource(tablename, strprimarykey, strforeignkey);//设置主外键 //newgrid.name = "childrenmaster"; //tpage.controls.add(newgrid); this.controls.add(newgrid); //this.borderstyle = borderstyle.fixedsingle; cmodule.applygridtheme(newgrid); cmodule.setgridrowheader(newgrid); newgrid.rowpostpaint += cmodule.rowpostpaint_headercount; childgrid.add(newgrid); } public void add(string tablename) { //tabpage tpage = new tabpage() { text = pagecaption }; //this.controls.add(tpage); datagridview newgrid = new ewin.client.frame.controls.ewingrid() { dock = dockstyle.fill, datasource = new dataview(_cdataset.tables[tablename]) }; newgrid.allowusertoaddrows = false; //tpage.controls.add(newgrid); this.controls.add(newgrid); cmodule.applygridtheme(newgrid); cmodule.setgridrowheader(newgrid); newgrid.rowpostpaint += cmodule.rowpostpaint_headercount; childgrid.add(newgrid); } public void removecontrol() { this.controls.remove(childgrid[]); childgrid.clear(); } #endregion }
3、cmodule.cs用来设置样式
namespace ewin.client.frame.ucgrid { /// <summary> /// 折叠控件样式以及行数操作类 /// </summary> sealed class cmodule { #region customgrid static system.windows.forms.datagridviewcellstyle datecellstyle = new system.windows.forms.datagridviewcellstyle { alignment = datagridviewcontentalignment.middleright }; static system.windows.forms.datagridviewcellstyle amountcellstyle = new system.windows.forms.datagridviewcellstyle { alignment = datagridviewcontentalignment.middleright, format = "n" }; static system.windows.forms.datagridviewcellstyle gridcellstyle = new system.windows.forms.datagridviewcellstyle { alignment = system.windows.forms.datagridviewcontentalignment.middleleft, backcolor = system.drawing.color.fromargb(system.convert.toint(system.convert.tobyte()), system.convert.toint(system.convert.tobyte()), system.convert.toint(system.convert.tobyte())), font = new system.drawing.font("segoe ui", (float)(.f), system.drawing.fontstyle.regular, system.drawing.graphicsunit.point, system.convert.tobyte()), forecolor = system.drawing.systemcolors.controllightlight, selectionbackcolor = system.drawing.systemcolors.highlight, selectionforecolor = system.drawing.systemcolors.highlighttext, wrapmode = system.windows.forms.datagridviewtristate.true }; static system.windows.forms.datagridviewcellstyle gridcellstyle = new system.windows.forms.datagridviewcellstyle { alignment = system.windows.forms.datagridviewcontentalignment.middleleft, backcolor = system.drawing.systemcolors.controllightlight, font = new system.drawing.font("segoe ui", (float)(.f), system.drawing.fontstyle.regular, system.drawing.graphicsunit.point, system.convert.tobyte()), forecolor = system.drawing.systemcolors.controltext, selectionbackcolor = system.drawing.color.fromargb(system.convert.toint(system.convert.tobyte()), system.convert.toint(system.convert.tobyte()), system.convert.toint(system.convert.tobyte())), selectionforecolor = system.drawing.systemcolors.highlighttext, wrapmode = system.windows.forms.datagridviewtristate.false }; static system.windows.forms.datagridviewcellstyle gridcellstyle = new system.windows.forms.datagridviewcellstyle { alignment = system.windows.forms.datagridviewcontentalignment.middleleft, backcolor = system.drawing.color.whitesmoke, font = new system.drawing.font("segoe ui", (float)(.f), system.drawing.fontstyle.regular, system.drawing.graphicsunit.point, system.convert.tobyte()), forecolor = system.drawing.systemcolors.windowtext, selectionbackcolor = system.drawing.color.fromargb(system.convert.toint(system.convert.tobyte()), system.convert.toint(system.convert.tobyte()), system.convert.toint(system.convert.tobyte())), selectionforecolor = system.drawing.systemcolors.highlighttext, wrapmode = system.windows.forms.datagridviewtristate.true }; //设置表格的主题样式 static public void applygridtheme(datagridview grid) { grid.allowusertoaddrows = false; grid.allowusertodeleterows = false; grid.backgroundcolor = system.drawing.systemcolors.window; grid.borderstyle = system.windows.forms.borderstyle.none; grid.columnheadersborderstyle = system.windows.forms.datagridviewheaderborderstyle.single; grid.columnheadersdefaultcellstyle = gridcellstyle; grid.columnheadersheight = ; grid.columnheadersheightsizemode = system.windows.forms.datagridviewcolumnheadersheightsizemode.disableresizing; grid.defaultcellstyle = gridcellstyle; grid.enableheadersvisualstyles = false; grid.gridcolor = system.drawing.systemcolors.gradientinactivecaption; //grid.readonly = true; grid.rowheadersvisible = true; grid.rowheadersborderstyle = system.windows.forms.datagridviewheaderborderstyle.single; grid.rowheadersdefaultcellstyle = gridcellstyle; grid.font = gridcellstyle.font; } //设置表格单元格样式 static public void setgridrowheader(datagridview dgv, bool hsize = false) { dgv.topleftheadercell.value = "no "; dgv.topleftheadercell.style.alignment = datagridviewcontentalignment.middlecenter; dgv.autoresizerowheaderswidth(datagridviewrowheaderswidthsizemode.autosizetodisplayedheaders); foreach (datagridviewcolumn ccol in dgv.columns) { if (ccol.valuetype.tostring() == typeof(datetime).tostring()) { ccol.defaultcellstyle = datecellstyle; } else if (ccol.valuetype.tostring() == typeof(decimal).tostring() || ccol.valuetype.tostring() == typeof(double).tostring()) { ccol.defaultcellstyle = amountcellstyle; } } if (hsize) { dgv.rowheaderswidth = dgv.rowheaderswidth + ; } dgv.autoresizecolumns(); } //设置表格的行号 static public void rowpostpaint_headercount(object obj_sender, datagridviewrowpostpainteventargs e) { try { var sender = (datagridview)obj_sender; //set rowheader count datagridview grid = (datagridview)sender; string rowidx = system.convert.tostring((e.rowindex + ).tostring()); var centerformat = new stringformat(); centerformat.alignment = stringalignment.center; centerformat.linealignment = stringalignment.center; rectangle headerbounds = new rectangle(e.rowbounds.left, e.rowbounds.top, grid.rowheaderswidth, e.rowbounds.height - sender.rows[e.rowindex].dividerheight); e.graphics.drawstring(rowidx, grid.font, systembrushes.controltext, headerbounds, centerformat); } catch (exception ex) { } } #endregion } /// <summary> /// 控件类型,是最外层的表格还是中间层的表格 /// </summary> public enum controltype { outside = , middle = } /// <summary> /// 展开图标 /// </summary> public enum rowheadericons { expand = , collapse = } }
4、from页面调用
#region 使用方法一
//var odataset = getdataset(); // //masterdetail = new mastercontrol(odataset, controltype.outside); #endregion
#region 使用方法二
var dicrelatedata1 = new dictionary<string, string>(); var dicrelatedata2 = new dictionary<string, string>(); dicrelatedata1.add("menu_id","menu_id");//表格一和表格二之间的主外键关系 dicrelatedata2.add("menu_name2","menu_name2");//表格二和表格三之间的主外键关系 masterdetail = new mastercontrol(getdatasource(), getdatasource2(), getdatasource3(), dicrelatedata1, dicrelatedata2, controltype.outside); #endregion panelview.controls.add(masterdetail);
昨天应领导要求,折叠控件增加了折叠线的效果,看起来有没有更加像模像样了。~~~
其实就在行重绘事件private void mastercontrol_rowpostpaint(object obj_sender, datagridviewrowpostpainteventargs e)里面增加了如下代码:
using (pen p = new pen(color.ghostwhite)) { var ihalfwidth = (e.rowbounds.left + sender.rowheaderswidth) / 2; var opointhlinestart = new point(rect1.x + ihalfwidth, rect1.y); var opointhlineend = new point(rect1.x + ihalfwidth, rect1.y + rect1.height / 2); e.graphics.drawline(p, opointhlinestart, opointhlineend); //折叠线 e.graphics.drawline(p, opointhlineend, new point(opointhlineend.x + ihalfwidth, opointhlineend.y)); }
效果如下:
以上所述是小编给大家介绍的winform中datagridview折叠控件的相关知识,希望对大家有所帮助!