C#自定義PropertyGrid屬性
最近用到了PropertyGrid,原來從來沒用到過,拿在手裡,一頭霧水,經過一段時間研究後,大概理解了Property的使用方法,下面仔細剖析一下。
PropertyGrid控制元件就是Visual Studio開發工具裡面的屬性瀏覽器,我們在VS裡面可以通過屬性瀏覽器檢視,修改控制元件的屬性,並主要通過使用反射來檢索專案的屬性。
一.如何顯示屬性
1)普通顯示
在PropertyGrid中顯示屬性很容易,我們可以直接給propertyGrid1.SelectedObject屬性賦值,SelectObject屬性可以獲取或設定當前選定的物件,資料型別為object,這就意味著我們可以直接將一個物件賦給它。針對一個物件,它會將物件中的所有公共屬性顯示在PropertyGrid上。假如我們定義一個Station類,如下
- publicclass Station
- {
- privatestring _StationName;
- privatedouble _Lon = 103;
- privatedouble _Lat = 38;
- private Color _color;
- privatestring _file = string.Empty;
- private Font _font;
- publicstring FileName
- {
- get { return _file; }
- set { _file = value; }
- }
- public Color Color
- {
- get { return _color; }
- set { _color = value; }
- }
- public Font Font
- {
- get { return _font; }
- set { _font = value; }
- }
- publicstring StationName
- {
- get { return _StationName; }
- set { _StationName = value; }
- }
- publicdouble Lon
- {
- get { return _Lon; }
- set { _Lon = value; }
- }
- publicdouble Lat
- {
- get { return _Lat; }
- set { _Lat = value; }
- }
- }
public class Station
{
private string _StationName;
private double _Lon = 103;
private double _Lat = 38;
private Color _color;
private string _file = string.Empty;
private Font _font;
public string FileName
{
get { return _file; }
set { _file = value; }
}
public Color Color
{
get { return _color; }
set { _color = value; }
}
public Font Font
{
get { return _font; }
set { _font = value; }
}
public string StationName
{
get { return _StationName; }
set { _StationName = value; }
}
public double Lon
{
get { return _Lon; }
set { _Lon = value; }
}
public double Lat
{
get { return _Lat; }
set { _Lat = value; }
}
}
然後在窗體中拖拉一個PropertyGrid控制元件propertygrid1,在Form_load中程式碼如下 [csharp] view plaincopyprint?
- privatevoid Form1_Load(object sender, EventArgs e)
- {
- Station s=new Station();
- propertygrid1.SelectObject=s;
- }
private void Form1_Load(object sender, EventArgs e)
{
Station s=new Station();
propertygrid1.SelectObject=s;
}
我們就可以看到如下效果:
我們看到屬性名顯示都是英文,那樣很不方便閱讀如果我們像顯示中文,該如何實現呢?
更改了顯示方式
要更改某些屬性的顯示方式,您可以對這些屬性應用不同的特性。特性是用於為型別、欄位、方法和屬性等程式設計元素新增批註的宣告標記,在執行時可以使用反射對其進行檢索。下面列出了其中的一部分:
DescriptionAttribute - 設定顯示在屬性下方說明幫助窗格中的屬性文字。這是一種為活動屬性(即具有焦點的屬性)提供幫助文字的有效方法。
BrowsableAttribute –
表示是否在網格中顯示屬性。此特性可用於在網格中隱藏屬性。預設情況下,公共屬性始終顯示在網格中。
ReadOnlyAttribute –
表示屬性是否為只讀。此特性可用於禁止在網格中編輯屬性。預設情況下,帶有 get 和 set 訪問函式的公共屬性在網格中是可以編輯的。
DefaultValueAttribute –
表示屬性的預設值。如果希望為屬性提供預設值,然後確定該屬性值是否與預設值相同,則可使用此特性。可以將此特性應用於所有屬性。
DefaultPropertyAttribute –
表示類的預設屬性。在網格中選擇某個類時,將首先突出顯示該類的預設屬性。
下面我們在Station類中的屬性Lon上方新增[CategoryAttribute("座標"),DisplayNameAttribute("經度")],效果如下:
如果想要在屬性表中新增顏色選擇和字型選擇那是很容易一件事,可以在Station類中新增Color型別屬性,和Font型別屬性,繫結後,就可以進行顏色選擇和字型選擇了,程式碼在Station中已經實現。
2)自定義顯示
我們可以看出這種上面這種顯示屬性方法並不夠靈活,我們不能方便的及時增加或者刪除屬性。
//屬性表管理類
[csharp] view plaincopyprint?- <span style="font-size: 13px;"> publicclass PropertyManageCls : CollectionBase, ICustomTypeDescriptor
- {
- publicvoid Add(Property value)
- {
- int flag=-1;
- if (value != null)
- {
- if (base.List.Count>0)
- {
- IList <Property> mList=new List<Property>();
- for (int i = 0; i < base.List.Count; i++)
- {
- Property p = base.List[i] as Property;
- if (value.Name == p.Name)
- {
- flag = i;
- }
- mList.Add(p);
- }
- if (flag == -1)
- {
- mList.Add(value);
- }
- base.List.Clear();
- foreach (Property p in mList)
- {
- base.List.Add(p);
- }
- }
- else
- {
- base.List.Add(value);
- }
- }
- }
- publicvoid Remove(Property value)
- {
- if(value!=null&&base.List.Count>0)
- base.List.Remove(value);
- }
- public Property this[int index]
- {
- get
- {
- return (Property)base.List[index];
- }
- set
- {
- base.List[index] = (Property)value;
- }
- }
- #region ICustomTypeDescriptor 成員
- public AttributeCollection GetAttributes()
- {
- return TypeDescriptor.GetAttributes(this,true);
- }
- publicstring GetClassName()
- {
- return TypeDescriptor.GetClassName(this, true);
- }
- publicstring GetComponentName()
- {
- return TypeDescriptor.GetComponentName(this, true);
- }
- public TypeConverter GetConverter()
- {
- return TypeDescriptor.GetConverter(this, true);
- }
- public EventDescriptor GetDefaultEvent()
- {
- return TypeDescriptor.GetDefaultEvent(this, true);
- }
- public PropertyDescriptor GetDefaultProperty()
- {
- return TypeDescriptor.GetDefaultProperty(this, true);
- }
- publicobject GetEditor(Type editorBaseType)
- {
- return TypeDescriptor.GetEditor(this, editorBaseType, true);
- }
- public EventDescriptorCollection GetEvents(Attribute[] attributes)
- {
- return TypeDescriptor.GetEvents(this, attributes, true);
- }
- public EventDescriptorCollection GetEvents()
- {
- return TypeDescriptor.GetEvents(this,true);
- }
- public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
- {
- PropertyDescriptor[] newProps = new PropertyDescriptor[this.Count];
- for (int i = 0; i < this.Count; i++)
- {
- Property prop = (Property)this[i];
- newProps[i] = new CustomPropertyDescriptor(ref prop, attributes);
- }
- returnnew PropertyDescriptorCollection(newProps);
- }
- public PropertyDescriptorCollection GetProperties()
- {
- return TypeDescriptor.GetProperties(this, true);
- }
- publicobject GetPropertyOwner(PropertyDescriptor pd)
- {
- returnthis;
- }
- #endregion
- }
- //屬性類
- publicclass Property
- {
- privatestring _name=string.Empty;
- privateobject _value=null;
- privatebool _readonly=false;
- privatebool _visible=true;
- privatestring _category=string.Empty;
- TypeConverter _converter=null;
- object _editor = null;
- privatestring _displayname = string.Empty;
- public Property(string sName, object sValue)
- {
- this._name = sName;
- this._value = sValue;
- }
- public Property(string sName, object sValue, bool sReadonly, bool sVisible)
- {
- this._name = sName;
- this._value = sValue;
- this._readonly = sReadonly;
- this._visible = sVisible;
- }
- publicstring Name //獲得屬性名
- {
- get
- {
- return _name;
- }
- set
- {
- _name=value;
- }
- }
- publicstring DisplayName //屬性顯示名稱
- {
- get
- {
- return _displayname;
- }
- set
- {
- _displayname = value;
- }
- }
- public TypeConverter Converter //型別轉換器,我們在製作下拉列表時需要用到
- {
- get
- {
- return _converter;
- }
- set
- {
- _converter = value;
- }
- }
- publicstring Category //屬性所屬類別
- {
- get
- {
- return _category;
- }
- set
- {
- _category = value;
- }
- }
- publicobject Value //屬性值
- {
- get
- {
- return _value;
- }
- set
- {
- _value=value;
- }
- }
- publicbool ReadOnly //是否為只讀屬性
- {
- get
- {
- return _readonly;
- }
- set
- {
- _readonly = value;
- }
- }
- publicbool Visible //是否可見
- {
- get
- {
- return _visible;
- }
- set
- {
- _visible = value;
- }
- }
- publicvirtualobject Editor //屬性編輯器
- {
- get
- {
- return _editor;
- }
- set
- {
- _editor = value;
- }
- }
- }
- publicclass CustomPropertyDescriptor : PropertyDescriptor
- {
- Property m_Property;
- public CustomPropertyDescriptor(ref Property myProperty, Attribute[] attrs)
- : base(myProperty.Name, attrs)
- {
- m_Property = myProperty;
- }
- #region PropertyDescriptor 重寫方法
- publicoverridebool CanResetValue(object component)
- {
- returnfalse;
- }
- publicoverride Type ComponentType
- {
- get
- {
- returnnull;
- }
- }
- publicoverrideobject GetValue(object component)
- {
- return m_Property.Value;
- }
- publicoverridestring Description
- {
- get
- {
- return m_Property.Name;
- }
- }
- publicoverridestring Category
- {
- get
- {
- return m_Property.Category;
- }
- }
- publicoverridestring DisplayName
- {
- get
- {
- return m_Property.DisplayName!=""?m_Property.DisplayName:m_Property.Name;
- }
- }
- publicoverridebool IsReadOnly
- {
- get
- {
- return m_Property.ReadOnly;
- }
- }
- publicoverridevoid ResetValue(object component)
- {
- //Have to implement
- }
- publicoverridebool ShouldSerializeValue(object component)
- {
- returnfalse;
- }
- publicoverridevoid SetValue(object component, object value)
- {
- m_Property.Value = value;
- }
- publicoverride TypeConverter Converter
- {
- get
- {
- return m_Property.Converter;
- }
- }
- publicoverride Type PropertyType
- {
- get { return m_Property.Value.GetType(); }
- }
- publicoverrideobject GetEditor(Type editorBaseType)
- {
- return m_Property.Editor==null? base.GetEditor(editorBaseType):m_Property.Editor;
- }
- #endregion
- }</span>
public class PropertyManageCls : CollectionBase, ICustomTypeDescriptor
{
public void Add(Property value)
{
int flag=-1;
if (value != null)
{
if (base.List.Count>0)
{
IList <Property> mList=new List<Property>();
for (int i = 0; i < base.List.Count; i++)
{
Property p = base.List[i] as Property;
if (value.Name == p.Name)
{
flag = i;
}
mList.Add(p);
}
if (flag == -1)
{
mList.Add(value);
}
base.List.Clear();
foreach (Property p in mList)
{
base.List.Add(p);
}
}
else
{
base.List.Add(value);
}
}
}
public void Remove(Property value)
{
if(value!=null&&base.List.Count>0)
base.List.Remove(value);
}
public Property this[int index]
{
get
{
return (Property)base.List[index];
}
set
{
base.List[index] = (Property)value;
}
}
#region ICustomTypeDescriptor 成員
public AttributeCollection GetAttributes()
{
return TypeDescriptor.GetAttributes(this,true);
}
public string GetClassName()
{
return TypeDescriptor.GetClassName(this, true);
}
public string GetComponentName()
{
return TypeDescriptor.GetComponentName(this, true);
}
public TypeConverter GetConverter()
{
return TypeDescriptor.GetConverter(this, true);
}
public EventDescriptor GetDefaultEvent()
{
return TypeDescriptor.GetDefaultEvent(this, true);
}
public PropertyDescriptor GetDefaultProperty()
{
return TypeDescriptor.GetDefaultProperty(this, true);
}
public object GetEditor(Type editorBaseType)
{
return TypeDescriptor.GetEditor(this, editorBaseType, true);
}
public EventDescriptorCollection GetEvents(Attribute[] attributes)
{
return TypeDescriptor.GetEvents(this, attributes, true);
}
public EventDescriptorCollection GetEvents()
{
return TypeDescriptor.GetEvents(this,true);
}
public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
PropertyDescriptor[] newProps = new PropertyDescriptor[this.Count];
for (int i = 0; i < this.Count; i++)
{
Property prop = (Property)this[i];
newProps[i] = new CustomPropertyDescriptor(ref prop, attributes);
}
return new PropertyDescriptorCollection(newProps);
}
public PropertyDescriptorCollection GetProperties()
{
return TypeDescriptor.GetProperties(this, true);
}
public object GetPropertyOwner(PropertyDescriptor pd)
{
return this;
}
#endregion
}
//屬性類
public class Property
{
private string _name=string.Empty;
private object _value=null;
private bool _readonly=false;
private bool _visible=true;
private string _category=string.Empty;
TypeConverter _converter=null;
object _editor = null;
private string _displayname = string.Empty;
public Property(string sName, object sValue)
{
this._name = sName;
this._value = sValue;
}
public Property(string sName, object sValue, bool sReadonly, bool sVisible)
{
this._name = sName;
this._value = sValue;
this._readonly = sReadonly;
this._visible = sVisible;
}
public string Name //獲得屬性名
{
get
{
return _name;
}
set
{
_name=value;
}
}
public string DisplayName //屬性顯示名稱
{
get
{
return _displayname;
}
set
{
_displayname = value;
}
}
public TypeConverter Converter //型別轉換器,我們在製作下拉列表時需要用到
{
get
{
return _converter;
}
set
{
_converter = value;
}
}
public string Category //屬性所屬類別
{
get
{
return _category;
}
set
{
_category = value;
}
}
public object Value //屬性值
{
get
{
return _value;
}
set
{
_value=value;
}
}
public bool ReadOnly //是否為只讀屬性
{
get
{
return _readonly;
}
set
{
_readonly = value;
}
}
public bool Visible //是否可見
{
get
{
return _visible;
}
set
{
_visible = value;
}
}
public virtual object Editor //屬性編輯器
{
get
{
return _editor;
}
set
{
_editor = value;
}
}
}
public class CustomPropertyDescriptor : PropertyDescriptor
{
Property m_Property;
public CustomPropertyDescriptor(ref Property myProperty, Attribute[] attrs)
: base(myProperty.Name, attrs)
{
m_Property = myProperty;
}
#region PropertyDescriptor 重寫方法
public override bool CanResetValue(object component)
{
return false;
}
public override Type ComponentType
{
get
{
return null;
}
}
public override object GetValue(object component)
{
return m_Property.Value;
}
public override string Description
{
get
{
return m_Property.Name;
}
}
public override string Category
{
get
{
return m_Property.Category;
}
}
public override string DisplayName
{
get
{
return m_Property.DisplayName!=""?m_Property.DisplayName:m_Property.Name;
}
}
public override bool IsReadOnly
{
get
{
return m_Property.ReadOnly;
}
}
public override void ResetValue(object component)
{
//Have to implement
}
public override bool ShouldSerializeValue(object component)
{
return false;
}
public override void SetValue(object component, object value)
{
m_Property.Value = value;
}
public override TypeConverter Converter
{
get
{
return m_Property.Converter;
}
}
public override Type PropertyType
{
get { return m_Property.Value.GetType(); }
}
public override object GetEditor(Type editorBaseType)
{
return m_Property.Editor==null? base.GetEditor(editorBaseType):m_Property.Editor;
}
#endregion
}
下面我們來看看該如何使用,我們仍然在Form_load中新增程式碼如下:
[csharp] view plaincopyprint?- <span style="font-size: 13px;"> PropertyManageCls pmc = new PropertyManageCls();
- Property pp = new Property("ID", "1", false, true);
- pp.Category = "基本資訊";
- pp.DisplayName = "我的ID";
- pmc.Add(pp);
- propertyGrid1.SelectObject=pmc;</span>
PropertyManageCls pmc = new PropertyManageCls();
Property pp = new Property("ID", "1", false, true);
pp.Category = "基本資訊";
pp.DisplayName = "我的ID";
pmc.Add(pp);
propertyGrid1.SelectObject=pmc;
顯示結果:
我們可以看到上面的屬性顯示很簡單,如果想要自定義一個下拉框,或者有一個路徑選擇的該怎麼辦呢。
1)型別轉換器
要實現下拉框的方法:使用型別轉換器,需要繼承與TypeConverter或者StringConverter,然後重寫方法,程式碼如下:
[csharp] view plaincopyprint?- <span style="font-size: 13px;"> //下拉框型別轉換器
- publicclass DropDownListConverter : StringConverter
- {
- object[] m_Objects;
- public DropDownListConverter(object[] objects)
- {
- m_Objects = objects;
- }
- publicoverridebool GetStandardValuesSupported(ITypeDescriptorContext context)
- {
- returntrue;
- }
- publicoverridebool GetStandardValuesExclusive(ITypeDescriptorContext context)
- {
- returntrue;</span><span style='color: rgb(0, 130, 0); line-height: 15.39px; font-family: Consolas, "Bitstream Vera Sans Mono", "Courier New", Courier, monospace; font-size: 14px;'>//true下拉框不可編輯</span><span style="font-size: 13px;">
- </span><span style="font-size: 13px;">
- }
- publicoverride
- System.ComponentModel.TypeConverter.StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
- {
- returnnew StandardValuesCollection(m_Objects);//我們可以直接在內部定義一個數組,但並不建議這樣做,這樣對於下拉框的靈活 //性有很大影響
- }
- }</span>