类型转换器与XAML

本主题介绍 XAML 语言的一项常规功能,即从字符串进行类型转换的目的。 在 .NET Framework 中,该 TypeConverter 类作为托管自定义类实现的一部分,该类可用作 XAML 属性用法中的属性值的一部分。 如果编写自定义类,并且希望类的实例可作为 XAML 可设置属性值使用,则可能需要将类 TypeConverterAttribute 应用到类、编写自定义 TypeConverter 类或两者。

类型转换概念

XAML 和字符串值

在 XAML 文件中设置属性值时,该值的初始类型是纯文本中的字符串。 即使是其他基元(例如 Double 最初是 XAML 处理器的文本字符串)。

XAML 处理器需要两段信息才能处理属性值。 第一条信息是正在设置的属性的值类型。 定义属性值且在 XAML 中处理的任何字符串最终都必须转换为该类型的值或解析。 如果该值是 XAML 分析器(如数值)理解的基元,则尝试直接转换字符串。 如果值为枚举,则字符串用于检查名称是否与该枚举中的命名常量匹配。 如果该值既不是分析器基元,也不是枚举,则所处理的类型必须能够基于转换的字符串提供类型实例或值。 这是通过指示类型转换器类来完成的。 类型转换器实际上是一个帮助程序类,用于为 XAML 方案提供另一个类的值,也可能用于 .NET 代码中的代码调用。

在 XAML 中使用现有类型转换行为

根据你对基础 XAML 概念的熟悉,你可能已经在基本应用程序 XAML 中使用类型转换行为,而无需实现它。 例如,WPF 定义了数百个采用类型值 Point的属性。 A Point 是一个值,用于描述二维坐标空间中的坐标,它实际上只有两个重要属性: XY。在 XAML 中指定点时,将其指定为分隔符(通常为逗号)的 X 字符串和 Y 提供的值。 例如: <LinearGradientBrush StartPoint="0,0" EndPoint="1,1"/>

即使在 XAML 中,这种简单类型 Point 及其简单用法也涉及类型转换器。 在本例中,即类 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 在 mscorlib 中定义,这是 .NET 中最基本的库。 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 来说,关键的代码路径是传递一个destinationTypeString

CanConvertToCanConvertFrom 是在服务查询 TypeConverter 实现功能时使用的支持方法。 必须实现这些方法才能为转换器支持的等效转换方法的类型特定情况返回 true 。 出于 XAML 目的,这通常意味着类型 String

XAML 的文化信息和类型转换器

每个 TypeConverter 实现都可以自行解释构成转换的有效字符串,还可以使用或忽略作为参数传递的类型说明。 关于文化和 XAML 类型转换,需要考虑一个重要因素。 XAML 完全支持将可本地化字符串用作属性值。 但是不支持将可本地化字符串作为具有特定文化要求的类型转换器输入,因为 XAML 属性值的类型转换器需要使用en-US文化进行固定语言解析。 有关此限制的设计原因的详细信息,应查阅 XAML 语言规范([MS-XAML])。

例如,区域性可能是一个问题,某些区域性使用逗号作为数字的小数点分隔符。 这将与许多 WPF XAML 类型转换器的行为相冲突,即使用逗号作为分隔符(基于常见的 X、Y 窗体或逗号分隔列表等历史先例)。 即使在 XAML 中传递一个文化(例如通过设置 Languagexml:lang 为使用逗号作为小数点的文化 sl-SI),问题依然无法解决。

实现 ConvertFrom

作为支持 XAML 的 TypeConverter 实现,ConvertFrom 转换器的方法必须接受字符串作为 value 参数。 如果字符串的格式有效,并且可以被 TypeConverter 实现转换,则返回的对象必须支持转为属性所期望的类型。 否则,实现 ConvertFrom 必须返回 null

每个 TypeConverter 实现都可以自行解释构成转换的有效字符串,还可以使用或忽略作为参数传递的类型说明或区域性上下文。 但是,WPF XAML 处理可能不会在所有情况下将值传递给类型描述上下文,也可能不会根据 xml:lang 传递文化背景。

注释

不要将大括号字符(尤其是 {)用作字符串格式的可能元素。 这些字符保留为标记扩展序列的入口和退出。

实现 ConvertTo

ConvertTo 可能用于序列化支持。 通过 ConvertTo 为自定义类型及其类型转换器提供序列化支持并不是一个绝对的要求。 但是,如果要实现控件或使用序列化作为类的功能或设计的一部分,则应实现 ConvertTo

要作为 TypeConverter 支持 XAML 的实现使用, ConvertTo 该转换器的方法必须接受作为参数支持 value 的类型(或值)的实例。 当destinationType参数为类型String时,返回的对象必须能够被转换为String。 返回的字符串必须表示序列化值 。value 理想情况下,您选择的序列化格式应该能够在将该字符串传递给同一转换器的 ConvertFrom 实现时,生成相同的值,而不会显著丢失信息。

如果无法序列化值,或者转换器不支持序列化,则 ConvertTo 实现必须返回 null,并允许在这种情况下引发异常。 但是,如果确实引发异常,则应报告无法将该转换用作实现的 CanConvertTo 一部分,以便支持先进行检查 CanConvertTo 以避免异常的最佳做法。

如果 destinationType 参数不是类型 String,则可以选择自己的转换器处理。 通常情况下,你会恢复到基本实现的处理方式,而在最底层的 ConvertTo 中会引发特定的异常。

实现 CanConvertTo

您的CanConvertTo实现应针对true类型的destinationType返回String,否则应依赖于基础实现。

实现 CanConvertFrom

您的CanConvertFrom实现应针对true类型的sourceType返回String,否则应依赖于基础实现。

应用类型转换器属性 TypeConverterAttribute

为了使自定义类型转换器能够被 XAML 处理器用作自定义类的代理类型转换器,您必须在类定义中应用 TypeConverterAttribute。 您通过该属性指定的ConverterTypeName,必须是您的自定义类型转换器的类型名称。 应用此属性后,当 XAML 处理器处理属性类型使用自定义类类型的值时,它可以输入字符串并返回对象实例。

还可以按属性提供类型转换器。 不是将 TypeConverterAttribute 应用于类定义,而是将其应用于属性定义(即主定义,不包括其中的 get/set 实现)。 属性的类型必须与自定义类型转换器处理的类型匹配。 应用此属性后,当 XAML 处理器处理该属性的值时,它可以处理输入字符串并返回对象实例。 选择使用 Microsoft .NET Framework 的属性类型,或来自某些您无法控制类定义且无法应用 TypeConverterAttribute 的库时,按属性类型转换器技术会特别有用。

另请参阅