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

前提

入行已经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://blog.csdn.net/kwwwvagaa/article/details/100586547

用处及效果

准备工作

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

复制代码

完整代码

// ***********************************************************************
// Assembly         : HZH_Controls
// Created          : 2019-09-11
//
// ***********************************************************************
// <copyright file="UCMindMapping.cs">
//     Copyright by Huang Zhenghui(黄正辉) All, QQ group:568015492 QQ:623128629 Email:623128629@qq.com
// </copyright>
//
// Blog: https://www.cnblogs.com/bfyx
// GitHub:https://github.com/kwwwvagaa/NetWinformControl
// gitee:https://gitee.com/kwwwvagaa/net_winform_custom_control.git
//
// If you use this code, please keep this note.
// ***********************************************************************
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.ComponentModel;

namespace HZH_Controls.Controls
{
    /// <summary>
    /// Class UCMindMapping.
    /// Implements the <see cref="System.Windows.Forms.UserControl" />
    /// </summary>
    /// <seealso cref="System.Windows.Forms.UserControl" />
    internal class UCMindMapping : UserControl
    {
        /// <summary>
        /// The line color
        /// </summary>
        private Color lineColor = Color.Black;

        /// <summary>
        /// Gets or sets the color of the line.
        /// </summary>
        /// <value>The color of the line.</value>
        [Description("线条颜色"), Category("自定义")]
        public Color LineColor
        {
            get { return lineColor; }
            set
            {
                lineColor = value;
                Refresh();
            }
        }
        /// <summary>
        /// The split width
        /// </summary>
        private int splitWidth = 50;
        // private int itemHeight = 20;
        /// <summary>
        /// The padding
        /// </summary>
        private int padding = 20;

