前提 入行已經7,8年了,一直想做一套漂亮點的自定義控制項,於是就有了本系列文章。 GitHub:https://github.com/kwwwvagaa/NetWinformControl 碼雲:https://gitee.com/kwwwvagaa/net_winform_custom_contr ...
前提
入行已經7,8年了,一直想做一套漂亮點的自定義控制項,於是就有了本系列文章。
GitHub:https://github.com/kwwwvagaa/NetWinformControl
碼雲:https://gitee.com/kwwwvagaa/net_winform_custom_control.git
如果覺得寫的還行,請點個 star 支持一下吧
麻煩博客下方點個【推薦】,謝謝
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 ///