Proposal: TypeConverter-equivalent for class properties (attribute)
See original GitHub issueProposal: TypeConverter-equivalent for class properties
Summary
In WPF, it’s possible to define a TypeConverter
type to manually implement conversion logic from a string
to arbitrary types when parsing XAML code. This is not supported on UWP. UWP does have the [CreateFromString] attribute, but that only works for custom types. This is not a replacement for TypeConverter
, as it misses one important use case: being able to implement parsing logic for types you don’t own, when used as properties in types you do own.
Example
I’m working on a rewrite for the Microsoft.Toolkit.Uwp.UI.Animations
package (here), and I have a bunch of classes that would ideally expose properties of types such as Vector3
and Vector4
. The XAML compiler currently does not support parsing these types automatically on custom types. But even if it did, that would not be a solution, as developers need to be able to define parsing logic themselves (we can’t just assume they’ll never have to parse some type that’s already built-in in the XAML parser).
Consider this simple example:
public sealed class TranslationAnimation
{
public Vector3? From { get; set; }
public Vector3? To { get; set; }
}
This will just fail to parse in XAML, and it will crash. I can’t use [CreateFromString]
, because I do not own Vector3
.
There are currently two solutions:
- Add a markup extension that converts
string
toVector3?
. This retains type safety on the class, but makes the code very verbose, as now consumers will always need to doFrom="{ex:Vector3Extensions Value=20,0,0}"
instead of justFrom="20,0,0"
. This is too much to ask to consumers for every single usage, and that’s why I didn’t go for this solution in the Toolkit either. - Introduce a generic type in the class architecture that allows specifying parsing type and parsed type, so that for types not supported by XAML, such as this one, classes can just use
string
as public type and then implement a custom logic. Something like this, which is untimately what I did in that PR:
public abstract class Animation<TValue, TKeyFrame>
where TKeyFrame : unmanaged
{
public TValue? To { get; set; }
public TValue? From { get; set; }
protected abstract (TKeyFrame? To, TKeyFrame? From) GetParsedValues();
}
public sealed class TranslationAnimation : Animation<string, Vector3>
{
protected override (Vector3?, Vector3?) GetParsedValues()
{
return (To?.ToVector3(), From?.ToVector3());
}
}
Consumers can now use the nice compact syntax in XAML, and classes can handle all the parsing logic. Now, this works, but it’s… Pretty terrible for a number of reasons:
- You’re forced to introduce extra complexity (more type parameters, extra abstract parsing methods, etc.)
- The actual type of values in the animation is… Wrong. This
TranslationAnimation
will exposeTo
andFrom
asstring
values instead of justVector3?
values as it should be. This makes it pretty bad in cases where users might have wanted to actually set a concreteVector3
value directly, as they’ll have to round-trip to astring
first.
I’m not super familiar with the various WinRT limitations that could come into play with implementing a solution here, but really any sort of solution that would allow consumers to just add an attribute over properties to indicate how to parse them would work fine. Either:
- Add support for
[TypeConverter]
- Allow
[CreateFromString]
to be used as an attribute over properties (and not just type) - Some new attribute specifically for properties
Any of these 3 solutions or an equivalent one would work just great, really 😄
Rationale
- Allow consumers to fill in where the built-in XAML parser doesn’t support types
- Allow consumers to customize the XAML parsing behavior for properties
- Reduce the code complexity to work around this limitation today
Scope
Capability | Priority |
---|---|
Allow developers to annotate properties to supply a custom parsing method | Must |
API example/proposal
Here’s an example of how this could work in case [CreateFromString]
support for properties was introduced:
public sealed class TranslationAnimation
{
[CreateFromString(MethodName = "Microsoft.Toolkit.Uwp.UI.Extensions.VisualExtensions.ToVector3")]
public Vector3? From { get; set; }
[CreateFromString(MethodName = "Microsoft.Toolkit.Uwp.UI.Extensions.VisualExtensions.ToVector3")]
public Vector3? To { get; set; }
}
Usage:
<TranslationAnimation From="0" To="20,0,0" />
Perfect! 🎉
Issue Analytics
- State:
- Created 3 years ago
- Reactions:12
- Comments:9 (5 by maintainers)
Top GitHub Comments
@ryandemopoulos seems like a cool idea
I like the approach of [CreateFromString] supporting the property case.
What I don’t like about type converters is that there’s no type information telling you what input types are supported or what type you’re going to get out. With CreateFromString it’s more clear (though you still don’t know the specific string syntax). It’s also nice that CreateFromString is clear as to how to get the same functionality from code as from markup (you can do the same with a TypeConverter but the code’s more cumbersome and less obvious and not typed).