C#自定义PropertyGrid属性

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

一.如何显示属性

1)普通显示

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

  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中代码如下
  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)自定义显示

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

   //属性表管理类

  1. "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  mList=new List();  
  11.                     for (int 0; base.List.Count; i++)  
  12.                      
  13.                         Property 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 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(thiseditorBaseType, true);  
  80.          
  81.         public EventDescriptorCollectioGetEvents(Attribute[] attributes)  
  82.          
  83.             return TypeDescriptor.GetEvents(thisattributes, true);  
  84.          
  85.         public EventDescriptorCollectioGetEvents()  
  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 0; 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.     }  

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

  1. "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;  
显示结果:


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

1)类型转换器

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

  1. "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;"LINE-HEIGHT: 15.39px; FONT-FAMILY: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace; COLOR: rgb(0,130,0); FONT-SIZE: 14px">//true下拉框不可编辑   
  16. "FONT-SIZE: 13px" 
  17.          
  18.         public override  
  19.         System.ComponentModel.TypeConverter.StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)  
  20.          
  21.             return new StandardValuesCollection(m_Objects);//我们可以直接在内部定义一个数组,但并不建议这样做,这样对于下拉框的灵活             //性有很大影响   
  22.          
  23.     }  

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

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

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

  1. "FONT-FAMILY: Arial; COLOR: #333333">"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.     }  
效果图:



2)属性编辑器

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

  1. "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.     }  

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

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

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

  1. "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.     }  
效果图如下:

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 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、付费专栏及课程。

余额充值