C#自定义PropertyGrid属性

最近用到了PropertyGrid,原来从来没用到过,拿在手里,一头雾水,经过一段时间研究后,大概理解了Property的使用方法,下面仔细剖析一下。

PropertyGrid控件就是Visual Studio开发工具里面的属性浏览器,我们在VS里面可以通过属性浏览器查看,修改控件的属性,并主要通过使用反射来检索项目的属性。

一.如何显示属性

1)普通显示

在PropertyGrid中显示属性很容易,我们可以直接给propertyGrid1.SelectedObject属性赋值,SelectObject属性可以获取或设置当前选定的对象,数据类型为object,这就意味着我们可以直接将一个对象赋给它。针对一个对象,它会将对象中的所有公共属性显示在PropertyGrid上。假如我们定义一个Station类,如下

[csharp]  view plain copy print ?
  1. public class Station   
  2.    {  
  3.        private string _StationName;  
  4.        private double _Lon = 103;  
  5.        private double _Lat = 38;  
  6.        private Color _color;  
  7.        private string _file = string.Empty;  
  8.        private Font _font;  
  9.        public string FileName  
  10.        {  
  11.            get { return _file; }  
  12.            set { _file = value; }  
  13.        }  
  14.        public Color Color  
  15.        {  
  16.            get { return _color; }  
  17.            set { _color = value; }  
  18.        }  
  19.        public Font Font  
  20.        {  
  21.            get { return _font; }  
  22.            set { _font = value; }  
  23.        }  
  24.        public string StationName  
  25.        {  
  26.            get { return _StationName; }  
  27.            set { _StationName = value; }  
  28.        }  
  29.        public double Lon  
  30.        {  
  31.            get { return _Lon; }  
  32.            set { _Lon = value; }  
  33.        }  
  34.        public double Lat  
  35.        {  
  36.            get { return _Lat; }  
  37.            set { _Lat = value; }  
  38.        }  
  39.    }  

然后在窗体中拖拉一个PropertyGrid控件propertygrid1,在Form_load中代码如下

[csharp]  view plain copy print ?
  1. private void Form1_Load(object sender, EventArgs e)  
  2. {  
  3.   Station s=new Station();  
  4.   propertygrid1.SelectObject=s;  
  5. }  
我们就可以看到如下效果:


我们看到属性名显示都是英文,那样很不方便阅读如果我们像显示中文,该如何实现呢?

更改了显示方式

要更改某些属性的显示方式,您可以对这些属性应用不同的特性。特性是用于为类型、字段、方法和属性等编程元素添加批注的声明标记,在运行时可以使用反射对其进行检索。下面列出了其中的一部分:

DescriptionAttribute - 设置显示在属性下方说明帮助窗格中的属性文本。这是一种为活动属性(即具有焦点的属性)提供帮助文本的有效方法。

CategoryAttribute - 设置属性在网格中所属的类别。当您需要将属性按类别名称分组时,此特性非常有用。如果没有为属性指定类别,该属性将被分配给杂项 类别。可以将此特性应用于所有属性。

BrowsableAttribute – 表示是否在网格中显示属性。此特性可用于在网格中隐藏属性。默认情况下,公共属性始终显示在网格中。

ReadOnlyAttribute – 表示属性是否为只读。此特性可用于禁止在网格中编辑属性。默认情况下,带有 get 和 set 访问函数的公共属性在网格中是可以编辑的。

DefaultValueAttribute – 表示属性的默认值。如果希望为属性提供默认值,然后确定该属性值是否与默认值相同,则可使用此特性。可以将此特性应用于所有属性。

DefaultPropertyAttribute – 表示类的默认属性。在网格中选择某个类时,将首先突出显示该类的默认属性。

下面我们在Station类中的属性Lon上方添加[CategoryAttribute("坐标"),DisplayNameAttribute("经度")],效果如下:


