C# 屬性控件2


PropertyGrid,.net框架下的一個控件,這是一個軟件升級的項目,原來的軟件用的是C++,控件用的還是第三方,這次升級到visual studio .net4.0版本,原以為.net的東西用起來不會費勁的,沒想到想要實現項目需要的效果還真沒那么簡單。

 

由於需要,我這里主要是為了能動態的生成屬性頁,還要帶能動態生成下來菜單,所以今天主要從這方面總結。

 

首先定義一個屬性類:

//單條屬性類

public class XProp { private string theId = ""; //屬性Id,我的項目中需要,大家可以忽略

private string theCategory = ""; //屬性所屬類別

private string theName = ""; //屬性名稱

private bool theReadOnly = false; //屬性的只讀性,true為只讀

private string theDescription = ""; //屬性的描述內容

private object theValue = null; //值

private System.Type theType = null; //類型

private bool theBrowsable = true; //顯示或隱藏,true為顯示

TypeConverter theConverter = null; //類型轉換

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; } } }

 

我舉一個例子: private string strdemo;

[DescriptionAttribute("用於舉例說明"), CategoryAttribute("公有屬性"), DefaultValueAttribute(“測試屬性”), ReadOnlyAttribute(false), BrowsableAttribute(true), TypeConverter(typeof(MyComboTypeConvert)) ] public string strDemo { get { return strdemo; } set { strdemo = value; } }

 

這是個寫死的屬性,那在我的項目中,根據對象的不同,會需要生產不同的屬性頁,所以需要一個可以動態生成的屬性頁,將上述這個一般屬性定義,利用XProp類,寫成: Private XProp newXpro = new XProp(); newXpro.Category = ”公有屬性”; newXpro.Name = ” strDemo”; newXpro.Id = "A"; newXpro.Description = “用於舉例說明”; newXpro.ReadOnly =false; newXpro.Value = “測試屬性”; newXpro.ProType = typeof(string); newXpro.Browsable = true; newXpro.Converter = null;

這樣,一條屬性就完成了。當然你也可以根據需要自己重寫更多的屬性相關定義。這里的Converter屬性是在后面的下拉菜單中需要用到的,如果不是基礎類型的(string,int,bool,enum等),我們可以賦值為null.

 

當然,這只是一條屬性,原本按之前的方法,只要定義一個類,然后這個類里面定義多條屬性就可以了。但是現在,由於屬性是動態生成的,我們並不能確定需要幾個屬性,也就不能直接在一個類里面定義完。

 

所以我們需要再定義一個List<XProp>類,作為一一張list可以隨意添加多個項,然后將PropertyGrid.SelectedObject設置為這個類就好了:

先來定義以下兩個類:

public class XProps : List<XProp>, ICustomTypeDescriptor

{

#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(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 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);

}

 

public PropertyDescriptorCollection GetProperties()

{

return TypeDescriptor.GetProperties(this, true);

}

 

public object GetPropertyOwner(PropertyDescriptor pd)

{

return this;

}

 

#endregion

 

public override 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

{

XProp theProp;

public XPropDescriptor(XProp prop, Attribute[] attrs): base(prop.Name, attrs)

{

theProp = prop;

}

 

public override bool CanResetValue(object component)

{

return false;

}

public override string Category

{

get { return theProp.Category; }

}

 

public override string Description

{

get { return theProp.Description; }

}

 

public override TypeConverter Converter

{

get { return theProp.Converter; }

}

 

public override System.Type ComponentType

{

get { return this.GetType(); }

}

 

public override object GetValue(object component)

{

return theProp.Value;

}

 

public override bool IsReadOnly

{

get { return theProp.ReadOnly; }

}

 

public override System.Type PropertyType

{

get { return theProp.ProType; }

}

 

public override void ResetValue(object component)

{

}

 

public override void SetValue(object component, object value)

{

theProp.Value = value;

}

 

public override bool ShouldSerializeValue(object component)

{

return false;

}

}

 

然后我們新聲明一個屬性列表: Private XProps xprops = new XProps(); 再將剛剛那個動態生成的屬性添加進去,將屬性頁的selectobject賦值為這個類: xprops.add(newXpro); PropertyGridDemo.SelectedObject = xprops; 現在我們來看看效果:

 

