Markup Extensions
Markup Extension
允许在 XAML 中以方便、可重用的语法对目标属性的 setter 逻辑进行基于代码的自定义。大括号用于区分普通文本的使用。
Avalonia 提供了以下内容:
MarkupExtension | 分配给属性 |
---|---|
StaticResource | 一个存在的有键值的资源,并且不会在更改时更新 |
DynamicResource | 延迟加载的键资源,会在更改时更新 |
Binding | 基于默认的绑定偏好:编译或反射 |
CompiledBinding | 基于编译的绑定 |
ReflectionBinding | 基于反射的绑定 |
TemplateBinding | 基于简化的绑定,仅在 ControlTemplate 内使用 |
OnPlatform | 当处于指定平台时有条件地使用 |
OnFormFactor | 当处于指定因素时有条件地使用 |
编译器内在函数
这些技术上不属于 MarkupExtension
,而是 XAML 编译器的一部分,但 和XAML 语法相同。
内在函数 | 分配给属性 |
---|---|
x:True | true 字面量 |
x:False | false 字面量 |
x:Null | null 字面量 |
x:Static | 静态成员值 |
x:Type | System.Type 字面量 |
x:True
和 x:False
字面量在目标绑定属性为 object
且需要提供布尔值的情况下有用。在这些缺少类型信息的场景中,提供 "True" 仍然是一个字符串。
<Button Command="{Binding SetStateCommand}" CommandParameter="{x:True}" />
创建 MarkupExtensions
派生自 MarkupExtension
或添加以下通过鸭子类型支持的签名之一:
T ProvideValue();
T ProvideValue(IServiceProvider provider);
object ProvideValue();
object ProvideValue(IServiceProvider provider);
当使用强类型而不是 object
时,如果在 XAML 中构造参数、属性或 ProvideValue
中的返回值不匹配,将会收到编译时错误。当返回 object
时,实际返回的类型必须与目标属性的类型匹配,否则运行时会抛出 InvalidCastException
。
接收字面量参数
当需要参数时,使用构造函数按顺序接收每个参数。
对于可选或无序参数,使用属性代替。允许混合使用多个构造函数,包括无参数构造函数。
public class MultiplyLiteral
{
private readonly double _first;
private readonly double _second;
public double? Third { get; set; }
public MultiplyLiteral(double first, double second)
{
_first = first;
_second = second;
}
public double ProvideValue(IServiceProvider provider)
{
return First * Second * Third ?? 1;
}
}
<TextBlock Text="This has FontSize=40" FontSize="{namespace:MultiplyLiteral 10, 8, Third=0.5}" />
从绑定接收参 数
常见的场景是希望转换来自绑定的数据并更新目标属性。当所有参数都来自绑定时,通过创建一个带有 IMultiValueConverter
的 MultiBinding
来实现这一点相对简单。在下面的示例中,MultiplyBinding
需要两个绑定参数。如果需要混合字面量和绑定参数,创建一个 IMultiValueConverter
可以允许传递字面量作为构造函数或 init
参数。BindingBase
允许使用 CompiledBinding
和 ReflectionBinding
,但不允许使用字面量。
public class MultiplyBinding
{
private readonly BindingBase _first;
private readonly BindingBase _second;
public MultiplyBinding(BindingBase first, BindingBase second)
{
_first = first;
_second = second;
}
public object ProvideValue()
{
var mb = new MultiBinding()
{
Bindings = new[] { _first, _second },
Converter = new FuncMultiValueConverter<double, double>(doubles => doubles.Aggregate(1d, (x, y) => x * y))
};
return mb;
}
}
<TextBlock FontSize="{local:MultiplyBinding {Binding Multiplier}, {Binding Multiplicand}}"
Text="MarkupExtension with Bindings!" />
另一种方法是返回 IObservable<T>.ToBinding()
。
返回参数
为了使 MarkupExtension
兼容多种目标属性类型,返回 object
并分别处理每种支持的类型。
public object ProvideValue(IServiceProvider provider)
{
var target = (IProvideValueTarget)provider.GetService(typeof(IProvideValueTarget))!;
var targetProperty = target.TargetProperty as AvaloniaProperty;
var targetType = targetProperty?.PropertyType;
double result = First * Second * (Third ?? 1);
if (targetType == typeof(double))
return result;
else if (targetType == typeof(float))
return (float)result;
else if (targetType == typeof(int))
return (int)result;
else
throw new NotSupportedException();
}
构造函数也可以使用 object
方法接收参数类型,但编译时错误同样会变成运行时异常。
MarkupExtension 属性注解
[ConstructorArgument]
- 关联属性可以通过构造函数参数初始化,并且如果使用了构造函数,则在 XAML 序列化时应忽略该属性。[MarkupExtensionOption]
,[MarkupExtensionDefaultOption]
- 与ShouldProvideOption
一起使用,查看OnPlatform
和OnFormFactor
的源代码以获取示例。