如果想要在属性表中添加颜色选择和字体选择那是很容易一件事,可以在Station类中添加Color类型属性,和Font类型属性,绑定后,就可以进行颜色选择和字体选择了,代码在Station中已经实现。

2)自定义显示

我们可以看出这种上面这种显示属性方法并不够灵活,我们不能方便的及时增加或者删除属性。

   //属性表管理类

[csharp]  view plain copy print ?
  1. <span style="font-size:13px;"public class PropertyManageCls : CollectionBase, ICustomTypeDescriptor  
  2.     {  
  3.         public void Add(Property value)  
  4.         {  
  5.             int flag=-1;  
  6.             if (value != null)  
  7.             {  
  8.                 if (base.List.Count>0)  
  9.                 {  
  10.                     IList <Property> mList=new List<Property>();  
  11.                     for (int i = 0; i < base.List.Count; i++)  
  12.                     {  
  13.                         Property p = base.List[i] as Property;  
  14.                         if (value.Name == p.Name)  
  15.                         {  
  16.                             flag = i;  
  17.                         }  
  18.                         mList.Add(p);  
  19.                     }  
  20.                     if (flag == -1)  
  21.                     {  
  22.                         mList.Add(value);  
  23.                     }  
  24.                     base.List.Clear();  
  25.                     foreach (Property p in mList)  
  26.                     {  
  27.                         base.List.Add(p);  
  28.                     }  
  29.                 }  
  30.                 else  
  31.                 {  
  32.                     base.List.Add(value);  
  33.                 }  
  34.             }  
  35.         }  
  36.         public void Remove(Property value)  
  37.         {  
  38.             if(value!=null&&base.List.Count>0)  
  39.             base.List.Remove(value);  
  40.         }  
  41.         public Property this[int index]  
  42.         {  
  43.             get  
  44.             {  
  45.                 return (Property)base.List[index];  
  46.             }  
  47.             set  
  48.             {  
  49.                 base.List[index] = (Property)value;  
  50.             }  
  51.         }  
  52.         #region ICustomTypeDescriptor 成员  
  53.         public AttributeCollection GetAttributes()  
  54.         {  
  55.             return TypeDescriptor.GetAttributes(this,true);  
  56.         }  
  57.         public string GetClassName()  
  58.         {  
  59.             return TypeDescriptor.GetClassName(thistrue);  
  60.         }  
  61.         public string GetComponentName()  
  62.         {  
  63.             return TypeDescriptor.GetComponentName(thistrue);  
  64.         }  
  65.         public TypeConverter GetConverter()  
  66.         {  
  67.             return TypeDescriptor.GetConverter(thistrue);  
  68.         }  
  69.         public EventDescriptor GetDefaultEvent()  
  70.         {  
  71.             return TypeDescriptor.GetDefaultEvent(thistrue);  
  72.         }  
  73.         public PropertyDescriptor GetDefaultProperty()  
  74.         {  
  75.             return TypeDescriptor.GetDefaultProperty(thistrue);  
  76.         }  
  77.         public object GetEditor(Type editorBaseType)  
  78.         {  
  79.             return TypeDescriptor.GetEditor(this, editorBaseType, true);  
  80.         }  
  81.         public EventDescriptorCollection GetEvents(Attribute[] attributes)  
  82.         {  
  83.             return TypeDescriptor.GetEvents(this, attributes, true);  
  84.         }  
  85.         public EventDescriptorCollection GetEvents()  
  86.         {  
  87.             return TypeDescriptor.GetEvents(this,true);  
  88.         }  
  89.         public PropertyDescriptorCollection GetProperties(Attribute[] attributes)  
  90.         {  
  91.             PropertyDescriptor[] newProps = new PropertyDescriptor[this.Count];  
  92.             for (int i = 0; i < this.Count; i++)  
  93.             {  
  94.                 Property prop = (Property)this[i];  
  95.                 newProps[i] = new CustomPropertyDescriptor(ref prop, attributes);  
  96.             }  
  97.             return new PropertyDescriptorCollection(newProps);  
  98.         }  
  99.         public PropertyDescriptorCollection GetProperties()  
  100.         {  
  101.             return TypeDescriptor.GetProperties(thistrue);  
  102.         }  
  103.         public object GetPropertyOwner(PropertyDescriptor pd)  
  104.         {  
  105.             return this;          
  106.         }  
  107.         #endregion  
  108.     }  
  109. //属性类  
  110.     public class Property  
  111.     {  
  112.         private string _name=string.Empty;  
  113.         private object _value=null;  
  114.         private bool _readonly=false;  
  115.         private bool _visible=true;  
  116.         private string _category=string.Empty;  
  117.         TypeConverter _converter=null;  
  118.         object _editor = null;  
  119.         private string _displayname = string.Empty;  
  120.         public Property(string sName, object sValue)  
  121.         {  
  122.             this._name = sName;  
  123.             this._value = sValue;  
  124.         }  
  125.         public Property(string sName, object sValue, bool sReadonly, bool sVisible)  
  126.         {  
  127.             this._name = sName;  
  128.             this._value = sValue;  
  129.             this._readonly = sReadonly;  
  130.             this._visible = sVisible;  
  131.         }  
  132.         public string Name  //获得属性名  
  133.         {  
  134.             get  
  135.             {  
  136.                 return _name;  
  137.             }  
  138.             set  
  139.             {  
  140.                 _name=value;  
  141.             }  
  142.         }  
  143.         public string DisplayName   //属性显示名称  
  144.         {  
  145.             get  
  146.             {  
  147.                 return _displayname;  
  148.             }  
  149.             set  
  150.             {  
  151.                 _displayname = value;  
  152.             }  
  153.         }  
  154.         public TypeConverter Converter  //类型转换器,我们在制作下拉列表时需要用到  
  155.         {  
  156.             get   
  157.             {  
  158.                 return _converter;  
  159.             }  
  160.             set  
  161.             {  
  162.                 _converter = value;  
  163.             }  
  164.         }  
  165.         public string Category  //属性所属类别  
  166.         {  
  167.             get  
  168.             {  
  169.                 return _category;  
  170.             }  
  171.             set  
  172.             {  
  173.                 _category = value;  
  174.             }  
  175.         }  
  176.         public object Value  //属性值  
  177.         {  
  178.             get  
  179.             {  
  180.                 return _value;  
  181.             }  
  182.             set  
  183.             {  
  184.                 _value=value;  
  185.             }  
  186.         }  
  187.         public bool ReadOnly  //是否为只读属性  
  188.         {  
  189.             get  
  190.             {  
  191.                 return _readonly;  
  192.             }  
  193.             set  
  194.             {  
  195.                 _readonly = value;  
  196.             }  
  197.         }  
  198.         public bool Visible  //是否可见  
  199.         {  
  200.             get  
  201.             {  
  202.                 return _visible;  
  203.             }  
  204.             set  
  205.             {  
  206.                 _visible = value;  
  207.             }  
  208.         }  
  209.         public virtual object Editor   //属性编辑器  
  210.         {  
  211.             get   
  212.             {   
  213.                 return _editor;   
  214.             }  
  215.             set   
  216.             {   
  217.                 _editor = value;   
  218.             }  
  219.         }  
  220.     }  
  221.     public class CustomPropertyDescriptor : PropertyDescriptor  
  222.     {  
  223.         Property m_Property;  
  224.         public CustomPropertyDescriptor(ref Property myProperty, Attribute[] attrs)  
  225.             : base(myProperty.Name, attrs)  
  226.         {  
  227.             m_Property = myProperty;  
  228.         }  
  229.         #region PropertyDescriptor 重写方法  
  230.         public override bool CanResetValue(object component)  
  231.         {  
  232.             return false;  
  233.         }  
  234.         public override Type ComponentType  
  235.         {  
  236.             get  
  237.             {  
  238.                 return null;  
  239.             }  
  240.         }  
  241.         public override object GetValue(object component)  
  242.         {  
  243.             return m_Property.Value;  
  244.         }  
  245.         public override string Description  
  246.         {  
  247.             get  
  248.             {  
  249.                 return m_Property.Name;  
  250.             }  
  251.         }  
  252.         public override string Category  
  253.         {  
  254.             get  
  255.             {  
  256.                 return m_Property.Category;  
  257.             }  
  258.         }  
  259.         public override string DisplayName  
  260.         {  
  261.             get  
  262.             {  
  263.                 return m_Property.DisplayName!=""?m_Property.DisplayName:m_Property.Name;  
  264.             }  
  265.         }  
  266.         public override bool IsReadOnly  
  267.         {  
  268.             get  
  269.             {  
  270.                 return m_Property.ReadOnly;  
  271.             }  
  272.         }  
  273.         public override void ResetValue(object component)  
  274.         {  
  275.             //Have to implement  
  276.         }  
  277.         public override bool ShouldSerializeValue(object component)  
  278.         {  
  279.             return false;  
  280.         }  
  281.         public override void SetValue(object component, object value)  
  282.         {  
  283.             m_Property.Value = value;  
  284.         }  
  285.         public override TypeConverter Converter  
  286.         {  
  287.             get  
  288.             {  
  289.                 return m_Property.Converter;  
  290.             }  
  291.         }  
  292.         public override Type PropertyType  
  293.         {  
  294.             get { return m_Property.Value.GetType(); }  
  295.         }  
  296.         public override object GetEditor(Type editorBaseType)  
  297.         {  
  298.             return m_Property.Editor==nullbase.GetEditor(editorBaseType):m_Property.Editor;  
  299.         }  
  300.         #endregion  
  301.     }</span>  

