让我们暂时忘掉x:String,专注于Thickness。显然你可以这样创建Thickness 的实例:
<Thickness>1,1,1,1</Thickness>
现在,让我们将其与创建Label 进行比较:
<Label>Hello</Label>
虽然两条线看起来很相似,但它们做的事情却不同。使用Label,它创建Label 的实例(使用默认的无参数构造函数)并将Hello 分配给它的Text 属性(如Label 类的[ContentProperty("Text")] 属性所指示的那样)。但Thickness 并非如此。您不能只创建Thickness 的实例并将值1,1,1,1 分配给它的一个属性。有两个问题:
-
Thickness 没有字符串属性来接受逗号分隔值并初始化其 Bottom、Right、Top、Left 属性。
- 更糟糕的是,
Thickness 不是一个类。它是一个结构,因此它没有无参数的构造函数。那么 Xamarin 如何决定需要将哪些参数传递给它的构造函数呢?
现在您可以清楚地看到 ContentProperty 属性不是让我们能够做到的:<Thickness>1,1,1,1</Thickness>。此处 Xamarin.Forms 采用字符串值 1,1,1,1 并将其转换为 Thickness 的实例。这是通过 Xamarin.Forms 中的 TypeConverters 完成的。您可以通过检查 [TypeConverter(typeof(ThicknessTypeConverter))] 上的 Thickness 结构来验证这一点。
我认为这个想法与 Xamarin.Forms 中的内置原始类型相同。例如,当您编写 <x:Boolean>True</x:Boolean> 时,Xamarin.Forms 会使用字符串 True 并在其内部 TypeConverters 的帮助下将其转换为 System.Boolean。
奖励:如果您想使用自己的自定义结构实现相同的行为(我认为类不可能),您可以执行以下操作:
-
创建一个自定义 TypeConverter 以指示 Xamarin.Forms 如何将字符串值转换为结构的实例:
public class MyPointConverter : Xamarin.Forms.TypeConverter
{
public override object ConvertFromInvariantString(string value)
{
var parts = value.Split(new[] { ',' }, StringSplitOptions.None);
if (parts.Length != 2
|| !int.TryParse(parts[0], out var x)
|| !int.TryParse(parts[1], out var y))
{
return base.ConvertFromInvariantString(value);
}
var myPoint = new MyPoint(x, y);
return myPoint;
}
}
-
使用TypeConverterAttribute 装饰您的结构,以显示自定义TypeConverter 可用于此类型:
[Xamarin.Forms.TypeConverter(typeof(MyPointConverter))]
public struct MyPoint
{
public int X { get; set; }
public int Y { get; set; }
public MyPoint(int x, int y)
{
this.X = x;
this.Y = y;
}
}
-
在 Xaml 中创建结构的实例:
<ResourceDictionary>
<local:MyPoint x:Key="MyPoint">1,5</local:MyPoint>
</ResourceDictionary>