TabControlExt.cs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541
  1. // ***********************************************************************
  2. // Assembly : HZH_Controls
  3. // Created : 08-17-2019
  4. //
  5. // ***********************************************************************
  6. // <copyright file="TabControlExt.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.Diagnostics;
  20. using System.Drawing;
  21. using System.Drawing.Drawing2D;
  22. using System.Linq;
  23. using System.Runtime.InteropServices;
  24. using System.Text;
  25. using System.Windows.Forms;
  26. namespace HZH_Controls.Controls
  27. {
  28. /// <summary>
  29. /// Class TabControlExt.
  30. /// Implements the <see cref="System.Windows.Forms.TabControl" />
  31. /// </summary>
  32. /// <seealso cref="System.Windows.Forms.TabControl" />
  33. public class TabControlExt : TabControl
  34. {
  35. /// <summary>
  36. /// Initializes a new instance of the <see cref="TabControlExt" /> class.
  37. /// </summary>
  38. public TabControlExt()
  39. : base()
  40. {
  41. SetStyles();
  42. //this.Multiline = true;
  43. this.ItemSize = new Size(this.ItemSize.Width, 50);
  44. }
  45. /// <summary>
  46. /// Sets the styles.
  47. /// </summary>
  48. private void SetStyles()
  49. {
  50. base.SetStyle(
  51. ControlStyles.UserPaint |
  52. ControlStyles.DoubleBuffer |
  53. ControlStyles.OptimizedDoubleBuffer |
  54. ControlStyles.AllPaintingInWmPaint |
  55. ControlStyles.ResizeRedraw |
  56. ControlStyles.SupportsTransparentBackColor, true);
  57. base.UpdateStyles();
  58. }
  59. /// <summary>
  60. /// Gets or sets a value indicating whether this instance is show close BTN.
  61. /// </summary>
  62. /// <value><c>true</c> if this instance is show close BTN; otherwise, <c>false</c>.</value>
  63. [Description("是否显示关闭按钮"), Category("自定义")]
  64. public bool IsShowCloseBtn { get; set; }
  65. /// <summary>
  66. /// The back color
  67. /// </summary>
  68. private Color _backColor = Color.White;
  69. /// <summary>
  70. /// 此成员对于此控件无意义。
  71. /// </summary>
  72. /// <value>The color of the back.</value>
  73. [Browsable(true)]
  74. [EditorBrowsable(EditorBrowsableState.Always)]
  75. [DefaultValue(typeof(Color), "White")]
  76. public override Color BackColor
  77. {
  78. get { return _backColor; }
  79. set
  80. {
  81. _backColor = value;
  82. base.Invalidate(true);
  83. }
  84. }
  85. /// <summary>
  86. /// The border color
  87. /// </summary>
  88. private Color _borderColor = Color.FromArgb(232, 232, 232);
  89. /// <summary>
  90. /// Gets or sets the color of the border.
  91. /// </summary>
  92. /// <value>The color of the border.</value>
  93. [DefaultValue(typeof(Color), "232, 232, 232")]
  94. [Description("TabContorl边框色")]
  95. public Color BorderColor
  96. {
  97. get { return _borderColor; }
  98. set
  99. {
  100. _borderColor = value;
  101. base.Invalidate(true);
  102. }
  103. }
  104. /// <summary>
  105. /// The head selected back color
  106. /// </summary>
  107. private Color _headSelectedBackColor = Color.FromArgb(255, 85, 51);
  108. /// <summary>
  109. /// Gets or sets the color of the head selected back.
  110. /// </summary>
  111. /// <value>The color of the head selected back.</value>
  112. [DefaultValue(typeof(Color), "255, 85, 51")]
  113. [Description("TabPage头部选中后的背景颜色")]
  114. public Color HeadSelectedBackColor
  115. {
  116. get { return _headSelectedBackColor; }
  117. set { _headSelectedBackColor = value; }
  118. }
  119. /// <summary>
  120. /// The head selected border color
  121. /// </summary>
  122. private Color _headSelectedBorderColor = Color.FromArgb(232, 232, 232);
  123. /// <summary>
  124. /// Gets or sets the color of the head selected border.
  125. /// </summary>
  126. /// <value>The color of the head selected border.</value>
  127. [DefaultValue(typeof(Color), "232, 232, 232")]
  128. [Description("TabPage头部选中后的边框颜色")]
  129. public Color HeadSelectedBorderColor
  130. {
  131. get { return _headSelectedBorderColor; }
  132. set { _headSelectedBorderColor = value; }
  133. }
  134. /// <summary>
  135. /// The header back color
  136. /// </summary>
  137. private Color _headerBackColor = Color.White;
  138. /// <summary>
  139. /// Gets or sets the color of the header back.
  140. /// </summary>
  141. /// <value>The color of the header back.</value>
  142. [DefaultValue(typeof(Color), "White")]
  143. [Description("TabPage头部默认背景颜色")]
  144. public Color HeaderBackColor
  145. {
  146. get { return _headerBackColor; }
  147. set { _headerBackColor = value; }
  148. }
  149. /// <summary>
  150. /// 绘制控件的背景。
  151. /// </summary>
  152. /// <param name="pevent">包含有关要绘制的控件的信息的 <see cref="T:System.Windows.Forms.PaintEventArgs" />。</param>
  153. protected override void OnPaintBackground(PaintEventArgs pevent)
  154. {
  155. if (this.DesignMode == true)
  156. {
  157. LinearGradientBrush backBrush = new LinearGradientBrush(
  158. this.Bounds,
  159. SystemColors.ControlLightLight,
  160. SystemColors.ControlLight,
  161. LinearGradientMode.Vertical);
  162. pevent.Graphics.FillRectangle(backBrush, this.Bounds);
  163. backBrush.Dispose();
  164. }
  165. else
  166. {
  167. this.PaintTransparentBackground(pevent.Graphics, this.ClientRectangle);
  168. }
  169. }
  170. /// <summary>
  171. /// TabContorl 背景色设置
  172. /// </summary>
  173. /// <param name="g">The g.</param>
  174. /// <param name="clipRect">The clip rect.</param>
  175. protected void PaintTransparentBackground(Graphics g, Rectangle clipRect)
  176. {
  177. if ((this.Parent != null))
  178. {
  179. clipRect.Offset(this.Location);
  180. PaintEventArgs e = new PaintEventArgs(g, clipRect);
  181. GraphicsState state = g.Save();
  182. g.SmoothingMode = SmoothingMode.HighSpeed;
  183. try
  184. {
  185. g.TranslateTransform((float)-this.Location.X, (float)-this.Location.Y);
  186. this.InvokePaintBackground(this.Parent, e);
  187. this.InvokePaint(this.Parent, e);
  188. }
  189. finally
  190. {
  191. g.Restore(state);
  192. clipRect.Offset(-this.Location.X, -this.Location.Y);
  193. //新加片段,待测试
  194. using (SolidBrush brush = new SolidBrush(_backColor))
  195. {
  196. clipRect.Inflate(1, 1);
  197. g.FillRectangle(brush, clipRect);
  198. }
  199. }
  200. }
  201. else
  202. {
  203. System.Drawing.Drawing2D.LinearGradientBrush backBrush = new System.Drawing.Drawing2D.LinearGradientBrush(this.Bounds, SystemColors.ControlLightLight, SystemColors.ControlLight, System.Drawing.Drawing2D.LinearGradientMode.Vertical);
  204. g.FillRectangle(backBrush, this.Bounds);
  205. backBrush.Dispose();
  206. }
  207. }
  208. /// <summary>
  209. /// 引发 <see cref="E:System.Windows.Forms.Control.Paint" /> 事件。
  210. /// </summary>
  211. /// <param name="e">包含事件数据的 <see cref="T:System.Windows.Forms.PaintEventArgs" />。</param>
  212. protected override void OnPaint(PaintEventArgs e)
  213. {
  214. // Paint the Background
  215. base.OnPaint(e);
  216. this.PaintTransparentBackground(e.Graphics, this.ClientRectangle);
  217. this.PaintAllTheTabs(e);
  218. this.PaintTheTabPageBorder(e);
  219. this.PaintTheSelectedTab(e);
  220. }
  221. /// <summary>
  222. /// Paints all the tabs.
  223. /// </summary>
  224. /// <param name="e">The <see cref="System.Windows.Forms.PaintEventArgs" /> instance containing the event data.</param>
  225. private void PaintAllTheTabs(System.Windows.Forms.PaintEventArgs e)
  226. {
  227. if (this.TabCount > 0)
  228. {
  229. for (int index = 0; index < this.TabCount; index++)
  230. {
  231. this.PaintTab(e, index);
  232. }
  233. }
  234. }
  235. /// <summary>
  236. /// Paints the tab.
  237. /// </summary>
  238. /// <param name="e">The <see cref="System.Windows.Forms.PaintEventArgs" /> instance containing the event data.</param>
  239. /// <param name="index">The index.</param>
  240. private void PaintTab(System.Windows.Forms.PaintEventArgs e, int index)
  241. {
  242. GraphicsPath path = this.GetTabPath(index);
  243. this.PaintTabBackground(e.Graphics, index, path);
  244. this.PaintTabBorder(e.Graphics, index, path);
  245. this.PaintTabText(e.Graphics, index);
  246. this.PaintTabImage(e.Graphics, index);
  247. if (IsShowCloseBtn)
  248. {
  249. Rectangle rect = this.GetTabRect(index);
  250. e.Graphics.DrawLine(new Pen(_borderColor, 1F), new Point(rect.Right - 15, rect.Top + 5), new Point(rect.Right - 5, rect.Top + 15));
  251. e.Graphics.DrawLine(new Pen(_borderColor, 1F), new Point(rect.Right - 5, rect.Top + 5), new Point(rect.Right - 15, rect.Top + 15));
  252. }
  253. }
  254. /// <summary>
  255. /// 设置选项卡头部颜色
  256. /// </summary>
  257. /// <param name="graph">The graph.</param>
  258. /// <param name="index">The index.</param>
  259. /// <param name="path">The path.</param>
  260. private void PaintTabBackground(System.Drawing.Graphics graph, int index, System.Drawing.Drawing2D.GraphicsPath path)
  261. {
  262. Rectangle rect = this.GetTabRect(index);
  263. if (rect.Width == 0 || rect.Height == 0) return;
  264. System.Drawing.Brush buttonBrush = new System.Drawing.Drawing2D.LinearGradientBrush(rect, _headerBackColor, _headerBackColor, LinearGradientMode.Vertical); //非选中时候的 TabPage 页头部背景色
  265. graph.FillPath(buttonBrush, path);
  266. //if (index == this.SelectedIndex)
  267. //{
  268. // //buttonBrush = new System.Drawing.SolidBrush(_headSelectedBackColor);
  269. // graph.DrawLine(new Pen(_headerBackColor), rect.Right+2, rect.Bottom, rect.Left + 1, rect.Bottom);
  270. //}
  271. buttonBrush.Dispose();
  272. }
  273. /// <summary>
  274. /// 设置选项卡头部边框色
  275. /// </summary>
  276. /// <param name="graph">The graph.</param>
  277. /// <param name="index">The index.</param>
  278. /// <param name="path">The path.</param>
  279. private void PaintTabBorder(System.Drawing.Graphics graph, int index, System.Drawing.Drawing2D.GraphicsPath path)
  280. {
  281. Pen borderPen = new Pen(_borderColor);// TabPage 非选中时候的 TabPage 头部边框色
  282. if (index == this.SelectedIndex)
  283. {
  284. borderPen = new Pen(_headSelectedBorderColor); // TabPage 选中后的 TabPage 头部边框色
  285. }
  286. graph.DrawPath(borderPen, path);
  287. borderPen.Dispose();
  288. }
  289. /// <summary>
  290. /// Paints the tab image.
  291. /// </summary>
  292. /// <param name="g">The g.</param>
  293. /// <param name="index">The index.</param>
  294. private void PaintTabImage(System.Drawing.Graphics g, int index)
  295. {
  296. Image tabImage = null;
  297. if (this.TabPages[index].ImageIndex > -1 && this.ImageList != null)
  298. {
  299. tabImage = this.ImageList.Images[this.TabPages[index].ImageIndex];
  300. }
  301. else if (this.TabPages[index].ImageKey.Trim().Length > 0 && this.ImageList != null)
  302. {
  303. tabImage = this.ImageList.Images[this.TabPages[index].ImageKey];
  304. }
  305. if (tabImage != null)
  306. {
  307. Rectangle rect = this.GetTabRect(index);
  308. g.DrawImage(tabImage, rect.Right - rect.Height - 4, 4, rect.Height - 2, rect.Height - 2);
  309. }
  310. }
  311. /// <summary>
  312. /// Paints the tab text.
  313. /// </summary>
  314. /// <param name="graph">The graph.</param>
  315. /// <param name="index">The index.</param>
  316. private void PaintTabText(System.Drawing.Graphics graph, int index)
  317. {
  318. string tabtext = this.TabPages[index].Text;
  319. System.Drawing.StringFormat format = new System.Drawing.StringFormat();
  320. format.Alignment = StringAlignment.Near;
  321. format.LineAlignment = StringAlignment.Center;
  322. format.Trimming = StringTrimming.EllipsisCharacter;
  323. Brush forebrush = null;
  324. if (this.TabPages[index].Enabled == false)
  325. {
  326. forebrush = SystemBrushes.ControlDark;
  327. }
  328. else
  329. {
  330. forebrush = SystemBrushes.ControlText;
  331. }
  332. Font tabFont = this.Font;
  333. if (index == this.SelectedIndex)
  334. {
  335. if (this.TabPages[index].Enabled != false)
  336. {
  337. forebrush = new SolidBrush(_headSelectedBackColor);
  338. }
  339. }
  340. Rectangle rect = this.GetTabRect(index);
  341. var txtSize = ControlHelper.GetStringWidth(tabtext, graph, tabFont);
  342. Rectangle rect2 = new Rectangle(rect.Left + (rect.Width - txtSize) / 2 - 1, rect.Top, rect.Width, rect.Height);
  343. graph.DrawString(tabtext, tabFont, forebrush, rect2, format);
  344. }
  345. /// <summary>
  346. /// 设置 TabPage 内容页边框色
  347. /// </summary>
  348. /// <param name="e">The <see cref="System.Windows.Forms.PaintEventArgs" /> instance containing the event data.</param>
  349. private void PaintTheTabPageBorder(System.Windows.Forms.PaintEventArgs e)
  350. {
  351. if (this.TabCount > 0)
  352. {
  353. Rectangle borderRect = this.TabPages[0].Bounds;
  354. //borderRect.Inflate(1, 1);
  355. Rectangle rect = new Rectangle(borderRect.X - 2, borderRect.Y - 1, borderRect.Width + 5, borderRect.Height + 2);
  356. ControlPaint.DrawBorder(e.Graphics, rect, this.BorderColor, ButtonBorderStyle.Solid);
  357. }
  358. }
  359. /// <summary>
  360. /// Paints the selected tab.
  361. /// </summary>
  362. /// <param name="e">The <see cref="System.Windows.Forms.PaintEventArgs" /> instance containing the event data.</param>
  363. private void PaintTheSelectedTab(System.Windows.Forms.PaintEventArgs e)
  364. {
  365. if (this.SelectedIndex == -1)
  366. return;
  367. Rectangle selrect;
  368. int selrectRight = 0;
  369. selrect = this.GetTabRect(this.SelectedIndex);
  370. selrectRight = selrect.Right;
  371. e.Graphics.DrawLine(new Pen(_headSelectedBackColor), selrect.Left, selrect.Bottom + 1, selrectRight, selrect.Bottom + 1);
  372. }
  373. /// <summary>
  374. /// Gets the tab path.
  375. /// </summary>
  376. /// <param name="index">The index.</param>
  377. /// <returns>GraphicsPath.</returns>
  378. private GraphicsPath GetTabPath(int index)
  379. {
  380. System.Drawing.Drawing2D.GraphicsPath path = new System.Drawing.Drawing2D.GraphicsPath();
  381. path.Reset();
  382. Rectangle rect = this.GetTabRect(index);
  383. switch (Alignment)
  384. {
  385. case TabAlignment.Top:
  386. break;
  387. case TabAlignment.Bottom:
  388. break;
  389. case TabAlignment.Left:
  390. break;
  391. case TabAlignment.Right:
  392. break;
  393. }
  394. path.AddLine(rect.Left, rect.Top, rect.Left, rect.Bottom + 1);
  395. path.AddLine(rect.Left, rect.Top, rect.Right, rect.Top);
  396. path.AddLine(rect.Right, rect.Top, rect.Right, rect.Bottom + 1);
  397. path.AddLine(rect.Right, rect.Bottom + 1, rect.Left, rect.Bottom + 1);
  398. return path;
  399. }
  400. /// <summary>
  401. /// Sends the message.
  402. /// </summary>
  403. /// <param name="hWnd">The h WND.</param>
  404. /// <param name="Msg">The MSG.</param>
  405. /// <param name="wParam">The w parameter.</param>
  406. /// <param name="lParam">The l parameter.</param>
  407. /// <returns>IntPtr.</returns>
  408. [DllImport("user32.dll")]
  409. private static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
  410. /// <summary>
  411. /// The wm setfont
  412. /// </summary>
  413. private const int WM_SETFONT = 0x30;
  414. /// <summary>
  415. /// The wm fontchange
  416. /// </summary>
  417. private const int WM_FONTCHANGE = 0x1d;
  418. /// <summary>
  419. /// 引发 <see cref="M:System.Windows.Forms.Control.CreateControl" /> 方法。
  420. /// </summary>
  421. protected override void OnCreateControl()
  422. {
  423. base.OnCreateControl();
  424. this.OnFontChanged(EventArgs.Empty);
  425. }
  426. /// <summary>
  427. /// 引发 <see cref="E:System.Windows.Forms.Control.FontChanged" /> 事件。
  428. /// </summary>
  429. /// <param name="e">包含事件数据的 <see cref="T:System.EventArgs" />。</param>
  430. protected override void OnFontChanged(EventArgs e)
  431. {
  432. base.OnFontChanged(e);
  433. IntPtr hFont = this.Font.ToHfont();
  434. SendMessage(this.Handle, WM_SETFONT, hFont, (IntPtr)(-1));
  435. SendMessage(this.Handle, WM_FONTCHANGE, IntPtr.Zero, IntPtr.Zero);
  436. this.UpdateStyles();
  437. }
  438. /// <summary>
  439. /// 此成员重写 <see cref="M:System.Windows.Forms.Control.WndProc(System.Windows.Forms.Message@)" />。
  440. /// </summary>
  441. /// <param name="m">一个 Windows 消息对象。</param>
  442. protected override void WndProc(ref Message m)
  443. {
  444. if (m.Msg == 0x0201) // WM_LBUTTONDOWN
  445. {
  446. if (!DesignMode)
  447. {
  448. if (IsShowCloseBtn)
  449. {
  450. var mouseLocation = this.PointToClient(Control.MousePosition);
  451. int index = GetMouseDownTabHead(mouseLocation);
  452. if (index >= 0)
  453. {
  454. Rectangle rect = this.GetTabRect(index);
  455. var closeRect = new Rectangle(rect.Right - 15, rect.Top + 5, 10, 10);
  456. if (closeRect.Contains(mouseLocation))
  457. {
  458. this.TabPages.RemoveAt(index);
  459. return;
  460. }
  461. }
  462. }
  463. }
  464. }
  465. base.WndProc(ref m);
  466. }
  467. /// <summary>
  468. /// 在调度键盘或输入消息之前,在消息循环内对它们进行预处理。
  469. /// </summary>
  470. /// <param name="msg">通过引用传递的 <see cref="T:System.Windows.Forms.Message" />,它表示要处理的消息。可能的值有 WM_KEYDOWN、WM_SYSKEYDOWN、WM_CHAR 和 WM_SYSCHAR。</param>
  471. /// <returns>如果消息已由控件处理,则为 true;否则为 false。</returns>
  472. /// <PermissionSet>
  473. /// <IPermission class="System.Security.Permissions.EnvironmentPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" />
  474. /// <IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" />
  475. /// <IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="UnmanagedCode, ControlEvidence" />
  476. /// <IPermission class="System.Diagnostics.PerformanceCounterPermission, System, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" />
  477. /// </PermissionSet>
  478. public override bool PreProcessMessage(ref Message msg)
  479. {
  480. return base.PreProcessMessage(ref msg);
  481. }
  482. /// <summary>
  483. /// Gets the mouse down tab head.
  484. /// </summary>
  485. /// <param name="point">The point.</param>
  486. /// <returns>System.Int32.</returns>
  487. private int GetMouseDownTabHead(Point point)
  488. {
  489. for (int i = 0; i < this.TabCount; i++)
  490. {
  491. Rectangle rect = this.GetTabRect(i);
  492. if (rect.Contains(point))
  493. {
  494. return i;
  495. }
  496. }
  497. return -1;
  498. }
  499. }
  500. }