1. 程式人生 > >C# PropertyGrid 載入動態屬性

C# PropertyGrid 載入動態屬性

最近在用winform做專案時,遇到大量要自定義動態配置的資料,為了減少工作量,考慮把資料類與PropertyGrid繫結,以達到簡潔地進行資料載入和更改的目的,下面把Demo過程和相關程式碼記錄下來作為備忘錄,同時希望能給後來者帶來幫助(在Demo過程中查閱了其他相關開發者的文章,在此對其作者表示感謝!)

第一步:根據自己需要建立Attribute基本類(至少包含四個PropertyGrid顯示的基本屬性)

[Serializable]
    public class MyAttribute
    {
        private string _categoryAttr = ""
; //屬性所屬類別 private string _displayNameAttr = ""; //屬性名稱 private string _descriptionAttr = ""; //屬性的描述內容 private object _myValue = null; //值 public string CategoryAttr { get { return _categoryAttr; } set { _categoryAttr = value; } } public
string DisplayNameAttr { get { return _displayNameAttr; } set { _displayNameAttr = value; } } public string DescriptionAttr { get { return _descriptionAttr; } set { _descriptionAttr = value; } } public
object Value { get { return _myValue; } set { _myValue = value; } } public MyAttribute () { } public MyAttribute(string category, string displayName, string description, object value) { _categoryAttr = category; _displayNameAttr = displayName; _descriptionAttr = description; _myValue = value; } }

第二步,建立自己的基本資料類DataBse(類的屬性為PropertyGrid中類別相同的屬性)

[Serializable]
    public class MyDataBase
    {
        private MyAttribute _data1;
        private MyAttribute _data2;

        public MyAttribute Data1
        {
            get { return _data1; }
            set { _data1 = value; }
        }

        public MyAttribute Data2
        {
            get { return _data2; }
            set { _data2 = value; }
        }

        public MyDataBase() { }
        public MyDataBase(string dataName)
        {
            _data1 = new MyAttribute(dataName, "Value1", "測試資料1", 0);
            _data2 = new MyAttribute(dataName, "Value2", "測試資料2", 0);
        }
        public MyDataBase(string dataName, object value1, object value2)
        {
            _data1 = new MyAttribute(dataName, "Value1", "測試資料1", value1);
            _data2 = new MyAttribute(dataName, "Value2", "測試資料2", value2);
        }
        public MyDataBase(MyAttribute data1, MyAttribute data2)
        {
            _data1 = data1;
            _data2 = data2;
        }
    }

第三步,建立自己的資料集合類DataHub

[Serializable]
    public class MyDataHub
    {
        private MyDataBase _hub1;
        private MyDataBase _hub2;

        public MyDataBase Hub1
        {
            get { return _hub1; }
            set { _hub2 = value; }
        }

        public MyDataBase Hub2
        {
            get { return _hub2; }
            set { _hub2 = value; }
        }

        public MyDataHub() 
        {
            _hub1 = new MyDataBase("測試組1");
            _hub2 = new MyDataBase("測試組2");
        }

        public MyDataHub Clone()
        {
            BinaryFormatter bf = new BinaryFormatter();
            MemoryStream ms = new MemoryStream();
            bf.Serialize(ms, this); //複製到流中   
            ms.Position = 0;
            return (MyDataHub)bf.Deserialize(ms);
        }

        public string ToString()
        {
            return string.Format("{0},{1},{2},{3}", _hub1.Data1.Value.ToString(), _hub1.Data2.Value.ToString(),
                _hub2.Data1.Value.ToString(), _hub2.Data2.Value.ToString());
        }

    }

第四步:將第一步建立的 Attribute 基本類與屬性描述抽象類 PropertyDescriptor 關聯

