1. 程式人生 > WINDOWS開發 >C#自定義屬性轉換類---型別轉換器

C#自定義屬性轉換類---型別轉換器

  在VS視覺化工具欄中,我們設計winform窗體時可以通過屬性視窗對窗體或者控制元件進行相關的屬性操作設定,並立即重新整理顯示。如

技術分享圖片

  在改變Font屬性後,控制元件呈現的字型將發生改變,並且可以直接修改Font左邊的值:“宋體,9pt”改為“微乳雅黑,15pt”(中間只能是英文逗號隔開)。那麼屬性編輯器是如何實現的呢?

  微軟在框架中添加了一個設計時框架,實現了一個屬性轉換類。

1、實現圓角矩形轉換類的控制

  此例項將實現圓角矩形屬性在屬性編輯視窗中編輯後的相關變化實現:

2、通用型別轉換器

  該型別轉換器與圓角矩形屬性轉換器具有相似或更強大的功能,實現了不同屬性之間的通用,避免針對不同屬性開發不同的型別轉換器。但某些也需要特定定製,如前面的Font,展開後有很多屬性,但顯示在左邊的值只有部分設定後的顯示。

  實現通用型別轉換器,主要使用了泛型與反射的技術:

技術分享圖片
/// <summary>     
/// 實現型別屬性分別編輯的通用型別轉換器
/// </summary>     
/// <typeparam name="T">泛型</typeparam>  
class GeneralTypeConverter<T> : TypeConverter where T : new()
{
    /// <summary>
    /// 返回此轉換器是否可以將一種型別的物件轉換為此轉換器的型別。
    /// </summary>
/// <param name="context">提供格式上下文的ITypeDescriptorContext。</param> /// <param name="sourceType">表示你想從轉換的型別</param> /// <returns>返回為True則可以轉換,為false則不能轉換</returns> public override bool CanConvertFrom(ITypeDescriptorContext context,Type sourceType) {
if (sourceType == typeof(string)) { return true; } return base.CanConvertFrom(context,sourceType); } /// <summary> /// 使用指定的上下文和區域性資訊將給定物件轉換為此轉換器的型別 /// </summary> /// <param name="context">一個 ITypeDescriptorContext,用於提供格式上下文</param> /// <param name="culture">CultureInfo 要用作當前區域性</param> /// <param name="value">要轉換的 Object</param> /// <returns>轉換後的值</returns> public override object ConvertFrom(ITypeDescriptorContext context,CultureInfo culture,object value) { string strValue = (value as string).Trim(); if (strValue == null) { return base.ConvertFrom(context,culture,value); } strValue = strValue.Trim(); if (strValue.Length == 0) { return null; } if (culture == null) { culture = CultureInfo.CurrentCulture; } char separator = ;;//採用英文分號隔開,與下面的ConvertTo對應 Type type = typeof(T); //1、去掉“ClassName { ”和“ }”兩部分 ,與下面的ConvertTo對應 string withStart = "{ "; string withEnd = " }"; if (strValue.StartsWith(withStart) && strValue.EndsWith(withEnd)) { strValue = strValue.Substring(withStart.Length,strValue.Length - withStart.Length - withEnd.Length); } //2、分割屬性值 string[] strArray = strValue.Split(new char[] { separator }); //3、做成屬性集合表 Hashtable properties = new Hashtable(); for (int i = 0; i < strArray.Length; i++) { if (strArray[i].Trim().IndexOf(=) != -1) { string[] str = strArray[i].Trim().Split(new char[] { = }); string propName = str[0]; PropertyInfo pi = type.GetProperty(str[0]); if (pi != null) { //該屬性對應型別的型別轉換器 TypeConverter converter = TypeDescriptor.GetConverter(pi.PropertyType); properties.Add(propName,converter.ConvertFromString(str[1])); } } } return this.CreateInstance(context,properties); } /// <summary> /// 返回此轉換器是否可將該物件轉換為指定的型別。 /// </summary> /// <param name="context">提供格式上下文的ITypeDescriptorContext。</param> /// <param name="sourceType">表示你想從轉換的型別</param> /// <returns>返回為True則可以轉換,為false則不能轉換</returns> public override bool CanConvertTo(ITypeDescriptorContext context,Type destinationType) { if (destinationType == typeof(InstanceDescriptor)) { return true; } return base.CanConvertTo(context,destinationType); } /// <summary> /// 使用指定的上下文和區域性資訊將給定的值物件轉換為指定的型別 /// </summary> /// <param name="context">一個提供格式上下文的 System.ComponentModel.ITypeDescriptorContext。</param> /// <param name="culture">System.Globalization.CultureInfo。如果傳遞 null,則採用當前區域性</param> /// <param name="value"> 要轉換的 System.Object。</param> /// <param name="destinationType">value 引數要轉換到的 System.Type。</param> /// <returns>表示轉換的 value 的 System.Object。</returns> public override object ConvertTo(ITypeDescriptorContext context,object value,Type destinationType) { if (destinationType == null) { throw new ArgumentNullException("DestinationType"); } //如果需要返回詳細資訊則 if (value is T) { if (destinationType == typeof(string)) { if (culture == null) { culture = CultureInfo.CurrentCulture; } string separator = "; ";//採用英文分號隔開,與ConvertFrom對應 StringBuilder sb = new StringBuilder(); Type type = value.GetType(); //組合為屬性視窗顯示的值 sb.Append("{ "); PropertyInfo[] proInfo = type.GetProperties(BindingFlags.Instance | BindingFlags.Public); for (int i = 0; i < proInfo.Length; i++) { if (!proInfo[i].CanRead) continue; //只顯示可見的 object[] attributes = proInfo[i].GetCustomAttributes(typeof(BrowsableAttribute),false); bool isBrowsable = true; foreach(object obj in attributes) { isBrowsable = (obj as BrowsableAttribute).Browsable; } if (isBrowsable == false) { continue; } Type typeProp = proInfo[i].PropertyType; string nameProp = proInfo[i].Name; object valueProp = proInfo[i].GetValue(value,null); TypeConverter converter = TypeDescriptor.GetConverter(typeProp); sb.AppendFormat("{0}={1}" + separator,nameProp,converter.ConvertToString(context,valueProp)); } string strContent = sb.ToString(); if (strContent.EndsWith(separator)) { strContent = strContent.Substring(0,strContent.Length - separator.Length); } strContent += " }"; return strContent.Trim(); } if (destinationType == typeof(InstanceDescriptor)) { ConstructorInfo constructor = typeof(T).GetConstructor(new Type[0]); if (constructor != null) { return new InstanceDescriptor(constructor,new object[0],false); } } } return base.ConvertTo(context,value,destinationType); } /// <summary> /// 在已知物件的屬性值集的情況下,使用指定的上下文建立與此關聯的型別的例項。 /// </summary> /// <param name="context">一個提供格式上下文的ITypeDescriptorContext</param> /// <param name="propertyValues">新屬性值的IDictionary。</param> /// <returns>一個 System.Object,表示給定的IDictionary,或者,如果無法建立該物件,則為 null。此方法始終返回</returns> public override object CreateInstance(ITypeDescriptorContext context,IDictionary propertyValues) { if (propertyValues == null) { throw new ArgumentNullException("propertyValues"); } Type type = typeof(T); ConstructorInfo ctrInfo = type.GetConstructor(new Type[0]); if (ctrInfo == null) { return null; } //呼叫預設的建構函式構造例項 object obj = ctrInfo.Invoke(new object[0]); //設定屬性值 PropertyInfo[] proInfo = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); object propValue = null; for (int i = 0; i < proInfo.Length; i++) { //判斷是否具有Set方法 if (!proInfo[i].CanWrite) { continue; } propValue = propertyValues[proInfo[i].Name]; if (propValue != null) { proInfo[i].SetValue(obj,propValue,null); } } return obj; } /// <summary> /// 返回更改此物件的值是否要求呼叫CreateInstance(System.Collections.IDictionary)方法來建立新值。 /// </summary> /// <param name="context">一個提供格式上下文的 System.ComponentModel.ITypeDescriptorContext</param> /// <returns>如果更改此物件的屬性需要呼叫CreateInstance(System.Collections.IDictionary)來建立新值,則為 true;否則為 false。</returns> public override bool GetCreateInstanceSupported(ITypeDescriptorContext context) { return true; } /// <summary> /// 使用指定的上下文和特性返回由 value 引數指定的陣列型別的屬性的集合 /// </summary> /// <param name="context">一個提供格式上下文的ITypeDescriptorContext</param> /// <param name="value">一個 System.Object,指定要為其獲取屬性的陣列型別</param> /// <param name="attributes">用作篩選器的 System.Attribute 型別陣列。</param> /// <returns>具有為此資料型別公開的屬性的 PropertyDescriptorCollection;或者,如果沒有屬性,則為null。</returns> public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context,Attribute[] attributes) { Type type = value.GetType(); PropertyInfo[] proInfo = type.GetProperties(BindingFlags.Instance | BindingFlags.Public); string[] names = new string[proInfo.Length]; for (int i = 0; i < names.Length; i++) { names[i] = proInfo[i].Name; } Array.Sort<string>(names); return TypeDescriptor.GetProperties(typeof(T),attributes).Sort(names); } /// <summary> /// 使用指定的上下文返回該物件是否支援屬性。 /// </summary> /// <param name="context">一個提供格式上下文的 System.ComponentModel.ITypeDescriptorContext</param> /// <returns>如果應呼叫 System.ComponentModel.TypeConverter.GetProperties(System.Object) 來查詢此物件的屬性,則為true;否則為 false。</returns> public override bool GetPropertiesSupported(ITypeDescriptorContext context) { return true; } }
View Code

  自定義了一個屬性類,並應用通用型別轉換器的結果