属性(Property)的性质(Attribute)介绍
属性(property)作为c#语言中一个重要的组成部分,尤其是在我们自己编写组件的时候显得更加重要。我相信大家一定对其有一定的了解。但是大家是否注意到了一个非常关键得细节问题呢?那就是在大家使用任何得组件的时候都需要通过属性浏览器给每一属性赋值,而且更加友好的是对于每种不同类型属性都会自己的形式。比如:数字类型、字符串类型是默认简单的输入的形式,而如Font、Color类型的属性则可以对话框或下拉列表框的形式。不知道大家是否知道这些是如何编写的?其实这些功能都是通过属性(Property)的(Attribute)来实现的,下面我就这个问题和大家一起学习,不过在阅读本文之前要求大家对属性(Property)有一定的了解,好下面我们言归正传。
一:属性转换器(TypeConverter)
1、 下拉列表框的形式: 要使用下拉列表框的形式的属性我们首先要定义一个属性,在这个例子中我定义了一个字符串类型的属性 FileName。 private string _fileName; public string FileName { get { return this._fileName;} set { this._fileName=value; } } 定义完属性之后,我们还要自己一个属性转换器。那么什么是属性转换器呢?其实在属性浏览器中只能够识别字符串类型,所以我们要通过属性转换器把我们的属性转换成字符串,还要在属性浏览器改变这个字符串之后在把这个字符串转换成我们自己的属性。大家听起来是不是有一些胡涂了?没关系下面我们做一个属性转换器大家就知道了。 因为在本例中用的属性是字符串类型的所以我们要从System.ComponentModel.StringConverter继承一个新的字符串形式的属性转换器。下面就是这段代码和代码中的注释,相信大家一定能够看懂的: /// <summary> /// 扩展字符串的转换器(实现下拉列表框的样式) /// </summary> public class FileNameConverter:System.ComponentModel.StringConverter { /// <summary> /// 根据返回值确定是否支持下拉框的形式 /// </summary> /// <returns> /// true: 下来框的形式 /// false: 普通文本编辑的形式 /// </returns> public override bool GetStandardValuesSupported(System.ComponentModel.ITypeDescriptorContext context) { return true; } /// <summary> /// 下拉框中具体的内容 /// </summary> public override System.ComponentModel.TypeConverter.StandardValuesCollection GetStandardValues(System.ComponentModel.ITypeDescriptorContext context) { return new StandardValuesCollection(new string[]{"File1.bat","File2.exe","File3.dll"}); } /// <summary> /// 根据返回值确定是否是不可编辑的文本框 /// </summary> /// <returns> /// true: 文本框不可以编辑 /// flase: 文本框可以编辑 /// </returns> public override bool GetStandardValuesExclusive(System.ComponentModel.ITypeDescriptorContext context) { return true; } 好了,属性转换器写完了,最后别忘了把这个属性转换器指定到我们刚才所写的属性上哦,代码如下: [CategoryAttribute("自定义的复杂类型设置(包括自定义类型转换器)"), TypeConverterAttribute(typeof(PropertyGridApp.FileNameConverter)), ReadOnlyAttribute(false)] public string FileName { get { return this._fileName;} set { this._fileName=value; } } 编译之后的程序画面如下
2、展开的形式
展开的形式多用于一个属性为我们自定义类的类型,比如我们定义了一个类,该类中的一个属性是另一个我们定义的类。在这种情况下属性浏览器默认是没有办法来进行类型转换的,所以显示为不可编辑的内容。如果我们要以展开的形式编辑这个属性就需要我们向上面一样来重写属性转换器。 我们首先定义一个自己的类来作为以后的属性类型。具体代码如下: public class ExpandProperty { private int _intList=0; public int IntList { get { return this._intList;} set { this._intList=value; } } private string _strList="Null"; public string StrList { get { return this._strList;} set { this._strList= value;} } } 然后我们在自己的另一个类中声明一个这个类型的属性,在这里如果我们不加任何的性质限制,属性浏览器是不能转换改属性的。具体实现该属性的代码如下: private ExpandProperty _dropList; [CategoryAttribute("自定义的复杂类型设置(包括自定义类型转换器)"), TypeConverterAttribute(typeof(PropertyGridApp.ExpandConverter)), ReadOnlyAttribute(false)] public ExpandProperty DropList { get { return this._dropList;} set { this._dropList= value;} } 为了让属性浏览器能够编辑该属性,也就是说能够把该属性转换成字符串,而且能够从字符串转换成该类的一个实例需要我们写如下的代码: /// <summary> /// 可以展开的类型转换器 /// ExpandProperty /// </summary> public class ExpandConverter:System.ComponentModel.ExpandableObjectConverter { public ExpandConverter() { } /// <summary> /// 覆盖此方法已确定属性是否可以转换 /// </summary> public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext context,System.Type destinationType) { if (destinationType==typeof(PropertyGridApp.ExpandProperty)) return true; return base.CanConvertTo(context,destinationType); } /// <summary> /// 覆盖此方法并确保destinationType参数是一个String,然后格式化所显示的内容 /// </summary> public override object ConvertTo(System.ComponentModel.ITypeDescriptorContext context,System.Globalization.CultureInfo culture,object value,System.Type destinationType) { if (destinationType == typeof (System.String) && value is PropertyGridApp.ExpandProperty) { PropertyGridApp.ExpandProperty source=(PropertyGridApp.ExpandProperty)value; return source.IntList+","+source.StrList; } return base.ConvertTo(context,culture,value,destinationType); } /// <summary> /// 覆盖此方法已确定输入的字符串是可以被转化 /// </summary> public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext context,System.Type sourceType) { if (sourceType==typeof(string)) return true; return base.CanConvertFrom(context,sourceType); } /// <summary> /// 覆盖此方法根据 ConvertTo() 方法的转换格式来把所输入的字符串转换成类,并返回该类 /// </summary> public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext context,object value) { if (value is string) { string s=(string)value; int comma=s.IndexOf(","); if (comma!=-1) { try { string intList=s.Substring(0,comma); string strList=s.Substring(comma+1,s.Length-comma-1); PropertyGridApp.ExpandProperty Ep=new ExpandProperty(); Ep.IntList=int.Parse(intList); Ep.StrList=strList; return Ep; } catch { return base.ConvertFrom(context,value); } } } return base.ConvertFrom(context,value); } } 编译之后的画面如下:
二:UI属性编辑器(UITypeEditor) 这里的属性编辑器的意思是能够实现上面提到的弹出对话框和下拉UI的形式。废话不说下面我们一一介绍。 1、 弹出对话框的形式 在本例中我使用了string类型的属性来显示版本的信息,大家可以随便的写各类的属性,这里只需要指定改属性的编辑器就可以了。 首先我们要建立一个string类型的属性,代码如下: private string _appVer="1.0"; [CategoryAttribute("自定义编辑器"),DefaultValueAttribute("1.0"),DescriptionAttribute("版本信息"),ReadOnlyAttribute(true),EditorAttribute(typeof(AppVerConverter),typeof(System.Drawing.Design.UITypeEditor))] public string AppVer { get {return this._appVer;} set {this._appVer=value;} } 大家可能已经注意到了在这个属性之多出了一个性质EditorAttribute(typeof(AppVerConverter),typeof(System.Drawing.Design.UITypeEditor)),具体的意思大家可以参考MSDN我在这里就不用多说了,那么我们看看AppVerConverter这个类是怎么实现的就可以了。具体代码如下: /// <summary> /// 自定义UI的属性编辑器(弹出消息) /// </summary> public class AppVerConverter:System.Drawing.Design.UITypeEditor { /// <summary> /// 覆盖此方法以返回编辑器的类型。 /// </summary> public override System.Drawing.Design.UITypeEditorEditStyle GetEditStyle(System.ComponentModel.ITypeDescriptorContext context) { return System.Drawing.Design.UITypeEditorEditStyle.Modal; } /// <summary> /// 覆盖此方法以显示版本信息 /// </summary> public override object EditValue(System.ComponentModel.ITypeDescriptorContext context,System.IServiceProvider provider,object value) { System.Windows.Forms.MessageBox.Show("版本:1.0/n作者:张翔","版本信息"); return value; } } 这里需要说明的是我们的属性编辑器必须从System.Drawing.Design.UITypeEditor继承,要不然就不能显示UI了。UITypeEditorEditStyle方法的返回值决定了改属性编辑器的类型大家可以参考msdn我在这里就不多说了。编译之后就可以看到如下的画面了: 2、 下拉UI的类型 下拉UI类型主要是提供给用户一个简单的界面来选择所要确定的属性,这种方式提供给用户非常友好的界面。下面的例子我们首先定义里一个Point类型的属性,在默认的情况下这种类型的属性是会以展开的形式来让用户编辑的。在这里我们扩展了他的功能,不仅仅能通过直接输入的方式来改变值,而且还可以下拉出来一个控件,用户可以在这个控件上根据鼠标的位置来确定具体的值。下面具体的代码: private System.Drawing.Point _dropUI; [CategoryAttribute("自定义编辑器"),DefaultValueAttribute("1"),DescriptionAttribute("下拉可视控件"),ReadOnlyAttribute(false),EditorAttribute(typeof(DropEditor),typeof(System.Drawing.Design.UITypeEditor))] public System.Drawing.Point DropUI { get { return this._dropUI;} set { this._dropUI=value; } } public class DropEditor:System.Drawing.Design.UITypeEditor { public override System.Drawing.Design.UITypeEditorEditStyle GetEditStyle(System.ComponentModel.ITypeDescriptorContext context) { return System.Drawing.Design.UITypeEditorEditStyle.DropDown; } public override object EditValue(System.ComponentModel.ITypeDescriptorContext context,object value) { System.Windows.Forms.Design.IWindowsFormsEditorService iws=(System.Windows.Forms.Design.IWindowsFormsEditorService)provider.GetService(typeof(System.Windows.Forms.Design.IWindowsFormsEditorService)); if (iws!=null) { PropertyGridApp.DropUIControl UIControl=new PropertyGridApp.DropUIControl((System.Drawing.Point)value,iws); iws.DropDownControl(UIControl); return UIControl.Value; } return value; } } internal class DropUIControl:System.Windows.Forms.UserControl { public DropUIControl(System.Drawing.Point avalue,System.Windows.Forms.Design.IWindowsFormsEditorService iws) { this.Value=avalue; this._tmpvalue=avalue; this._iws=iws; this.SetStyle(System.Windows.Forms.ControlStyles.DoubleBuffer|System.Windows.Forms.ControlStyles.UserPaint|System.Windows.Forms.ControlStyles.AllPaintingInWmPaint,true); this.BackColor=System.Drawing.SystemColors.Control; } private System.Drawing.Point _value; public System.Drawing.Point Value { get { return this._value;} set { this._value=value; } } private System.Drawing.Point _tmpvalue; private System.Windows.Forms.Design.IWindowsFormsEditorService _iws; protected override void OnPaint(System.Windows.Forms.PaintEventArgs e) { string str="X:"+this._tmpvalue.X.ToString()+" ;Y:"+this._tmpvalue.Y.ToString(); System.Drawing.Graphics g=e.Graphics; System.Drawing.SizeF sizef= g.MeasureString(str,this.Font); g.DrawString(str,this.Font,new System.Drawing.SolidBrush(System.Drawing.Color.Black),(int)((this.Width-(int)sizef.Width)/2),this.Height-(int)sizef.Height); g.PageUnit=System.Drawing.GraphicsUnit.Pixel; g.FillEllipse(new System.Drawing.SolidBrush(System.Drawing.Color.Red),this.Value.X-2,this.Value.Y-2,4,4); } protected override void OnMouseMove(System.Windows.Forms.MouseEventArgs e) { base.OnMouseMove(e); this._tmpvalue=new System.Drawing.Point(e.X,e.Y); this.Invalidate(); } protected override void OnMouseUp(System.Windows.Forms.MouseEventArgs e) { base.OnMouseUp(e); this.Value=this._tmpvalue; this.Invalidate(); if (e.Button==System.Windows.Forms.MouseButtons.Left) this._iws.CloseDropDown(); } } 以上的代码度非常的简单,相信大家一定能够看懂,如果有不明白的地方看看帮助,那里面解释的非常的清楚。 在编写属性编辑器中我们都需要覆盖其中的EditValue这个方法,大家是否注意到了其中的object Value这个参数?其实这个参数就是已经装箱的属性值,在我们自定义的处理完这个值的时候同样可以返回一个装箱的值来确定已经修改的属性。在上面的两个例子中我们只是简单的使用了这个值,大家理解这个内容之后就可以做出更加个性化的编辑器了。
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |