1. 程式人生 > >在WPF中一種較好的繫結Enums資料方法

在WPF中一種較好的繫結Enums資料方法

## 引言 在你使用wpf應用程式開發的時候,是否需要進行資料繫結到`Enum`資料呢?在這篇文章中,我將向你展示在WPF中處理`Enum`資料繫結的方法。 假設存在一個這樣的`Enum`資料的定義,具體內容如下文程式碼中所示: ``` namespace LocalizeFrameworkWpfApp { public enum Status { Horrible, Bad, SoSo, Good, Better, Best } } ``` ## 一、WPF中的通常處理方法 #### 1.1 新增引用 在`MainWindow.xaml`檔案中從`mscorlib`中引入名稱空間`System`。 ``` xmlns:sys="clr-namespace:System;assembly=mscorlib" ``` #### 1.2 建立一個`ObjectDataProvider`資源 在此步驟中,你需要建立一個`ObjectDataProvider`的資源,並給它一個鍵名`x:Key="DataFromEnum"`,這樣就可以使用`DataFromEnum`在程式碼中使用它。並且你需要給`MethodName`設定為`Enum`型別上存在的`GetValues`,然後將`ObjectType`設定為`Enum`型別。接下來,你將需設定`ObjectDataProvider.MethodParameters`的`Enum`型別。最後,你新增的`ObjectDataProvider`資源如下面程式碼所示 ``` ``` #### 1.3 `Binding`資料處理 現在,你可以使用資料綁定了。例如,想將資料繫結到`ComboBox`上面,那麼你需要設定`ItemSource`為一個新的繫結,並將資料來源繫結到我們上面定義的名為`DataFromEnum`的資源。 ``` ``` 到現在為止,所有的已經處理完成,執行程式可以看到資料已經正確繫結到`ComboBox`上面。 ![](https://img2020.cnblogs.com/blog/1746998/202010/1746998-20201026135245623-1854864113.png) ## 二、較好的處理方法 讓我們來看看當資料繫結`Enum`型別時,如何使用WPF特性來改進程式碼的使用和可讀性。首先,想封裝`Enum`型別的繫結而不需要`ObjectDataProvider`資源的邏輯處理,還希望不需要必須定義資源才能在xaml中使用繫結功能。理想情況下,應該像處理普通物件的繫結一樣,將所有內容都內聯處理。為此,需要利用定製`MarkupExtension`的幫助類。這個擴充套件將簡單的接受`Enum`型別,然後為控制元件建立一個可繫結`Enum`值的列表,這種實現其實很簡單。 #### 2.1 `MarkupExtension`幫助類 `MarkupExtension`幫助類定義如下: ``` namespace LocalizeFrameworkWpfApp { public class EnumBindingSourceExtension:MarkupExtension { private Type _enumType; public Type EnumType { get { return _enumType; } set { if (value != _enumType) { if (null != value) { var enumType = Nullable.GetUnderlyingType(value) ?? value; if (!enumType.IsEnum) { throw new ArgumentException("Type must bu for an Enum"); } } _enumType = value; } } } public EnumBindingSourceExtension() { } public EnumBindingSourceExtension(Type enumType) { EnumType = enumType; } public override object ProvideValue(IServiceProvider serviceProvider) { if (null == _enumType) { throw new InvalidOperationException("The EnumTYpe must be specified."); } var actualEnumType = Nullable.GetUnderlyingType(_enumType) ?? _enumType; var enumValues = Enum.GetValues(actualEnumType); if (actualEnumType == _enumType) { return enumValues; } var tempArray = Array.CreateInstance(actualEnumType, enumValues.Length + 1); enumValues.CopyTo(tempArray, 1); return tempArray; } } } ``` #### 2.2 `Binding`資料處理 ``` ``` 看一下執行結果: ![](https://img2020.cnblogs.com/blog/1746998/202010/1746998-20201026141353684-1505268413.gif) ## 三、擴充套件:新增`Enum`型別的描述(Description)支援 現在我們可以不用使用`ObjectDataProvider`資源進行`Enum`型別的繫結工作了。這兩種方法進行對比一下,詳細這個新方法會讓你耳目一新,像發現了新大陸一般。 而`Enum`型別的值一般使用在程式中,而為了讓使用者獲得更好的使用體驗,一般都會在列舉值前面新增上屬性:Description描述。為了完成此工作,我們只需使用`TypeConverter`進行轉換。 ``` namespace LocalizeFrameworkWpfApp { public class EnumDescriptionTypeConverter:EnumConverter { public EnumDescriptionTypeConverter(Type type) : base(type) { } public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { if (destinationType == typeof(string)) { if (null != value) { FieldInfo fi = value.GetType().GetField(value.ToString()); if (null != fi) { var attributes = (DescriptionAttribute[]) fi.GetCustomAttributes(typeof(DescriptionAttribute), false); return ((attributes.Length > 0) && (!string.IsNullOrEmpty(attributes[0].Description))) ? attributes[0].Description : value.ToString(); } } return string.Empty; } return base.ConvertTo(context, culture, value, destinationType); } } } ``` 然後對定義的列舉值新增上`[Description]`屬性 ``` namespace LocalizeFrameworkWpfApp { [TypeConverter(typeof(EnumDescriptionTypeConverter))] public enum Status { [Description("This is horrible")] Horrible, [Description("This is Bad")] Bad, [Description("This is SoSo")] SoSo, [Description("This is Good")] Good, [Description("This is Better")] Better, [Description("This is Best")] Best } } ``` 程式執行結果: ![](https://img2020.cnblogs.com/blog/1746998/202010/1746998-20201026142938731-1426186465.gif) 可以看到,我們添加了`[Description]`屬性時,這兩種方法都可以將`[Description]`屬性的值繫結到指定控制元件中。 如果你覺得不錯,掃描下面公眾號給個關注,在此感謝!! ![](https://img2018.cnblogs.com/common/1746998/202001/1746998-20200109105448247-15728021