前提
入行已经7,8年了,一直想做一套漂亮点的自定义控件,于是就有了本系列文章。
GitHub:https://github.com/kwwwvagaa/NetWinformControl
码云:https://gitee.com/kwwwvagaa/net_winform_custom_control.git
如果觉得写的还行,请点个 star 支持一下吧
麻烦博客下方点个【推荐】,谢谢
NuGet
Install-Package HZH_Controls
目录
https://www.cnblogs.com/bfyx/p/11364884.html
用处及效果
使用分页控件效果
不使用分页控件效果
准备工作
我们需要元素控件,需要列表控件,另外为了具有更好的扩展性,元素控件实现接口,方便进行扩展
我们用到了分页控件,如果你还不了解,请移步查看
我们这里的元素控件用到圆角,故继承基类控件UCControlBase,如果不了解,请移步查看
开始
添加一个接口,用来约束元素控件
1 public interface IListViewItem 2 { 3 /// <summary> 4 /// 数据源 5 /// </summary> 6 object DataSource { get; set; } 7 /// <summary> 8 /// 选中项事件 9 /// </summary> 10 event EventHandler SelectedItemEvent; 11 /// <summary> 12 /// 选中处理,一般用以更改选中效果 13 /// </summary> 14 /// <param name="blnSelected">是否选中</param> 15 void SetSelected(bool blnSelected); 16 }
添加一个元素控件,命名UCListViewItem,我们这里继承基类控件UCControlBase,实现接口IListViewItem
1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Drawing; 5 using System.Data; 6 using System.Linq; 7 using System.Text; 8 using System.Windows.Forms; 9 10 namespace HZH_Controls.Controls 11 { 12 [ToolboxItem(false)] 13 public partial class UCListViewItem : UCControlBase, IListViewItem 14 { 15 private object m_dataSource; 16 public object DataSource 17 { 18 get 19 { 20 return m_dataSource; 21 } 22 set 23 { 24 m_dataSource = value; 25 lblTitle.Text = value.ToString(); 26 } 27 } 28 29 public event EventHandler SelectedItemEvent; 30 public UCListViewItem() 31 { 32 InitializeComponent(); 33 lblTitle.MouseDown += lblTitle_MouseDown; 34 } 35 36 void lblTitle_MouseDown(object sender, MouseEventArgs e) 37 { 38 if (SelectedItemEvent != null) 39 { 40 SelectedItemEvent(this, e); 41 } 42 } 43 44 public void SetSelected(bool blnSelected) 45 { 46 if (blnSelected) 47 this.FillColor = Color.FromArgb(255, 247, 245); 48 else 49 this.FillColor = Color.White; 50 this.Refresh(); 51 } 52 } 53 }
1 namespace HZH_Controls.Controls 2 { 3 partial class UCListViewItem 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.lblTitle = new System.Windows.Forms.Label(); 32 this.SuspendLayout(); 33 // 34 // lblTitle 35 // 36 this.lblTitle.Dock = System.Windows.Forms.DockStyle.Fill; 37 this.lblTitle.Location = new System.Drawing.Point(0, 0); 38 this.lblTitle.Name = "lblTitle"; 39 this.lblTitle.Size = new System.Drawing.Size(107, 96); 40 this.lblTitle.TabIndex = 0; 41 this.lblTitle.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; 42 // 43 // UCListViewItem 44 // 45 this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None; 46 this.BackColor = System.Drawing.Color.Transparent; 47 this.Controls.Add(this.lblTitle); 48 this.FillColor = System.Drawing.Color.White; 49 this.IsRadius = true; 50 this.IsShowRect = true; 51 this.Name = "UCListViewItem"; 52 this.RectColor = System.Drawing.Color.FromArgb(((int)(((byte)(232)))), ((int)(((byte)(232)))), ((int)(((byte)(232))))); 53 this.Size = new System.Drawing.Size(107, 96); 54 this.ResumeLayout(false); 55 56 } 57 58 #endregion 59 60 private System.Windows.Forms.Label lblTitle; 61 } 62 }
然后需要一个列表来显示元素控件
添加一个用户控件,命名UCListView
一些属性
1 int m_intCellWidth = 130;//单元格宽度 2 int m_intCellHeight = 120;//单元格高度 3 4 private Type m_itemType = typeof(UCListViewItem); 5 6 [Description("单元格类型,如果无法满足您的需求,你可以自定义单元格控件,并实现接口IListViewItem"), Category("自定义")] 7 public Type ItemType 8 { 9 get { return m_itemType; } 10 set 11 { 12 if (!typeof(IListViewItem).IsAssignableFrom(value) || !value.IsSubclassOf(typeof(Control))) 13 throw new Exception("单元格控件没有继承实现接口IListViewItem"); 14 m_itemType = value; 15 } 16 } 17 18 private UCPagerControlBase m_page = null; 19 /// <summary> 20 /// 翻页控件 21 /// </summary> 22 [Description("翻页控件,如果UCPagerControl不满足你的需求,请自定义翻页控件并继承UCPagerControlBase"), Category("自定义")] 23 public UCPagerControlBase Page 24 { 25 get { return m_page; } 26 set 27 { 28 m_page = value; 29 if (value != null) 30 { 31 if (!typeof(IPageControl).IsAssignableFrom(value.GetType()) || !value.GetType().IsSubclassOf(typeof(UCPagerControlBase))) 32 throw new Exception("翻页控件没有继承UCPagerControlBase"); 33 this.panMain.AutoScroll = false; 34 panPage.Visible = true; 35 this.Controls.SetChildIndex(panMain, 0); 36 m_page.ShowSourceChanged += m_page_ShowSourceChanged; 37 m_page.Dock = DockStyle.Fill; 38 this.panPage.Controls.Clear(); 39 this.panPage.Controls.Add(m_page); 40 GetCellCount(); 41 this.DataSource = m_page.GetCurrentSource(); 42 } 43 else 44 { 45 this.panMain.AutoScroll = true; 46 m_page = null; 47 panPage.Visible = false; 48 } 49 } 50 } 51 52 53 54 private object m_dataSource = null; 55 56 [Description("数据源,如果使用翻页控件,请使用翻页控件的DataSource"), Category("自定义")] 57 public object DataSource 58 { 59 get { return m_dataSource; } 60 set 61 { 62 if (value == null) 63 return; 64 if (!typeof(IList).IsAssignableFrom(value.GetType())) 65 { 66 throw new Exception("数据源不是有效的数据类型,列表"); 67 } 68 m_dataSource = value; 69 ReloadSource(); 70 } 71 } 72 73 int m_intCellCount = 0;//单元格总数 74 [Description("单元格总数"), Category("自定义")] 75 public int CellCount 76 { 77 get { return m_intCellCount; } 78 private set 79 { 80 m_intCellCount = value; 81 if (value > 0 && m_page != null) 82 { 83 m_page.PageSize = m_intCellCount; 84 m_page.Reload(); 85 } 86 } 87 } 88 89 private List<object> m_selectedSource = new List<object>(); 90 91 [Description("选中的数据"), Category("自定义")] 92 public List<object> SelectedSource 93 { 94 get { return m_selectedSource; } 95 set 96 { 97 m_selectedSource = value; 98 ReloadSource(); 99 } 100 } 101 102 private bool m_isMultiple = true; 103 104 [Description("是否多选"), Category("自定义")] 105 public bool IsMultiple 106 { 107 get { return m_isMultiple; } 108 set { m_isMultiple = value; } 109 } 110 111 [Description("选中项事件"), Category("自定义")] 112 public event EventHandler SelectedItemEvent; 113 public delegate void ReloadGridStyleEventHandle(int intCellCount); 114 /// <summary> 115 /// 样式改变事件 116 /// </summary> 117 [Description("样式改变事件"), Category("自定义")] 118 public event ReloadGridStyleEventHandle ReloadGridStyleEvent;
一下辅助函数
1 #region 重新加载数据源 2 /// <summary> 3 /// 功能描述:重新加载数据源 4 /// 作 者:HZH 5 /// 创建日期:2019-06-27 16:47:32 6 /// 任务编号:POS 7 /// </summary> 8 public void ReloadSource() 9 { 10 ControlHelper.FreezeControl(this, true); 11 if (this.panMain.Controls.Count <= 0) 12 { 13 ReloadGridStyle(); 14 } 15 if (m_dataSource == null || ((IList)m_dataSource).Count <= 0) 16 { 17 for (int i = this.panMain.Controls.Count - 1; i >= 0; i--) 18 { 19 this.panMain.Controls[i].Visible = false; 20 } 21 22 return; 23 } 24 int intCount = Math.Min(((IList)m_dataSource).Count, this.panMain.Controls.Count); 25 26 for (int i = 0; i < intCount; i++) 27 { 28 ((IListViewItem)this.panMain.Controls[i]).DataSource = ((IList)m_dataSource)[i]; 29 if (m_selectedSource.Contains(((IList)m_dataSource)[i])) 30 { 31 ((IListViewItem)this.panMain.Controls[i]).SetSelected(true); 32 } 33 else 34 { 35 ((IListViewItem)this.panMain.Controls[i]).SetSelected(false); 36 } 37 this.panMain.Controls[i].Visible = true; 38 } 39 40 for (int i = this.panMain.Controls.Count - 1; i >= intCount; i--) 41 { 42 if (this.panMain.Controls[i].Visible) 43 this.panMain.Controls[i].Visible = false; 44 } 45 ControlHelper.FreezeControl(this, false); 46 } 47 #endregion 48 49 #region 刷新表格 50 /// <summary> 51 /// 功能描述:刷新表格样式 52 /// 作 者:HZH 53 /// 创建日期:2019-06-27 16:35:25 54 /// 任务编号:POS 55 /// </summary> 56 public void ReloadGridStyle() 57 { 58 Form frmMain = this.FindForm(); 59 if (frmMain != null && !frmMain.IsDisposed && frmMain.Visible && this.Visible) 60 { 61 GetCellCount(); 62 try 63 { 64 ControlHelper.FreezeControl(this, true); 65 if (this.panMain.Controls.Count < m_intCellCount) 66 { 67 int intControlsCount = this.panMain.Controls.Count; 68 for (int i = 0; i < m_intCellCount - intControlsCount; i++) 69 { 70 Control uc = (Control)Activator.CreateInstance(m_itemType); 71 uc.Margin = new System.Windows.Forms.Padding(5, 5, 5, 5); 72 73 (uc as IListViewItem).SelectedItemEvent += UCListView_SelectedItemEvent; 74 uc.Visible = false; 75 this.panMain.Controls.Add(uc); 76 } 77 } 78 else if (this.panMain.Controls.Count > m_intCellCount) 79 { 80 int intControlsCount = this.panMain.Controls.Count; 81 for (int i = intControlsCount - 1; i > m_intCellCount - 1; i--) 82 { 83 this.panMain.Controls.RemoveAt(i); 84 } 85 } 86 foreach (Control item in this.panMain.Controls) 87 { 88 item.Size = new Size(m_intCellWidth, m_intCellHeight); 89 } 90 } 91 finally 92 { 93 ControlHelper.FreezeControl(this, false); 94 } 95 if (ReloadGridStyleEvent != null) 96 { 97 ReloadGridStyleEvent(m_intCellCount); 98 } 99 } 100 101 } 102 103 void UCListView_SelectedItemEvent(object sender, EventArgs e) 104 { 105 var selectedItem = sender as IListViewItem; 106 107 if (m_selectedSource.Contains(selectedItem.DataSource)) 108 { 109 m_selectedSource.Remove(selectedItem.DataSource); 110 selectedItem.SetSelected(false); 111 } 112 else 113 { 114 if (m_isMultiple) 115 { 116 m_selectedSource.Add(selectedItem.DataSource); 117 selectedItem.SetSelected(true); 118 } 119 else 120 { 121 if (m_selectedSource.Count > 0) 122 { 123 int intCount = Math.Min(((IList)m_dataSource).Count, this.panMain.Controls.Count); 124 for (int i = 0; i < intCount; i++) 125 { 126 var item = ((IListViewItem)this.panMain.Controls[i]); 127 if (m_selectedSource.Contains(item.DataSource)) 128 { 129 item.SetSelected(false); 130 break; 131 } 132 } 133 } 134 135 m_selectedSource = new List<object>() { selectedItem.DataSource }; 136 selectedItem.SetSelected(true); 137 138 } 139 } 140 141 if (SelectedItemEvent != null) 142 { 143 SelectedItemEvent(sender, e); 144 } 145 } 146 #endregion 147 148 #region 获取cell总数 149 /// <summary> 150 /// 功能描述:获取cell总数 151 /// 作 者:HZH 152 /// 创建日期:2019-06-27 16:28:58 153 /// 任务编号:POS 154 /// </summary> 155 private void GetCellCount() 156 { 157 if (this.panMain.Width == 0) 158 return; 159 Control item = (Control)Activator.CreateInstance(m_itemType); 160 161 162 int intXCount = (this.panMain.Width - 10) / (item.Width + 10); 163 m_intCellWidth = item.Width + ((this.panMain.Width - 10) % (item.Width + 10)) / intXCount; 164 165 int intYCount = (this.panMain.Height - 10) / (item.Height + 10); 166 m_intCellHeight = item.Height + ((this.panMain.Height - 10) % (item.Height + 10)) / intYCount; 167 int intCount = intXCount * intYCount; 168 169 if (Page == null) 170 { 171 if (((IList)m_dataSource).Count > intCount) 172 { 173 intXCount = (this.panMain.Width - 10 - 20) / (item.Width + 10); 174 m_intCellWidth = item.Width + ((this.panMain.Width - 10 - 20) % (item.Width + 10)) / intXCount; 175 } 176 intCount = Math.Max(intCount, ((IList)m_dataSource).Count); 177 } 178 179 CellCount = intCount; 180 } 181 #endregion
一些事件
private void panMain_Resize(object sender, EventArgs e) { ReloadGridStyle(); } void m_page_ShowSourceChanged(object currentSource) { this.DataSource = currentSource; }
你会发现,有个ItemType属性,这个用来定义列表呈现那种子元素,这么用的好处就是,当你觉得我写的这个元素控件UCListViewItem并不能满足你需求的时候,你可以添加一个自定义控件,并实现接口IListViewItem,然后将你自定义的控件指定给这个属性,列表就会呈现出来了,是不是很方便,列表会自动根据你的元素控件的大小来适量调整来填充到列表中的。
还有一个Page属性,这个是用来表示用哪个分页控件,当然你也可以不用,我已经提供了2种分页控件,如果你觉得还是不满足你的话,去参考分页控件那个文章,自己添加一个分页控件吧。
最后的话
如果你喜欢的话,请到 https://gitee.com/kwwwvagaa/net_winform_custom_control 点个星星吧