對(duì)于這幾個(gè)月的這個(gè)項(xiàng)目,一直想做個(gè)總結(jié),但是鑒于本人記性之差,總是將這件事想起又忘記,終于在這個(gè)月工作的最后幾天有了幾天的空閑,把這個(gè)經(jīng)驗(yàn)好好的記錄下來(lái)。
PropertyGrid,.net框架下的一個(gè)控件,這是一個(gè)軟件升級(jí)的項(xiàng)目,原來(lái)的軟件用的是C++,控件用的還是第三方,這次升級(jí)到visualstudio .net4.0版本,原以為.net的東西用起來(lái)不會(huì)費(fèi)勁的,沒(méi)想到想要實(shí)現(xiàn)項(xiàng)目需要的效果還真沒(méi)那么簡(jiǎn)單。
由于需要,我這里主要是為了能動(dòng)態(tài)的生成屬性頁(yè),還要帶能動(dòng)態(tài)生成下來(lái)菜單,所以今天主要從這方面總結(jié)。
首先定義一個(gè)屬性類:
//單條屬性類
public class XProp
{
private string theId = "";//屬性Id,我的項(xiàng)目中需要,大家可以忽略
private string theCategory = ""; //屬性所屬類別
private string theName ="";//屬性名稱
private bool theReadOnly = false;//屬性的只讀性,true為只讀
private string theDescription = "";//屬性的描述內(nèi)容
private object theValue =null;//值
private System.Type theType = null; //類型
private bool theBrowsable = true;//顯示或隱藏,true為顯示
TypeConverter theConverter = null;//類型轉(zhuǎn)換
public string Id
{
get { return theId; }
set { theId = value; }
}
public string Category
{
get { return theCategory; }
set { theCategory = value; }
}
public bool ReadOnly
{
get { return theReadOnly; }
set { theReadOnly = value; }
}
public string Name
{
get { return this.theName; }
set { this.theName = value; }
}
public object Value
{
get { return this.theValue; }
set { this.theValue = value; }
}
public string Description
{
get { return theDescription; }
set { theDescription = value; }
}
public System.Type ProType
{
get { return theType; }
set { theType = value; }
}
public bool Browsable
{
get { return theBrowsable; }
set { theBrowsable = value; }
}
public virtual TypeConverter Converter
{
get { return theConverter; }
set { theConverter = value; }
}
}
我舉一個(gè)例子:
private string strdemo;
[DescriptionAttribute("用于舉例說(shuō)明"),
CategoryAttribute("公有屬性"),
DefaultValueAttribute(“測(cè)試屬性”),
ReadOnlyAttribute(false),
BrowsableAttribute(true),
TypeConverter(typeof(MyComboTypeConvert))
]
public string strDemo
{
get { return strdemo; }
set { strdemo = value; }
}
這是個(gè)寫死的屬性,那在我的項(xiàng)目中,根據(jù)對(duì)象的不同,會(huì)需要生產(chǎn)不同的屬性頁(yè),所以需要一個(gè)可以動(dòng)態(tài)生成的屬性頁(yè),將上述這個(gè)一般屬性定義,利用XProp類,寫成:
Private XProp newXpro = new XProp();
newXpro.Category = ”公有屬性”;
newXpro.Name = ” strDemo”;
newXpro.Id = "A";
newXpro.Description = “用于舉例說(shuō)明”;
newXpro.ReadOnly =false;
newXpro.Value = “測(cè)試屬性”;
newXpro.ProType = typeof(string);
newXpro.Browsable = true;
newXpro.Converter = null;
這樣,一條屬性就完成了。當(dāng)然你也可以根據(jù)需要自己重寫更多的屬性相關(guān)定義。這里的Converter屬性是在后面的下拉菜單中需要用到的,如果不是基礎(chǔ)類型的(string,int,bool,enum等),我們可以賦值為null.
當(dāng)然,這只是一條屬性,原本按之前的方法,只要定義一個(gè)類,然后這個(gè)類里面定義多條屬性就可以了。但是現(xiàn)在,由于屬性是動(dòng)態(tài)生成的,我們并不能確定需要幾個(gè)屬性,也就不能直接在一個(gè)類里面定義完。
所以我們需要再定義一個(gè)List<XProp>類,作為一一張list可以隨意添加多個(gè)項(xiàng),然后將PropertyGrid.SelectedObject設(shè)置為這個(gè)類就好了:
先來(lái)定義以下兩個(gè)類:
public class XProps :List<XProp>,ICustomTypeDescriptor
{
#regionICustomTypeDescriptor 成員
publicAttributeCollection GetAttributes()
{
return TypeDescriptor.GetAttributes(this, true);
}
publicstring GetClassName()
{
return TypeDescriptor.GetClassName(this, true);
}
publicstring GetComponentName()
{
return TypeDescriptor.GetComponentName(this, true);
}
publicTypeConverter GetConverter()
{
return TypeDescriptor.GetConverter(this, true);
}
publicEventDescriptor GetDefaultEvent()
{
return TypeDescriptor.GetDefaultEvent(this, true);
}
publicPropertyDescriptor GetDefaultProperty()
{
return TypeDescriptor.GetDefaultProperty(this, true);
}
publicobject GetEditor(System.Type editorBaseType)
{
return TypeDescriptor.GetEditor(this, editorBaseType, true);
}
publicEventDescriptorCollection GetEvents(System.Attribute[]attributes)
{
return TypeDescriptor.GetEvents(this, attributes, true);
}
publicEventDescriptorCollection GetEvents()
{
return TypeDescriptor.GetEvents(this, true);
}
publicPropertyDescriptorCollection GetProperties(System.Attribute[]attributes)
{
ArrayList props = new ArrayList();
for (int i = 0; i < this.Count; i++)
{ //判斷屬性是否顯示
if (this[i].Browsable == true)
{
XPropDescriptor psd = new XPropDescriptor(this[i], attributes);
props.Add(psd);
}
}
PropertyDescriptor[] propArray =(PropertyDescriptor[])props.ToArray(typeof(PropertyDescriptor));
return new PropertyDescriptorCollection(propArray);
}
publicPropertyDescriptorCollection GetProperties()
{
return TypeDescriptor.GetProperties(this, true);
}
publicobject GetPropertyOwner(PropertyDescriptor pd)
{
return this;
}
#endregion
publicoverride string ToString()
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < this.Count; i++)
{
sb.Append("[" + i + "] " + this[i].ToString() +System.Environment.NewLine);
}
return sb.ToString();
}
}
private class XPropDescriptor :PropertyDescriptor
{
XProptheProp;
publicXPropDescriptor(XProp prop, Attribute[] attrs): base(prop.Name,attrs)
{
theProp = prop;
}
publicoverride bool CanResetValue(object component)
{
return false;
}
publicoverride string Category
{
get { return theProp.Category; }
}
publicoverride string Description
{
get { return theProp.Description; }
}
publicoverride TypeConverter Converter
{
get { return theProp.Converter; }
}
publicoverride System.Type ComponentType
{
get { return this.GetType(); }
}
publicoverride object GetValue(object component)
{
return theProp.Value;
}
publicoverride bool IsReadOnly
{
get { return theProp.ReadOnly; }
}
publicoverride System.Type PropertyType
{
get { return theProp.ProType; }
}
publicoverride void ResetValue(object component)
{
}
publicoverride void SetValue(object component, object value)
{
theProp.Value = value;
}
publicoverride bool ShouldSerializeValue(object component)
{
return false;
}
}
然后我們新聲明一個(gè)屬性列表:
Private XProps xprops = new XProps();
再將剛剛那個(gè)動(dòng)態(tài)生成的屬性添加進(jìn)去,將屬性頁(yè)的selectobject賦值為這個(gè)類:
xprops.add(newXpro);
PropertyGridDemo.SelectedObject = xprops;
現(xiàn)在我們來(lái)看看效果:
根據(jù)需要,你可以添加多條屬性。
也許你會(huì)想問(wèn),這里哪有動(dòng)態(tài)生成啊,那些屬性名稱,屬性類型什么的不還是寫死的么。呵呵,我這里只是舉個(gè)例子所以寫死了,在實(shí)際應(yīng)用中,你可以根據(jù)需要,在生成屬性列表的時(shí)候,動(dòng)態(tài)賦值,例如你是從xml文件里讀取到的,例如你是從服務(wù)器中獲取到的。根據(jù)你讀取的對(duì)象,一個(gè)一個(gè)賦值就可以了。
下面來(lái)介紹如何在屬性頁(yè)中動(dòng)態(tài)生成下拉菜單框,關(guān)于下拉菜單真的是很復(fù)雜,在.net的PropertyGrid控件中,想要具有下拉菜單的屬性,簡(jiǎn)單的可以通過(guò)枚舉類型來(lái)實(shí)現(xiàn),還是以剛剛的那個(gè)例子來(lái)說(shuō)明:
首先定義個(gè)枚舉類型:
public enum enumType
{
BOOLVAL,
DIGITALVAL,
STRINGVAL,
CHECKVAL,
RATIOVAL,
IPVAL,
COMBOBOX,
RESETBTN
}
然后代碼中將屬性值與屬性類型修改為:
newXpro.Value =CustomClass.enumType.BOOLVAL;//這里的屬性值當(dāng)然必須為枚舉中的某一項(xiàng)
newXpro.ProType = typeof(enumType);
然后我們來(lái)看看實(shí)現(xiàn)的效果:
這就是最簡(jiǎn)單的下拉菜單的實(shí)現(xiàn)方式了。
但是這里的枚舉類型仍然是需要代碼中寫死的,而我在網(wǎng)上也所搜了很久,枚舉類型似乎沒(méi)辦法動(dòng)態(tài)生成,如果要實(shí)現(xiàn)動(dòng)態(tài)生成恐怕要另尋他途。
所幸的是,我曾經(jīng)見(jiàn)過(guò)重寫combobox來(lái)生成屬性頁(yè)的下來(lái)菜單的,我們需要定義:
//重寫下拉菜單中的項(xiàng),使之與屬性頁(yè)的項(xiàng)關(guān)聯(lián)
public abstract class ComboBoxItemTypeConvert : TypeConverter
{
public Hashtable myhash = null;
public ComboBoxItemTypeConvert()
{
myhash = new Hashtable();
GetConvertHash();
}
public abstract void GetConvertHash();
//是否支持選擇列表的編輯
public override boolGetStandardValuesSupported(ITypeDescriptorContext context)
{
return true;
}
//重寫combobox的選擇列表
public override StandardValuesCollectionGetStandardValues(ITypeDescriptorContext context)
{
int[] ids = new int[myhash.Values.Count];
int i = 0;
foreach (DictionaryEntry myDE in myhash)
{
ids[i++] = (int)(myDE.Key);
}
return new StandardValuesCollection(ids);
}
//判斷轉(zhuǎn)換器是否可以工作
public override bool CanConvertFrom(ITypeDescriptorContext context,Type sourceType)
{
if (sourceType == typeof(string))
{
return true;
}
return base.CanConvertFrom(context, sourceType);
}
//重寫轉(zhuǎn)換器,將選項(xiàng)列表(即下拉菜單)中的值轉(zhuǎn)換到該類型的值
public override object ConvertFrom(ITypeDescriptorContext context,System.Globalization.CultureInfo culture, object obj)
{
if (obj is string)
{
foreach (DictionaryEntry myDE in myhash)
{
if (myDE.Value.Equals((obj.ToString())))
return myDE.Key;
}
}
return base.ConvertFrom(context, culture, obj);
}
public override bool CanConvertTo(ITypeDescriptorContext context,Type destinationType)
{
if (destinationType == typeof(string))
{
return true;
}
return base.CanConvertTo(context, destinationType);
}
//重寫轉(zhuǎn)換器將該類型的值轉(zhuǎn)換到選擇列表中
public override object ConvertTo(ITypeDescriptorContext context,System.Globalization.CultureInfo culture, object obj, TypedestinationType)
{
if (destinationType == typeof(string))
{
foreach (DictionaryEntry myDE in myhash)
{
if (myDE.Key.Equals(obj))
return myDE.Value.ToString();
}
return "";
}
return base.ConvertTo(context, culture, obj,destinationType);
}
public override boolGetStandardValuesExclusive(ITypeDescriptorContext context)
{
return false;
}
}
//重寫下拉菜單,在這里實(shí)現(xiàn)定義下拉菜單內(nèi)的項(xiàng)
public class MyComboItemConvert : ComboBoxItemTypeConvert
{
private Hashtable hash;
public override void GetConvertHash()
{
try
{
myhash = hash;
}
catch
{
throw new NotImplementedException();
}
}
public MyComboItemConvert(string str)
{
hash = new Hashtable();
string[] stest = str.Split(',');
for (int i = 0; i < stest.Length; i++)
{
hash.Add(i, stest[i]);
}
GetConvertHash();
value = 0;
}
public int value { get; set; }
public MyComboItemConvert(string str,int s)
{
hash = new Hashtable();
string[] stest = str.Split(',');
for (int i = 0; i < stest.Length; i++)
{
hash.Add(i, stest[i]);
}
GetConvertHash();
value = s;
}
}
在這里你可以看到,MyComboItemConvert有兩個(gè)重載,分別有不同的參數(shù),其中string類型的那個(gè)參數(shù)就是用于獲取下拉菜單的項(xiàng)的。當(dāng)然你也可以根據(jù)需要定義為其他類型的,例如List或是HashTable。只要在函數(shù)中將下拉菜單的每一項(xiàng)放入哈希表中就可以了。
而在生成的代碼中,我們就需要用到剛剛沒(méi)有使用的Converter屬性了:
舉個(gè)例子:
XProps xps = new XProps();
XProp xprop = new XProp();
xprop.Name = "姓名";
xprop.Value = "某 人;
xprop.Category = "人類";
xprop.Description = "姓甚名誰(shuí)";
xprop.ProType = typeof(String);
xprop.ReadOnly = true;
xps.Add(xprop);
xprop = new XProp();
xprop.Category = "人類";
xprop.Name = "年齡";
xprop.ProType = typeof(int);
xprop.Value = "2";
xprop.Description = "多大年紀(jì)";
xprop.ReadOnly = false;
xps.Add(xprop);
xprop = new XProp();
xprop.Category = "人類";
xprop.Name = "性別";
xprop.Value = 1;
xprop.ReadOnly = false;
xprop.ProType = typeof(CustomClass.MyComboItemConvert);
xprop.Converter = new CustomClass.MyComboItemConvert("M,F");
xprop.Description = "性別是男是女";
xps.Add(xprop);
xprop = new XProp();
xprop.Category = "人類";
xprop.ReadOnly = false;
xprop.Name = "國(guó)籍";
xprop.Value = 1;
xprop.ProType = typeof(CustomClass.MyComboItemConvert);
xprop.Converter = newCustomClass.MyComboItemConvert("中,英,美,法");
xprop.Description = "國(guó)籍";
xps.Add(xprop);
PropertyGrideTest.SelectedObject = xps;
來(lái)看一下效果:
到這里,我需要的功能都能實(shí)現(xiàn)啦。
在這個(gè)過(guò)程中,我也在網(wǎng)上搜尋了很久,要感謝以下幾篇博文,及其博主:
http://blog.csdn.net/luyifeiniu/article/details/5426960#創(chuàng)建PropertyGrid 控件
http://blog.csdn.net/akron/article/details/2750566
http://www.cnblogs.com/greatverve/archive/2012/02/09/propertygird-bind.html
愛(ài)華網(wǎng)本文地址 » http://www.klfzs.com/a/25101016/296464.html
愛(ài)華網(wǎng)



