入行已经7,8年了,一直想做一套漂亮点的自定义控件,于是就有了本系列文章。
GitHub:https://github.com/kwwwvagaa/NetWinformControl
码云:https://gitee.com/kwwwvagaa/net_winform_custom_control.git
如果觉得写的还行,请点个 star 支持一下吧
https://www.cnblogs.com/bfyx/p/11364884.html
有时候我们需要左侧的导航菜单,那么来整一个吧
先来分析分析,导航菜单一般分为2级或多级,如果是多级的话 用前面的treeview更合适,这里只做2级,为了父子节点样式更方便控制,我们分别实现父子节点。
为了更加的Open,我们使用接口来定义一下
定义一个节点数据绑定实体
1 [Serializable]
2 public class MenuItemEntity
3 {
4 /// <summary>
5 /// 键
6 /// </summary>
7 public string Key { get; set; }
8 /// <summary>
9 /// 文字
10 /// </summary>
11 public string Text { get; set; }
12 /// <summary>
13 /// 子节点
14 /// </summary>
15 public List<MenuItemEntity> Childrens { get; set; }
16 /// <summary>
17 /// 自定义数据源,一般用于扩展展示,比如定义节点图片等
18 /// </summary>
19 public object DataSource { get; set; }
20
21 }
再定义一个接口来约束
1 public interface IMenuItem
2 {
3 event EventHandler SelectedItem;
4 MenuItemEntity DataSource { get; set; }
5 /// <summary>
6 /// 设置样式
7 /// </summary>
8 /// <param name="styles">key:属性名称,value:属性值</param>
9 void SetStyle(Dictionary<string, object> styles);
10 /// <summary>
11 /// 设置选中样式
12 /// </summary>
13 /// <param name="blnSelected">是否选中</param>
14 void SetSelectedStyle(bool blnSelected);
15 }
首先看父节点定义,添加一个用户控件,命名UCMenuParentItem,并且实现接口IMenuItem
public event EventHandler SelectedItem;
private MenuItemEntity m_dataSource;
public MenuItemEntity DataSource
{
get
{
return m_dataSource;
}
set
{
m_dataSource = value;
if (value != null)
{
lblTitle.Text = value.Text;
}
}
}
public void SetStyle(Dictionary<string, object> styles)
{
Type t = this.GetType();
foreach (var item in styles)
{
var pro = t.GetProperty(item.Key);
if (pro != null && pro.CanWrite)
{
try
{
pro.SetValue(this, item.Value, null);
}
catch (Exception ex)
{
throw new Exception("菜单元素设置样式异常", ex);
}
}
}
}
public void SetSelectedStyle(bool blnSelected)
{
if (blnSelected)
{
this.lblTitle.Image = Properties.Resources.sanjiao1;
}
else
{
this.lblTitle.Image = Properties.Resources.sanjiao2;
}
}
然后处理下点击事件
lblTitle.MouseDown += lblTitle_MouseDown;
void lblTitle_MouseDown(object sender, MouseEventArgs e)
{
if (SelectedItem != null)
{
SelectedItem(this, e);
}
}
这样就完事了,看下完整代码
1 // 版权所有 黄正辉 交流群:568015492 QQ:623128629
2 // 文件名称:UCMenuParentItem.cs
3 // 创建日期:2019-08-15 16:02:35
4 // 功能描述:Menu
5 // 项目地址:https://gitee.com/kwwwvagaa/net_winform_custom_control
6 using System;
7 using System.Collections.Generic;
8 using System.ComponentModel;
9 using System.Drawing;
10 using System.Data;
11 using System.Linq;
12 using System.Text;
13 using System.Windows.Forms;
14
15 namespace HZH_Controls.Controls
16 {
17 /// <summary>
18 /// 父类节点
19 /// </summary>
20 [ToolboxItem(false)]
21 public partial class UCMenuParentItem : UserControl, IMenuItem
22 {
23 public event EventHandler SelectedItem;
24
25 private MenuItemEntity m_dataSource;
26 public MenuItemEntity DataSource
27 {
28 get
29 {
30 return m_dataSource;
31 }
32 set
33 {
34 m_dataSource = value;
35 if (value != null)
36 {
37 lblTitle.Text = value.Text;
38 }
39 }
40 }
41
42 public UCMenuParentItem()
43 {
44 InitializeComponent();
45 lblTitle.MouseDown += lblTitle_MouseDown;
46 }
47
48 public void SetStyle(Dictionary<string, object> styles)
49 {
50 Type t = this.GetType();
51 foreach (var item in styles)
52 {
53 var pro = t.GetProperty(item.Key);
54 if (pro != null && pro.CanWrite)
55 {
56 try
57 {
58 pro.SetValue(this, item.Value, null);
59 }
60 catch (Exception ex)
61 {
62 throw new Exception("菜单元素设置样式异常", ex);
63 }
64 }
65 }
66 }
67
68 public void SetSelectedStyle(bool blnSelected)
69 {
70 if (blnSelected)
71 {
72 this.lblTitle.Image = Properties.Resources.sanjiao1;
73 }
74 else
75 {
76 this.lblTitle.Image = Properties.Resources.sanjiao2;
77 }
78 }
79
80 void lblTitle_MouseDown(object sender, MouseEventArgs e)
81 {
82 if (SelectedItem != null)
83 {
84 SelectedItem(this, e);
85 }
86 }
87 }
88 }
1 namespace HZH_Controls.Controls
2 {
3 partial class UCMenuParentItem
4 {
5 /// <summary>
6 /// 必需的设计器变量。
7 /// </summary>
8 private System.ComponentModel.IContainer components = null;
9
10 /// <summary>
11 /// 清理所有正在使用的资源。
12 /// </summary>
13 /// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
14 protected override void Dispose(bool disposing)
15 {
16 if (disposing && (components != null))
17 {
18 components.Dispose();
19 }
20 base.Dispose(disposing);
21 }
22
23 #region 组件设计器生成的代码
24
25 /// <summary>
26 /// 设计器支持所需的方法 - 不要
27 /// 使用代码编辑器修改此方法的内容。
28 /// </summary>
29 private void InitializeComponent()
30 {
31 this.ucSplitLine_H1 = new HZH_Controls.Controls.UCSplitLine_H();
32 this.lblTitle = new System.Windows.Forms.Label();
33 this.SuspendLayout();
34 //
35 // ucSplitLine_H1
36 //
37 this.ucSplitLine_H1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(20)))), ((int)(((byte)(30)))), ((int)(((byte)(39)))));
38 this.ucSplitLine_H1.Dock = System.Windows.Forms.DockStyle.Bottom;
39 this.ucSplitLine_H1.Location = new System.Drawing.Point(0, 59);
40 this.ucSplitLine_H1.Name = "ucSplitLine_H1";
41 this.ucSplitLine_H1.Size = new System.Drawing.Size(200, 1);
42 this.ucSplitLine_H1.TabIndex = 0;
43 this.ucSplitLine_H1.TabStop = false;
44 //
45 // lblTitle
46 //
47 this.lblTitle.Dock = System.Windows.Forms.DockStyle.Fill;
48 this.lblTitle.ForeColor = System.Drawing.Color.White;
49 this.lblTitle.Image = global::HZH_Controls.Properties.Resources.sanjiao2;
50 this.lblTitle.ImageAlign = System.Drawing.ContentAlignment.MiddleRight;
51 this.lblTitle.Location = new System.Drawing.Point(0, 0);
52 this.lblTitle.Name = "lblTitle";
53 this.lblTitle.Size = new System.Drawing.Size(200, 59);
54 this.lblTitle.TabIndex = 1;
55 this.lblTitle.Text = "父项";
56 this.lblTitle.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
57 //
58 // UCMenuParentItem
59 //
60 this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
61 this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(29)))), ((int)(((byte)(43)))), ((int)(((byte)(54)))));
62 this.Controls.Add(this.lblTitle);
63 this.Controls.Add(this.ucSplitLine_H1);
64 this.Font = new System.Drawing.Font("微软雅黑", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
65 this.Name = "UCMenuParentItem";
66 this.Size = new System.Drawing.Size(200, 60);
67 this.ResumeLayout(false);
68
69 }
70
71 #endregion
72
73 private UCSplitLine_H ucSplitLine_H1;
74 private System.Windows.Forms.Label lblTitle;
75 }
76 }
设计效果就是这样子的了
父节点弄好了,下面就是子节点了
添加用户控件,命名UCMenuChildrenItem,实现接口IMenuItem
public event EventHandler SelectedItem;
private MenuItemEntity m_dataSource;
public MenuItemEntity DataSource
{
get
{
return m_dataSource;
}
set
{
m_dataSource = value;
if (value != null)
{
lblTitle.Text = value.Text;
}
}
}
public void SetStyle(Dictionary<string, object> styles)
{
Type t = this.GetType();
foreach (var item in styles)
{
var pro = t.GetProperty(item.Key);
if (pro != null && pro.CanWrite)
{
try
{
pro.SetValue(this, item.Value, null);
}
catch (Exception ex)
{
throw new Exception("菜单元素设置样式异常", ex);
}
}
}
}
处理下点击事件
1 this.lblTitle.MouseDown += lblTitle_MouseDown;
2
3 void lblTitle_MouseDown(object sender, MouseEventArgs e)
4 {
5 if (SelectedItem != null)
6 {
7 SelectedItem(this, null);
8 }
9 }
这样就完成了,看下完整代码
1 // 版权所有 黄正辉 交流群:568015492 QQ:623128629
2 // 文件名称:UCMenuChildrenItem.cs
3 // 创建日期:2019-08-15 16:02:26
4 // 功能描述:Menu
5 // 项目地址:https://gitee.com/kwwwvagaa/net_winform_custom_control
6 using System;
7 using System.Collections.Generic;
8 using System.ComponentModel;
9 using System.Drawing;
10 using System.Data;
11 using System.Linq;
12 using System.Text;
13 using System.Windows.Forms;
14
15 namespace HZH_Controls.Controls
16 {
17 /// <summary>
18 /// 子类节点
19 /// </summary>
20 [ToolboxItem(false)]
21 public partial class UCMenuChildrenItem : UserControl, IMenuItem
22 {
23 public event EventHandler SelectedItem;
24
25 private MenuItemEntity m_dataSource;
26 public MenuItemEntity DataSource
27 {
28 get
29 {
30 return m_dataSource;
31 }
32 set
33 {
34 m_dataSource = value;
35 if (value != null)
36 {
37 lblTitle.Text = value.Text;
38 }
39 }
40 }
41 public UCMenuChildrenItem()
42 {
43 InitializeComponent();
44 this.lblTitle.MouseDown += lblTitle_MouseDown;
45 }
46
47 void lblTitle_MouseDown(object sender, MouseEventArgs e)
48 {
49 if (SelectedItem != null)
50 {
51 SelectedItem(this, null);
52 }
53 }
54
55 public void SetStyle(Dictionary<string, object> styles)
56 {
57 Type t = this.GetType();
58 foreach (var item in styles)
59 {
60 var pro = t.GetProperty(item.Key);
61 if (pro != null && pro.CanWrite)
62 {
63 try
64 {
65 pro.SetValue(this, item.Value, null);
66 }
67 catch (Exception ex)
68 {
69 throw new Exception("菜单元素设置样式异常", ex);
70 }
71 }
72 }
73 }
74
75 public void SetSelectedStyle(bool blnSelected)
76 {
77
78 }
79 }
80 }
1 namespace HZH_Controls.Controls
2 {
3 partial class UCMenuChildrenItem
4 {
5 /// <summary>
6 /// 必需的设计器变量。
7 /// </summary>
8 private System.ComponentModel.IContainer components = null;
9
10 /// <summary>
11 /// 清理所有正在使用的资源。
12 /// </summary>
13 /// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
14 protected override void Dispose(bool disposing)
15 {
16 if (disposing && (components != null))
17 {
18 components.Dispose();
19 }
20 base.Dispose(disposing);
21 }
22
23 #region 组件设计器生成的代码
24
25 /// <summary>
26 /// 设计器支持所需的方法 - 不要
27 /// 使用代码编辑器修改此方法的内容。
28 /// </summary>
29 private void InitializeComponent()
30 {
31 this.ucSplitLine_H1 = new HZH_Controls.Controls.UCSplitLine_H();
32 this.lblTitle = new System.Windows.Forms.Label();
33 this.SuspendLayout();
34 //
35 // ucSplitLine_H1
36 //
37 this.ucSplitLine_H1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(29)))), ((int)(((byte)(43)))), ((int)(((byte)(54)))));
38 this.ucSplitLine_H1.Dock = System.Windows.Forms.DockStyle.Bottom;
39 this.ucSplitLine_H1.Location = new System.Drawing.Point(0, 59);
40 this.ucSplitLine_H1.Name = "ucSplitLine_H1";
41 this.ucSplitLine_H1.Size = new System.Drawing.Size(200, 1);
42 this.ucSplitLine_H1.TabIndex = 0;
43 this.ucSplitLine_H1.TabStop = false;
44 //
45 // lblTitle
46 //
47 this.lblTitle.Dock = System.Windows.Forms.DockStyle.Fill;
48 this.lblTitle.Font = new System.Drawing.Font("微软雅黑", 12F);
49 this.lblTitle.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(145)))), ((int)(((byte)(152)))), ((int)(((byte)(170)))));
50 this.lblTitle.Location = new System.Drawing.Point(0, 0);
51 this.lblTitle.Name = "lblTitle";
52 this.lblTitle.Size = new System.Drawing.Size(200, 59);
53 this.lblTitle.TabIndex = 1;
54 this.lblTitle.Text = "子项";
55 this.lblTitle.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
56 //
57 // UCMenuChildrenItem
58 //
59 this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
60 this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(20)))), ((int)(((byte)(30)))), ((int)(((byte)(39)))));
61 this.Controls.Add(this.lblTitle);
62 this.Controls.Add(this.ucSplitLine_H1);
63 this.Font = new System.Drawing.Font("微软雅黑", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
64 this.Name = "UCMenuChildrenItem";
65 this.Size = new System.Drawing.Size(200, 60);
66 this.ResumeLayout(false);
67
68 }
69
70 #endregion
71
72 private UCSplitLine_H ucSplitLine_H1;
73 private System.Windows.Forms.Label lblTitle;
74
75 }
76 }
设计效果是这样子的
你们有没有发现,父节点和子节点代码非常的相似呢?是的,基本上都是一样的,他们都实现了接口IMenuItem,
那既然如此为什么还要分为2个,这不是代码冗余了吗?这么做的好处就是你可以更方便的控制节点的设计样式,假如有一天,子节点你不想用文字了,你先够用图片呢?
到此,节点定义已经完成了,剩下的就是处理列表了,继续往下看吧。
定义一个用户控件,命名UCMenu
首先定义一个枚举
public enum MenuStyle
{
/// <summary>
/// 平铺
/// </summary>
Fill = 1,
/// <summary>
/// 顶部对齐
/// </summary>
Top = 2,
}
这个枚举的作用就是改变样式,默认是Fill,也就是子节点面板填充铺满,选中父节点上面的兄弟节点顶端对齐,下面的兄弟节点低端对齐,当父节点较多时候就会出现子节点无法显示的问题,这个时候使用Top就可以了,所有父节点顶端对齐
先看下有哪些属性
1 /// <summary>
2 /// 选中项事件
3 /// </summary>
4 public event EventHandler SelectedItem;
5 private Type m_parentItemType = typeof(UCMenuParentItem);
6 /// <summary>
7 /// 父类节点类型
8 /// </summary>
9 public Type ParentItemType
10 {
11 get { return m_parentItemType; }
12 set
13 {
14 if (value == null)
15 return;
16 if (!typeof(IMenuItem).IsAssignableFrom(value) || !value.IsSubclassOf(typeof(Control)))
17 throw new Exception("节点控件没有实现IMenuItem接口");
18 m_parentItemType = value;
19
20 }
21 }
22
23 private Type m_childrenItemType = typeof(UCMenuChildrenItem);
24 /// <summary>
25 /// 子类节点类型
26 /// </summary>
27 public Type ChildrenItemType
28 {
29 get { return m_childrenItemType; }
30 set
31 {
32 if (value == null)
33 return;
34 if (!typeof(IMenuItem).IsAssignableFrom(value) || !value.IsSubclassOf(typeof(Control)))
35 throw new Exception("节点控件没有实现IMenuItem接口");
36 m_childrenItemType = value;
37 }
38 }
39
40 private Dictionary<string, object> m_parentItemStyles;
41 /// <summary>
42 /// 父类节点样式设置,key:属性名称,value:属性值
43 /// </summary>
44 public Dictionary<string, object> ParentItemStyles
45 {
46 get { return m_parentItemStyles; }
47 set { m_parentItemStyles = value; }
48 }
49
50 private Dictionary<string, object> m_childrenItemStyles;
51 /// <summary>
52 /// 子类节点样式设置,key:属性名称,value:属性值
53 /// </summary>
54 public Dictionary<string, object> ChildrenItemStyles
55 {
56 get { return m_childrenItemStyles; }
57 set { m_childrenItemStyles = value; }
58 }
59
60 private List<MenuItemEntity> m_dataSource;
61 /// <summary>
62 /// 数据源
63 /// </summary>
64 public List<MenuItemEntity> DataSource
65 {
66 get { return m_dataSource; }
67 set
68 {
69 m_dataSource = value;
70
71 ReloadItems();
72 }
73 }
74 private bool m_isShowFirstItem = true;
75 /// <summary>
76 /// 是否自动展开第一个节点
77 /// </summary>
78 public bool IsShowFirstItem
79 {
80 get { return m_isShowFirstItem; }
81 set { m_isShowFirstItem = value; }
82 }
83
84 private MenuStyle m_menuStyle = MenuStyle.Fill;
85 /// <summary>
86 /// 菜单样式
87 /// </summary>
88 public MenuStyle MenuStyle
89 {
90 get { return m_menuStyle; }
91 set { m_menuStyle = value; }
92 }
93
94 private List<Control> m_lstParentItems = new List<Control>();
95
96 private IMenuItem m_selectParentItem = null;
97 private IMenuItem m_selectChildrenItem = null;
98
99 private Panel m_panChildren = null;
数据源改变时需要重新加载
1 private void ReloadItems()
2 {
3 try
4 {
5 ControlHelper.FreezeControl(this, true);
6 this.Controls.Clear();
7 m_lstParentItems.Clear();
8 if (m_dataSource != null && m_dataSource.Count > 0)
9 {
10 foreach (var parent in m_dataSource)
11 {
12 IMenuItem parentItem = (IMenuItem)Activator.CreateInstance(m_parentItemType);
13 parentItem.DataSource = parent;
14 if (m_parentItemStyles != null)
15 parentItem.SetStyle(m_parentItemStyles);
16 parentItem.SelectedItem += parentItem_SelectedItem;
17 Control c = parentItem as Control;
18 c.Dock = DockStyle.Top;
19 this.Controls.Add(c);
20 this.Controls.SetChildIndex(c, 0);
21 m_lstParentItems.Add(c);
22 }
23 }
24 m_panChildren = new Panel();
25 if (m_menuStyle == HZH_Controls.Controls.MenuStyle.Fill)
26 {
27 m_panChildren.Dock = DockStyle.Fill;
28 m_panChildren.Height = 0;
29 }
30 else
31 {
32 m_panChildren.Dock = DockStyle.Top;
33 m_panChildren.Height = 0;
34 }
35 m_panChildren.AutoScroll = true;
36 this.Controls.Add(m_panChildren);
37 }
38 finally
39 {
40 ControlHelper.FreezeControl(this, false);
41 }
42
43 if (m_isShowFirstItem && m_lstParentItems != null && m_lstParentItems.Count > 0)
44 {
45 parentItem_SelectedItem(m_lstParentItems[0], null);
46 }
47 }
选中父节点时候加载子节点
1 void parentItem_SelectedItem(object sender, EventArgs e)
2 {
3 this.FindForm().ActiveControl = this;
4 IMenuItem item = sender as IMenuItem;
5 if (m_lstParentItems.Contains(sender as Control))
6 {
7 if (m_selectParentItem != item)
8 {
9 if (m_selectParentItem != null)
10 {
11 m_selectParentItem.SetSelectedStyle(false);
12 }
13 m_selectParentItem = item;
14 m_selectParentItem.SetSelectedStyle(true);
15 SetChildrenControl(m_selectParentItem);
16 }
17 else
18 {
19 m_selectParentItem.SetSelectedStyle(false);
20 m_selectParentItem = null;
21 SetChildrenControl(m_selectParentItem, false);
22 }
23 }
24 else if (m_panChildren.Controls.Contains(sender as Control))
25 {
26 if (m_selectChildrenItem != item)
27 {
28 if (m_selectChildrenItem != null)
29 {
30 m_selectChildrenItem.SetSelectedStyle(false);
31 }
32 m_selectChildrenItem = item;
33 m_selectChildrenItem.SetSelectedStyle(true);
34 }
35 }
36 if (SelectedItem != null)
37 {
38 SelectedItem(sender, e);
39 }
40 }
41
42 private void SetChildrenControl(IMenuItem menuitem, bool blnChildren = true)
43 {
44 try
45 {
46 ControlHelper.FreezeControl(this, true);
47 if (m_menuStyle == HZH_Controls.Controls.MenuStyle.Fill)
48 {
49 if (blnChildren)
50 {
51 Control cMenu = menuitem as Control;
52 int index = m_lstParentItems.IndexOf(cMenu);
53 for (int i = 0; i <= index; i++)
54 {
55 m_lstParentItems[i].Dock = DockStyle.Top;
56 this.Controls.SetChildIndex(m_lstParentItems[i], 1);
57 }
58 for (int i = index + 1; i < m_lstParentItems.Count; i++)
59 {
60 m_lstParentItems[i].Dock = DockStyle.Bottom;
61 this.Controls.SetChildIndex(m_lstParentItems[i], m_lstParentItems.Count);
62 }
63 m_panChildren.Controls.Clear();
64 int intItemHeigth = 0;
65 foreach (var item in menuitem.DataSource.Childrens)
66 {
67 IMenuItem parentItem = (IMenuItem)Activator.CreateInstance(m_childrenItemType);
68 parentItem.DataSource = item;
69 if (m_childrenItemStyles != null)
70 parentItem.SetStyle(m_childrenItemStyles);
71 parentItem.SelectedItem += parentItem_SelectedItem;
72 Control c = parentItem as Control;
73 if (intItemHeigth == 0)
74 intItemHeigth = c.Height;
75 c.Dock = DockStyle.Top;
76 m_panChildren.Controls.Add(c);
77 m_panChildren.Controls.SetChildIndex(c, 0);
78 }
79 //m_panChildren.MinimumSize = new Size(0, menuitem.DataSource.Childrens.Count * intItemHeigth);
80 }
81 else
82 {
83 m_panChildren.Controls.Clear();
84 foreach (var item in m_lstParentItems)
85 {
86 item.Dock = DockStyle.Top;
87 this.Controls.SetChildIndex(item, 1);
88 }
89 }
90 }
91 else
92 {
93 if (blnChildren)
94 {
95 Control cMenu = menuitem as Control;
96 int index = m_lstParentItems.IndexOf(cMenu);
97 this.Controls.SetChildIndex(m_panChildren, m_lstParentItems.Count - index - 1);
98 m_panChildren.Controls.Clear();
99 int intItemHeigth = 0;
100 foreach (var item in menuitem.DataSource.Childrens)
101 {
102 IMenuItem parentItem = (IMenuItem)Activator.CreateInstance(m_childrenItemType);
103 parentItem.DataSource = item;
104 if (m_childrenItemStyles != null)
105 parentItem.SetStyle(m_childrenItemStyles);
106 parentItem.SelectedItem += parentItem_SelectedItem;
107 Control c = parentItem as Control;
108 if (intItemHeigth == 0)
109 intItemHeigth = c.Height;
110 c.Dock = DockStyle.Top;
111 m_panChildren.Controls.Add(c);
112 m_panChildren.Controls.SetChildIndex(c, 0);
113 }
114 m_panChildren.Height = menuitem.DataSource.Childrens.Count * intItemHeigth;
115 }
116 else
117 {
118 m_panChildren.Controls.Clear();
119 m_panChildren.Height = 0;
120 }
121 }
122 }
123 finally
124 {
125 ControlHelper.FreezeControl(this, false);
126 }
127 }
代码就这么多,看下完整代码
1 // 版权所有 黄正辉 交流群:568015492 QQ:623128629
2 // 文件名称:UCMenu.cs
3 // 创建日期:2019-08-15 16:02:22
4 // 功能描述:Menu
5 // 项目地址:https://gitee.com/kwwwvagaa/net_winform_custom_control
6 using System;
7 using System.Collections.Generic;
8 using System.ComponentModel;
9 using System.Drawing;
10 using System.Data;
11 using System.Linq;
12 using System.Text;
13 using System.Windows.Forms;
14
15 namespace HZH_Controls.Controls
16 {
17 public partial class UCMenu : UserControl
18 {
19 /// <summary>
20 /// 选中项事件
21 /// </summary>
22 public event EventHandler SelectedItem;
23 private Type m_parentItemType = typeof(UCMenuParentItem);
24 /// <summary>
25 /// 父类节点类型
26 /// </summary>
27 public Type ParentItemType
28 {
29 get { return m_parentItemType; }
30 set
31 {
32 if (value == null)
33 return;
34 if (!typeof(IMenuItem).IsAssignableFrom(value) || !value.IsSubclassOf(typeof(Control)))
35 throw new Exception("节点控件没有实现IMenuItem接口");
36 m_parentItemType = value;
37
38 }
39 }
40
41 private Type m_childrenItemType = typeof(UCMenuChildrenItem);
42 /// <summary>
43 /// 子类节点类型
44 /// </summary>
45 public Type ChildrenItemType
46 {
47 get { return m_childrenItemType; }
48 set
49 {
50 if (value == null)
51 return;
52 if (!typeof(IMenuItem).IsAssignableFrom(value) || !value.IsSubclassOf(typeof(Control)))
53 throw new Exception("节点控件没有实现IMenuItem接口");
54 m_childrenItemType = value;
55 }
56 }
57
58 private Dictionary<string, object> m_parentItemStyles;
59 /// <summary>
60 /// 父类节点样式设置,key:属性名称,value:属性值
61 /// </summary>
62 public Dictionary<string, object> ParentItemStyles
63 {
64 get { return m_parentItemStyles; }
65 set { m_parentItemStyles = value; }
66 }
67
68 private Dictionary<string, object> m_childrenItemStyles;
69 /// <summary>
70 /// 子类节点样式设置,key:属性名称,value:属性值
71 /// </summary>
72 public Dictionary<string, object> ChildrenItemStyles
73 {
74 get { return m_childrenItemStyles; }
75 set { m_childrenItemStyles = value; }
76 }
77
78 private List<MenuItemEntity> m_dataSource;
79 /// <summary>
80 /// 数据源
81 /// </summary>
82 public List<MenuItemEntity> DataSource
83 {
84 get { return m_dataSource; }
85 set
86 {
87 m_dataSource = value;
88
89 ReloadItems();
90 }
91 }
92 private bool m_isShowFirstItem = true;
93 /// <summary>
94 /// 是否自动展开第一个节点
95 /// </summary>
96 public bool IsShowFirstItem
97 {
98 get { return m_isShowFirstItem; }
99 set { m_isShowFirstItem = value; }
100 }
101
102 private MenuStyle m_menuStyle = MenuStyle.Fill;
103 /// <summary>
104 /// 菜单样式
105 /// </summary>
106 public MenuStyle MenuStyle
107 {
108 get { return m_menuStyle; }
109 set { m_menuStyle = value; }
110 }
111
112 private List<Control> m_lstParentItems = new List<Control>();
113
114 private IMenuItem m_selectParentItem = null;
115 private IMenuItem m_selectChildrenItem = null;
116
117 private Panel m_panChildren = null;
118
119 private void ReloadItems()
120 {
121 try
122 {
123 ControlHelper.FreezeControl(this, true);
124 this.Controls.Clear();
125 m_lstParentItems.Clear();
126 if (m_dataSource != null && m_dataSource.Count > 0)
127 {
128 foreach (var parent in m_dataSource)
129 {
130 IMenuItem parentItem = (IMenuItem)Activator.CreateInstance(m_parentItemType);
131 parentItem.DataSource = parent;
132 if (m_parentItemStyles != null)
133 parentItem.SetStyle(m_parentItemStyles);
134 parentItem.SelectedItem += parentItem_SelectedItem;
135 Control c = parentItem as Control;
136 c.Dock = DockStyle.Top;
137 this.Controls.Add(c);
138 this.Controls.SetChildIndex(c, 0);
139 m_lstParentItems.Add(c);
140 }
141 }
142 m_panChildren = new Panel();
143 if (m_menuStyle == HZH_Controls.Controls.MenuStyle.Fill)
144 {
145 m_panChildren.Dock = DockStyle.Fill;
146 m_panChildren.Height = 0;
147 }
148 else
149 {
150 m_panChildren.Dock = DockStyle.Top;
151 m_panChildren.Height = 0;
152 }
153 m_panChildren.AutoScroll = true;
154 this.Controls.Add(m_panChildren);
155 }
156 finally
157 {
158 ControlHelper.FreezeControl(this, false);
159 }
160
161 if (m_isShowFirstItem && m_lstParentItems != null && m_lstParentItems.Count > 0)
162 {
163 parentItem_SelectedItem(m_lstParentItems[0], null);
164 }
165 }
166
167 void parentItem_SelectedItem(object sender, EventArgs e)
168 {
169 this.FindForm().ActiveControl = this;
170 IMenuItem item = sender as IMenuItem;
171 if (m_lstParentItems.Contains(sender as Control))
172 {
173 if (m_selectParentItem != item)
174 {
175 if (m_selectParentItem != null)
176 {
177 m_selectParentItem.SetSelectedStyle(false);
178 }
179 m_selectParentItem = item;
180 m_selectParentItem.SetSelectedStyle(true);
181 SetChildrenControl(m_selectParentItem);
182 }
183 else
184 {
185 m_selectParentItem.SetSelectedStyle(false);
186 m_selectParentItem = null;
187 SetChildrenControl(m_selectParentItem, false);
188 }
189 }
190 else if (m_panChildren.Controls.Contains(sender as Control))
191 {
192 if (m_selectChildrenItem != item)
193 {
194 if (m_selectChildrenItem != null)
195 {
196 m_selectChildrenItem.SetSelectedStyle(false);
197 }
198 m_selectChildrenItem = item;
199 m_selectChildrenItem.SetSelectedStyle(true);
200 }
201 }
202 if (SelectedItem != null)
203 {
204 SelectedItem(sender, e);
205 }
206 }
207
208 private void SetChildrenControl(IMenuItem menuitem, bool blnChildren = true)
209 {
210 try
211 {
212 ControlHelper.FreezeControl(this, true);
213 if (m_menuStyle == HZH_Controls.Controls.MenuStyle.Fill)
214 {
215 if (blnChildren)
216 {
217 Control cMenu = menuitem as Control;
218 int index = m_lstParentItems.IndexOf(cMenu);
219 for (int i = 0; i <= index; i++)
220 {
221 m_lstParentItems[i].Dock = DockStyle.Top;
222 this.Controls.SetChildIndex(m_lstParentItems[i], 1);
223 }
224 for (int i = index + 1; i < m_lstParentItems.Count; i++)
225 {
226 m_lstParentItems[i].Dock = DockStyle.Bottom;
227 this.Controls.SetChildIndex(m_lstParentItems[i], m_lstParentItems.Count);
228 }
229 m_panChildren.Controls.Clear();
230 int intItemHeigth = 0;
231 foreach (var item in menuitem.DataSource.Childrens)
232 {
233 IMenuItem parentItem = (IMenuItem)Activator.CreateInstance(m_childrenItemType);
234 parentItem.DataSource = item;
235 if (m_childrenItemStyles != null)
236 parentItem.SetStyle(m_childrenItemStyles);
237 parentItem.SelectedItem += parentItem_SelectedItem;
238 Control c = parentItem as Control;
239 if (intItemHeigth == 0)
240 intItemHeigth = c.Height;
241 c.Dock = DockStyle.Top;
242 m_panChildren.Controls.Add(c);
243 m_panChildren.Controls.SetChildIndex(c, 0);
244 }
245 //m_panChildren.MinimumSize = new Size(0, menuitem.DataSource.Childrens.Count * intItemHeigth);
246 }
247 else
248 {
249 m_panChildren.Controls.Clear();
250 foreach (var item in m_lstParentItems)
251 {
252 item.Dock = DockStyle.Top;
253 this.Controls.SetChildIndex(item, 1);
254 }
255 }
256 }
257 else
258 {
259 if (blnChildren)
260 {
261 Control cMenu = menuitem as Control;
262 int index = m_lstParentItems.IndexOf(cMenu);
263 this.Controls.SetChildIndex(m_panChildren, m_lstParentItems.Count - index - 1);
264 m_panChildren.Controls.Clear();
265 int intItemHeigth = 0;
266 foreach (var item in menuitem.DataSource.Childrens)
267 {
268 IMenuItem parentItem = (IMenuItem)Activator.CreateInstance(m_childrenItemType);
269 parentItem.DataSource = item;
270 if (m_childrenItemStyles != null)
271 parentItem.SetStyle(m_childrenItemStyles);
272 parentItem.SelectedItem += parentItem_SelectedItem;
273 Control c = parentItem as Control;
274 if (intItemHeigth == 0)
275 intItemHeigth = c.Height;
276 c.Dock = DockStyle.Top;
277 m_panChildren.Controls.Add(c);
278 m_panChildren.Controls.SetChildIndex(c, 0);
279 }
280 m_panChildren.Height = menuitem.DataSource.Childrens.Count * intItemHeigth;
281 }
282 else
283 {
284 m_panChildren.Controls.Clear();
285 m_panChildren.Height = 0;
286 }
287 }
288 }
289 finally
290 {
291 ControlHelper.FreezeControl(this, false);
292 }
293 }
294
295 public UCMenu()
296 {
297 InitializeComponent();
298 }
299 }
300
301 public enum MenuStyle
302 {
303 /// <summary>
304 /// 平铺
305 /// </summary>
306 Fill = 1,
307 /// <summary>
308 /// 顶部对齐
309 /// </summary>
310 Top = 2,
311 }
312
313 }
1 namespace HZH_Controls.Controls
2 {
3 partial class UCMenu
4 {
5 /// <summary>
6 /// 必需的设计器变量。
7 /// </summary>
8 private System.ComponentModel.IContainer components = null;
9
10 /// <summary>
11 /// 清理所有正在使用的资源。
12 /// </summary>
13 /// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
14 protected override void Dispose(bool disposing)
15 {
16 if (disposing && (components != null))
17 {
18 components.Dispose();
19 }
20 base.Dispose(disposing);
21 }
22
23 #region 组件设计器生成的代码
24
25 /// <summary>
26 /// 设计器支持所需的方法 - 不要
27 /// 使用代码编辑器修改此方法的内容。
28 /// </summary>
29 private void InitializeComponent()
30 {
31 this.SuspendLayout();
32 //
33 // UCMenu
34 //
35 this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
36 this.AutoScroll = true;
37 this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(20)))), ((int)(((byte)(30)))), ((int)(((byte)(39)))));
38 this.Name = "UCMenu";
39 this.Size = new System.Drawing.Size(204, 468);
40 this.ResumeLayout(false);
41
42 }
43
44 #endregion
45
46 }
47 }
示例代码
List<MenuItemEntity> lstMenu = new List<MenuItemEntity>();
for (int i = 0; i < 5; i++)
{
MenuItemEntity item = new MenuItemEntity()
{
Key = "p" + i.ToString(),
Text = "菜单项" + i,
DataSource = "这里编写一些自定义的数据源,用于扩展"
};
item.Childrens = new List<MenuItemEntity>();
for (int j = 0; j < 5; j++)
{
MenuItemEntity item2 = new MenuItemEntity()
{
Key = "c" + i.ToString(),
Text = "菜单子项" + i + "-" + j,
DataSource = "这里编写一些自定义的数据源,用于扩展"
};
item.Childrens.Add(item2);
}
lstMenu.Add(item);
}
this.ucMenu1.MenuStyle = MenuStyle.Top;
this.ucMenu1.DataSource = lstMenu;