欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

(六十五)c#Winform自定义控件-思维导图/组织架构图(工业)

程序员文章站 2022-07-04 23:30:42
前提 入行已经7,8年了,一直想做一套漂亮点的自定义控件,于是就有了本系列文章。 GitHub:https://github.com/kwwwvagaa/NetWinformControl 码云:https://gitee.com/kwwwvagaa/net_winform_custom_contr ......

前提

入行已经7,8年了,一直想做一套漂亮点的自定义控件,于是就有了本系列文章。

github:https://github.com/kwwwvagaa/netwinformcontrol

码云:

如果觉得写的还行,请点个 star 支持一下吧

欢迎前来交流探讨: 企鹅群568015492 

麻烦博客下方点个【推荐】,谢谢

nuget

install-package hzh_controls

目录

用处及效果

(六十五)c#Winform自定义控件-思维导图/组织架构图(工业)

准备工作

依然是用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 }

最后的话

如果你喜欢的话,请到  点个星星吧