Overview of type converters for XAML
Type converters supply logic for an object writer that converts from a string in XAML markup into particular objects in an object graph. In .NET XAML Services, the type converter must be a class that derives from TypeConverter. Some converters also support the XAML save path and can be used to serialize an object into a string form in serialization markup. This topic describes how and when type converters in XAML are invoked, and provides implementation advice for the method overrides of TypeConverter.
Type Conversion Concepts
The following sections explain basic concepts about how XAML uses strings, and how object writers in .NET XAML Services use type converters to process some of the string values that are encountered in a XAML source.
XAML and String Values
When you set an attribute value in a XAML file, the initial type of that value is a string in a general sense, and a string attribute value in an XML sense. Even other primitives such as Double are initially strings to a XAML processor.
In most cases, a XAML processor needs two pieces of information to process an attribute value. The first piece of information is the value type of the property that is being set. Any string that defines an attribute value and that is processed in XAML must ultimately be converted or resolved to a value of that type. If the value is a primitive that is understood by the XAML parser (such as a numeric value), a direct conversion of the string is attempted. If the value for the attribute references an enumeration, the supplied string is checked for a name match to a named constant in that enumeration. If the value is not a parser-understood primitive or a constant name from an enumeration, the applicable type must be able to provide a value or reference that is based on a converted string.
Note
XAML language directives do not use type converters.
Type Converters and Markup Extensions
Markup extension usages must be handled by a XAML processor before it checks for property type and other considerations. For example, if a property being set as an attribute normally has a type conversion, but in a particular case is set by a markup extension usage, then the markup extension behavior processes first. One common situation where a markup extension is necessary is to make a reference to an object that already exists. For this scenario, a stateless type converter can only generate a new instance, which might not be desirable. For more information about markup extensions, see Markup Extensions for XAML Overview.
Native Type Converters
In the Windows Presentation Foundation (WPF) and .NET XAML services implementations, there are certain CLR types that have native type conversion handling. However, those CLR types are not conventionally thought of as primitives. An example of such a type is DateTime. One reason for this is how the .NET Framework architecture works: the type DateTime is defined in mscorlib, the most basic library in .NET. DateTime is not permitted to be attributed with an attribute that comes from another assembly that introduces a dependency (TypeConverterAttribute is from System). Therefore, the usual type converter discovery mechanism by attributing cannot be supported. Instead, the XAML parser has a list of types that need native processing, and it processes these types similar to how the true primitives are processed. In the case of DateTime, this processing involves a call to Parse.
Implementing a Type Converter
The following sections discuss the API of the TypeConverter class.
TypeConverter
Under .NET XAML Services, all type converters that are used for XAML purposes are classes that derive from the base class TypeConverter. The TypeConverter class existed in versions of the .NET Framework before XAML existed; one of the original TypeConverter scenarios was to provide string conversion for property editors in visual designers.
For XAML, the role of TypeConverter is expanded. For XAML purposes, TypeConverter is the base class for providing support for certain to-string and from-string conversions. From-string enables parsing a string attribute value from XAML. To-string might enable processing a run-time value of a particular object property back into an attribute in XAML for serialization.
TypeConverter defines four members that are relevant for converting to-string and from-string for XAML processing purposes:
Of these members, the most important method is ConvertFrom, which converts the input string to the required object type. The ConvertFrom method can be implemented to convert a wider range of types into the intended destination type of the converter. Therefore, it can serve purposes that extend beyond XAML, such as supporting run-time conversions. However, for XAML use, only the code path that can process a String input is important.
The second-most important method is ConvertTo. If an application is converted to a markup representation (for example, if it is saved to XAML as a file), ConvertTo is involved in the larger scenario of a XAML text writer to produce a markup representation. In this case, the important code path for XAML is when the caller passes a destinationType
of String.
CanConvertTo and CanConvertFrom are support methods that are used when a service queries the capabilities of the TypeConverter implementation. You must implement these methods to return true
for type-specific cases that the equivalent conversion methods of your converter support. For XAML purposes, this generally means the String type.
Culture Information and Type Converters for XAML
Each TypeConverter implementation can uniquely interpret what is a valid string for a conversion, and it can also use or ignore the type description that is passed as parameters. An important consideration for culture and XAML type conversion is the following: although using localizable strings as attribute values is supported by XAML, you cannot use these localizable strings as type converter input with specific culture requirements. This limitation is because type converters for XAML attribute values involve a necessarily fixed-language XAML-processing behavior that uses en-US
culture. For more information about the design reasons for this restriction, see the XAML language specification ([MS-XAML]) or WPF Globalization and Localization Overview.
As an example where culture can be an issue, some cultures use a comma instead of a period as the decimal point delimiter for numbers in string form. This use collides with the behavior that many existing type converters have, which is to use a comma as a delimiter. Passing a culture through xml:lang
in the surrounding XAML does not solve the issue.
Implementing ConvertFrom
To be usable as a TypeConverter implementation that supports XAML, the ConvertFrom method for that converter must accept a string as the value
parameter. If the string is in a valid format and can be converted by the TypeConverter implementation, the returned object must support a cast to the type that is expected by the property. Otherwise, the ConvertFrom implementation must return null
.
Each TypeConverter implementation can uniquely interpret what constitutes a valid string for a conversion, and it can also use or ignore the type description or culture contexts that are passed as parameters. However, the WPF XAML processing might not pass values to the type description context in all cases and also might not pass culture based on xml:lang
.
Note
Do not use the braces ({}), specifically the opening brace ({), as an element of your string format. These characters are reserved as the entry and exit for a markup extension sequence.
It is appropriate to throw an exception when your type converter must have access to a XAML service from .NET XAML Services object writer, but the GetService call that is made against the context does not return that service.
Implementing ConvertTo
ConvertTo is potentially used for serialization support. Serialization support through ConvertTo for your custom type and its type converter is not an absolute requirement. However, if you are implementing a control, or using serialization of as part of the features or design of your class, you should implement ConvertTo.
To be usable as a TypeConverter implementation that supports XAML, the ConvertTo method for that converter must accept an instance of the type (or a value) that is supported as the value
parameter. When the destinationType
parameter is of type String, the returned object must be able to be cast as String. The returned string must represent a serialized value of value
. Ideally, the serialization format that you choose should be able to generate the same value as if that string were passed to the ConvertFrom implementation of the same converter, without significant loss of information.
If the value cannot be serialized or the converter does not support serialization, the ConvertTo implementation must return null
and can throw an exception. However, if you do throw exceptions, you should report the inability to use that conversion as part of your CanConvertTo implementation so that the best practice of checking with CanConvertTo first to avoid exceptions is supported.
If the destinationType
parameter is not of type String, you can choose your own converter handling. Typically, you revert to base implementation handling, which in the base ConvertTo raises a specific exception.
It is appropriate to throw an exception when your type converter must have access to a XAML service from .NET XAML Services object writer, but the GetService call that is made against the context does not return that service.
Implementing CanConvertFrom
Your CanConvertFrom implementation should return true
for sourceType
of type String and otherwise, defer to the base implementation. Do not throw exceptions from CanConvertFrom.
Implementing CanConvertTo
Your CanConvertTo implementation should return true
for destinationType
of type String, and otherwise defer to the base implementation. Do not throw exceptions from CanConvertTo.
Applying the TypeConverterAttribute
For your custom type converter to be used as the acting type converter for a custom class by .NET XAML Services, you must apply the TypeConverterAttribute to your class definition. The ConverterTypeName that you specify through the attribute must be the type name of your custom type converter. If you apply this attribute, when a XAML processor handles values where the property type uses your custom class type, it can input strings and return object instances.
You can also provide a type converter on a per-property basis. Instead of applying a TypeConverterAttribute to the class definition, apply it to a property definition (the main definition, not the get
/set
implementations within it). The type of the property must match the type that is processed by your custom type converter. With this attribute applied, when a XAML processor handles values of that property, it can process input strings and return object instances. The per-property type converter technique is useful if you choose to use a property type from Microsoft .NET Framework or from some other library where you cannot control the class definition and cannot apply a TypeConverterAttribute there.
To supply a type conversion behavior for a custom attached member, apply TypeConverterAttribute to the Get
accessor method of the implementation pattern for the attached member.
Accessing Service Provider Context from a Markup Extension Implementation
The available services are the same for any value converter. The difference is in how each value converter receives the service context. Accessing services and the services available are documented in the topic Type Converters and Markup Extensions for XAML.
Type Converters in the XAML Node Stream
If you are working with a XAML node stream, the action or end result of a type converter is not yet executed. In a load path, the attribute string that eventually needs to be type-converted in order to load remains as a text value within a start member and end member. The type converter that is eventually needed for this operation can be determined by using the XamlMember.TypeConverter property. However, obtaining a valid value from XamlMember.TypeConverter relies on having a XAML schema context, which can access such information through the underlying member, or the type of the object value that the member uses. Invoking the type conversion behavior also requires the XAML schema context because that requires type-mapping and creating a converter instance.
See also
.NET Desktop feedback