C# PropertyGrid 載入動態屬性
阿新 • • 發佈:2019-02-16
最近在用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());
}