在WPF中一種較好的繫結Enums資料方法
阿新 • • 發佈:2020-10-26
## 引言
在你使用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