下面我们来看看该如何使用,我们仍然在Form_load中添加代码如下:

[csharp]  view plain copy print ?
  1. <span style="font-size:13px;">            PropertyManageCls pmc = new PropertyManageCls();  
  2.             Property pp = new Property("ID""1"falsetrue);  
  3.             pp.Category = "基本信息";  
  4.             pp.DisplayName = "我的ID";  
  5.             pmc.Add(pp);  
  6.             propertyGrid1.SelectObject=pmc;</span>  
显示结果:


我们可以看到上面的属性显示很简单,如果想要自定义一个下拉框,或者有一个路径选择的该怎么办呢。

1)类型转换器

要实现下拉框的方法:使用类型转换器,需要继承与TypeConverter或者StringConverter,然后重写方法,代码如下:

[csharp]  view plain copy print ?
  1. <span style="font-size:13px;">    //下拉框类型转换器  
  2.     public class DropDownListConverter : StringConverter  
  3.     {  
  4.         object[] m_Objects;  
  5.         public DropDownListConverter(object[] objects)  
  6.         {  
  7.             m_Objects = objects;  
  8.         }  
  9.         public override bool GetStandardValuesSupported(ITypeDescriptorContext context)  
  10.         {  
  11.             return true;  
  12.         }  
  13.         public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)  
  14.         {  
  15.             return true;</span><span style="color: rgb(0, 130, 0); font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace; font-size: 14px; line-height: 15.390625px; ">//true下拉框不可编辑</span><span style="font-size: 13px; ">  
  16. </span><span style="font-size:13px;">  
  17.         }  
  18.         public override  
  19.         System.ComponentModel.TypeConverter.StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)  
  20.         {  
  21.             return new StandardValuesCollection(m_Objects);//我们可以直接在内部定义一个数组,但并不建议这样做,这样对于下拉框的灵活             //性有很大影响  
  22.         }  
  23.     }</span>  