根據需要,你可以添加多條屬性。

也許你會想問,這里哪有動態生成啊,那些屬性名稱,屬性類型什么的不還是寫死的么。呵呵,我這里只是舉個例子所以寫死了,在實際應用中,你可以根據需要,在生成屬性列表的時候,動態賦值,例如你是從xml文件里讀取到的,例如你是從服務器中獲取到的。根據你讀取的對象,一個一個賦值就可以了。

 

下面來介紹如何在屬性頁中動態生成下拉菜單框,關於下拉菜單真的是很復雜,在.net的PropertyGrid控件中,想要具有下拉菜單的屬性,簡單的可以通過枚舉類型來實現,還是以剛剛的那個例子來說明: 首先定義個枚舉類型:

public enum enumType { BOOLVAL, DIGITALVAL, STRINGVAL, CHECKVAL, RATIOVAL, IPVAL, COMBOBOX, RESETBTN }

 

然后代碼中將屬性值與屬性類型修改為: newXpro.Value = CustomClass.enumType.BOOLVAL; //這里的屬性值當然必須為枚舉中的某一項 newXpro.ProType = typeof(enumType); 然后我們來看看實現的效果:

 

這就是最簡單的下拉菜單的實現方式了。 但是這里的枚舉類型仍然是需要代碼中寫死的,而我在網上也所搜了很久,枚舉類型似乎沒辦法動態生成,如果要實現動態生成恐怕要另尋他途。 所幸的是,我曾經見過重寫combobox來生成屬性頁的下來菜單的,我們需要定義:

 

//重寫下拉菜單中的項,使之與屬性頁的項關聯 public abstract class ComboBoxItemTypeConvert : TypeConverter { public Hashtable myhash = null; public ComboBoxItemTypeConvert() { myhash = new Hashtable(); GetConvertHash(); } public abstract void GetConvertHash(); //是否支持選擇列表的編輯 public override bool GetStandardValuesSupported(ITypeDescriptorContext context) { return true; } //重寫combobox的選擇列表 public override StandardValuesCollection GetStandardValues(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); } //判斷轉換器是否可以工作 public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { if (sourceType == typeof(string)) { return true; } return base.CanConvertFrom(context, sourceType);

} //重寫轉換器,將選項列表(即下拉菜單)中的值轉換到該類型的值 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);

}

//重寫轉換器將該類型的值轉換到選擇列表中 public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object obj, Type destinationType) {

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 bool GetStandardValuesExclusive(ITypeDescriptorContext context) { return false; } } //重寫下拉菜單,在這里實現定義下拉菜單內的項 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有兩個重載,分別有不同的參數,其中string類型的那個參數就是用於獲取下拉菜單的項的。當然你也可以根據需要定義為其他類型的,例如List或是HashTable。只要在函數中將下拉菜單的每一項放入哈希表中就可以了。 而在生成的代碼中,我們就需要用到剛剛沒有使用的Converter屬性了: 舉個例子:

 

XProps xps = new XProps(); XProp xprop = new XProp(); xprop.Name = "姓名"; xprop.Value = "某人; xprop.Category = "人類"; xprop.Description = "姓甚名誰"; 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 = "多大年紀"; 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 = "國籍"; xprop.Value = 1; xprop.ProType = typeof(CustomClass.MyComboItemConvert); xprop.Converter = new CustomClass.MyComboItemConvert("中,英,美,法"); xprop.Description = "國籍"; xps.Add(xprop);

PropertyGrideTest.SelectedObject = xps;

 

來看一下效果:

 



到這里,我需要的功能都能實現啦。

在這個過程中,我也在網上搜尋了很久,要感謝以下幾篇博文,及其博主:

http://blog.csdn.net/luyifeiniu/article/details/5426960#創建 PropertyGrid 控件

http://blog.csdn.net/akron/article/details/2750566

http://www.cnblogs.com/greatverve/archive/2012/02/09/propertygird-bind.html

 

 

轉載自http://blog.sina.com.cn/s/blog_6f14b7010101b91b.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM