前提

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

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

码云:https://gitee.com/kwwwvagaa/net_winform_custom_control.git
<https://gitee.com/kwwwvagaa/net_winform_custom_control.git>

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

欢迎前来交流探讨: 企鹅群568015492 
<https://shang.qq.com/wpa/qunwpa?idkey=6e08741ef16fe53bf0314c1c9e336c4f626047943a8b76bac062361bab6b4f8d>

麻烦博客下方点个【推荐】,谢谢

NuGet
Install-Package HZH_Controls
目录

https://www.cnblogs.com/bfyx/p/11364884.html
<https://www.cnblogs.com/bfyx/p/11364884.html>

用处及效果

使用分页控件效果



不使用分页控件效果



准备工作

我们需要元素控件,需要列表控件,另外为了具有更好的扩展性,元素控件实现接口,方便进行扩展

我们用到了分页控件,如果你还不了解,请移步查看

(十二)c#Winform自定义控件-分页控件 <https://www.cnblogs.com/bfyx/p/11363075.html>

我们这里的元素控件用到圆角,故继承基类控件UCControlBase,如果不了解,请移步查看

(一)c#Winform自定义控件-基类控件 <https://www.cnblogs.com/bfyx/p/11361809.html>

开始

添加一个接口,用来约束元素控件
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.Controls11 { 12 [ToolboxItem(false)] 13 public partial class
UCListViewItem : UCControlBase, IListViewItem14 { 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 } View Code
然后需要一个列表来显示元素控件

添加一个用户控件,命名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 ///
作  者:HZH152 /// 创建日期: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
<https://gitee.com/kwwwvagaa/net_winform_custom_control> 点个星星吧