winform 控件属性(十三CWinFrom自定义控件系列-导航菜单)

前提

入行已经7,8年了,一直想做一套漂亮点的自定义控件,于是就有了本系列文章。

本系列文章将讲解各种控件的开发及思路,欢迎各位批评指正。

此系列控件开发教程将全部在原生控件基础上进行重绘开发,目标的扁平化、漂亮、支持触屏。

如果有什么好的建议也可以评论留言来交流。

源码地址:

GitHub:https://github.com/kwwwvagaa/NetWinformControl

码云:https://gitee.com/kwwwvagaa/net_winform_custom_Control.git

如果觉得写的还行,请点个 star 支持一下吧

欢迎前来交流探讨: 企鹅群568015492

目录

http://toutiao.com/item/6824291838963220999/

准备工作

有时候我们需要左侧的导航菜单,那么来整一个吧

先来分析分析,导航菜单一般分为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); } }

这样就完事了

设计效果就是这样子的了

winform 控件属性(十三CWinFrom自定义控件系列-导航菜单)(1)

父节点弄好了,下面就是子节点了

添加用户控件,命名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 }

这样就完成了

设计效果是这样子的

winform 控件属性(十三CWinFrom自定义控件系列-导航菜单)(2)

你们有没有发现,父节点和子节点代码非常的相似呢?是的,基本上都是一样的,他们都实现了接口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 }

代码就这么多

用处及效果

winform 控件属性(十三CWinFrom自定义控件系列-导航菜单)(3)

示例代码

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;

最后的话

如果你喜欢的话,请到 码云或Github 点个星星吧

,

免责声明:本文仅代表文章作者的个人观点,与本站无关。其原创性、真实性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容文字的真实性、完整性和原创性本站不作任何保证或承诺,请读者仅作参考,并自行核实相关内容。文章投诉邮箱:anhduc.ph@yahoo.com

    分享
    投诉
    首页