(四十三)c#Winform自定义控件-Listview
程序员文章站
2023-11-06 10:43:10
前提 入行已经7,8年了,一直想做一套漂亮点的自定义控件,于是就有了本系列文章。 GitHub:https://github.com/kwwwvagaa/NetWinformControl 码云:https://gitee.com/kwwwvagaa/net_winform_custom_contr ......
前提
入行已经7,8年了,一直想做一套漂亮点的自定义控件,于是就有了本系列文章。
github:https://github.com/kwwwvagaa/netwinformcontrol
码云:
如果觉得写的还行,请点个 star 支持一下吧
欢迎前来交流探讨: 企鹅群568015492
麻烦博客下方点个【推荐】,谢谢
nuget
install-package hzh_controls
目录
用处及效果
使用分页控件效果
不使用分页控件效果
准备工作
我们需要元素控件,需要列表控件,另外为了具有更好的扩展性,元素控件实现接口,方便进行扩展
我们用到了分页控件,如果你还不了解,请移步查看
我们这里的元素控件用到圆角,故继承基类控件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种分页控件,如果你觉得还是不满足你的话,去参考分页控件那个文章,自己添加一个分页控件吧。
最后的话
如果你喜欢的话,请到 点个星星吧