(十三)c#Winform自定义控件-导航菜单-HZHControls
官网
前提
入行已经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;
最后的话
如果你喜欢的话,请到 https://gitee.com/kwwwvagaa/net_winform_custom_control 点个星 星吧
作者:冰封一夏
出处:http://www.cnblogs.com/bfyx/
HZHControls官网:http://www.hzhcontrols.cn
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,
且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
GitHub:https://github.com/kwwwvagaa/NetWinformControl
码云:https://gitee.com/kwwwvagaa/net_winform_custom_control.git