WinForms 数据绑定类型转换的两种方式
2024-11-02 17:33:25

在数据绑定中,有种常见的需求就是将枚举类型绑定到下拉框列表,比如这样:

1
2
3
4
5
6
7
8
public enum MyOptions
{
Option1,
Option2,
Option3
}

comboBox1.DataBindings.Add(nameof(comboBox1.SelectedIndex), this, nameof(Option));

众所周知 ComboBox.SelectedIndex 属性是一个 int 类型,当绑定的控件读取属性时会将 MyOptions 强转为 int 类型,所以从数据源到控件是没有问题的。
但是当控件值变化时却无法写回数据源,会得到一个运行时错误:

1
引发的异常:“System.InvalidCastException”(位于 mscorlib.dll 中)

这个异常在内部消化了,并不会引起应用崩溃,但是这会导致绑定只有单向的效果。

用 Binding.Parse 事件转换

Binding.Parse 事件在绑定控件的值更改时发生。

1
2
3
4
5
6
7
8
9
public static void ParseEnum<T>(object sender, ConvertEventArgs e) where T: Enum
{
if (e.DesiredType != typeof(T)) return;
e.Value = (T)e.Value;
}

var bind = new Binding(nameof(comboBox1.SelectedIndex), this, nameof(Option), false, DataSourceUpdateMode.OnPropertyChanged);
bind.Parse += ParseEnum<MyOptions>;
comboBox1.DataBindings.Add(bind);

它可以工作,但是不够完美。因为在绑定 Parse 事件时需要提供具体的目标类型,没有通用性。

用 TypeConverter 转换

在 WPF 中,数据绑定有 IValueConverter 接口可以实现转换功能。而在 .NET Framework 中有个类似的类 TypeConverter
使用很简单,主要有4个方法:

  1. CanConvertTo
  2. ConvertTo
  3. CanConvertFrom
  4. ConvertFrom

前两个方法当从数据源到控件时会被调用,而后两个从控件到数据源时会被调用,所以我们只需要重写后两个即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
internal sealed class EnumValueConverter : EnumConverter
{
public EnumValueConverter(Type type) : base(type) { }

public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
// sourceType 就是绑定的控件属性的类型,这里是 int
return sourceType == typeof(int) || base.CanConvertFrom(context, sourceType);
}

public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
return Enum.Parse(EnumType, value.ToString());
}
}

接着需要在枚举类型上用 TypeConverterAttribute 标注。

1
2
3
4
5
6
7
[TypeConverter(typeof(EnumValueConverter))]
public enum MyOptions
{
Option1,
Option2,
Option3
}

接着需要在绑定时将第4个参数formattingEnabled属性设置为true,表示启用数据格式化。

1
2
var bind = new Binding(nameof(comboBox1.SelectedIndex), this, nameof(Option), true, DataSourceUpdateMode.OnPropertyChanged);
comboBox1.DataBindings.Add(bind);

参考

How to convert enum to a bool for DataBinding in Winforms?
Winforms data binding: Can a TypeConverter be used instead of the Format/Parse events?