我们实现了下拉框类型转换器,但该如何使用呢?

使用方法一:我们仍然以Station类作为例子,在属性上方添加标记[TypeConverter(typeof(DropDownListConverter))],但在这种情况下,我们需要预先在DropDownListConverter中定义下拉框内容。

使用方法二:这种方法我们可以在外部定义数组,使用方便,使用方法代码如下:

[csharp]  view plain copy print ?
  1. <span style="font-family:Arial;color:#333333;"><span style="font-size:13px;">        private void Form_load(object sender, EventArgs e)  
  2.         {  
  3.                 PropertyManageCls pmc = new PropertyManageCls();  
  4.                 string []s=new string[] { "1""2""3""4" };  
  5.                 Property pp = new Property(txtname.Text,txtvalue.Text, falsetrue);  
  6.                 pp.Category = "基本信息";  
  7.                 pp.DisplayName = "我的ID";  
  8.                 pp.Converter = new DropDownListConverter(s);//Property的Converter属性就可以设置类型转换  
  9.                 pmc.Add(pp);  
  10.                 propertyGrid1.SelectObject = pmc;  
  11.     }</span></span>  
效果图:



2)属性编辑器

使用属性编辑器实现路径选择:属性编辑器需要继承与UITypeEditor

[csharp]  view plain copy print ?
  1. <span style="font-size:13px;">//文件路径选择                                                                                                                       public class PropertyGridFileItem : UITypeEditor  
  2.     {  
  3.   
  4.         public override UITypeEditorEditStyle GetEditStyle(System.ComponentModel.ITypeDescriptorContext context)  
  5.         {  
  6.   
  7.             return UITypeEditorEditStyle.Modal;  
  8.   
  9.         }  
  10.   
  11.         public override object EditValue(System.ComponentModel.ITypeDescriptorContext context, System.  
  12.   
  13. IServiceProvider provider, object value)  
  14.         {  
  15.   
  16.             IWindowsFormsEditorService edSvc =  
  17.                 (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));  
  18.   
  19.             if (edSvc != null)  
  20.             {  
  21.   
  22.                 // 可以打开任何特定的对话框  
  23.   
  24.                 OpenFileDialog dialog = new OpenFileDialog();  
  25.   
  26.                 dialog.AddExtension = false;  
  27.   
  28.                 if (dialog.ShowDialog().Equals(DialogResult.OK))  
  29.                 {  
  30.   
  31.                     return dialog.FileName;  
  32.   
  33.                 }  
  34.             }  
  35.   
  36.             return value;  
  37.   
  38.         }  
  39.   
  40.     }</span>  

