官网

http://www.hzhcontrols.com

前提

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

GitHub:https://github.com/kwwwvagaa/NetWinformControl

码云:https://gitee.com/kwwwvagaa/net_winform_custom_control.git

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

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

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

NuGet

Install-Package HZH_Controls

目录

https://www.cnblogs.com/bfyx/p/11364884.html

用处及效果

准备工作

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

View Code

最后再添加一个父控件,来包裹该控件,用以可以有滚动条的处理

添加一个用户控件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 }

View Code

最后的话

如果你喜欢的话,请到 https://gitee.com/kwwwvagaa/net_winform_custom_control 点个星星吧

版权声明:本文为bfyx原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/bfyx/p/11506080.html