控件库,说白了,就是个类库项目。不过这个类库内定义了一系列自定义的控件。整个项目(类库+调用项目)结构大概是这个样
请忽略Class2和ClassDiagram1.cd,这是我之前一个博文
用到的项目,留下来也无关紧要。UserControl2.cs就是我们的一个控件了。长这个样
一个TextBox下面有一条线,线宽等于文本框的宽等于整个控件的宽。当然,这需要在cs代码中做一些调整(设置属性或是什么)。代码长这个样,仅供参考
privateintwidth;定义了一个字段,用来指示控件的宽度,private。然后定义了一个属性,也是用来指示控件宽度,public。这样在类外访问控件宽度需通过属性,而不是直接把字段暴露给外部。
[Description("控件宽度"),Category("自定义属性")]publicintselfWidth{get{returnwidth;}set{this.width=value;this.Width=value;this.txtBox.Width=value;this.pictureBox.Width=value;}}其中
[Description("控件宽度"),Category("自定义属性")]加上这行表明这个属性代表了控件宽度,后面的Category是分组情况。这行的意义是用了这个控件之后,就能在当前窗体设计器上面方便地改动属性值,就像这样
还有最后一段代码
写控件库一点都不难,只需要同样的过程重复N遍再加上一点创意,必要时还需要用点别人的东西。
今天学习了下C#用户控件开发添加自定义属性的事件,主要参考了MSDN,总结并实验了一些用于开发自定义属性和事件的特性(Attribute)。
在这里先说一下我的环境:
操作系统:Windows7旗舰版(ServicePack1)
VS版本:MicrosoftVisualStudioUltimate2012,版本11.0.50727.1RTMREL
.NETFramework版本:4.5.50938
C#版本:VisualC#2012
1、建立一个C#窗体应用程序,主窗体起名为FormMain,向解决方案中再加入一个用户控件,起名为TestUserControl
2、在TestUserControl中放一个按钮,取名为btnTest
3、控件做好后,会出现在【工具箱】内
4、将控件拖拽到一个窗体(Form)上就可以使用了,取名testUserControl1。这个名字是VS默认取的,即首字母小写,最后补上数字作为序号。
在TestUserControl类中,添加下面的代码:
///
///
在FormMain的代码中实现这个函数
privatevoidtestUserControl1_BtnTestClick(objectsender,EventArgse){MessageBox.Show(sender.ToString()+"\r\n"+e.ToString());}这时运行程序,点击控件testUserControl1内的按钮btnTest,就会有下面的效果:
[DefaultEvent("BtnTestClick")]那在Form编辑界面,双击控件testUserControl1就会自动进入testUserControl1_BtnTestClick事件。
这里再说明一下,C#中的System.Windows.Forms.Control类代码如下:
[ClassInterface(ClassInterfaceType.AutoDispatch)][ComVisible(true)][DefaultEvent("Click")][DefaultProperty("Text")][Designer("System.Windows.Forms.Design.ControlDesigner,System.Design,Version=4.0.0.0,Culture=neutral,PublicKeyToken=b03f5f7f11d50a3a")][DesignerSerializer("System.Windows.Forms.Design.ControlCodeDomSerializer,System.Design,Version=4.0.0.0,Culture=neutral,PublicKeyToken=b03f5f7f11d50a3a","System.ComponentModel.Design.Serialization.CodeDomSerializer,System.Design,Version=4.0.0.0,Culture=neutral,PublicKeyToken=b03f5f7f11d50a3a")][ToolboxItemFilter("System.Windows.Forms")]publicclassControl:Component,IDropTarget,ISynchronizeInvoke,IWin32Window,IBindableComponent,IComponent,IDisposable{/*...*/}这里可以看到DefaultEvent的值为“Click”,这也就是为什么拖入Form的按钮(Button),在双击后会进入它的Click事件:
[ClassInterface(ClassInterfaceType.AutoDispatch)][ComVisible(true)][DefaultBindingProperty("CheckState")][DefaultEvent("CheckedChanged")][DefaultProperty("Checked")][ToolboxItem("System.Windows.Forms.Design.AutoSizeToolboxItem,System.Design,Version=4.0.0.0,Culture=neutral,PublicKeyToken=b03f5f7f11d50a3a")]publicclassCheckBox:ButtonBase{/*...*/}这里的DefaultEvent被写上了“CheckedChange”,因此在Form的编辑界面,双击复选框时默认进入的编辑事件为
privatevoidcheckBox1_CheckedChanged(objectsender,EventArgse)自定义的控件(直接继承自UserControl),如果不添加这个属性,在编辑界面双击后进入的事件是Load事件。
[Browsable(false)]则控件testUserControl1的属性界面就不会出现BtnName的设置了,下图红线部分为之前BtnName所在的位置
如果某属性或事件没有添加Browsable特性,那么该属性或事件也可以在“属性”窗口中看到。这里还要说明以下,Browsable只能决定某属性或事件在“属性”窗口内的可见性,Browsable被置为false的属性和事件,仍可以在编辑器中通过代码中使用。
如在BtnName上添加下面代码:
[Description("设置按钮上显示的文字")]也可以带上Browsable特性一起使用:
[Browsable(true)][Description("设置按钮上显示的文字")]或写在一对方括号里,用逗号隔开:
[Browsable(true),Description("设置按钮上显示的文字")]在“属性”界面中看到的说明文字,效果如下:
EditorBrowsableAttribute的构造函数如下:
publicEditorBrowsableAttribute(EditorBrowsableStatestate);其中,EditorBrowsableState是一个枚举(enum),这个枚举共有三个值,分别是Always、Never和Advanced
Always:该属性或方法在编辑器中始终是可浏览的
Never:该属性或方法始终不能在编辑器中浏览
Advanced:该属性或方法是只有高级用户才可以看到的功能。编辑器可以显示或隐藏这些属性
前面两个都好理解,第三个Advanced着实会让人一头雾水(什么才叫“高级用户”?)。后来查了一些资料,才知道对于高级成员的可见性,可以在“工具”菜单下的“选项”中进行配置。
如果勾选了“隐藏高级成员”,那么用代码“[EditorBrowsable(EditorBrowsableState.Advanced)]”标记的属性,将不能在IDE中自动显示。但这也仅仅是不自动显示而已,如果在代码中真的调用了不可见的属性,编译不会报错,运行也不会有问题。
如下图:BtnName被标记为“EditorBrowsableState.Never”,因此这个属性不会出现在VS的智能提示(学名叫IntelliSense)中,但如果写到代码里,却没有问题。
需要注意的是,这种隐藏只有在该控件代码为当前解决方案不可见时有效,也就是说,如果这个控件的实现代码就在你的解决方案内,EditorBrowsable并不能保证用户看不见这个属性。但如果这个控件时被放在一个dll文件中添加引用到当前的解决方案中,EditorBrowsable特性才能按其文字描述中说明的那样起作用。
参数为DesignerSerializationVisibility类型的枚举:
Hidden:代码生成器不生成对象的代码
Visible:代码生成器生成对象的代码
Content:代码生成器产生对象内容的代码,而不是对象本身的代码
这个说法一眼看上去并不易理解,因此我决定还是用两个具体例子说明一下:
1、Hidden与Visible、Content的不同
还是以我们上面的BtnName属性为例,参数为【DesignerSerializationVisibility.Hidden】的情况
[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden)]publicstringBtnName{get{returnbtnTest.Text;}set{btnTest.Text=value;}}将控件拖入FormMain的窗体设计器中,可用在文件FormMain.Designer.cs中看到:
///
///
this.testUserControl1.BtnName="button1";使用了Hidden就没有,使用了Visible就会有(使用了Content也会有)
使用了Hidden后,在“属性”界面中,无论怎么修改BtnName属性的值,编译时编译器都不会理睬这个值,而是使用默认值(这个例子里面就是button1)。使用了Hidden后,即使在FormMain.Designer.cs里手动把上面那行赋值的代码加上,这行代码在程序重新编译后还是会消失。
2、Visible与Content的不同
Content被用在可以序列化的集合,例如System.Windows.Forms.DataGridView类(数据表格)
////摘要://获取一个包含控件中所有列的集合。////返回结果://一个System.Windows.Forms.DataGridViewColumnCollection,包含System.Windows.Forms.DataGridView//控件中的所有列。[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)][Editor("System.Windows.Forms.Design.DataGridViewColumnCollectionEditor,System.Design,Version=4.0.0.0,Culture=neutral,PublicKeyToken=b03f5f7f11d50a3a",typeof(UITypeEditor))][MergableProperty(false)]publicDataGridViewColumnCollectionColumns{get;}IDE只是生成这些属性中包含组件的代码,而不会生成属性本身的代码。在使用IDE添加各个DataGridViewTextBoxColumn时,各个DataGridViewTextBoxColumn的代码会被放在FormMain.Designer.cs文件中,而有关Columns属性本身只会在在函数InitializeComponent()中生成这样一段代码: