入行已经7,8年了,一直想做一套漂亮点的自定义控件,于是就有了本系列文章。
GitHub:https://github.com/kwwwvagaa/NetWinformControl
码云:https://gitee.com/kwwwvagaa/net_winform_custom_control.git
Install-Package HZH_Controls
https://www.cnblogs.com/bfyx/p/11364884.html
主要用的就是停靠窗体了,(十九)c#Winform自定义控件-停靠窗体,不了解的可以先去看一下
思路:
通过实体对象设置的对齐方式来实现左右对齐,
当鼠标进入一项的时候,判断是否弹出下拉列表,或关闭其他列表
添加一个类用来设置节点信息
1 public class NavigationMenuItem
2 {
3 /// <summary>
4 /// The icon
5 /// </summary>
6 private Image icon;
7 /// <summary>
8 /// Gets or sets the icon.
9 /// </summary>
10 /// <value>The icon.</value>
11 [Description("图标,仅顶级节点有效")]
12 public Image Icon
13 {
14 get { return icon; }
15 set { icon = value; }
16 }
17
18 /// <summary>
19 /// The text
20 /// </summary>
21 private string text;
22 /// <summary>
23 /// Gets or sets the text.
24 /// </summary>
25 /// <value>The text.</value>
26
27 [Description("文本")]
28 public string Text
29 {
30 get { return text; }
31 set { text = value; }
32 }
33
34 /// <summary>
35 /// The show tip
36 /// </summary>
37 private bool showTip;
38 /// <summary>
39 /// Gets or sets a value indicating whether [show tip].当TipText为空时只显示一个小圆点,否则显示TipText文字
40 /// </summary>
41 /// <value><c>true</c> if [show tip]; otherwise, <c>false</c>.</value>
42 [Description("是否显示角标,仅顶级节点有效")]
43 public bool ShowTip
44 {
45 get { return showTip; }
46 set { showTip = value; }
47 }
48
49 /// <summary>
50 /// The tip text
51 /// </summary>
52 private string tipText;
53 /// <summary>
54 /// Gets or sets the tip text
55 /// </summary>
56 /// <value>The tip text.</value>
57 [Description("角标文字,仅顶级节点有效")]
58 public string TipText
59 {
60 get { return tipText; }
61 set { tipText = value; }
62 }
63 /// <summary>
64 /// The items
65 /// </summary>
66 private NavigationMenuItem[] items;
67 /// <summary>
68 /// Gets or sets the items.
69 /// </summary>
70 /// <value>The items.</value>
71 [Description("子项列表")]
72 public NavigationMenuItem[] Items
73 {
74 get { return items; }
75 set
76 {
77 items = value;
78 if (value != null)
79 {
80 foreach (var item in value)
81 {
82 item.ParentItem = this;
83 }
84 }
85 }
86 }
87
88 /// <summary>
89 /// The anchor right
90 /// </summary>
91 private bool anchorRight;
92
93 /// <summary>
94 /// Gets or sets a value indicating whether [anchor right].
95 /// </summary>
96 /// <value><c>true</c> if [anchor right]; otherwise, <c>false</c>.</value>
97 [Description("是否靠右对齐")]
98 public bool AnchorRight
99 {
100 get { return anchorRight; }
101 set { anchorRight = value; }
102 }
103
104 /// <summary>
105 /// The item width
106 /// </summary>
107 private int itemWidth = 100;
108
109 /// <summary>
110 /// Gets or sets the width of the item.
111 /// </summary>
112 /// <value>The width of the item.</value>
113 [Description("宽度")]
114 public int ItemWidth
115 {
116 get { return itemWidth; }
117 set { itemWidth = value; }
118 }
119
120 /// <summary>
121 /// Gets or sets the data source.
122 /// </summary>
123 /// <value>The data source.</value>
124 [Description("数据源")]
125 public object DataSource { get; set; }
126 /// <summary>
127 /// Gets or sets a value indicating whether this instance has split lint at top.
128 /// </summary>
129 /// <value><c>true</c> if this instance has split lint at top; otherwise, <c>false</c>.</value>
130 [Description("是否在此项顶部显示一个分割线")]
131 public bool HasSplitLintAtTop { get; set; }
132
133 /// <summary>
134 /// Gets the parent item.
135 /// </summary>
136 /// <value>The parent item.</value>
137 [Description("父节点")]
138 public NavigationMenuItem ParentItem { get; private set; }
139 }
添加一个自定义控件UCNavigationMenu
添加一些属性
1 /// <summary>
2 /// Occurs when [click itemed].
3 /// </summary>
4 [Description("点击节点事件"), Category("自定义")]
5
6 public event EventHandler ClickItemed;
7 /// <summary>
8 /// The select item
9 /// </summary>
10 private NavigationMenuItem selectItem = null;
11
12 /// <summary>
13 /// Gets the select item.
14 /// </summary>
15 /// <value>The select item.</value>
16 [Description("选中的节点"), Category("自定义")]
17 public NavigationMenuItem SelectItem
18 {
19 get { return selectItem; }
20 private set { selectItem = value; }
21 }
22
23
24 /// <summary>
25 /// The items
26 /// </summary>
27 NavigationMenuItem[] items;
28
29 /// <summary>
30 /// Gets or sets the items.
31 /// </summary>
32 /// <value>The items.</value>
33 [Description("节点列表"), Category("自定义")]
34 public NavigationMenuItem[] Items
35 {
36 get { return items; }
37 set
38 {
39 items = value;
40 ReloadMenu();
41 }
42 }
43
44 /// <summary>
45 /// The tip color
46 /// </summary>
47 private Color tipColor = Color.FromArgb(255, 87, 34);
48
49 /// <summary>
50 /// Gets or sets the color of the tip.
51 /// </summary>
52 /// <value>The color of the tip.</value>
53 [Description("角标颜色"), Category("自定义")]
54 public Color TipColor
55 {
56 get { return tipColor; }
57 set { tipColor = value; }
58 }
59
60 /// <summary>
61 /// 获取或设置控件的前景色。
62 /// </summary>
63 /// <value>The color of the fore.</value>
64 /// <PermissionSet>
65 /// <IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" />
66 /// </PermissionSet>
67 public override System.Drawing.Color ForeColor
68 {
69 get
70 {
71 return base.ForeColor;
72 }
73 set
74 {
75 base.ForeColor = value;
76 foreach (Control c in this.Controls)
77 {
78 c.ForeColor = value;
79 }
80 }
81 }
82 /// <summary>
83 /// 获取或设置控件显示的文字的字体。
84 /// </summary>
85 /// <value>The font.</value>
86 /// <PermissionSet>
87 /// <IPermission class="System.Security.Permissions.EnvironmentPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" />
88 /// <IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" />
89 /// <IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="UnmanagedCode, ControlEvidence" />
90 /// <IPermission class="System.Diagnostics.PerformanceCounterPermission, System, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" />
91 /// </PermissionSet>
92 public override Font Font
93 {
94 get
95 {
96 return base.Font;
97 }
98 set
99 {
100 base.Font = value;
101 foreach (Control c in this.Controls)
102 {
103 c.Font = value;
104 }
105 }
106 }
107
108 /// <summary>
109 /// The m LST anchors
110 /// </summary>
111 Dictionary<NavigationMenuItem, FrmAnchor> m_lstAnchors = new Dictionary<NavigationMenuItem, FrmAnchor>();
重载菜单
1 private void ReloadMenu()
2 {
3 try
4 {
5 ControlHelper.FreezeControl(this, true);
6 this.Controls.Clear();
7 if (items != null && items.Length > 0)
8 {
9 foreach (var item in items)
10 {
11 var menu = (NavigationMenuItem)item;
12 Label lbl = new Label();
13 lbl.AutoSize = false;
14 lbl.TextAlign = ContentAlignment.MiddleCenter;
15 lbl.Width = menu.ItemWidth;
16 lbl.Text = menu.Text;
17
18 lbl.Font = Font;
19 lbl.ForeColor = ForeColor;
20
21 lbl.Paint += lbl_Paint;
22 lbl.MouseEnter += lbl_MouseEnter;
23 lbl.Tag = menu;
24 lbl.Click += lbl_Click;
25 if (menu.AnchorRight)
26 {
27 lbl.Dock = DockStyle.Right;
28 }
29 else
30 {
31 lbl.Dock = DockStyle.Left;
32 }
33 this.Controls.Add(lbl);
34
35 lbl.BringToFront();
36 }
37
38
39 }
40 }
41 finally
42 {
43 ControlHelper.FreezeControl(this, false);
44 }
45 }
显示下级菜单
1 private void ShowMoreMenu(Label lbl)
2 {
3 var menu = (NavigationMenuItem)lbl.Tag;
4 if (CheckShow(menu))
5 {
6 if (menu.Items != null && menu.Items.Length > 0)
7 {
8 Panel panel = new Panel();
9 panel.BackColor = Color.White;
10 panel.Paint += panel_Paint;
11 panel.Padding = new System.Windows.Forms.Padding(1);
12 Size size = GetItemsSize(menu.Items);
13 var height = size.Height * menu.Items.Length + 2;
14 height += menu.Items.Count(p => p.HasSplitLintAtTop);//分割线
15 if (size.Width < lbl.Width)
16 size.Width = lbl.Width;
17 panel.Size = new Size(size.Width, height);
18
19 foreach (var item in menu.Items)
20 {
21 if (item.HasSplitLintAtTop)
22 {
23 UCSplitLine_H line = new UCSplitLine_H();
24 line.Dock = DockStyle.Top;
25 panel.Controls.Add(line);
26 line.BringToFront();
27 }
28 Label _lbl = new Label();
29 _lbl.Font = Font;
30 _lbl.ForeColor = this.BackColor;
31 _lbl.AutoSize = false;
32 _lbl.TextAlign = ContentAlignment.MiddleCenter;
33 _lbl.Height = size.Height;
34 _lbl.Text = item.Text;
35 _lbl.Dock = DockStyle.Top;
36 _lbl.BringToFront();
37 _lbl.Paint += lbl_Paint;
38 _lbl.MouseEnter += lbl_MouseEnter;
39 _lbl.Tag = item;
40 _lbl.Click += lbl_Click;
41 _lbl.Size = new System.Drawing.Size(size.Width, size.Height);
42 panel.Controls.Add(_lbl);
43 _lbl.BringToFront();
44 }
45 Point point = Point.Empty;
46
47 if (menu.ParentItem != null)
48 {
49 Point p = lbl.Parent.PointToScreen(lbl.Location);
50 if (p.X + lbl.Width + panel.Width > Screen.PrimaryScreen.Bounds.Width)
51 {
52 point = new Point(-1 * panel.Width - 2, -1 * lbl.Height);
53 }
54 else
55 {
56 point = new Point(panel.Width + 2, -1 * lbl.Height);
57 }
58 }
59 m_lstAnchors[menu] = new FrmAnchor(lbl, panel, point);
60 m_lstAnchors[menu].FormClosing += UCNavigationMenu_FormClosing;
61 m_lstAnchors[menu].Show();
62 m_lstAnchors[menu].Size = new Size(size.Width, height);
63 }
64 }
65
66 }
辅助函数
1 /// <summary>
2 /// Checks the show.
3 /// </summary>
4 /// <param name="menu">The menu.</param>
5 /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
6 private bool CheckShow(NavigationMenuItem menu)
7 {
8 //检查已经打开的节点
9 if (m_lstAnchors.ContainsKey(menu))
10 {
11 CloseList(menu);
12 return false;
13 }
14 if (HasInCacheChild(menu))
15 {
16 if (m_lstAnchors.ContainsKey(menu.ParentItem))
17 {
18 CloseList(menu.ParentItem);
19 return true;
20 }
21 return false;
22 }
23 else
24 {
25 for (int i = 0; i < 1; )
26 {
27 try
28 {
29 foreach (var item in m_lstAnchors)
30 {
31 if (m_lstAnchors[item.Key] != null && !m_lstAnchors[item.Key].IsDisposed)
32 {
33 m_lstAnchors[item.Key].Close();
34 }
35 }
36 }
37 catch
38 {
39 continue;
40 }
41 i++;
42 }
43 m_lstAnchors.Clear();
44 return true;
45 }
46 }
47
48 /// <summary>
49 /// Determines whether [has in cache child] [the specified menu].
50 /// </summary>
51 /// <param name="menu">The menu.</param>
52 /// <returns><c>true</c> if [has in cache child] [the specified menu]; otherwise, <c>false</c>.</returns>
53 private bool HasInCacheChild(NavigationMenuItem menu)
54 {
55 foreach (var item in m_lstAnchors)
56 {
57 if (item.Key == menu)
58 {
59 return true;
60 }
61 else
62 {
63 if (item.Key.Items != null)
64 {
65 if (item.Key.Items.Contains(menu))
66 return true;
67 }
68 }
69 }
70 return false;
71 }
72
73 /// <summary>
74 /// Closes the list.
75 /// </summary>
76 /// <param name="menu">The menu.</param>
77 private void CloseList(NavigationMenuItem menu)
78 {
79 if (menu.Items != null)
80 {
81 foreach (var item in menu.Items)
82 {
83 CloseList(item);
84 if (m_lstAnchors.ContainsKey(item))
85 {
86 if (m_lstAnchors[item] != null && !m_lstAnchors[item].IsDisposed)
87 {
88 m_lstAnchors[item].Close();
89 m_lstAnchors[item] = null;
90 m_lstAnchors.Remove(item);
91 }
92 }
93 }
94 }
95 }
一些事件
1 /// <summary>
2 /// Handles the Click event of the lbl control.
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 lbl_Click(object sender, EventArgs e)
7 {
8 Label lbl = sender as Label;
9 if (lbl.Tag != null)
10 {
11 var menu = (NavigationMenuItem)lbl.Tag;
12 if (menu.Items == null || menu.Items.Length <= 0)
13 {
14 selectItem = menu;
15
16 while (m_lstAnchors.Count > 0)
17 {
18 try
19 {
20 foreach (var item in m_lstAnchors)
21 {
22 item.Value.Close();
23 m_lstAnchors.Remove(item.Key);
24 }
25 }
26 catch { }
27 }
28
29 if (ClickItemed != null)
30 {
31 ClickItemed(this, e);
32 }
33 }
34 else
35 {
36 CloseList(menu);
37 if (m_lstAnchors.ContainsKey(menu))
38 {
39 if (m_lstAnchors[menu] != null && !m_lstAnchors[menu].IsDisposed)
40 {
41 m_lstAnchors[menu].Close();
42 }
43 m_lstAnchors.Remove(menu);
44 }
45 ShowMoreMenu(lbl);
46 }
47 }
48 }
49
50 /// <summary>
51 /// Handles the MouseEnter event of the lbl control.
52 /// </summary>
53 /// <param name="sender">The source of the event.</param>
54 /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
55 void lbl_MouseEnter(object sender, EventArgs e)
56 {
57 Label lbl = sender as Label;
58 ShowMoreMenu(lbl);
59 }
60 /// <summary>
61 /// Handles the FormClosing event of the UCNavigationMenu control.
62 /// </summary>
63 /// <param name="sender">The source of the event.</param>
64 /// <param name="e">The <see cref="FormClosingEventArgs"/> instance containing the event data.</param>
65 void UCNavigationMenu_FormClosing(object sender, FormClosingEventArgs e)
66 {
67 FrmAnchor frm = sender as FrmAnchor;
68 if (m_lstAnchors.ContainsValue(frm))
69 {
70 foreach (var item in m_lstAnchors)
71 {
72 if (item.Value == frm)
73 {
74 m_lstAnchors.Remove(item.Key);
75 return;
76 }
77 }
78 }
79 }
80
81 /// <summary>
82 /// Handles the Paint event of the panel control.
83 /// </summary>
84 /// <param name="sender">The source of the event.</param>
85 /// <param name="e">The <see cref="PaintEventArgs"/> instance containing the event data.</param>
86 void panel_Paint(object sender, PaintEventArgs e)
87 {
88 e.Graphics.SetGDIHigh();
89 Rectangle rect = new Rectangle(0, 0, e.ClipRectangle.Width - 1, e.ClipRectangle.Height - 1);
90 var path = rect.CreateRoundedRectanglePath(2);
91 e.Graphics.DrawPath(new Pen(new SolidBrush(LineColors.Light)), path);
92 }
93
94
95
96 /// <summary>
97 /// Gets the size of the items.
98 /// </summary>
99 /// <param name="items">The items.</param>
100 /// <returns>Size.</returns>
101 private Size GetItemsSize(NavigationMenuItem[] items)
102 {
103 Size size = Size.Empty;
104 if (items != null && items.Length > 0)
105 {
106 using (var g = this.CreateGraphics())
107 {
108 foreach (NavigationMenuItem item in items)
109 {
110 var s = g.MeasureString(item.Text, Font);
111 if (s.Width + 25 > size.Width)
112 {
113 size.Width = (int)s.Width + 25;
114 }
115 if (s.Height + 10 > size.Height)
116 {
117 size.Height = (int)s.Height + 10;
118 }
119 }
120 }
121 }
122 return size;
123 }
124
125
126 /// <summary>
127 /// Handles the Paint event of the lbl control.
128 /// </summary>
129 /// <param name="sender">The source of the event.</param>
130 /// <param name="e">The <see cref="PaintEventArgs"/> instance containing the event data.</param>
131 void lbl_Paint(object sender, PaintEventArgs e)
132 {
133 Label lbl = sender as Label;
134 if (lbl.Tag != null)
135 {
136 var menu = (NavigationMenuItem)lbl.Tag;
137 e.Graphics.SetGDIHigh();
138 if (menu.ParentItem == null)//顶级节点支持图标和角标
139 {
140 if (menu.ShowTip)
141 {
142 if (!string.IsNullOrEmpty(menu.TipText))
143 {
144 var rect = new Rectangle(lbl.Width - 25, lbl.Height / 2 - 10, 20, 20);
145 var path = rect.CreateRoundedRectanglePath(5);
146 e.Graphics.FillPath(new SolidBrush(tipColor), path);
147 e.Graphics.DrawString(menu.TipText, new Font("微软雅黑", 8f), new SolidBrush(Color.White), rect, new StringFormat() { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center });
148 }
149 else
150 {
151 e.Graphics.FillEllipse(new SolidBrush(tipColor), new Rectangle(lbl.Width - 20, lbl.Height / 2 - 10, 10, 10));
152 }
153 }
154 if (menu.Icon != null)
155 {
156 e.Graphics.DrawImage(menu.Icon, new Rectangle(1, (lbl.Height - 25) / 2, 25, 25), 0, 0, menu.Icon.Width, menu.Icon.Height, GraphicsUnit.Pixel);
157 }
158 }
159 if (menu.ParentItem != null && menu.Items != null && menu.Items.Length > 0)
160 {
161 ControlHelper.PaintTriangle(e.Graphics, new SolidBrush(this.BackColor), new Point(lbl.Width - 11, (lbl.Height - 5) / 2), 5, GraphDirection.Rightward);
162 }
163 }
164 }
完整代码
1 // ***********************************************************************
2 // Assembly : HZH_Controls
3 // Created : 2019-10-08
4 //
5 // ***********************************************************************
6 // <copyright file="UCNavigationMenu.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 using HZH_Controls.Forms;
25
26 namespace HZH_Controls.Controls
27 {
28 /// <summary>
29 /// Class UCNavigationMenu.
30 /// Implements the <see cref="System.Windows.Forms.UserControl" />
31 /// </summary>
32 /// <seealso cref="System.Windows.Forms.UserControl" />
33 [DefaultEvent("ClickItemed")]
34 public partial class UCNavigationMenu : UserControl
35 {
36 /// <summary>
37 /// Occurs when [click itemed].
38 /// </summary>
39 [Description("点击节点事件"), Category("自定义")]
40
41 public event EventHandler ClickItemed;
42 /// <summary>
43 /// The select item
44 /// </summary>
45 private NavigationMenuItem selectItem = null;
46
47 /// <summary>
48 /// Gets the select item.
49 /// </summary>
50 /// <value>The select item.</value>
51 [Description("选中的节点"), Category("自定义")]
52 public NavigationMenuItem SelectItem
53 {
54 get { return selectItem; }
55 private set { selectItem = value; }
56 }
57
58
59 /// <summary>
60 /// The items
61 /// </summary>
62 NavigationMenuItem[] items;
63
64 /// <summary>
65 /// Gets or sets the items.
66 /// </summary>
67 /// <value>The items.</value>
68 [Description("节点列表"), Category("自定义")]
69 public NavigationMenuItem[] Items
70 {
71 get { return items; }
72 set
73 {
74 items = value;
75 ReloadMenu();
76 }
77 }
78
79 /// <summary>
80 /// The tip color
81 /// </summary>
82 private Color tipColor = Color.FromArgb(255, 87, 34);
83
84 /// <summary>
85 /// Gets or sets the color of the tip.
86 /// </summary>
87 /// <value>The color of the tip.</value>
88 [Description("角标颜色"), Category("自定义")]
89 public Color TipColor
90 {
91 get { return tipColor; }
92 set { tipColor = value; }
93 }
94
95 /// <summary>
96 /// 获取或设置控件的前景色。
97 /// </summary>
98 /// <value>The color of the fore.</value>
99 /// <PermissionSet>
100 /// <IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" />
101 /// </PermissionSet>
102 public override System.Drawing.Color ForeColor
103 {
104 get
105 {
106 return base.ForeColor;
107 }
108 set
109 {
110 base.ForeColor = value;
111 foreach (Control c in this.Controls)
112 {
113 c.ForeColor = value;
114 }
115 }
116 }
117 /// <summary>
118 /// 获取或设置控件显示的文字的字体。
119 /// </summary>
120 /// <value>The font.</value>
121 /// <PermissionSet>
122 /// <IPermission class="System.Security.Permissions.EnvironmentPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" />
123 /// <IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" />
124 /// <IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="UnmanagedCode, ControlEvidence" />
125 /// <IPermission class="System.Diagnostics.PerformanceCounterPermission, System, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" />
126 /// </PermissionSet>
127 public override Font Font
128 {
129 get
130 {
131 return base.Font;
132 }
133 set
134 {
135 base.Font = value;
136 foreach (Control c in this.Controls)
137 {
138 c.Font = value;
139 }
140 }
141 }
142
143 /// <summary>
144 /// The m LST anchors
145 /// </summary>
146 Dictionary<NavigationMenuItem, FrmAnchor> m_lstAnchors = new Dictionary<NavigationMenuItem, FrmAnchor>();
147
148 /// <summary>
149 /// Initializes a new instance of the <see cref="UCNavigationMenu"/> class.
150 /// </summary>
151 public UCNavigationMenu()
152 {
153 InitializeComponent();
154 items = new NavigationMenuItem[0];
155 if (ControlHelper.IsDesignMode())
156 {
157 items = new NavigationMenuItem[4];
158 for (int i = 0; i < 4; i++)
159 {
160 items[i] = new NavigationMenuItem()
161 {
162 Text = "菜单" + (i + 1),
163 AnchorRight = i >= 2
164 };
165 }
166 }
167 }
168
169 /// <summary>
170 /// Reloads the menu.
171 /// </summary>
172 private void ReloadMenu()
173 {
174 try
175 {
176 ControlHelper.FreezeControl(this, true);
177 this.Controls.Clear();
178 if (items != null && items.Length > 0)
179 {
180 foreach (var item in items)
181 {
182 var menu = (NavigationMenuItem)item;
183 Label lbl = new Label();
184 lbl.AutoSize = false;
185 lbl.TextAlign = ContentAlignment.MiddleCenter;
186 lbl.Width = menu.ItemWidth;
187 lbl.Text = menu.Text;
188
189 lbl.Font = Font;
190 lbl.ForeColor = ForeColor;
191
192 lbl.Paint += lbl_Paint;
193 lbl.MouseEnter += lbl_MouseEnter;
194 lbl.Tag = menu;
195 lbl.Click += lbl_Click;
196 if (menu.AnchorRight)
197 {
198 lbl.Dock = DockStyle.Right;
199 }
200 else
201 {
202 lbl.Dock = DockStyle.Left;
203 }
204 this.Controls.Add(lbl);
205
206 lbl.BringToFront();
207 }
208
209
210 }
211 }
212 finally
213 {
214 ControlHelper.FreezeControl(this, false);
215 }
216 }
217
218
219
220 /// <summary>
221 /// Handles the Click event of the lbl control.
222 /// </summary>
223 /// <param name="sender">The source of the event.</param>
224 /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
225 void lbl_Click(object sender, EventArgs e)
226 {
227 Label lbl = sender as Label;
228 if (lbl.Tag != null)
229 {
230 var menu = (NavigationMenuItem)lbl.Tag;
231 if (menu.Items == null || menu.Items.Length <= 0)
232 {
233 selectItem = menu;
234
235 while (m_lstAnchors.Count > 0)
236 {
237 try
238 {
239 foreach (var item in m_lstAnchors)
240 {
241 item.Value.Close();
242 m_lstAnchors.Remove(item.Key);
243 }
244 }
245 catch { }
246 }
247
248 if (ClickItemed != null)
249 {
250 ClickItemed(this, e);
251 }
252 }
253 else
254 {
255 CloseList(menu);
256 if (m_lstAnchors.ContainsKey(menu))
257 {
258 if (m_lstAnchors[menu] != null && !m_lstAnchors[menu].IsDisposed)
259 {
260 m_lstAnchors[menu].Close();
261 }
262 m_lstAnchors.Remove(menu);
263 }
264 ShowMoreMenu(lbl);
265 }
266 }
267 }
268
269 /// <summary>
270 /// Handles the MouseEnter event of the lbl control.
271 /// </summary>
272 /// <param name="sender">The source of the event.</param>
273 /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
274 void lbl_MouseEnter(object sender, EventArgs e)
275 {
276 Label lbl = sender as Label;
277 ShowMoreMenu(lbl);
278 }
279
280 /// <summary>
281 /// Checks the show.
282 /// </summary>
283 /// <param name="menu">The menu.</param>
284 /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
285 private bool CheckShow(NavigationMenuItem menu)
286 {
287 //检查已经打开的节点
288 if (m_lstAnchors.ContainsKey(menu))
289 {
290 CloseList(menu);
291 return false;
292 }
293 if (HasInCacheChild(menu))
294 {
295 if (m_lstAnchors.ContainsKey(menu.ParentItem))
296 {
297 CloseList(menu.ParentItem);
298 return true;
299 }
300 return false;
301 }
302 else
303 {
304 for (int i = 0; i < 1; )
305 {
306 try
307 {
308 foreach (var item in m_lstAnchors)
309 {
310 if (m_lstAnchors[item.Key] != null && !m_lstAnchors[item.Key].IsDisposed)
311 {
312 m_lstAnchors[item.Key].Close();
313 }
314 }
315 }
316 catch
317 {
318 continue;
319 }
320 i++;
321 }
322 m_lstAnchors.Clear();
323 return true;
324 }
325 }
326
327 /// <summary>
328 /// Determines whether [has in cache child] [the specified menu].
329 /// </summary>
330 /// <param name="menu">The menu.</param>
331 /// <returns><c>true</c> if [has in cache child] [the specified menu]; otherwise, <c>false</c>.</returns>
332 private bool HasInCacheChild(NavigationMenuItem menu)
333 {
334 foreach (var item in m_lstAnchors)
335 {
336 if (item.Key == menu)
337 {
338 return true;
339 }
340 else
341 {
342 if (item.Key.Items != null)
343 {
344 if (item.Key.Items.Contains(menu))
345 return true;
346 }
347 }
348 }
349 return false;
350 }
351
352 /// <summary>
353 /// Closes the list.
354 /// </summary>
355 /// <param name="menu">The menu.</param>
356 private void CloseList(NavigationMenuItem menu)
357 {
358 if (menu.Items != null)
359 {
360 foreach (var item in menu.Items)
361 {
362 CloseList(item);
363 if (m_lstAnchors.ContainsKey(item))
364 {
365 if (m_lstAnchors[item] != null && !m_lstAnchors[item].IsDisposed)
366 {
367 m_lstAnchors[item].Close();
368 m_lstAnchors[item] = null;
369 m_lstAnchors.Remove(item);
370 }
371 }
372 }
373 }
374 }
375
376 /// <summary>
377 /// Shows the more menu.
378 /// </summary>
379 /// <param name="lbl">The label.</param>
380 private void ShowMoreMenu(Label lbl)
381 {
382 var menu = (NavigationMenuItem)lbl.Tag;
383 if (CheckShow(menu))
384 {
385 if (menu.Items != null && menu.Items.Length > 0)
386 {
387 Panel panel = new Panel();
388 panel.BackColor = Color.White;
389 panel.Paint += panel_Paint;
390 panel.Padding = new System.Windows.Forms.Padding(1);
391 Size size = GetItemsSize(menu.Items);
392 var height = size.Height * menu.Items.Length + 2;
393 height += menu.Items.Count(p => p.HasSplitLintAtTop);//分割线
394 if (size.Width < lbl.Width)
395 size.Width = lbl.Width;
396 panel.Size = new Size(size.Width, height);
397
398 foreach (var item in menu.Items)
399 {
400 if (item.HasSplitLintAtTop)
401 {
402 UCSplitLine_H line = new UCSplitLine_H();
403 line.Dock = DockStyle.Top;
404 panel.Controls.Add(line);
405 line.BringToFront();
406 }
407 Label _lbl = new Label();
408 _lbl.Font = Font;
409 _lbl.ForeColor = this.BackColor;
410 _lbl.AutoSize = false;
411 _lbl.TextAlign = ContentAlignment.MiddleCenter;
412 _lbl.Height = size.Height;
413 _lbl.Text = item.Text;
414 _lbl.Dock = DockStyle.Top;
415 _lbl.BringToFront();
416 _lbl.Paint += lbl_Paint;
417 _lbl.MouseEnter += lbl_MouseEnter;
418 _lbl.Tag = item;
419 _lbl.Click += lbl_Click;
420 _lbl.Size = new System.Drawing.Size(size.Width, size.Height);
421 panel.Controls.Add(_lbl);
422 _lbl.BringToFront();
423 }
424 Point point = Point.Empty;
425
426 if (menu.ParentItem != null)
427 {
428 Point p = lbl.Parent.PointToScreen(lbl.Location);
429 if (p.X + lbl.Width + panel.Width > Screen.PrimaryScreen.Bounds.Width)
430 {
431 point = new Point(-1 * panel.Width - 2, -1 * lbl.Height);
432 }
433 else
434 {
435 point = new Point(panel.Width + 2, -1 * lbl.Height);
436 }
437 }
438 m_lstAnchors[menu] = new FrmAnchor(lbl, panel, point);
439 m_lstAnchors[menu].FormClosing += UCNavigationMenu_FormClosing;
440 m_lstAnchors[menu].Show();
441 m_lstAnchors[menu].Size = new Size(size.Width, height);
442 }
443 }
444
445 }
446
447 /// <summary>
448 /// Handles the FormClosing event of the UCNavigationMenu control.
449 /// </summary>
450 /// <param name="sender">The source of the event.</param>
451 /// <param name="e">The <see cref="FormClosingEventArgs"/> instance containing the event data.</param>
452 void UCNavigationMenu_FormClosing(object sender, FormClosingEventArgs e)
453 {
454 FrmAnchor frm = sender as FrmAnchor;
455 if (m_lstAnchors.ContainsValue(frm))
456 {
457 foreach (var item in m_lstAnchors)
458 {
459 if (item.Value == frm)
460 {
461 m_lstAnchors.Remove(item.Key);
462 return;
463 }
464 }
465 }
466 }
467
468 /// <summary>
469 /// Handles the Paint event of the panel control.
470 /// </summary>
471 /// <param name="sender">The source of the event.</param>
472 /// <param name="e">The <see cref="PaintEventArgs"/> instance containing the event data.</param>
473 void panel_Paint(object sender, PaintEventArgs e)
474 {
475 e.Graphics.SetGDIHigh();
476 Rectangle rect = new Rectangle(0, 0, e.ClipRectangle.Width - 1, e.ClipRectangle.Height - 1);
477 var path = rect.CreateRoundedRectanglePath(2);
478 e.Graphics.DrawPath(new Pen(new SolidBrush(LineColors.Light)), path);
479 }
480
481
482
483 /// <summary>
484 /// Gets the size of the items.
485 /// </summary>
486 /// <param name="items">The items.</param>
487 /// <returns>Size.</returns>
488 private Size GetItemsSize(NavigationMenuItem[] items)
489 {
490 Size size = Size.Empty;
491 if (items != null && items.Length > 0)
492 {
493 using (var g = this.CreateGraphics())
494 {
495 foreach (NavigationMenuItem item in items)
496 {
497 var s = g.MeasureString(item.Text, Font);
498 if (s.Width + 25 > size.Width)
499 {
500 size.Width = (int)s.Width + 25;
501 }
502 if (s.Height + 10 > size.Height)
503 {
504 size.Height = (int)s.Height + 10;
505 }
506 }
507 }
508 }
509 return size;
510 }
511
512
513 /// <summary>
514 /// Handles the Paint event of the lbl control.
515 /// </summary>
516 /// <param name="sender">The source of the event.</param>
517 /// <param name="e">The <see cref="PaintEventArgs"/> instance containing the event data.</param>
518 void lbl_Paint(object sender, PaintEventArgs e)
519 {
520 Label lbl = sender as Label;
521 if (lbl.Tag != null)
522 {
523 var menu = (NavigationMenuItem)lbl.Tag;
524 e.Graphics.SetGDIHigh();
525 if (menu.ParentItem == null)//顶级节点支持图标和角标
526 {
527 if (menu.ShowTip)
528 {
529 if (!string.IsNullOrEmpty(menu.TipText))
530 {
531 var rect = new Rectangle(lbl.Width - 25, lbl.Height / 2 - 10, 20, 20);
532 var path = rect.CreateRoundedRectanglePath(5);
533 e.Graphics.FillPath(new SolidBrush(tipColor), path);
534 e.Graphics.DrawString(menu.TipText, new Font("微软雅黑", 8f), new SolidBrush(Color.White), rect, new StringFormat() { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center });
535 }
536 else
537 {
538 e.Graphics.FillEllipse(new SolidBrush(tipColor), new Rectangle(lbl.Width - 20, lbl.Height / 2 - 10, 10, 10));
539 }
540 }
541 if (menu.Icon != null)
542 {
543 e.Graphics.DrawImage(menu.Icon, new Rectangle(1, (lbl.Height - 25) / 2, 25, 25), 0, 0, menu.Icon.Width, menu.Icon.Height, GraphicsUnit.Pixel);
544 }
545 }
546 if (menu.ParentItem != null && menu.Items != null && menu.Items.Length > 0)
547 {
548 ControlHelper.PaintTriangle(e.Graphics, new SolidBrush(this.BackColor), new Point(lbl.Width - 11, (lbl.Height - 5) / 2), 5, GraphDirection.Rightward);
549 }
550 }
551 }
552 }
553 }