1. 程式人生 > >C#自定義PropertyGrid屬性

C#自定義PropertyGrid屬性

最近用到了PropertyGrid,原來從來沒用到過,拿在手裡,一頭霧水,經過一段時間研究後,大概理解了Property的使用方法,下面仔細剖析一下。

PropertyGrid控制元件就是Visual Studio開發工具裡面的屬性瀏覽器,我們在VS裡面可以通過屬性瀏覽器檢視,修改控制元件的屬性,並主要通過使用反射來檢索專案的屬性。

一.如何顯示屬性

1)普通顯示

在PropertyGrid中顯示屬性很容易,我們可以直接給propertyGrid1.SelectedObject屬性賦值,SelectObject屬性可以獲取或設定當前選定的物件,資料型別為object,這就意味著我們可以直接將一個物件賦給它。針對一個物件,它會將物件中的所有公共屬性顯示在PropertyGrid上。假如我們定義一個Station類,如下

[csharp] view plaincopyprint?
  1. publicclass Station   
  2.    {  
  3.        privatestring _StationName;  
  4.        privatedouble _Lon = 103;  
  5.        privatedouble _Lat = 38;  
  6.        private Color _color;  
  7.        privatestring _file = string.Empty;  
  8.        private Font _font;  
  9.        publicstring 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.        publicstring StationName  
  25.        {  
  26.            get { return _StationName; }  
  27.            set { _StationName = value; }  
  28.        }  
  29.        publicdouble Lon  
  30.        {  
  31.            get { return _Lon; }  
  32.            set { _Lon = value; }  
  33.        }  
  34.        publicdouble Lat  
  35.        {  
  36.            get { return _Lat; }  
  37.            set { _Lat = value; }  
  38.        }  
  39.    }  
 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?
  1. privatevoid Form1_Load(object sender, EventArgs e)  
  2. {  
  3.   Station s=new Station();  
  4.   propertygrid1.SelectObject=s;  
  5. }  
        private void Form1_Load(object sender, EventArgs e)
        {
          Station s=new Station();
          propertygrid1.SelectObject=s;
        }
我們就可以看到如下效果:


我們看到屬性名顯示都是英文,那樣很不方便閱讀如果我們像顯示中文,該如何實現呢?

更改了顯示方式

要更改某些屬性的顯示方式,您可以對這些屬性應用不同的特性。特性是用於為型別、欄位、方法和屬性等程式設計元素新增批註的宣告標記,在執行時可以使用反射對其進行檢索。下面列出了其中的一部分:

DescriptionAttribute - 設定顯示在屬性下方說明幫助窗格中的屬性文字。這是一種為活動屬性(即具有焦點的屬性)提供幫助文字的有效方法。

CategoryAttribute - 設定屬性在網格中所屬的類別。當您需要將屬性按類別名稱分組時,此特性非常有用。如果沒有為屬性指定類別,該屬性將被分配給雜項 類別。可以將此特性應用於所有屬性。

BrowsableAttribute –  表示是否在網格中顯示屬性。此特性可用於在網格中隱藏屬性。預設情況下,公共屬性始終顯示在網格中。

ReadOnlyAttribute –  表示屬性是否為只讀。此特性可用於禁止在網格中編輯屬性。預設情況下,帶有 get 和 set 訪問函式的公共屬性在網格中是可以編輯的。

DefaultValueAttribute –  表示屬性的預設值。如果希望為屬性提供預設值,然後確定該屬性值是否與預設值相同,則可使用此特性。可以將此特性應用於所有屬性。

DefaultPropertyAttribute –  表示類的預設屬性。在網格中選擇某個類時,將首先突出顯示該類的預設屬性。

下面我們在Station類中的屬性Lon上方新增[CategoryAttribute("座標"),DisplayNameAttribute("經度")],效果如下:


如果想要在屬性表中新增顏色選擇和字型選擇那是很容易一件事,可以在Station類中新增Color型別屬性,和Font型別屬性,繫結後,就可以進行顏色選擇和字型選擇了,程式碼在Station中已經實現。

2)自定義顯示

我們可以看出這種上面這種顯示屬性方法並不夠靈活,我們不能方便的及時增加或者刪除屬性。

   //屬性表管理類