        /// <summary>
        /// The m rect working
        /// </summary>
        Rectangle m_rectWorking = Rectangle.Empty;
        /// <summary>
        /// Occurs when [item clicked].
        /// </summary>
        public event EventHandler ItemClicked;
        /// <summary>
        /// The data source
        /// </summary>
        private MindMappingItemEntity dataSource;
        /// <summary>
        /// Gets or sets the data source.
        /// </summary>
        /// <value>The data source.</value>
        [Description("数据源"), Category("自定义")]
        public MindMappingItemEntity DataSource
        {
            get { return dataSource; }
            set
            {
                dataSource = value;

                ResetSize();
            }
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="UCMindMapping"/> class.
        /// </summary>
        public UCMindMapping()
        {
            this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            this.SetStyle(ControlStyles.DoubleBuffer, true);
            this.SetStyle(ControlStyles.ResizeRedraw, true);
            this.SetStyle(ControlStyles.Selectable, true);
            this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
            this.SetStyle(ControlStyles.UserPaint, true);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
            this.Click += UCMindMapping_Click;
            this.DoubleClick += UCMindMapping_DoubleClick;
            this.Load += UCMindMapping_Load;
        }

        /// <summary>
        /// Handles the Load event of the UCMindMapping control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
        void UCMindMapping_Load(object sender, EventArgs e)
        {
            if (this.Parent != null)
            {
                //父控件大小改变时重置大小和位置
                this.Parent.SizeChanged += (a, b) =>
                {
                    ResetSize();
                };
            }
        }

        /// <summary>
        /// 双击处理,主要用于检测节点双击展开折叠
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
        void UCMindMapping_DoubleClick(object sender, EventArgs e)
        {
            var mouseLocation = this.PointToClient(Control.MousePosition);

            bool bln = CheckExpansionDoubleClick(dataSource, mouseLocation);
            if (bln)
            {
                ResetSize();
                this.Parent.Refresh();
            }
        }

        /// <summary>
        /// 单击处理,主要用于单击节点事件和,展开关闭圆圈处理
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
        void UCMindMapping_Click(object sender, EventArgs e)
        {
            var mouseLocation = this.PointToClient(Control.MousePosition);

            bool bln = CheckExpansionClick(dataSource, mouseLocation);
            if (bln)
            {
                ResetSize();
                this.Parent.Refresh();
            }
        }

        /// <summary>
        /// 双击检查
        /// </summary>
        /// <param name="item">The item.</param>
        /// <param name="mouseLocation">The mouse location.</param>
        /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
        private bool CheckExpansionDoubleClick(MindMappingItemEntity item, Point mouseLocation)
        {
            if (item == null)
                return false;
            else
            {
                if (item.DrawRectangle.Contains(mouseLocation))
                {
                    item.IsExpansion = !item.IsExpansion;
                    return true;
                }
                if (item.Childrens != null && item.Childrens.Length > 0)
                {
                    foreach (var child in item.Childrens)
                    {
                        var bln = CheckExpansionDoubleClick(child, mouseLocation);
                        if (bln)
                            return bln;
                    }
                }
            }
            return false;
        }

        /// <summary>
        /// 单击检查
        /// </summary>
        /// <param name="item">The item.</param>
        /// <param name="mouseLocation">The mouse location.</param>
        /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
        private bool CheckExpansionClick(MindMappingItemEntity item, Point mouseLocation)
        {
            if (item == null)
                return false;
            else
            {
                if (ItemClicked != null && item.WorkingRectangle.Contains(mouseLocation))
                {
                    ItemClicked(item, null);
                }
                else if (item.ExpansionRectangle.Contains(mouseLocation))
                {
                    item.IsExpansion = !item.IsExpansion;
                    return true;
                }
                if (item.Childrens != null && item.Childrens.Length > 0)
                {
                    foreach (var child in item.Childrens)
                    {
                        var bln = CheckExpansionClick(child, mouseLocation);
                        if (bln)
                            return bln;
                    }
                }
            }
            return false;
        }

        /// <summary>
        /// 引发 <see cref="E:System.Windows.Forms.Control.Paint" /> 事件。
        /// </summary>
        /// <param name="e">包含事件数据的 <see cref="T:System.Windows.Forms.PaintEventArgs" />。</param>
        protected override void OnPaint(PaintEventArgs e)
        {
            try
            {
                base.OnPaint(e);
                if (m_rectWorking == Rectangle.Empty || m_rectWorking == null)
                    return;
                var g = e.Graphics;
                g.SetGDIHigh();

                int intHeight = dataSource.AllChildrensMaxShowHeight;
                dataSource.WorkingRectangle = new RectangleF(m_rectWorking.Left, m_rectWorking.Top + (m_rectWorking.Height - intHeight) / 2, m_rectWorking.Width, intHeight);

                DrawItem(dataSource, g);
            }
            catch (Exception exc)
            {
                MessageBox.Show(exc.ToString(), "错误");
            }
        }

        /// <summary>
        /// 画节点
        /// </summary>
        /// <param name="item">The item.</param>
        /// <param name="g">The g.</param>
        private void DrawItem(MindMappingItemEntity item, Graphics g)
        {
            //节点
            var size = g.MeasureString(item.Text, item.Font);
            item.DrawRectangle = new RectangleF(item.WorkingRectangle.Left + 2, item.WorkingRectangle.Top + (item.WorkingRectangle.Height - size.Height) / 2 + 2, size.Width + 4, size.Height + 4);
            GraphicsPath drawPath = item.DrawRectangle.CreateRoundedRectanglePath(5);
            g.FillPath(new SolidBrush(item.BackColor), drawPath);
            g.DrawString(item.Text, item.Font, new SolidBrush(item.ForeColor), item.DrawRectangle.Location.X + 2, item.DrawRectangle.Location.Y + 2);
            //子节点
            if (item.Childrens != null && item.IsExpansion)
            {
                for (int i = 0; i < item.Childrens.Length; i++)
                {
                    var child = item.Childrens[i];
                    if (i == 0)
                    {
                        child.WorkingRectangle = new RectangleF(item.DrawRectangle.Right + splitWidth, item.WorkingRectangle.Top, item.WorkingRectangle.Width - (item.DrawRectangle.Width + splitWidth), child.AllChildrensMaxShowHeight);
                    }
                    else
                    {
                        child.WorkingRectangle = new RectangleF(item.DrawRectangle.Right + splitWidth, item.Childrens[i - 1].WorkingRectangle.Bottom, item.WorkingRectangle.Width - (item.DrawRectangle.Width + splitWidth), child.AllChildrensMaxShowHeight);
                    }
                    DrawItem(child, g);
                }
            }
            //连线
            if (item.ParentItem != null)
            {
                g.DrawLines(new Pen(new SolidBrush(lineColor), 1), new PointF[] 
                { 
                    new PointF(item.ParentItem.DrawRectangle.Right,item.ParentItem.DrawRectangle.Top+item.ParentItem.DrawRectangle.Height/2),
                    new PointF(item.ParentItem.DrawRectangle.Right+12,item.ParentItem.DrawRectangle.Top+item.ParentItem.DrawRectangle.Height/2),
                    //new PointF(item.ParentItem.DrawRectangle.Right+12,item.DrawRectangle.Top+item.DrawRectangle.Height/2),                    
                    new PointF(item.DrawRectangle.Left-12,item.DrawRectangle.Top+item.DrawRectangle.Height/2),                 
                    new PointF(item.DrawRectangle.Left,item.DrawRectangle.Top+item.DrawRectangle.Height/2),
                });
            }
            //展开折叠按钮
            if (item.Childrens != null && item.Childrens.Length > 0)
            {
                RectangleF _rect = new RectangleF(item.DrawRectangle.Right + 1, item.DrawRectangle.Top + (item.DrawRectangle.Height - 10) / 2, 10, 10);
                item.ExpansionRectangle = _rect;
                g.FillEllipse(new SolidBrush(this.BackColor), _rect);
                g.DrawEllipse(new Pen(new SolidBrush(Color.Black)), _rect);
                g.DrawLine(new Pen(new SolidBrush(lineColor)), _rect.Left + 2, _rect.Y + _rect.Height / 2, _rect.Right - 2, _rect.Y + _rect.Height / 2);
                if (!item.IsExpansion)
                {
                    g.DrawLine(new Pen(new SolidBrush(lineColor)), _rect.Left + _rect.Width / 2, _rect.Top + 2, _rect.Left + _rect.Width / 2, _rect.Bottom - 2);
                }
            }
        }


        /// <summary>
        /// 重置大小
        /// </summary>
        private void ResetSize()
        {
            if (this.Parent == null)
                return;
            try
            {
                ControlHelper.FreezeControl(this, true);
                if (dataSource == null)
                {
                    m_rectWorking = Rectangle.Empty;
                    this.Size = this.Parent.Size;
                }
                else
                {
                    int intWidth = dataSource.AllChildrensMaxShowWidth;
                    int intHeight = dataSource.AllChildrensMaxShowHeight;
                    this.Width = intWidth + padding * 2;
                    this.Height = intHeight + padding * 2;
                    if (this.Width < this.Parent.Width)
                        this.Width = this.Parent.Width;
                    m_rectWorking = new Rectangle(padding, padding, intWidth, intHeight);
                    if (this.Height > this.Parent.Height)
                    {
                        //this.Location = new Point(0, 0);
                    }
                    else
                        this.Location = new Point(0, (this.Parent.Height - this.Height) / 2);
                }
            }
            finally
            {
                ControlHelper.FreezeControl(this, false);
            }
        }
    }
}

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

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

复制代码

namespace HZH_Controls.Controls
{
    partial class UCMindMappingPanel
    {
        /// <summary> 
        /// 必需的设计器变量。
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary> 
        /// 清理所有正在使用的资源。
        /// </summary>
        /// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region 组件设计器生成的代码

        /// <summary> 
        /// 设计器支持所需的方法 - 不要
        /// 使用代码编辑器修改此方法的内容。
        /// </summary>
        private void InitializeComponent()
        {
            this.ucMindMapping1 = new HZH_Controls.Controls.UCMindMapping();
            this.SuspendLayout();
            // 
            // ucMindMapping1
            // 
            this.ucMindMapping1.Location = new System.Drawing.Point(0, 0);
            this.ucMindMapping1.Name = "ucMindMapping1";
            this.ucMindMapping1.Size = new System.Drawing.Size(200, 200);
            this.ucMindMapping1.TabIndex = 0;
            // 
            // UCMindMappingPanel
            // 
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
            this.AutoScroll = true;
            this.BackColor = System.Drawing.Color.White;
            this.Controls.Add(this.ucMindMapping1);
            this.Name = "UCMindMappingPanel";
            this.Size = new System.Drawing.Size(200, 200);
            this.ResumeLayout(false);

        }

        #endregion

        private UCMindMapping ucMindMapping1;
    }
}

最后的话

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

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值