(六十五)c#Winform自定义控件-思维导图/组织架构图(工业)
前提
入行已经7,8年了,一直想做一套漂亮点的自定义控件,于是就有了本系列文章。
github:https://github.com/kwwwvagaa/netwinformcontrol
码云:
如果觉得写的还行,请点个 star 支持一下吧
欢迎前来交流探讨: 企鹅群568015492
麻烦博客下方点个【推荐】,谢谢
nuget
install-package hzh_controls
目录
用处及效果
准备工作
依然是用gdi+画的,不懂的可以先百度一下
开始
添加一个实体类,用以记录数据源节点信息
1 public class mindmappingitementity 2 { 3 /// <summary> 4 /// gets or sets the identifier. 5 /// </summary> 6 /// <value>the identifier.</value> 7 public string id { get; set; } 8 private string _text; 9 /// <summary> 10 /// gets or sets the text. 11 /// </summary> 12 /// <value>the text.</value> 13 public string text 14 { 15 get { return _text; } 16 set 17 { 18 _text = value; 19 resetsize(); 20 } 21 } 22 /// <summary> 23 /// gets or sets the data source. 24 /// </summary> 25 /// <value>the data source.</value> 26 public object datasource { get; set; } 27 /// <summary> 28 /// the childrens 29 /// </summary> 30 private mindmappingitementity[] _childrens; 31 /// <summary> 32 /// gets or sets the childrens. 33 /// </summary> 34 /// <value>the childrens.</value> 35 public mindmappingitementity[] childrens 36 { 37 get { return _childrens; } 38 set 39 { 40 _childrens = value; 41 if (value != null && value.length > 0) 42 { 43 value.tolist().foreach(p => { if (p != null) { p.parentitem = this; } }); 44 } 45 } 46 } 47 /// <summary> 48 /// the back color 49 /// </summary> 50 private color backcolor = color.transparent; 51 52 /// <summary> 53 /// gets or sets the color of the back. 54 /// </summary> 55 /// <value>the color of the back.</value> 56 public color backcolor 57 { 58 get { return backcolor; } 59 set { backcolor = value; } 60 } 61 62 private font font = new font("微软雅黑", 10); 63 64 public font font 65 { 66 get { return font; } 67 set 68 { 69 font = value; 70 resetsize(); 71 } 72 } 73 74 /// <summary> 75 /// the fore color 76 /// </summary> 77 private color forecolor = color.black; 78 79 /// <summary> 80 /// gets or sets the color of the fore. 81 /// </summary> 82 /// <value>the color of the fore.</value> 83 public color forecolor 84 { 85 get { return forecolor; } 86 set { forecolor = value; } 87 } 88 private bool _isexpansion = false; 89 /// <summary> 90 /// gets or sets a value indicating whether the instance is expanded. 91 /// </summary> 92 /// <value><c>true</c> if this instance is expansion; otherwise, <c>false</c>.</value> 93 public bool isexpansion 94 { 95 get 96 { 97 return _isexpansion; 98 } 99 set 100 { 101 if (value == _isexpansion) 102 return; 103 _isexpansion = value; 104 if (!value) 105 { 106 _childrens.tolist().foreach(p => { if (p != null) { p.isexpansion = false; } }); 107 } 108 109 } 110 } 111 112 /// <summary> 113 /// gets the parent item. 114 /// </summary> 115 /// <value>the parent item.</value> 116 public mindmappingitementity parentitem { get; private set; } 117 /// <summary> 118 /// gets all childrens maximum show count. 119 /// </summary> 120 /// <value>all childrens maximum show count.</value> 121 public int allchildrensmaxshowheight { get { return getallchildrensmaxshowheight(); } } 122 /// <summary> 123 /// gets the maximum level. 124 /// </summary> 125 /// <value>the maximum level.</value> 126 public int allchildrensmaxshowwidth { get { return getallchildrensmaxshowwidth(); } } 127 128 /// <summary> 129 /// gets all childrens maximum show count. 130 /// </summary> 131 /// <returns>system.int32.</returns> 132 private int getallchildrensmaxshowheight() 133 { 134 if (!_isexpansion || _childrens == null || _childrens.length <= 0) 135 return itemheight + 10; 136 else 137 { 138 return _childrens.sum(p => p == null ? 0 : p.allchildrensmaxshowheight); 139 } 140 } 141 /// <summary> 142 /// gets the maximum level. 143 /// </summary> 144 /// <returns>system.int32.</returns> 145 private int getallchildrensmaxshowwidth() 146 { 147 if (!_isexpansion || _childrens == null || _childrens.length <= 0) 148 return itemwidth + 50; 149 else 150 { 151 return 1 + _childrens.max(p => p == null ? 0 : p.allchildrensmaxshowwidth); 152 } 153 } 154 /// <summary> 155 /// gets or sets the working rectangle. 156 /// </summary> 157 /// <value>the working rectangle.</value> 158 internal rectanglef workingrectangle { get; set; } 159 /// <summary> 160 /// gets or sets the draw rectangle. 161 /// </summary> 162 /// <value>the draw rectangle.</value> 163 internal rectanglef drawrectangle { get; set; } 164 /// <summary> 165 /// gets or sets the expansion rectangle. 166 /// </summary> 167 /// <value>the expansion rectangle.</value> 168 internal rectanglef expansionrectangle { get; set; } 169 /// <summary> 170 /// gets the height of the item. 171 /// </summary> 172 /// <value>the height of the item.</value> 173 private int itemheight { private get; private set; } 174 /// <summary> 175 /// gets the width of the item. 176 /// </summary> 177 /// <value>the width of the item.</value> 178 private int itemwidth { private get; private set; } 179 /// <summary> 180 /// resets the size. 181 /// </summary> 182 private void resetsize() 183 { 184 string _t = _text; 185 if (string.isnullorempty(_t)) 186 { 187 _t = "aaaa"; 188 } 189 bitmap bit = new bitmap(1, 1); 190 var g = graphics.fromimage(bit); 191 var size = g.measurestring(_t, font); 192 g.dispose(); 193 bit.dispose(); 194 itemheight = (int)size.height; 195 itemwidth = (int)size.width; 196 } 197 }
主要属性说明:
text:显示文字
childrens:子节点信息
backcolor:节点颜色
isexpansion:是否展开子节点
parentitem:父级节点
allchildrensmaxshowheight:该节点包含所有子节点需要显示的高度,通过私有函数getallchildrensmaxshowheight()返回结果
allchildrensmaxshowwidth:该节点包含所有子节点需要显示的宽度,通过私有函数getallchildrensmaxshowwidth()返回结果
workingrectangle:该节点以及所有子节点的工作区域
drawrectangle:该节点的绘制区域
expansionrectangle:展开折叠按钮的绘制区域
itemheight:该节点的高度
itemwidth:该节点的宽度
主要函数说明:
getallchildrensmaxshowheight:获取当前节点及所有子节点需要的最大高度
getallchildrensmaxshowwidth:获取当前节点及所有子节点需要的最大宽度
resetsize:当文本和字体改变时重新计算宽高
添加一个类ucmindmapping,继承usercontrol
添加一些属性
1 /// <summary> 2 /// the line color 3 /// </summary> 4 private color linecolor = color.black; 5 6 /// <summary> 7 /// gets or sets the color of the line. 8 /// </summary> 9 /// <value>the color of the line.</value> 10 [description("线条颜色"), category("自定义")] 11 public color linecolor 12 { 13 get { return linecolor; } 14 set 15 { 16 linecolor = value; 17 refresh(); 18 } 19 } 20 /// <summary> 21 /// the split width 22 /// </summary> 23 private int splitwidth = 50; 24 // private int itemheight = 20; 25 /// <summary> 26 /// the padding 27 /// </summary> 28 private int padding = 20; 29 30 /// <summary> 31 /// the m rect working 32 /// </summary> 33 rectangle m_rectworking = rectangle.empty; 34 /// <summary> 35 /// occurs when [item clicked]. 36 /// </summary> 37 public event eventhandler itemclicked; 38 /// <summary> 39 /// the data source 40 /// </summary> 41 private mindmappingitementity datasource; 42 /// <summary> 43 /// gets or sets the data source. 44 /// </summary> 45 /// <value>the data source.</value> 46 [description("数据源"), category("自定义")] 47 public mindmappingitementity datasource 48 { 49 get { return datasource; } 50 set 51 { 52 datasource = value; 53 54 resetsize(); 55 } 56 }
一个辅助函数,用以在大小过数据改变时计算工作区域和位置
1 /// <summary> 2 /// 重置大小 3 /// </summary> 4 private void resetsize() 5 { 6 if (this.parent == null) 7 return; 8 try 9 { 10 controlhelper.freezecontrol(this, true); 11 if (datasource == null) 12 { 13 m_rectworking = rectangle.empty; 14 this.size = this.parent.size; 15 } 16 else 17 { 18 int intwidth = datasource.allchildrensmaxshowwidth; 19 int intheight = datasource.allchildrensmaxshowheight; 20 this.width = intwidth + padding * 2; 21 this.height = intheight + padding * 2; 22 if (this.width < this.parent.width) 23 this.width = this.parent.width; 24 m_rectworking = new rectangle(padding, padding, intwidth, intheight); 25 if (this.height > this.parent.height) 26 { 27 //this.location = new point(0, 0); 28 } 29 else 30 this.location = new point(0, (this.parent.height - this.height) / 2); 31 } 32 } 33 finally 34 { 35 controlhelper.freezecontrol(this, false); 36 } 37 }
重绘
1 /// <summary> 2 /// 引发 <see cref="e:system.windows.forms.control.paint" /> 事件。 3 /// </summary> 4 /// <param name="e">包含事件数据的 <see cref="t:system.windows.forms.painteventargs" />。</param> 5 protected override void onpaint(painteventargs e) 6 { 7 try 8 { 9 base.onpaint(e); 10 if (m_rectworking == rectangle.empty || m_rectworking == null) 11 return; 12 var g = e.graphics; 13 g.setgdihigh(); 14 15 int intheight = datasource.allchildrensmaxshowheight; 16 datasource.workingrectangle = new rectanglef(m_rectworking.left, m_rectworking.top + (m_rectworking.height - intheight) / 2, m_rectworking.width, intheight); 17 18 drawitem(datasource, g); 19 } 20 catch (exception exc) 21 { 22 messagebox.show(exc.tostring(), "错误"); 23 } 24 }
1 /// <summary> 2 /// 画节点 3 /// </summary> 4 /// <param name="item">the item.</param> 5 /// <param name="g">the g.</param> 6 private void drawitem(mindmappingitementity item, graphics g) 7 { 8 //节点 9 var size = g.measurestring(item.text, item.font); 10 item.drawrectangle = new rectanglef(item.workingrectangle.left + 2, item.workingrectangle.top + (item.workingrectangle.height - size.height) / 2 + 2, size.width + 4, size.height + 4); 11 graphicspath drawpath = item.drawrectangle.createroundedrectanglepath(5); 12 g.fillpath(new solidbrush(item.backcolor), drawpath); 13 g.drawstring(item.text, item.font, new solidbrush(item.forecolor), item.drawrectangle.location.x + 2, item.drawrectangle.location.y + 2); 14 //子节点 15 if (item.childrens != null && item.isexpansion) 16 { 17 for (int i = 0; i < item.childrens.length; i++) 18 { 19 var child = item.childrens[i]; 20 if (i == 0) 21 { 22 child.workingrectangle = new rectanglef(item.drawrectangle.right + splitwidth, item.workingrectangle.top, item.workingrectangle.width - (item.drawrectangle.width + splitwidth), child.allchildrensmaxshowheight); 23 } 24 else 25 { 26 child.workingrectangle = new rectanglef(item.drawrectangle.right + splitwidth, item.childrens[i - 1].workingrectangle.bottom, item.workingrectangle.width - (item.drawrectangle.width + splitwidth), child.allchildrensmaxshowheight); 27 } 28 drawitem(child, g); 29 } 30 } 31 //连线 32 if (item.parentitem != null) 33 { 34 g.drawlines(new pen(new solidbrush(linecolor), 1), new pointf[] 35 { 36 new pointf(item.parentitem.drawrectangle.right,item.parentitem.drawrectangle.top+item.parentitem.drawrectangle.height/2), 37 new pointf(item.parentitem.drawrectangle.right+12,item.parentitem.drawrectangle.top+item.parentitem.drawrectangle.height/2), 38 //new pointf(item.parentitem.drawrectangle.right+12,item.drawrectangle.top+item.drawrectangle.height/2), 39 new pointf(item.drawrectangle.left-12,item.drawrectangle.top+item.drawrectangle.height/2), 40 new pointf(item.drawrectangle.left,item.drawrectangle.top+item.drawrectangle.height/2), 41 }); 42 } 43 //展开折叠按钮 44 if (item.childrens != null && item.childrens.length > 0) 45 { 46 rectanglef _rect = new rectanglef(item.drawrectangle.right + 1, item.drawrectangle.top + (item.drawrectangle.height - 10) / 2, 10, 10); 47 item.expansionrectangle = _rect; 48 g.fillellipse(new solidbrush(this.backcolor), _rect); 49 g.drawellipse(new pen(new solidbrush(color.black)), _rect); 50 g.drawline(new pen(new solidbrush(linecolor)), _rect.left + 2, _rect.y + _rect.height / 2, _rect.right - 2, _rect.y + _rect.height / 2); 51 if (!item.isexpansion) 52 { 53 g.drawline(new pen(new solidbrush(linecolor)), _rect.left + _rect.width / 2, _rect.top + 2, _rect.left + _rect.width / 2, _rect.bottom - 2); 54 } 55 } 56 }
控件的单击和双击时间来处理展开折叠事件
1 /// <summary> 2 /// 双击处理,主要用于检测节点双击展开折叠 3 /// </summary> 4 /// <param name="sender">the source of the event.</param> 5 /// <param name="e">the <see cref="eventargs"/> instance containing the event data.</param> 6 void ucmindmapping_doubleclick(object sender, eventargs e) 7 { 8 var mouselocation = this.pointtoclient(control.mouseposition); 9 10 bool bln = checkexpansiondoubleclick(datasource, mouselocation); 11 if (bln) 12 { 13 resetsize(); 14 this.parent.refresh(); 15 } 16 } 17 18 /// <summary> 19 /// 单击处理,主要用于单击节点事件和,展开关闭圆圈处理 20 /// </summary> 21 /// <param name="sender">the source of the event.</param> 22 /// <param name="e">the <see cref="eventargs"/> instance containing the event data.</param> 23 void ucmindmapping_click(object sender, eventargs e) 24 { 25 var mouselocation = this.pointtoclient(control.mouseposition); 26 27 bool bln = checkexpansionclick(datasource, mouselocation); 28 if (bln) 29 { 30 resetsize(); 31 this.parent.refresh(); 32 } 33 }
1 /// <summary> 2 /// 双击检查 3 /// </summary> 4 /// <param name="item">the item.</param> 5 /// <param name="mouselocation">the mouse location.</param> 6 /// <returns><c>true</c> if xxxx, <c>false</c> otherwise.</returns> 7 private bool checkexpansiondoubleclick(mindmappingitementity item, point mouselocation) 8 { 9 if (item == null) 10 return false; 11 else 12 { 13 if (item.drawrectangle.contains(mouselocation)) 14 { 15 item.isexpansion = !item.isexpansion; 16 return true; 17 } 18 if (item.childrens != null && item.childrens.length > 0) 19 { 20 foreach (var child in item.childrens) 21 { 22 var bln = checkexpansiondoubleclick(child, mouselocation); 23 if (bln) 24 return bln; 25 } 26 } 27 } 28 return false; 29 } 30 31 /// <summary> 32 /// 单击检查 33 /// </summary> 34 /// <param name="item">the item.</param> 35 /// <param name="mouselocation">the mouse location.</param> 36 /// <returns><c>true</c> if xxxx, <c>false</c> otherwise.</returns> 37 private bool checkexpansionclick(mindmappingitementity item, point mouselocation) 38 { 39 if (item == null) 40 return false; 41 else 42 { 43 if (itemclicked != null && item.workingrectangle.contains(mouselocation)) 44 { 45 itemclicked(item, null); 46 } 47 else if (item.expansionrectangle.contains(mouselocation)) 48 { 49 item.isexpansion = !item.isexpansion; 50 return true; 51 } 52 if (item.childrens != null && item.childrens.length > 0) 53 { 54 foreach (var child in item.childrens) 55 { 56 var bln = checkexpansionclick(child, mouselocation); 57 if (bln) 58 return bln; 59 } 60 } 61 } 62 return false; 63 }
完整代码
1 // *********************************************************************** 2 // assembly : hzh_controls 3 // created : 2019-09-11 4 // 5 // *********************************************************************** 6 // <copyright file="ucmindmapping.cs"> 7 // copyright by huang zhenghui(黄正辉) all, qq group:568015492 qq:623128629 email:623128629@qq.com 8 // </copyright> 9 // 10 // blog: https://www.cnblogs.com/bfyx 11 // github:https://github.com/kwwwvagaa/netwinformcontrol 12 // gitee:https://gitee.com/kwwwvagaa/net_winform_custom_control.git 13 // 14 // if you use this code, please keep this note. 15 // *********************************************************************** 16 using system; 17 using system.collections.generic; 18 using system.linq; 19 using system.text; 20 using system.windows.forms; 21 using system.drawing; 22 using system.drawing.drawing2d; 23 using system.componentmodel; 24 25 namespace hzh_controls.controls 26 { 27 /// <summary> 28 /// class ucmindmapping. 29 /// implements the <see cref="system.windows.forms.usercontrol" /> 30 /// </summary> 31 /// <seealso cref="system.windows.forms.usercontrol" /> 32 internal class ucmindmapping : usercontrol 33 { 34 /// <summary> 35 /// the line color 36 /// </summary> 37 private color linecolor = color.black; 38 39 /// <summary> 40 /// gets or sets the color of the line. 41 /// </summary> 42 /// <value>the color of the line.</value> 43 [description("线条颜色"), category("自定义")] 44 public color linecolor 45 { 46 get { return linecolor; } 47 set 48 { 49 linecolor = value; 50 refresh(); 51 } 52 } 53 /// <summary> 54 /// the split width 55 /// </summary> 56 private int splitwidth = 50; 57 // private int itemheight = 20; 58 /// <summary> 59 /// the padding 60 /// </summary> 61 private int padding = 20; 62 63 /// <summary> 64 /// the m rect working 65 /// </summary> 66 rectangle m_rectworking = rectangle.empty; 67 /// <summary> 68 /// occurs when [item clicked]. 69 /// </summary> 70 public event eventhandler itemclicked; 71 /// <summary> 72 /// the data source 73 /// </summary> 74 private mindmappingitementity datasource; 75 /// <summary> 76 /// gets or sets the data source. 77 /// </summary> 78 /// <value>the data source.</value> 79 [description("数据源"), category("自定义")] 80 public mindmappingitementity datasource 81 { 82 get { return datasource; } 83 set 84 { 85 datasource = value; 86 87 resetsize(); 88 } 89 } 90 /// <summary> 91 /// initializes a new instance of the <see cref="ucmindmapping"/> class. 92 /// </summary> 93 public ucmindmapping() 94 { 95 this.setstyle(controlstyles.allpaintinginwmpaint, true); 96 this.setstyle(controlstyles.doublebuffer, true); 97 this.setstyle(controlstyles.resizeredraw, true); 98 this.setstyle(controlstyles.selectable, true); 99 this.setstyle(controlstyles.supportstransparentbackcolor, true); 100 this.setstyle(controlstyles.userpaint, true); 101 this.autoscalemode = system.windows.forms.autoscalemode.none; 102 this.click += ucmindmapping_click; 103 this.doubleclick += ucmindmapping_doubleclick; 104 this.load += ucmindmapping_load; 105 } 106 107 /// <summary> 108 /// handles the load event of the ucmindmapping control. 109 /// </summary> 110 /// <param name="sender">the source of the event.</param> 111 /// <param name="e">the <see cref="eventargs"/> instance containing the event data.</param> 112 void ucmindmapping_load(object sender, eventargs e) 113 { 114 if (this.parent != null) 115 { 116 //父控件大小改变时重置大小和位置 117 this.parent.sizechanged += (a, b) => 118 { 119 resetsize(); 120 }; 121 } 122 } 123 124 /// <summary> 125 /// 双击处理,主要用于检测节点双击展开折叠 126 /// </summary> 127 /// <param name="sender">the source of the event.</param> 128 /// <param name="e">the <see cref="eventargs"/> instance containing the event data.</param> 129 void ucmindmapping_doubleclick(object sender, eventargs e) 130 { 131 var mouselocation = this.pointtoclient(control.mouseposition); 132 133 bool bln = checkexpansiondoubleclick(datasource, mouselocation); 134 if (bln) 135 { 136 resetsize(); 137 this.parent.refresh(); 138 } 139 } 140 141 /// <summary> 142 /// 单击处理,主要用于单击节点事件和,展开关闭圆圈处理 143 /// </summary> 144 /// <param name="sender">the source of the event.</param> 145 /// <param name="e">the <see cref="eventargs"/> instance containing the event data.</param> 146 void ucmindmapping_click(object sender, eventargs e) 147 { 148 var mouselocation = this.pointtoclient(control.mouseposition); 149 150 bool bln = checkexpansionclick(datasource, mouselocation); 151 if (bln) 152 { 153 resetsize(); 154 this.parent.refresh(); 155 } 156 } 157 158 /// <summary> 159 /// 双击检查 160 /// </summary> 161 /// <param name="item">the item.</param> 162 /// <param name="mouselocation">the mouse location.</param> 163 /// <returns><c>true</c> if xxxx, <c>false</c> otherwise.</returns> 164 private bool checkexpansiondoubleclick(mindmappingitementity item, point mouselocation) 165 { 166 if (item == null) 167 return false; 168 else 169 { 170 if (item.drawrectangle.contains(mouselocation)) 171 { 172 item.isexpansion = !item.isexpansion; 173 return true; 174 } 175 if (item.childrens != null && item.childrens.length > 0) 176 { 177 foreach (var child in item.childrens) 178 { 179 var bln = checkexpansiondoubleclick(child, mouselocation); 180 if (bln) 181 return bln; 182 } 183 } 184 } 185 return false; 186 } 187 188 /// <summary> 189 /// 单击检查 190 /// </summary> 191 /// <param name="item">the item.</param> 192 /// <param name="mouselocation">the mouse location.</param> 193 /// <returns><c>true</c> if xxxx, <c>false</c> otherwise.</returns> 194 private bool checkexpansionclick(mindmappingitementity item, point mouselocation) 195 { 196 if (item == null) 197 return false; 198 else 199 { 200 if (itemclicked != null && item.workingrectangle.contains(mouselocation)) 201 { 202 itemclicked(item, null); 203 } 204 else if (item.expansionrectangle.contains(mouselocation)) 205 { 206 item.isexpansion = !item.isexpansion; 207 return true; 208 } 209 if (item.childrens != null && item.childrens.length > 0) 210 { 211 foreach (var child in item.childrens) 212 { 213 var bln = checkexpansionclick(child, mouselocation); 214 if (bln) 215 return bln; 216 } 217 } 218 } 219 return false; 220 } 221 222 /// <summary> 223 /// 引发 <see cref="e:system.windows.forms.control.paint" /> 事件。 224 /// </summary> 225 /// <param name="e">包含事件数据的 <see cref="t:system.windows.forms.painteventargs" />。</param> 226 protected override void onpaint(painteventargs e) 227 { 228 try 229 { 230 base.onpaint(e); 231 if (m_rectworking == rectangle.empty || m_rectworking == null) 232 return; 233 var g = e.graphics; 234 g.setgdihigh(); 235 236 int intheight = datasource.allchildrensmaxshowheight; 237 datasource.workingrectangle = new rectanglef(m_rectworking.left, m_rectworking.top + (m_rectworking.height - intheight) / 2, m_rectworking.width, intheight); 238 239 drawitem(datasource, g); 240 } 241 catch (exception exc) 242 { 243 messagebox.show(exc.tostring(), "错误"); 244 } 245 } 246 247 /// <summary> 248 /// 画节点 249 /// </summary> 250 /// <param name="item">the item.</param> 251 /// <param name="g">the g.</param> 252 private void drawitem(mindmappingitementity item, graphics g) 253 { 254 //节点 255 var size = g.measurestring(item.text, item.font); 256 item.drawrectangle = new rectanglef(item.workingrectangle.left + 2, item.workingrectangle.top + (item.workingrectangle.height - size.height) / 2 + 2, size.width + 4, size.height + 4); 257 graphicspath drawpath = item.drawrectangle.createroundedrectanglepath(5); 258 g.fillpath(new solidbrush(item.backcolor), drawpath); 259 g.drawstring(item.text, item.font, new solidbrush(item.forecolor), item.drawrectangle.location.x + 2, item.drawrectangle.location.y + 2); 260 //子节点 261 if (item.childrens != null && item.isexpansion) 262 { 263 for (int i = 0; i < item.childrens.length; i++) 264 { 265 var child = item.childrens[i]; 266 if (i == 0) 267 { 268 child.workingrectangle = new rectanglef(item.drawrectangle.right + splitwidth, item.workingrectangle.top, item.workingrectangle.width - (item.drawrectangle.width + splitwidth), child.allchildrensmaxshowheight); 269 } 270 else 271 { 272 child.workingrectangle = new rectanglef(item.drawrectangle.right + splitwidth, item.childrens[i - 1].workingrectangle.bottom, item.workingrectangle.width - (item.drawrectangle.width + splitwidth), child.allchildrensmaxshowheight); 273 } 274 drawitem(child, g); 275 } 276 } 277 //连线 278 if (item.parentitem != null) 279 { 280 g.drawlines(new pen(new solidbrush(linecolor), 1), new pointf[] 281 { 282 new pointf(item.parentitem.drawrectangle.right,item.parentitem.drawrectangle.top+item.parentitem.drawrectangle.height/2), 283 new pointf(item.parentitem.drawrectangle.right+12,item.parentitem.drawrectangle.top+item.parentitem.drawrectangle.height/2), 284 //new pointf(item.parentitem.drawrectangle.right+12,item.drawrectangle.top+item.drawrectangle.height/2), 285 new pointf(item.drawrectangle.left-12,item.drawrectangle.top+item.drawrectangle.height/2), 286 new pointf(item.drawrectangle.left,item.drawrectangle.top+item.drawrectangle.height/2), 287 }); 288 } 289 //展开折叠按钮 290 if (item.childrens != null && item.childrens.length > 0) 291 { 292 rectanglef _rect = new rectanglef(item.drawrectangle.right + 1, item.drawrectangle.top + (item.drawrectangle.height - 10) / 2, 10, 10); 293 item.expansionrectangle = _rect; 294 g.fillellipse(new solidbrush(this.backcolor), _rect); 295 g.drawellipse(new pen(new solidbrush(color.black)), _rect); 296 g.drawline(new pen(new solidbrush(linecolor)), _rect.left + 2, _rect.y + _rect.height / 2, _rect.right - 2, _rect.y + _rect.height / 2); 297 if (!item.isexpansion) 298 { 299 g.drawline(new pen(new solidbrush(linecolor)), _rect.left + _rect.width / 2, _rect.top + 2, _rect.left + _rect.width / 2, _rect.bottom - 2); 300 } 301 } 302 } 303 304 305 /// <summary> 306 /// 重置大小 307 /// </summary> 308 private void resetsize() 309 { 310 if (this.parent == null) 311 return; 312 try 313 { 314 controlhelper.freezecontrol(this, true); 315 if (datasource == null) 316 { 317 m_rectworking = rectangle.empty; 318 this.size = this.parent.size; 319 } 320 else 321 { 322 int intwidth = datasource.allchildrensmaxshowwidth; 323 int intheight = datasource.allchildrensmaxshowheight; 324 this.width = intwidth + padding * 2; 325 this.height = intheight + padding * 2; 326 if (this.width < this.parent.width) 327 this.width = this.parent.width; 328 m_rectworking = new rectangle(padding, padding, intwidth, intheight); 329 if (this.height > this.parent.height) 330 { 331 //this.location = new point(0, 0); 332 } 333 else 334 this.location = new point(0, (this.parent.height - this.height) / 2); 335 } 336 } 337 finally 338 { 339 controlhelper.freezecontrol(this, false); 340 } 341 } 342 } 343 }
最后再添加一个父控件,来包裹该控件,用以可以有滚动条的处理
添加一个用户控件ucmindmappingpanel,控件上添加一个上面新增的ucmindmapping,
完整代码
1 // *********************************************************************** 2 // assembly : hzh_controls 3 // created : 2019-09-11 4 // 5 // *********************************************************************** 6 // <copyright file="ucmindmappingpanel.cs"> 7 // copyright by huang zhenghui(黄正辉) all, qq group:568015492 qq:623128629 email:623128629@qq.com 8 // </copyright> 9 // 10 // blog: https://www.cnblogs.com/bfyx 11 // github:https://github.com/kwwwvagaa/netwinformcontrol 12 // gitee:https://gitee.com/kwwwvagaa/net_winform_custom_control.git 13 // 14 // if you use this code, please keep this note. 15 // *********************************************************************** 16 using system; 17 using system.collections.generic; 18 using system.componentmodel; 19 using system.drawing; 20 using system.data; 21 using system.linq; 22 using system.text; 23 using system.windows.forms; 24 25 namespace hzh_controls.controls 26 { 27 /// <summary> 28 /// class ucmindmappingpanel. 29 /// implements the <see cref="system.windows.forms.usercontrol" /> 30 /// </summary> 31 /// <seealso cref="system.windows.forms.usercontrol" /> 32 public partial class ucmindmappingpanel : usercontrol 33 { 34 /// <summary> 35 /// the data source 36 /// </summary> 37 private mindmappingitementity datasource; 38 39 /// <summary> 40 /// gets or sets the data source. 41 /// </summary> 42 /// <value>the data source.</value> 43 [description("数据源"), category("自定义")] 44 public mindmappingitementity datasource 45 { 46 get { return datasource; } 47 set 48 { 49 datasource = value; 50 this.ucmindmapping1.datasource = value; 51 } 52 } 53 /// <summary> 54 /// gets or sets the data source. 55 /// </summary> 56 /// <value>the data source.</value> 57 [description("数据源"), category("自定义")] 58 public event eventhandler itemclicked; 59 60 /// <summary> 61 /// the line color 62 /// </summary> 63 private color linecolor = color.black; 64 65 /// <summary> 66 /// gets or sets the color of the line. 67 /// </summary> 68 /// <value>the color of the line.</value> 69 [description("线条颜色"), category("自定义")] 70 public color linecolor 71 { 72 get { return linecolor; } 73 set 74 { 75 linecolor = value; 76 this.ucmindmapping1.linecolor = value; 77 } 78 } 79 /// <summary> 80 /// initializes a new instance of the <see cref="ucmindmappingpanel"/> class. 81 /// </summary> 82 public ucmindmappingpanel() 83 { 84 this.setstyle(controlstyles.allpaintinginwmpaint, true); 85 this.setstyle(controlstyles.doublebuffer, true); 86 this.setstyle(controlstyles.resizeredraw, true); 87 this.setstyle(controlstyles.selectable, true); 88 this.setstyle(controlstyles.supportstransparentbackcolor, true); 89 this.setstyle(controlstyles.userpaint, true); 90 initializecomponent(); 91 ucmindmapping1.itemclicked += ucmindmapping1_itemclicked; 92 } 93 94 void ucmindmapping1_itemclicked(object sender, eventargs e) 95 { 96 if (itemclicked != null) 97 { 98 itemclicked(sender, e); 99 } 100 } 101 } 102 }
1 namespace hzh_controls.controls 2 { 3 partial class ucmindmappingpanel 4 { 5 /// <summary> 6 /// 必需的设计器变量。 7 /// </summary> 8 private system.componentmodel.icontainer components = null; 9 10 /// <summary> 11 /// 清理所有正在使用的资源。 12 /// </summary> 13 /// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param> 14 protected override void dispose(bool disposing) 15 { 16 if (disposing && (components != null)) 17 { 18 components.dispose(); 19 } 20 base.dispose(disposing); 21 } 22 23 #region 组件设计器生成的代码 24 25 /// <summary> 26 /// 设计器支持所需的方法 - 不要 27 /// 使用代码编辑器修改此方法的内容。 28 /// </summary> 29 private void initializecomponent() 30 { 31 this.ucmindmapping1 = new hzh_controls.controls.ucmindmapping(); 32 this.suspendlayout(); 33 // 34 // ucmindmapping1 35 // 36 this.ucmindmapping1.location = new system.drawing.point(0, 0); 37 this.ucmindmapping1.name = "ucmindmapping1"; 38 this.ucmindmapping1.size = new system.drawing.size(200, 200); 39 this.ucmindmapping1.tabindex = 0; 40 // 41 // ucmindmappingpanel 42 // 43 this.autoscalemode = system.windows.forms.autoscalemode.none; 44 this.autoscroll = true; 45 this.backcolor = system.drawing.color.white; 46 this.controls.add(this.ucmindmapping1); 47 this.name = "ucmindmappingpanel"; 48 this.size = new system.drawing.size(200, 200); 49 this.resumelayout(false); 50 51 } 52 53 #endregion 54 55 private ucmindmapping ucmindmapping1; 56 } 57 }
最后的话
如果你喜欢的话,请到 点个星星吧