使用方法一:以Station类为例,在属性上方添加标记[EditorAttribute(typeof(PropertyGridFileItem),

 typeof(System.Drawing.Design.UITypeEditor))],然后将PropertyGrid的SelectObject等于Station实例就可以了;

使用方法二:使用方法代码如下:

[csharp]  view plain copy print ?
  1. <span style="font-size:13px;">        private void Form_load(object sender, EventArgs e)  
  2.         {  
  3.                 PropertyManageCls pmc = new PropertyManageCls();  
  4.                 Property pp = new Property(txtname.Text,txtvalue.Text, falsetrue);  
  5.                 pp.Category = "基本信息";  
  6.                 pp.DisplayName = "我的ID";  
  7.                 pp.Editor= new PropertyGridFileItem();//Property的Editor属性就可以设置属性编辑  
  8.                 pmc.Add(pp);  
  9.                 propertyGrid1.SelectObject = pmc;  
  10.     }</span>  
效果图如下:


通过以上方法我们可以满足一些基本需求想要了解更多,可以看以下链接:

PropertyGrid控件心得

http://blog.csdn.net/luyifeiniu/article/details/5426960#创建 PropertyGrid 控件

Customized display of collection data in a PropertyGrid

http://www.codeproject.com/KB/tabs/customizingcollectiondata.aspx

TypeConverter的层次结构

http://msdn.microsoft.com/en-us/library/8cexyz1e

关于PropertyGrid中属性的值动态从数据库取出

http://topic.csdn.net/u/20100827/11/5524219a-4457-4921-b8f2-b4c63bc6b016.html

