XAML主要用于实例化和初始化对象。 但是,通常情况下,属性必须设置为复杂的对象,不能很容易地表示为XML字符串,有时由一个类定义的属性必须设置在子类上。 这两个需求需要属性元素和附加属性的基本XAML语法功能。
属性元素
在XAML中,类的属性通常设置为XML属性:
-
<Label Text="Hello, XAML!"
-
VerticalOptions="Center"
-
FontAttributes="Bold"
-
FontSize="Large"
-
TextColor="Aqua" />
但是,在XAML中有另一种设置属性的方法。 要使用TextColor尝试此替代方法,请首先删除现有的TextColor设置:
-
<Label Text="Hello, XAML!"
-
VerticalOptions="Center"
-
FontAttributes="Bold"
-
FontSize="Large" />
将空元素Label标签分离为开始标签和结束标签:
-
<Label Text="Hello, XAML!"
-
VerticalOptions="Center"
-
FontAttributes="Bold"
-
FontSize="Large">
-
-
</Label>
在这些标签中,添加包含类名和由句点分隔的属性名的开始和结束标签:
-
<Label Text="Hello, XAML!"
-
VerticalOptions="Center"
-
FontAttributes="Bold"
-
FontSize="Large">
-
<Label.TextColor>
-
-
</Label.TextColor>
-
</Label>
将属性值设置为这些新标记的内容,如下所示:
-
<Label Text="Hello, XAML!"
-
VerticalOptions="Center"
-
FontAttributes="Bold"
-
FontSize="Large">
-
<Label.TextColor>
-
Aqua
-
</Label.TextColor>
-
</Label>
这两种指定TextColor属性的方法在功能上是等价的,但是不要使用两种方法来实现同一个属性,因为这样做会有效地将属性设置两次,而且可能是不明确的。
用这个新的语法,可以引入一些方便的术语:
-
标签是一个对象元素。这是一个表示为XML元素的Xamarin.Forms对象。
-
文本,VerticalOptions,FontAttributes和FontSize是属性属性。它们是以XML属性表示的Xamarin.Forms属性。
-
在最后一个片段中,TextColor已经成为一个属性元素。这是一个Xamarin.Forms属性,但它现在是一个XML元素。
属性元素的定义可能首先看起来违反了XML语法,但事实并非如此。这段时间在XML中没有特别的意义。对于XML解码器来说,Label.TextColor只是一个普通的子元素。
然而,在XAML中,这个语法是非常特殊的。属性元素的规则之一就是Label.TextColor标签中不会出现任何其他内容。属性的值始终定义为属性元素开始和结束标记之间的内容。
您可以在多个属性上使用属性元素语法:
-
<Label Text="Hello, XAML!"
-
VerticalOptions="Center">
-
<Label.FontAttributes>
-
Bold
-
</Label.FontAttributes>
-
<Label.FontSize>
-
Large
-
</Label.FontSize>
-
<Label.TextColor>
-
Aqua
-
</Label.TextColor>
-
</Label>
或者,您可以对所有属性使用属性元素语法:
-
<Label>
-
<Label.Text>
-
Hello, XAML!
-
</Label.Text>
-
<Label.FontAttributes>
-
Bold
-
</Label.FontAttributes>
-
<Label.FontSize>
-
Large
-
</Label.FontSize>
-
<Label.TextColor>
-
Aqua
-
</Label.TextColor>
-
<Label.VerticalOptions>
-
Center
-
</Label.VerticalOptions>
-
</Label>
起初,属性元素语法可能看起来像是一个相当简单的不必要的冗长的替换,在这些例子当然是这种情况。
但是,如果属性的值太复杂,无法用简单的字符串表示,则属性元素语法就变得非常重要。 在属性元素标签内,您可以实例化另一个对象并设置其属性。 例如,您可以使用属性设置将诸如VerticalOptions之类的属性显式设置为LayoutOptions值:
-
<Label>
-
...
-
<Label.VerticalOptions>
-
<LayoutOptions Alignment="Center" />
-
</Label.VerticalOptions>
-
</Label>
另一个例子:Grid有两个名为RowDefinitions和ColumnDefinitions的属性。 这两个属性的类型是RowDefinitionCollection和ColumnDefinitionCollection,它们是RowDefinition和ColumnDefinition对象的集合。 您需要使用property元素语法来设置这些集合。
以下是GridDemoPage类的XAML文件的开头,显示了RowDefinitions和ColumnDefinitions集合的属性元素标签:
-
<ContentPage xmlns=""
-
xmlns:x=""
-
x:Class="XamlSamples.GridDemoPage"
-
Title="Grid Demo Page">
-
<Grid>
-
<Grid.RowDefinitions>
-
<RowDefinition Height="Auto" />
-
<RowDefinition Height="*" />
-
<RowDefinition Height="100" />
-
</Grid.RowDefinitions>
-
<Grid.ColumnDefinitions>
-
<ColumnDefinition Width="Auto" />
-
<ColumnDefinition Width="*" />
-
<ColumnDefinition Width="100" />
-
</Grid.ColumnDefinitions>
-
...
-
</Grid>
-
</ContentPage>
请注意用于定义自动调整大小的单元格的缩写语法,像素宽度和高度的单元格以及星形设置。
附加属性
您刚刚看到,Grid需要RowDefinitions和ColumnDefinitions集合的属性元素来定义行和列。 但是,程序员还必须有一些方法来指示网格的每个子节点所在的行和列。
在Grid的每个子项的标签内,使用以下属性指定该子项的行和列:
这些属性的默认值为0.您还可以指示子项是否跨越具有以下属性的多个行或列:
-
Grid.RowSpan
-
Grid.ColumnSpan
这两个属性的默认值是1。
这是完整的GridDemoPage.xaml文件:
-
<ContentPage xmlns=""
-
xmlns:x=""
-
x:Class="XamlSamples.GridDemoPage"
-
Title="Grid Demo Page">
-
-
<Grid>
-
<Grid.RowDefinitions>
-
<RowDefinition Height="Auto" />
-
<RowDefinition Height="*" />
-
<RowDefinition Height="100" />
-
</Grid.RowDefinitions>
-
-
<Grid.ColumnDefinitions>
-
<ColumnDefinition Width="Auto" />
-
<ColumnDefinition Width="*" />
-
<ColumnDefinition Width="100" />
-
</Grid.ColumnDefinitions>
-
-
<Label Text="Autosized cell"
-
Grid.Row="0" Grid.Column="0"
-
TextColor="White"
-
BackgroundColor="Blue" />
-
-
<BoxView Color="Silver"
-
HeightRequest="0"
-
Grid.Row="0" Grid.Column="1" />
-
-
<BoxView Color="Teal"
-
Grid.Row="1" Grid.Column="0" />
-
-
<Label Text="Leftover space"
-
Grid.Row="1" Grid.Column="1"
-
TextColor="Purple"
-
BackgroundColor="Aqua"
-
HorizontalTextAlignment="Center"
-
VerticalTextAlignment="Center" />
-
-
<Label Text="Span two rows (or more if you want)"
-
Grid.Row="0" Grid.Column="2" Grid.RowSpan="2"
-
TextColor="Yellow"
-
BackgroundColor="Blue"
-
HorizontalTextAlignment="Center"
-
VerticalTextAlignment="Center" />
-
-
<Label Text="Span two columns"
-
Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2"
-
TextColor="Blue"
-
BackgroundColor="Yellow"
-
HorizontalTextAlignment="Center"
-
VerticalTextAlignment="Center" />
-
-
<Label Text="Fixed 100x100"
-
Grid.Row="2" Grid.Column="2"
-
TextColor="Aqua"
-
BackgroundColor="Red"
-
HorizontalTextAlignment="Center"
-
VerticalTextAlignment="Center" />
-
-
</Grid>
-
</ContentPage>
0的Grid.Row和Grid.Column设置不是必需的,但为了清楚起见,通常包括在内。
以下是所有三个平台的外观:
仅仅从语法判断,Grid.Row,Grid.Column,Grid.RowSpan和Grid.ColumnSpan属性看起来都是Grid的静态字段或属性,但有趣的是,Grid并没有定义任何名为Row,Column,RowSpan,或ColumnSpan。
相反,Grid定义了四个名为RowProperty,ColumnProperty,RowSpanProperty和ColumnSpanProperty的可绑定属性。这些是被称为附加属性的特殊类型的可绑定属性。它们由网格类定义,但是设置在网格的子节点上。
当您希望在代码中使用这些附加属性时,Grid类将提供名为SetRow,GetColumn等静态方法。但是在XAML中,这些附加的属性使用简单的属性名称在网格的子元素中设置为属性。
附加属性在XAML文件中始终可识别为属性,其中包含由句点分隔的类和属性名称。它们被称为附加属性,因为它们是由一个类(在这种情况下为网格)定义的,但是附加到其他对象(在这种情况下是网格的子节点)。在布局过程中,网格可以询问这些附加属性的值,以知道每个孩子的放置位置。
AbsoluteLayout类定义了两个名为LayoutBounds和LayoutFlags的附加属性。以下是使用AbsoluteLayout的比例定位和尺寸特征实现的棋盘图案:
-
<ContentPage xmlns=""
-
xmlns:x=""
-
x:Class="XamlSamples.AbsoluteDemoPage"
-
Title="Absolute Demo Page">
-
-
<AbsoluteLayout BackgroundColor="#FF8080">
-
<BoxView Color="#8080FF"
-
AbsoluteLayout.LayoutBounds="0.33, 0, 0.25, 0.25"
-
AbsoluteLayout.LayoutFlags="All" />
-
-
<BoxView Color="#8080FF"
-
AbsoluteLayout.LayoutBounds="1, 0, 0.25, 0.25"
-
AbsoluteLayout.LayoutFlags="All" />
-
-
<BoxView Color="#8080FF"
-
AbsoluteLayout.LayoutBounds="0, 0.33, 0.25, 0.25"
-
AbsoluteLayout.LayoutFlags="All" />
-
-
<BoxView Color="#8080FF"
-
AbsoluteLayout.LayoutBounds="0.67, 0.33, 0.25, 0.25"
-
AbsoluteLayout.LayoutFlags="All" />
-
-
<BoxView Color="#8080FF"
-
AbsoluteLayout.LayoutBounds="0.33, 0.67, 0.25, 0.25"
-
AbsoluteLayout.LayoutFlags="All" />
-
-
<BoxView Color="#8080FF"
-
AbsoluteLayout.LayoutBounds="1, 0.67, 0.25, 0.25"
-
AbsoluteLayout.LayoutFlags="All" />
-
-
<BoxView Color="#8080FF"
-
AbsoluteLayout.LayoutBounds="0, 1, 0.25, 0.25"
-
AbsoluteLayout.LayoutFlags="All" />
-
-
<BoxView Color="#8080FF"
-
AbsoluteLayout.LayoutBounds="0.67, 1, 0.25, 0.25"
-
AbsoluteLayout.LayoutFlags="All" />
-
-
</AbsoluteLayout>
-
</ContentPage>
这里是:
对于这样的事情,你可能会质疑使用XAML的智慧。 当然,LayoutBounds矩形的重复性和规律性表明在代码中可能会更好地实现。
这当然是合理的问题,在定义用户界面时平衡代码和标记的使用是没有问题的。 在XAML中定义一些视觉效果很容易,然后使用代码隐藏文件的构造函数添加一些可能在循环中更好地生成的视觉效果。
内容属性
在前面的示例中,将StackLayout,Grid和AbsoluteLayout对象设置为ContentPage的Content属性,这些布局的子项实际上是Children集合中的项目。 然而,这些内容和儿童的属性是在XAML文件无处。
您当然可以包含Content和Children属性作为属性元素,例如在XamlPlusCode示例中:
-
<ContentPage xmlns=""
-
xmlns:x=""
-
x:Class="XamlSamples.XamlPlusCodePage"
-
Title="XAML + Code Page">
-
<ContentPage.Content>
-
<StackLayout>
-
<StackLayout.Children>
-
<Slider VerticalOptions="CenterAndExpand"
-
ValueChanged="OnSliderValueChanged" />
-
-
<Label x:Name="valueLabel"
-
Text="A simple Label"
-
FontSize="Large"
-
HorizontalOptions="Center"
-
VerticalOptions="CenterAndExpand" />
-
-
<Button Text="Click Me!"
-
HorizontalOptions="Center"
-
VerticalOptions="CenterAndExpand"
-
Clicked="OnButtonClicked" />
-
</StackLayout.Children>
-
</StackLayout>
-
</ContentPage.Content>
-
</ContentPage>
真正的问题是:为什么这些属性元素不需要在XAML文件中?
用于XAML的Xamarin.Forms中定义的元素允许在类的ContentProperty属性中标记一个属性。 如果您查看在线Xamarin.Forms文档中的ContentPage类,您将看到以下属性:
-
[Xamarin.Forms.ContentProperty("Content")]
-
public class ContentPage : TemplatedPage
这意味着Content属性元素标签不是必需的。 假定将出现在开始和结束ContentPage标记之间的任何XML内容分配给Content属性。
StackLayout,Grid,AbsoluteLayout和RelativeLayout都来自Layout
,如果你在Xamarin.Forms文档中查找Layout
,你会看到另一个ContentProperty属性:
-
[Xamarin.Forms.ContentProperty("Children")]
-
public abstract class Layout<T> : Layout ...
这允许将布局的内容自动添加到Children集合中,而无需显式的Children属性元素标签。
其他类也有ContentProperty属性定义。 例如,标签的内容属性是文本。 检查他人的API文档。
平台与OnPlatform的区别
在单页面应用程序中,通常在页面上设置Padding属性以避免覆盖iOS状态栏。 在代码中,您可以使用Device.RuntimePlatform属性来达到此目的:
-
if (Device.RuntimePlatform == Device.iOS)
-
{
-
Padding = new Thickness(0, 20, 0, 0);
-
}
您也可以使用OnPlatform和On类在XAML中执行类似的操作。 首先在页面顶部附近包含Padding属性的属性元素:
-
<ContentPage xmlns=""
-
xmlns:x=""
-
x:Class="...">
-
-
<ContentPage.Padding>
-
-
</ContentPage.Padding>
-
...
-
</ContentPage>
在这些标签中,包含一个OnPlatform标签。 OnPlatform是一个通用类。 您需要指定泛型类型参数,在这种情况下,厚度是Padding属性的类型。 幸运的是,有一个XAML属性专门用来定义一个名为x:TypeArguments的泛型参数。 这应该与您正在设置的属性的类型匹配:
-
<ContentPage xmlns=""
-
xmlns:x=""
-
x:Class="...">
-
-
<ContentPage.Padding>
-
<OnPlatform x:TypeArguments="Thickness">
-
-
</OnPlatform>
-
</ContentPage.Padding>
-
...
-
</ContentPage>
OnPlatform有一个名为Platforms的属性,它是On对象的IList。 为该属性使用属性元素标签:
-
<ContentPage xmlns=""
-
xmlns:x=""
-
x:Class="...">
-
-
<ContentPage.Padding>
-
<OnPlatform x:TypeArguments="Thickness">
-
<OnPlatform.Platforms>
-
-
</OnPlatform.Platforms>
-
</OnPlatform>
-
</ContentPage.Padding>
-
...
-
</ContentPage>
现在添加On元素。 对于每个onem,将厚度属性的平台属性和值属性设置为标记:
-
<ContentPage xmlns=""
-
xmlns:x=""
-
x:Class="...">
-
-
<ContentPage.Padding>
-
<OnPlatform x:TypeArguments="Thickness">
-
<OnPlatform.Platforms>
-
<On Platform="iOS" Value="0, 20, 0, 0" />
-
<On Platform="Android" Value="0, 0, 0, 0" />
-
<On Platform="UWP" Value="0, 0, 0, 0" />
-
</OnPlatform.Platforms>
-
</OnPlatform>
-
</ContentPage.Padding>
-
...
-
</ContentPage>
这个标记可以简化。 OnPlatform的内容属性是平台,所以这些属性元素标签可以被删除:
-
<ContentPage xmlns=""
-
xmlns:x=""
-
x:Class="...">
-
-
<ContentPage.Padding>
-
<OnPlatform x:TypeArguments="Thickness">
-
<On Platform="iOS" Value="0, 20, 0, 0" />
-
<On Platform="Android" Value="0, 0, 0, 0" />
-
<On Platform="UWP" Value="0, 0, 0, 0" />
-
</OnPlatform>
-
</ContentPage.Padding>
-
...
-
</ContentPage>
On的Platform属性的类型是IList ,因此如果值相同,则可以包含多个平台:
-
<ContentPage xmlns=""
-
xmlns:x=""
-
x:Class="...">
-
-
<ContentPage.Padding>
-
<OnPlatform x:TypeArguments="Thickness">
-
<On Platform="iOS" Value="0, 20, 0, 0" />
-
<On Platform="Android, UWP" Value="0, 0, 0, 0" />
-
</OnPlatform>
-
</ContentPage.Padding>
-
...
-
</ContentPage>
由于Android和Windows被设置为Padding的默认值,该标签可以被删除:
-
<ContentPage xmlns=""
-
xmlns:x=""
-
x:Class="...">
-
-
<ContentPage.Padding>
-
<OnPlatform x:TypeArguments="Thickness">
-
<On Platform="iOS" Value="0, 20, 0, 0" />
-
</OnPlatform>
-
</ContentPage.Padding>
-
...
-
</ContentPage>
这是在XAML中设置平台相关的Padding属性的标准方法。 如果“值”设置不能由单个字符串表示,则可以为其定义属性元素:
-
<ContentPage xmlns=""
-
xmlns:x=""
-
x:Class="...">
-
-
<ContentPage.Padding>
-
<OnPlatform x:TypeArguments="Thickness">
-
<On Platform="iOS">
-
<On.Value>
-
0, 20, 0, 0
-
</On.Value>
-
</On>
-
</OnPlatform>
-
</ContentPage.Padding>
-
...
-
</ContentPage>
概要
通过属性元素和附加属性,许多基本的XAML语法已经建立。 但是,有时您需要以间接方式(例如从资源字典中)将属性设置为对象。 这个方法在下一部分,第3部分中有介绍。XAML标记扩展。