前提 入行已经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 /// 4 /// Gets or sets the identifier. 5 /// 6 /// The identifier. 7 public string ID { get; set; } 8 private string _text; 9 /// 10 /// Gets or sets the text. 11 /// 12 /// The text. 13 public string Text 14 { 15 get { return _text; } 16 set 17 { 18 _text = value; 19 ResetSize(); 20 } 21 } 22 /// 23 /// Gets or sets the data source. 24 /// 25 /// The data source. 26 public object DataSource { get; set; } 27 /// 28 /// The childrens 29 /// 30 private MindMappingItemEntity[] _Childrens; 31 /// 32 /// Gets or sets the childrens. 33 /// 34 /// The childrens. 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 /// 48 /// The back color 49 /// 50 private Color backColor = Color.Transparent; 51 52 /// 53 /// Gets or sets the color of the back. 54 /// 55 /// The color of the back. 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 /// 75 /// The fore color 76 /// 77 private Color foreColor = Color.Black; 78 79 /// 80 /// Gets or sets the color of the fore. 81 /// 82 /// The color of the fore. 83 public Color ForeColor 84 { 85 get { return foreColor; } 86 set { foreColor = value; } 87 } 88 private bool _IsExpansion = false; 89 /// 90 /// Gets or sets a value indicating whether the instance is expanded. 91 /// 92 /// true if this instance is expansion; otherwise, false. 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 /// 113 /// Gets the parent item. 114 /// 115 /// The parent item. 116 public MindMappingItemEntity ParentItem { get; private set; } 117 /// 118 /// Gets all childrens maximum show count. 119 /// 120 /// All childrens maximum show count. 121 public int AllChildrensMaxShowHeight { get { return GetAllChildrensMaxShowHeight(); } } 122 /// 123 /// Gets the maximum level. 124 /// 125 /// The maximum level. 126 public int AllChildrensMaxShowWidth { get { return GetAllChildrensMaxShowWidth(); } } 127 128 /// 129 /// Gets all childrens maximum show count. 130 /// 131 /// System.Int32. 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 /// 142 /// Gets the maximum level. 143 /// 144 /// System.Int32. 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 /// 155 /// Gets or sets the working rectangle. 156 /// 157 /// The working rectangle. 158 internal RectangleF WorkingRectangle { get; set; } 159 /// 160 /// Gets or sets the draw rectangle. 161 /// 162 /// The draw rectangle. 163 internal RectangleF DrawRectangle { get; set; } 164 /// 165 /// Gets or sets the expansion rectangle. 166 /// 167 /// The expansion rectangle. 168 internal RectangleF ExpansionRectangle { get; set; } 169 /// 170 /// Gets the height of the item. 171 /// 172 /// The height of the item. 173 private int ItemHeight { private get; private set; } 174 /// 175 /// Gets the width of the item. 176 /// 177 /// The width of the item. 178 private int ItemWidth { private get; private set; } 179 /// 180 /// Resets the size. 181 /// 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 /// 2 /// The line color 3 /// 4 private Color lineColor = Color.Black; 5 6 /// 7 /// Gets or sets the color of the line. 8 /// 9 /// The color of the line. 10 [Description("线条颜色"), Category("自定义")] 11 public Color LineColor 12 { 13 get { return lineColor; } 14 set 15 { 16 lineColor = value; 17 Refresh(); 18 } 19 } 20 /// 21 /// The split width 22 /// 23 private int splitWidth = 50; 24 // private int itemHeight = 20; 25 /// 26 /// The padding 27 /// 28 private int padding = 20; 29 30 /// 31 /// The m rect working 32 /// 33 Rectangle m_rectWorking = Rectangle.Empty; 34 /// 35 /// Occurs when [item clicked]. 36 /// 37 public event EventHandler ItemClicked; 38 /// 39 /// The data source 40 /// 41 private MindMappingItemEntity dataSource; 42 /// 43 /// Gets or sets the data source. 44 /// 45 /// The data source. 46 [Description("数据源"), Category("自定义")] 47 public MindMappingItemEntity DataSource 48 { 49 get { return dataSource; } 50 set 51 { 52 dataSource = value; 53 54 ResetSize(); 55 } 56 } 复制代码 一个辅助函数,用以在大小过数据改变时计算工作区域和位置 复制代码 1 /// 2 /// 重置大小 3 /// 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 /// 2 /// 引发 事件。 3 /// 4 /// 包含事件数据的 。 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 /// 2 /// 画节点 3 /// 4 /// The item. 5 /// The g. 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.WorkingRectang