[csharp] view plaincopyprint?
  1. <span style="font-size: 13px;"publicclass PropertyManageCls : CollectionBase, ICustomTypeDescriptor  
  2.     {  
  3.         publicvoid 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.         publicvoid 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.         publicstring GetClassName()  
  58.         {  
  59.             return TypeDescriptor.GetClassName(thistrue);  
  60.         }  
  61.         publicstring 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.         publicobject 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.             returnnew PropertyDescriptorCollection(newProps);  
  98.         }  
  99.         public PropertyDescriptorCollection GetProperties()  
  100.         {  
  101.             return TypeDescriptor.GetProperties(thistrue);  
  102.         }  
  103.         publicobject GetPropertyOwner(PropertyDescriptor pd)  
  104.         {  
  105.             returnthis;          
  106.         }  
  107.         #endregion
  108.     }  
  109. //屬性類
  110.     publicclass Property  
  111.     {  
  112.         privatestring _name=string.Empty;  
  113.         privateobject _value=null;  
  114.         privatebool _readonly=false;  
  115.         privatebool _visible=true;  
  116.         privatestring _category=string.Empty;  
  117.         TypeConverter _converter=null;  
  118.         object _editor = null;  
  119.         privatestring _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.         publicstring Name  //獲得屬性名
  133.         {  
  134.             get
  135.             {  
  136.                 return _name;  
  137.             }  
  138.             set
  139.             {  
  140.                 _name=value;  
  141.             }  
  142.         }  
  143.         publicstring 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.         publicstring Category  //屬性所屬類別
  166.         {  
  167.             get
  168.             {  
  169.                 return _category;  
  170.             }  
  171.             set
  172.             {  
  173.                 _category = value;  
  174.             }  
  175.         }  
  176.         publicobject Value  //屬性值
  177.         {  
  178.             get
  179.             {  
  180.                 return _value;  
  181.             }  
  182.             set
  183.             {  
  184.                 _value=value;  
  185.             }  
  186.         }  
  187.         publicbool ReadOnly  //是否為只讀屬性
  188.         {  
  189.             get
  190.             {  
  191.                 return _readonly;  
  192.             }  
  193.             set
  194.             {  
  195.                 _readonly = value;  
  196.             }  
  197.         }  
  198.         publicbool Visible  //是否可見
  199.         {  
  200.             get
  201.             {  
  202.                 return _visible;  
  203.             }  
  204.             set
  205.             {  
  206.                 _visible = value;  
  207.             }  
  208.         }  
  209.         publicvirtualobject Editor   //屬性編輯器
  210.         {  
  211.             get
  212.             {   
  213.                 return _editor;   
  214.             }  
  215.             set
  216.             {   
  217.                 _editor = value;   
  218.             }  
  219.         }  
  220.     }  
  221.     publicclass 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.         publicoverridebool CanResetValue(object component)  
  231.         {  
  232.             returnfalse;  
  233.         }  
  234.         publicoverride Type ComponentType  
  235.         {  
  236.             get
  237.             {  
  238.                 returnnull;  
  239.             }  
  240.         }  
  241.         publicoverrideobject GetValue(object component)  
  242.         {  
  243.             return m_Property.Value;  
  244.         }  
  245.         publicoverridestring Description  
  246.         {  
  247.             get
  248.             {  
  249.                 return m_Property.Name;  
  250.             }  
  251.         }  
  252.         publicoverridestring Category  
  253.         {  
  254.             get
  255.             {  
  256.                 return m_Property.Category;  
  257.             }  
  258.         }  
  259.         publicoverridestring DisplayName  
  260.         {  
  261.             get
  262.             {  
  263.                 return m_Property.DisplayName!=""?m_Property.DisplayName:m_Property.Name;  
  264.             }  
  265.         }  
  266.         publicoverridebool IsReadOnly  
  267.         {  
  268.             get
  269.             {  
  270.                 return m_Property.ReadOnly;  
  271.             }  
  272.         }  
  273.         publicoverridevoid ResetValue(object component)  
  274.         {  
  275.             //Have to implement
  276.         }  
  277.         publicoverridebool ShouldSerializeValue(object component)  
  278.         {  
  279.             returnfalse;  
  280.         }  
  281.         publicoverridevoid SetValue(object component, object value)  
  282.         {  
  283.             m_Property.Value = value;  
  284.         }  
  285.         publicoverride TypeConverter Converter  
  286.         {  
  287.             get
  288.             {  
  289.                 return m_Property.Converter;  
  290.             }  
  291.         }  
  292.         publicoverride Type PropertyType  
  293.         {  
  294.             get { return m_Property.Value.GetType(); }  
  295.         }  
  296.         publicoverrideobject GetEditor(Type editorBaseType)  
  297.         {  
  298.             return m_Property.Editor==nullbase.GetEditor(editorBaseType):m_Property.Editor;  
  299.         }  
  300.         #endregion
  301.     }</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?
  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>  
            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?
  1. <span style="font-size: 13px;">    //下拉框型別轉換器
  2.     publicclass DropDownListConverter : StringConverter  
  3.     {  
  4.         object[] m_Objects;  
  5.         public DropDownListConverter(object[] objects)  
  6.         {  
  7.             m_Objects = objects;  
  8.         }  
  9.         publicoverridebool GetStandardValuesSupported(ITypeDescriptorContext context)  
  10.         {  
  11.             returntrue;  
  12.         }  
  13.         publicoverridebool GetStandardValuesExclusive(ITypeDescriptorContext context)  
  14.         {  
  15.             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;">
  16. </span><span style="font-size: 13px;">  
  17.         }  
  18.         publicoverride
  19.         System.ComponentModel.TypeConverter.StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)  
  20.         {  
  21.             returnnew StandardValuesCollection(m_Objects);//我們可以直接在內部定義一個數組,但並不建議這樣做,這樣對於下拉框的靈活             //性有很大影響
  22.         }  
  23.     }</span>