public class MyPropertyAttribute : PropertyDescriptor
    {
        private MyAttribute _myAttr;
        public MyPropertyAttribute(MyAttribute attr, Attribute[] attrs)
            : base(attr.DisplayNameAttr, attrs)
        {
            _myAttr = attr;
        }

        //實現抽象成員
        public override string Category
        {
            get { return _myAttr.CategoryAttr; }
        }

        public override string Description
        {
            get { return _myAttr.DescriptionAttr; }
        }

        public override Type ComponentType
        {
            get { return this.GetType(); }
        }

        public override bool IsReadOnly
        {
            get { return false; }
        }

        public override Type PropertyType
        {
            get { return _myAttr.Value.GetType(); }
        }

        public override bool CanResetValue(object component)
        {
            return false;
        }

        public override object GetValue(object component)
        {
            return _myAttr.Value;
        }

        public override void ResetValue(object component)
        {

        }

        public override void SetValue(object component, object value)
        {
            _myAttr.Value = value;
        }

        public override bool ShouldSerializeValue(object component)
        {
            return false;
        }
    }

第五步,將第三步建立的資料集合類 DataHub 與動態自定義型別資訊介面 ICustomTypeDescriptor 關聯

public class MyProperties : ICustomTypeDescriptor
    {
        private MyDataHub _myDataHub;

        public MyDataHub DataHub
        {
            get { return _myDataHub; }
            set { _myDataHub = value; }
        }

        #region 實現介面成員

        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(System.Type editorBaseType)
        {
            return TypeDescriptor.GetEditor(this, editorBaseType, true);
        }

        public EventDescriptorCollection GetEvents(System.Attribute[] attributes)
        {
            return TypeDescriptor.GetEvents(this, attributes, true);
        }

        public EventDescriptorCollection GetEvents()
        {
            return TypeDescriptor.GetEvents(this, true);
        }

        public PropertyDescriptorCollection GetProperties(System.Attribute[] attributes)
        {
            ArrayList array = new ArrayList();

            //獲取基類MyDataHub所有屬性成員
            PropertyInfo[] infos = _myDataHub.GetType().GetProperties();
            //遍歷新增屬性成員
            foreach (PropertyInfo pi in infos)
            {
                MyDataBase myData = (MyDataBase)pi.GetValue(_myDataHub, null);

                MyPropertyAttribute mpa1 = new MyPropertyAttribute(myData.Data1, attributes);
                array.Add(mpa1);

                MyPropertyAttribute mpa2 = new MyPropertyAttribute(myData.Data2, attributes);
                array.Add(mpa2);
            }

            PropertyDescriptor[] pdArray = (PropertyDescriptor[])array.ToArray(typeof(PropertyDescriptor));
            return new PropertyDescriptorCollection(pdArray);
        }

        public PropertyDescriptorCollection GetProperties()
        {
            return TypeDescriptor.GetProperties(this, true);
        }

        public object GetPropertyOwner(PropertyDescriptor pd)
        {
            return this;
        }

        #endregion
    }

先關測試過程如下:
1.資料與PropertyGrid繫結與初始化

private void BtnInit_Click(object sender, EventArgs e)
        {
            if (_myHub ==null )
            {
                _myHub = new MyDataHub();
            }

            if (_myPPT ==null )
            {
                _myPPT = new MyProperties();
            }
            _myPPT.DataHub = _myHub.Clone();  //PropertyGrid 資料更改,資料集合中的資料不會更改
            propertyGrid1.SelectedObject = _myPPT;
        }

這裡寫圖片描述

2.先更改資料,再點選顯示資料,顯示的結果是更改前的資料

 private void BtnShow_Click(object sender, EventArgs e)
        {
            MessageBox.Show(_myHub.ToString());
        }

這裡寫圖片描述

3.點選儲存更改並顯示,顯示的結果是更改後的資料

 private void BtnSave_Click(object sender, EventArgs e)
        {
            _myHub = _myPPT.DataHub.Clone();
            MessageBox.Show(_myHub.ToString());
        }

這裡寫圖片描述