在本系列文章中,讨论了如何创建一个简单的类型转换器,用于将对象转换为字符串以及从字符串转换回对象。本文将扩展上一个示例,包括更简洁的设计时代码生成、可扩展的属性支持,以及最终的自定义值列表。本文中的示例假设正在从第一部分的原始示例项目中工作。
当将控件或组件放置到设计时表面(如窗体)上时,IDE将自动生成初始化对象所需的任何代码。修改SampleClass类以继承Component,然后在窗体上放置一个实例并设置第一个属性。保存窗体,然后打开设计器文件。应该看到类似于下面的代码:
private void InitializeComponent()
{
CustomTypeConverter2.Length length1 = new CustomTypeConverter2.Length();
//... SNIP ...
length1.Unit = CustomTypeConverter2.Unit.px;
length1.Value = 32F;
this.sample.Length1 = length1;
this.sample.Length2 = null;
this.sample.Length3 = null;
//... SNIP ...
}
设计器生成了源代码,通过指定每个属性来填充对象。但是,如果想要同时设置两个属性,或者执行一些其他初始化代码,可以使用类型转换器来解决这个问题。
在调整类型转换器以支持代码生成之前,需要通过添加一个新的构造函数来扩展Length类。
public Length() { }
public Length(float value, Unit unit) : this()
{
this.Value = value;
this.Unit = unit;
}
添加了一个构造函数,它将设置类的Value和Unit属性。由于添加了带参数的构造函数,现在需要显式定义一个无参数的构造函数,因为不再生成隐式构造函数,仍然想要能够执行new Length()。
需要更新类型转换器,以声明它支持InstanceDescriptor类,这是IDE将用于自定义代码生成的机制。可以通过覆盖一个新方法CanConvertTo来实现。
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
return destinationType == typeof(InstanceDescriptor) || base.CanConvertTo(context, destinationType);
}
这些新的重载将通知调用者现在支持InstanceDescriptor类型,除了基本TypeConverter可以处理的类型之外。
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
Length length;
object result = null;
length = value as Length;
if (length != null)
{
if (destinationType == typeof(string))
result = length.ToString();
else if (destinationType == typeof(InstanceDescriptor))
{
ConstructorInfo constructorInfo;
constructorInfo = typeof(Length).GetConstructor(new[] { typeof(float), typeof(Unit) });
result = new InstanceDescriptor(constructorInfo, new object[] { length.Value, length.Unit });
}
}
return result ?? base.ConvertTo(context, culture, value, destinationType);
}
仍然进行null检查以确保有一个有效的值要转换,但现在检查类型是否是string或InstanceDescriptor,并相应地处理。
在撰写本文时,Visual Studio经常拒绝生成设计时代码。认为这可能是由于Visual Studio缓存了包含TypeConverter的程序集,或者这是无法在不销毁应用程序域的情况下卸载托管程序集的另一种表现。不管是什么原因,发现这很快就成为了一个需要频繁重启IDE以获取更改代码的来源。
返回到ExpandableObjectConverter和属性扩展,通过覆盖GetPropertiesSupported和GetProperties方法,可以轻松地将其添加到自定义转换器中。
public override bool GetPropertiesSupported(ITypeDescriptorContext context)
{
return true;
}
public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
{
return TypeDescriptor.GetProperties(value, attributes);
}
首先,通过覆盖GetPropertiesSupported,告诉调用者支持单个属性编辑。然后可以覆盖GetProperties以返回要显示的实际属性。
想展示的最后一个示例是自定义值。尽管可能认为需要创建一个自定义的UITypeEditor,如果只需要一个基本的下拉列表,可以直接从类型转换器中通过覆盖GetStandardValuesSupported和GetStandardValues来实现。
public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
{
return true;
}
public override TypeConverter.StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
{
List values = new List();
values.Add(new Length(16, Unit.px));
values.Add(new Length(32, Unit.px));
values.Add(new Length(64, Unit.px));
values.Add(new Length(128, Unit.px));
return new StandardValuesCollection(values);
}