在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, CultureInfo culture, 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, culture, 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, object value, 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; } }
自定義了一個屬性類,並應用通用類型轉換器的結果