动态可订制属性的 PropertyGrid

http://blog.csdn.net/akron/article/details/2750566

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
首先,我们需要创建一个自定义类来作为 PropertyGrid 的对象。这个类可以包含多个属性以及子属性。例如: ```c# public class Person { public string Name { get; set; } public int Age { get; set; } public Address Address { get; set; } } public class Address { public string Street { get; set; } public string City { get; set; } public string State { get; set; } } ``` 接下来,我们需要创建一个自定义PropertyDescriptor 类,用于控制 PropertyGrid 的显示方式。这个类需要继承自 PropertyDescriptor 类,并重写几个方法,包括 GetValue()、SetValue()、CanResetValue()、ResetValue()、ShouldSerializeValue() 等方法。例如: ```c# public class NestedPropertyDescriptor : PropertyDescriptor { private PropertyDescriptor _parent; private PropertyDescriptor _child; public NestedPropertyDescriptor(PropertyDescriptor parent, PropertyDescriptor child) : base(child.Name, null) { _parent = parent; _child = child; } public override object GetValue(object component) { object parentValue = _parent.GetValue(component); if (parentValue == null) return null; return _child.GetValue(parentValue); } public override void SetValue(object component, object value) { object parentValue = _parent.GetValue(component); if (parentValue == null) return; _child.SetValue(parentValue, value); OnValueChanged(component, EventArgs.Empty); } public override bool CanResetValue(object component) { object parentValue = _parent.GetValue(component); if (parentValue == null) return false; return _child.CanResetValue(parentValue); } public override void ResetValue(object component) { object parentValue = _parent.GetValue(component); if (parentValue == null) return; _child.ResetValue(parentValue); OnValueChanged(component, EventArgs.Empty); } public override bool ShouldSerializeValue(object component) { object parentValue = _parent.GetValue(component); if (parentValue == null) return false; return _child.ShouldSerializeValue(parentValue); } public override Type ComponentType { get { return _parent.ComponentType; } } public override bool IsReadOnly { get { return _child.IsReadOnly; } } public override Type PropertyType { get { return _child.PropertyType; } } public override string DisplayName { get { return _child.DisplayName; } } } ``` 最后,我们需要创建一个自定义的 TypeConverter 类,用于控制 PropertyGrid 的显示方式。这个类需要继承自 ExpandableObjectConverter 类,并重写几个方法,包括 GetProperties()、GetPropertiesSupported() 等方法。例如: ```c# public class NestedTypeConverter : ExpandableObjectConverter { public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes) { PropertyDescriptorCollection properties = base.GetProperties(context, value, attributes); List<PropertyDescriptor> nestedProperties = new List<PropertyDescriptor>(); foreach (PropertyDescriptor property in properties) { if (property.PropertyType.IsClass && property.PropertyType != typeof(string)) { PropertyDescriptorCollection nestedProps = TypeDescriptor.GetProperties(property.PropertyType, new Attribute[] { new BrowsableAttribute(true) }); foreach (PropertyDescriptor nestedProp in nestedProps) { nestedProperties.Add(new NestedPropertyDescriptor(property, nestedProp)); } } else { nestedProperties.Add(property); } } return new PropertyDescriptorCollection(nestedProperties.ToArray()); } public override bool GetPropertiesSupported(ITypeDescriptorContext context) { return true; } } ``` 最后,我们可以在窗体中使用 PropertyGrid 控件来显示我们自定义的类。例如: ```c# private void Form1_Load(object sender, EventArgs e) { PropertyGrid1.SelectedObject = new Person { Name = "John", Age = 30, Address = new Address { Street = "123 Main St", City = "Anytown", State = "CA" } }; PropertyGrid1.BrowsableAttributes = new Attribute[] { new BrowsableAttribute(true) }; PropertyGrid1.ExpandAllGridItems(); } ``` 以上就是 C# PropertyGrid 自定义多层显示的示例。希望能对你有所帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值