XAML标记扩展是XAML中的一个重要特性,它允许将属性设置为从其他源间接引用的对象或值。 XAML标记扩展对共享对象以及引用应用程序中使用的常量非常重要,但是它们在数据绑定中找到了最大的实用性。
XAML标记扩展
通常,使用XAML将对象的属性设置为显式值,例如字符串,数字,枚举成员或在后台转换为值的字符串。
然而,有时候,属性必须引用其他地方定义的值,或者在运行时需要一些代码处理。 为了这些目的,可以使用XAML标记扩展。
这些XAML标记扩展不是XML的扩展。 XAML是完全合法的XML。 他们被称为“扩展”,因为它们是由实现IMarkupExtension的类中的代码支持的。 您可以编写自己的自定义标记扩展。
在许多情况下,XAML标记扩展在XAML文件中可以立即识别,因为它们显示为由大括号{和}分隔的属性设置,但是有时候标记扩展会以常规元素的形式出现在标记中。
共享资源
一些XAML页面包含几个视图,其属性设置为相同的值。 例如,这些Button对象的许多属性设置是相同的:
如果其中一个属性需要更改,您可能更愿意只进行一次而不是三次。 如果这是代码,你可能会使用常量和静态只读对象来帮助保持这些值一致和易于修改。
在XAML中,一种流行的解决方案是将这些值或对象存储在资源字典中。 VisualElement类定义了一个名为ResourceDocument的属性,它是一个字符串类型为key,字段值为object的字典。 您可以将对象放入此字典中,然后从标记中引用它们,全部使用XAML。
要在页面上使用资源字典,请包含一对“资源”属性元素标记。 把这些放在页面顶部是最方便的:
.Resources> ...
It’s also necessary to explicitly include ResourceDictionary tags:
.Resources> ...
现在可以将各种类型的对象和值添加到资源字典中。 这些类型必须是可实例化的。 例如,它们不能是抽象类。 这些类型也必须有一个公共无参数的构造函数。 每个项目都需要使用x:Key属性指定的字典密钥。 例如:
.Resources> ...
这两个项目是结构类型LayoutOptions的值,每个都有一个唯一的键和一个或两个属性集。 在代码和标记中,使用LayoutOptions的静态字段更为常见,但在这里设置属性更方便。
现在有必要将这些按钮的HorizontalOptions和VerticalOptions属性设置为这些资源,并使用StaticResource XAML标记扩展完成:
StaticResource标记扩展总是用大括号分隔,并包含字典键。
名称StaticResource将其与DynamicResource区分开来,Xamarin.Forms也支持该类型。 DynamicResource用于与在运行时可能更改的值关联的字典键,而当构建页面上的元素时,StaticResource仅访问字典中的元素一次。
对于BorderWidth属性,有必要在字典中存储一个double。 XAML方便地定义常用数据类型的标签,如x:Double和x:Int32:
3 .Resources>
你不需要把它放在三条线上。 这个旋转角度的字典条目只占用一行:
3 -15.Resources>
这两个资源可以以与LayoutOptions值相同的方式引用:
对于Color类型的资源,可以使用直接指定这些类型的属性时使用的相同字符串表示形式。 在创建资源时调用类型转换器。 这是一个颜色类型的资源:
Red
FontSize属性提出了一个小问题。 该属性被定义为double类型。 当您将该属性设置为NamedSize枚举的成员(如Large)时,FontSizeConverter类将在幕后工作,以使用Device.GetNamedSized方法将其转换为依赖于平台的值。
但是,不能将字体大小的资源定义为double,并将值设置为“Large”。 当XAML解析器处理资源时,它不知道该值将被用作字体大小。
解决方法是使用x:String类型将资源定义为字符串:
Large
现在,除Text之外的所有属性均由资源设置定义:
也可以在资源字典中使用OnPlatform来为平台定义不同的值。 以下是OnPlatform对象如何成为不同文本颜色的资源字典的一部分:
请注意,OnPlatform同时获得x:Key属性,因为它是字典中的一个对象,而且是一个x:TypeArguments属性,因为它是一个通用类。 对象初始化时,iOS,Android和UWP属性将转换为“颜色”值。
下面是最后一个完整的XAML文件,其中三个按钮访问六个共享值:
3-15Large.Resources>
屏幕截图验证一致的样式和平台依赖的样式:
尽管在页面顶部定义Resources集合最为常见,但请记住,Resources属性是由VisualElement定义的,您可以在页面上的其他元素上集合Resources。 例如,在这个例子中,尝试添加一个到StackLayout:
Blue.Resources> ...
你会发现按钮的文本颜色现在是蓝色的。 基本上,只要XAML解析器遇到StaticResource标记扩展,就会搜索可视化树,并使用它遇到的第一个包含该键的ResourceDictionary。
存储在资源字典中的最常见类型对象之一是Xamarin.Forms样式,它定义了属性设置的集合。 样式在文章Styles中讨论。
有时,XAML的新手不知道是否可以将一个可视元素(如Label或Button)放在ResourceDictionary中。 虽然这当然是可能的,但没有多大意义。 ResourceDictionary的目的是共享对象。 视觉元素不能共享。 同一个实例不能在单个页面上出现两次。
x:Static标记扩展
尽管他们的名字相似,x:Static和StaticResource是非常不同的。 StaticResource从资源字典返回一个对象,而x:Static则访问以下之一:
- 一个公共静态字段
- 一个公共静态属性
- 一个公共恒定的领域
- 枚举成员。
定义资源字典的XAML实现支持StaticResource标记扩展,而x前缀揭示的x:Static是XAML的固有部分。
以下是一些演示x:Static如何显式引用静态字段和枚举成员的示例:
到目前为止,这不是很令人印象深刻。 但是x:静态标记扩展也可以引用你自己代码中的静态字段或属性。 例如,以下是一个AppConstants类,其中包含您可能希望在整个应用程序的多个页面上使用的一些静态字段:
using System; using Xamarin.Forms; namespace XamlSamples { static class AppConstants { public static readonly Thickness PagePadding; public static readonly Font TitleFont; public static readonly Color BackgroundColor = Color.Aqua; public static readonly Color ForegroundColor = Color.Brown; static AppConstants() { switch (Device.RuntimePlatform) { case Device.iOS: PagePadding = new Thickness(5, 20, 5, 0); TitleFont = Font.SystemFontOfSize(35, FontAttributes.Bold); break; case Device.Android: PagePadding = new Thickness(5, 0, 5, 0); TitleFont = Font.SystemFontOfSize(40, FontAttributes.Bold); break; case Device.UWP: PagePadding = new Thickness(5, 0, 5, 0); TitleFont = Font.SystemFontOfSize(50, FontAttributes.Bold); break; } } } }
为了在XAML文件中引用这个类的静态字段,需要在XAML文件中指定这个文件所在的位置。 您可以使用XML名称空间声明来执行此操作。
回想一下,作为标准Xamarin.Forms XAML模板的一部分创建的XAML文件包含两个XML名称空间声明:一个用于访问Xamarin.Forms类,另一个用于引用XAML固有的标记和属性:
xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
您将需要额外的XML名称空间声明来访问其他类。 每个附加的XML名称空间声明都定义一个新的前缀。 要访问共享应用程序PCL的本地类,如AppConstants,XAML程序员经常使用local local。 名称空间声明必须指明CLR(公共语言运行时)名称空间名称,也称为.NET名称空间名称,它是出现在C#名称空间定义或使用指令中的名称:
xmlns:local="clr-namespace:XamlSamples"
您还可以在PCL引用的任何程序集中为.NET名称空间定义XML名称空间声明。 例如,下面是标准.NET系统名称空间的系统前缀,位于mscorlib程序集中,它曾经是“Microsoft Common Object Runtime Library”的缩写,但现在意味着“多语言标准通用对象运行时库”。 因为这是另一个程序集,所以还必须指定程序集名称,在本例中为mscorlib:
xmlns:sys="clr-namespace:System;assembly=mscorlib"
请注意,关键字clr-namespace后跟一个冒号,然后是.NET命名空间名称,后跟分号,关键字程序集,等号和程序集名称。
是的,冒号跟在clr-namespace之后,但是等号后面是汇编。 以这种方式故意定义语法:大多数XML名称空间声明引用了一个URI,它开始了一个URI方案名称,例如http,后面总是后跟一个冒号。 该字符串的clr-namespace部分旨在模仿该约定。
这两个名称空间声明都包含在StaticConstantsPage示例中。 请注意,BoxView尺寸设置为Math.PI和Math.E,但缩放比例为100:
由此产生的BoxView相对于屏幕的大小取决于平台:
其他标准标记扩展
一些标记扩展是XAML固有的,在Xamarin.Forms XAML文件中是受支持的。 其中一些不常使用,但在需要时非常重要:
- 如果某个属性默认具有非空值,但您希望将其设置为null,请将其设置为{x:Null}标记扩展名。
- 如果属性的类型为Type,则可以使用标记扩展{x:Type someClass}将其分配给Type对象。
- 您可以使用x:Array标记扩展名在XAML中定义数组。 这个标记扩展有一个名为Type的必需属性,它指示了数组中元素的类型。
- 绑定标记扩展在第4部分讨论。数据绑定基础。
约束表达式标记扩展
标记扩展可以具有属性,但是它们不是像XML属性那样设置的。 在标记扩展中,属性设置用逗号分隔,并且大括号内不显示引号。
这可以用名为ConstraintExpression的Xamarin.Forms标记扩展来说明,它与RelativeLayout类一起使用。 您可以将子视图的位置或大小指定为常量,或相对于父级或其他命名视图。 ConstraintExpression的语法允许您使用Factor乘以另一个视图的属性以及Constant来设置视图的位置或大小。 比这更复杂的任何事情都需要代码。
这是一个例子:
也许这个示例中最重要的一点是标记扩展的语法:标记扩展的大括号中不能出现任何引号。 在XAML文件中输入标记扩展名时,想要将属性的值用引号括起来是很自然的。 抵制诱惑!
这里是程序运行:
概要
这里显示的XAML标记扩展提供了对XAML文件的重要支持。 但也许最有价值的XAML标记扩展是Binding,这在本系列的下一部分,第4部分中有介绍。数据绑定基础。