TypeConverters(类型转换器)和 XAML
本主题介绍作为 XAML 通用语言功能的字符串类型转换目的。 在 .NET Framework 中,TypeConverter 类作为托管自定义类实现的一部分,具有特定的用途,可以用作 XAML 属性使用中的属性值。 如果编写自定义类,并且希望类的实例可作为 XAML 可设置属性值使用,则可能需要将 TypeConverterAttribute 应用到类、编写自定义 TypeConverter 类或两者。
类型转换概念
XAML 和字符串值
在 XAML 文件中设置属性值时,该值的初始类型是纯文本中的字符串。 即使是其他基元(如 Double)最初也是 XAML 处理器的文本字符串。
XAML 处理器需要两段信息才能处理属性值。 第一条信息是正在设置的属性的值类型。 定义属性值且在 XAML 中处理的任何字符串最终都必须转换为该类型的值或解析。 如果该值是 XAML 分析器(如数值)理解的基元,则尝试直接转换字符串。 如果值为枚举,则字符串用于检查名称是否与该枚举中的命名常量匹配。 如果该值既不是分析器基元,也不是枚举,则所处理的类型必须能够基于转换的字符串提供类型实例或值。 这是通过指示类型转换器类来完成的。 类型转换器实际上是一个帮助程序类,用于为 XAML 方案提供另一个类的值,也可能用于 .NET 代码中的代码调用。
在 XAML 中使用现有类型转换行为
根据你对基础 XAML 概念的熟悉,你可能已经在基本应用程序 XAML 中使用类型转换行为,而无需实现它。 例如,WPF 定义了数百个值为 Point类型的属性。 Point 是描述二维坐标空间中的坐标的值,它实际上只有两个重要属性:X 和 Y。在 XAML 中指定点时,请在 X 和提供的 Y 值之间将其指定为带分隔符(通常是逗号)的字符串。 例如:<LinearGradientBrush StartPoint="0,0" EndPoint="1,1"/>
。
即使是这种简单类型的 Point 及其在 XAML 中的简单用法也涉及类型转换器。 在这个例子中,指的是类 PointConverter。
在类级别定义的 Point 类型转换器简化了所有使用 Point的属性的标记语言用法。 如果此处没有类型转换器,你将需要以下冗长得多的标记来实现之前示例中的相同效果:
<LinearGradientBrush>
<LinearGradientBrush.StartPoint>
<Point X="0" Y="0"/>
</LinearGradientBrush.StartPoint>
<LinearGradientBrush.EndPoint>
<Point X="1" Y="1"/>
</LinearGradientBrush.EndPoint>
</LinearGradientBrush>
是否使用类型转换字符串或更详细的等效语法通常是一个编码风格的选择。 XAML 工具工作流还可能会影响值设置方式。 一些 XAML 工具往往会输出最详细形式的标记,因为这样更容易转换回设计器视图或其自身的序列化机制。
通过检查类(或属性)上是否应用了 TypeConverterAttribute,可以在 WPF 和 .NET Framework 类型中通常发现现有的类型转换器。 此属性将指定一个类,该类是此类型值的支持类型转换器,用于 XAML 目的以及可能的其他用途。
类型转换器和标记扩展
标记扩展和类型转换器在 XAML 处理器的行为以及它们所应用的场景中扮演互不干扰的独立角色。 尽管在标记扩展用法中可以使用上下文,但通常不会在标记扩展的实现中检查标记扩展所提供的属性值的类型转换行为。 换句话说,即使标记扩展将文本字符串作为其 ProvideValue
输出返回,该字符串上应用于特定属性或属性值类型的类型转换行为也不会被调用,通常,标记扩展的目的是处理字符串并返回对象而不涉及任何类型转换器。
需要标记扩展而不是类型转换器的一种常见情况是引用已存在的对象。 充其量,无状态类型转换器只能生成一个新实例,这可能不理想。 有关标记扩展的详细信息,请参阅 标记扩展和 WPF XAML。
本机类型转换器
在 XAML 解析器的 WPF 和 .NET Framework 实现中,某些类型拥有原生类型转换处理,但并不是通常被视为原始类型的类型。 此类类型的一个示例是 DateTime。 原因基于 .NET Framework 体系结构的工作原理:类型 DateTime 是在 .NET 中最基本的库 mscorlib 中定义的。 禁止将DateTime与来自引入依赖项的其他程序集(如TypeConverterAttribute来自System)的属性相关联,因此无法支持通常的类型转换器发现机制。 相反,XAML 分析器包含需要此类本机处理的类型列表,并处理这些类型与处理真实基元的方式类似。 (对于 DateTime,这涉及到调用 Parse。)
实现类型转换器
类型转换器
在前面给出的 Point 示例中,提到了类 PointConverter。 对于 XAML 的 .NET 实现,用于 XAML 用途的所有类型转换器都是派生自基类 TypeConverter的类。 TypeConverter 类存在于存在 XAML 之前的 .NET Framework 版本中;其原始用法之一是为视觉设计器中的属性对话提供字符串转换。 对于 XAML,TypeConverter 的角色被扩展为基类,支持字符串到字符串的转换,这些转换能够解析字符串属性值,并可能将特定对象属性的运行时值转换回字符串,以便序列化为属性。
TypeConverter 定义了四个与字符串转换有关的成员,用于 XAML 处理目的。
其中,最重要的方法是 ConvertFrom。 此方法将输入字符串转换为所需的对象类型。 严格来说,可以实现 ConvertFrom 方法,将各种类型转换为转换器的目标类型,从而达到超越 XAML 的目的,例如支持运行时转换。但就 XAML 而言,关键在于能够处理 String 输入的代码路径。
下一个最重要的方法是 ConvertTo。 如果应用程序转换为标记表示形式(例如,如果应用程序作为文件保存到 XAML),ConvertTo 负责生成标记表示形式。 在这种情况下,与 XAML 相关的重要代码路径是当你传递 destinationType
到 String 时。
CanConvertTo 和 CanConvertFrom 是服务查询 TypeConverter 实现的功能时使用的支持方法。 必须实现这些方法才能为转换器支持的等效转换方法的类型特定情况返回 true
。 出于 XAML 目的,这通常意味着 String 类型。
XAML 中的文化信息和类型转换器
每个 TypeConverter 实现都可以自行解释哪些内容构成用于转换的有效字符串,并且还可以选择使用或忽略作为参数传递的类型描述。 关于文化和XAML类型转换,需要考虑一个重要因素。 XAML 完全支持将可本地化字符串用作属性值。 但是,不支持将可本地化的字符串用作具有特定文化要求的类型转换器输入,因为 XAML 属性值的类型转换器必须使用固定语言的解析行为,具体为 en-US
文化。 有关此限制的设计原因的详细信息,应查阅 XAML 语言规范([MS-XAML]。
例如,在有些文化中,文化可能会成为一个问题,因为某些文化使用逗号作为数字的小数点分隔符。 这将与许多 WPF XAML 类型转换器的行为相冲突,即使用逗号作为分隔符(基于常见的 X、Y 窗体或逗号分隔列表等历史先例)。 即使将周围 XAML 中的文化(将变量 Language
或 xml:lang
设置为使用逗号作为小数点的 sl-SI
文化的示例)也无法解决此问题。
实现 ConvertFrom
要作为支持 XAML 的 TypeConverter 实现使用,该转换器的 ConvertFrom 方法必须接受字符串作为 value
参数。 如果字符串格式正确,并且可以由 TypeConverter 实现进行转换,则返回的对象必须支持转换为属性预期的类型。 否则,ConvertFrom 实现必须返回 null
。
每个 TypeConverter 实现都可以对什么构成转换的有效字符串进行自己的解释,还可以使用或忽略作为参数传递的类型描述或文化环境。 但是,WPF XAML 处理可能不会在所有情况下将值传递给类型描述上下文,也可能不会基于 xml:lang
传递区域性。
注意
不要将大括号字符(尤其是 {)用作字符串格式的可能元素。 这些字符保留为标记扩展序列的入口和退出。
实现 ConvertTo函数
ConvertTo 可能用于序列化支持。 通过 ConvertTo 为您的自定义类型及其类型转换器提供的序列化支持并不是绝对必要的。 但是,如果要实现控件,或使用序列化作为类的功能或设计的一部分,则应实现 ConvertTo。
若要用作支持 XAML 的 TypeConverter 实现,该转换器的 ConvertTo 方法必须接受作为 value
参数支持的类型的实例(或值)。 当 destinationType
参数是类型 String时,返回的对象必须能够转换为 String。 返回的字符串必须表示 value
的序列化值。 理想情况下,您选择的序列化格式应确保在将该字符串传递到相同转换器的 ConvertFrom 实现时,能够生成相同的值且不会显著丢失信息。
如果无法序列化该值,或者转换器不支持序列化,ConvertTo 实现必须返回 null
,并且在这种情况下允许引发异常。 但是,如果你确实引发异常,应该报告无法使用该转换功能,并将其作为 CanConvertTo 实现的一部分,这样可以支持先通过 CanConvertTo 检查以避免异常的最佳实践。
如果 destinationType
参数的类型不是 String,则可以选择自己的转换器处理。 通常,你会恢复到基本实现的处理,在最基础的 ConvertTo 中引发特定异常。
实现 CanConvertTo
CanConvertTo 实现应为类型为 String的 destinationType
返回 true
,否则应委托给基类实现。
实现 CanConvertFrom
CanConvertFrom 实现应当针对类型为 String的 sourceType
返回 true
,否则应使用基本实现。
应用 TypeConverterAttribute
为了使自定义类型转换器用作 XAML 处理器中自定义类的实际类型转换器,必须将 TypeConverterAttribute 应用于类定义。 通过属性指定的 ConverterTypeName 必须是自定义类型转换器的类型名称。 应用此属性后,当 XAML 处理器处理属性类型使用自定义类类型的值时,它可以输入字符串并返回对象实例。
还可以按属性提供类型转换器。 不是将 TypeConverterAttribute 应用于类定义,而是将其应用到属性定义上(即主定义,而非其中的 get
/set
实现)。 属性的类型必须与自定义类型转换器处理的类型匹配。 应用此属性后,当 XAML 处理器处理该属性的值时,它可以处理输入字符串并返回对象实例。 当选择使用 Microsoft .NET Framework 或其他某个库中的属性类型,并且无法控制类定义或应用 TypeConverterAttribute 时,每个属性类型转换器技术会特别有用。