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>
    </Label.TextColor>
</Label>

这两种指定 TextColor 属性的方法在功能上是等效的,但不要对同一属性使用这两种方法,因为这实际上将设置属性两次,并且可能不明确。

使用此新语法,可以引入一些方便的术语:

  • Label对象元素。 它是表示 Xamarin.Forms 为 XML 元素的对象。
  • TextVerticalOptionsFontAttributesFontSize属性属性。 Xamarin.Forms它们是表示为 XML 属性的属性。
  • 在该最终代码片段中, TextColor 已成为 一个属性元素。 它是一个 Xamarin.Forms 属性,但它现在是一个 XML 元素。
  • 属性元素的定义最初可能违反了 XML 语法,但它不是。 句点在 XML 中没有特殊含义。 对于 XML 解码器, Label.TextColor 只是普通子元素。

    但是,在 XAML 中,此语法非常特殊。 属性元素的一个规则是,标记中 Label.TextColor 无法显示其他任何规则。 属性的值始终定义为属性元素开始和结束标记之间的内容。

    可以对多个属性使用属性元素语法:

    <Label Text="Hello, XAML!"
           VerticalOptions="Center">
        <Label.FontAttributes>
        </Label.FontAttributes>
        <Label.FontSize>
            Large
        </Label.FontSize>
        <Label.TextColor>
        </Label.TextColor>
    </Label>
    

    或者,可以对所有属性使用属性元素语法:

    <Label>
        <Label.Text>
            Hello, XAML!
        </Label.Text>
        <Label.FontAttributes>
        </Label.FontAttributes>
        <Label.FontSize>
            Large
        </Label.FontSize>
        <Label.TextColor>
        </Label.TextColor>
        <Label.VerticalOptions>
            Center
        </Label.VerticalOptions>
    </Label>
    

    起初,属性元素语法似乎对相对简单的东西来说是不必要的长风替换,在这些示例中,当然是这样。

    但是,当属性的值过于复杂,无法表示为简单字符串时,属性元素语法就变得至关重要。 在属性元素标记中,可以实例化另一个对象并设置其属性。 例如,可以显式设置属性,例如 VerticalOptions 具有 LayoutOptions 属性设置的值:

    <Label>
        <Label.VerticalOptions>
            <LayoutOptions Alignment="Center" />
        </Label.VerticalOptions>
    </Label>
    

    另一个示例:具有 Grid 两个名为 RowDefinitionsColumnDefinitions. 这两个属性的类型RowDefinitionCollectionColumnDefinitionCollection对象集合RowDefinitionColumnDefinition。 需要使用属性元素语法来设置这些集合。

    下面是类的 XAML 文件的GridDemoPage开头,其中显示了集合的属性ColumnDefinitions元素标记RowDefinitions

    <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 x:Class="XamlSamples.GridDemoPage"
                 Title="Grid Demo Page">
            <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 需要元素 RowDefinitionsColumnDefinitions 集合的属性元素来定义行和列。 但是,程序员还必须有某种方法来指示每个子级 Grid 所在的行和列。

    在标记中, Grid 使用以下属性指定该子项的行和列:

  • Grid.Row
  • Grid.Column
  • 这些属性的默认值为 0。 还可以指示子级是否跨多个行或具有以下属性的列:

  • Grid.RowSpan
  • Grid.ColumnSpan
  • 这两个属性的默认值为 1。

    下面是完整的 GridDemoPage.xaml 文件:

    <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 x:Class="XamlSamples.GridDemoPage"
                 Title="Grid Demo Page">
            <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>
    

    Grid.Row不需要 0 和Grid.Column设置,但通常包含在明确性方面。

    如下所示:

    从语法来看,这些、属性和属性似乎是静态字段或属性Grid,但有趣的是,Grid没有定义任何命名RowColumn、或RowSpanColumnSpanGrid.ColumnSpanGrid.RowSpanGrid.ColumnGrid.Row

    Grid而是定义四个可绑定属性,名为 RowPropertyColumnPropertyRowSpanPropertyColumnSpanProperty。 这些是称为 附加属性的特殊类型的可绑定属性。 它们由 Grid 类定义,但设置在子 Grid级上。

    如果要在代码中使用这些附加属性,类Grid会提供命名SetRowGetColumn的静态方法,等等。 但在 XAML 中,这些附加属性在使用简单属性名称的 Grid 子级中设置为属性。

    附加属性在 XAML 文件中始终可识别为包含类和由句点分隔的属性名称的属性。 它们称为 附加属性 ,因为它们在本例中由一个类 (定义, Grid) 但附加到此示例中的其他对象 (,即) 的 Grid 子级。 在布局期间, Grid 可以询问这些附加属性的值,以了解放置每个子项的位置。

    AbsoluteLayout 类定义两个名为 LayoutBoundsLayoutFlags. 的附加属性。 下面是使用比例定位和大小调整功能 AbsoluteLayout实现的棋板模式:

    <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 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 中定义某些视觉对象很容易,然后使用代码隐藏文件的构造函数添加一些可能更好地在循环中生成的视觉对象。

    在前面的示例中,对象StackLayoutGridAbsoluteLayout对象设置为Content属性ContentPage,这些布局的子级实际上是集合中的Children项。 然而,这些 Content 属性和 Children 属性在 XAML 文件中没有位置。

    当然,可以包括ContentChildren属性元素和属性,例如在 XamlPlusCode 示例中:

    <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 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 文件中 不需要 这些属性元素?

    允许在 Xamarin.Forms XAML 中使用的元素在类的属性 ContentProperty 中标记一个属性。 如果在联机Xamarin.Forms文档中查找ContentPage该类,你将看到此属性:

    [Xamarin.Forms.ContentProperty("Content")]
    public class ContentPage : TemplatedPage
    

    这意味着 Content 不需要属性元素标记。 假定在开始标记和结束 ContentPage 标记之间显示的任何 XML 内容都分配给该 Content 属性。

    StackLayoutGridAbsoluteLayoutRelativeLayout所有派生自Layout<View>,如果在文档中查找Layout<T>Xamarin.Forms,你将看到另一个ContentProperty属性:

    [Xamarin.Forms.ContentProperty("Children")]
    public abstract class Layout<T> : Layout ...
    

    这允许将布局的内容自动添加到集合中 Children ,而无需显式 Children 属性元素标记。

    其他类还具有 ContentProperty 属性定义。 例如,内容属性为 LabelText. 查看其他 API 文档。

    与 OnPlatform 的平台差异

    在单页应用程序中,通常设置 Padding 页面上的属性以避免覆盖 iOS 状态栏。 在代码中,可以使用此属性 Device.RuntimePlatform 实现此目的:

    if (Device.RuntimePlatform == Device.iOS)
        Padding = new Thickness(0, 20, 0, 0);
    

    还可以使用 OnPlatformOn 类在 XAML 中执行类似操作。 首先包括页面顶部附近属性的属性元素 Padding

    <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 x:Class="...">
        <ContentPage.Padding>
        </ContentPage.Padding>
    </ContentPage>
    

    在这些标记中,包括标记 OnPlatformOnPlatform 是泛型类。 在这种情况下,需要指定泛型类型参数, Thickness即属性的类型 Padding 。 幸运的是,有一个 XAML 属性专门定义调用 x:TypeArguments的泛型参数。 这应与要设置的属性的类型匹配:

    <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 x:Class="...">
        <ContentPage.Padding>
            <OnPlatform x:TypeArguments="Thickness">
            </OnPlatform>
        </ContentPage.Padding>
    </ContentPage>
    

    OnPlatform 具有一 PlatformsIListOn 个名为对象的属性。 对该属性使用属性元素标记:

    <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 x:Class="...">
        <ContentPage.Padding>
            <OnPlatform x:TypeArguments="Thickness">
                <OnPlatform.Platforms>
                </OnPlatform.Platforms>
            </OnPlatform>
        </ContentPage.Padding>
    </ContentPage>
    

    现在添加 On 元素。 对于每个属性,将 Platform 属性和 Value 属性设置为 Thickness 标记属性:

    <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 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>
    

    可以简化此标记。 的内容属性 OnPlatformPlatforms因此可以删除这些属性元素标记:

    <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 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<string>,因此,如果值相同,则可以包含多个平台:

    <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 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 和 UWP 设置为默认值 Padding,因此可以删除该标记:

    <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 x:Class="...">
        <ContentPage.Padding>
            <OnPlatform x:TypeArguments="Thickness">
                <On Platform="iOS" Value="0, 20, 0, 0" />
            </OnPlatform>
        </ContentPage.Padding>
    </ContentPage>
    

    这是在 XAML 中设置依赖于 Padding 平台的属性的标准方法。 Value如果设置不能由单个字符串表示,则可以为其定义属性元素:

    <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 x:Class="...">
        <ContentPage.Padding>
            <OnPlatform x:TypeArguments="Thickness">
                <On Platform="iOS">
                    <On.Value>
                        0, 20, 0, 0
                    </On.Value>
            </OnPlatform>
        </ContentPage.Padding>
    </ContentPage>
    

    还可以 OnPlatform 在 XAML 中使用标记扩展来自定义每个平台的 UI 外观。 它提供的功能与OnPlatformOn类相同,但具有更简洁的表示形式。 有关详细信息,请参阅 OnPlatform 标记扩展

    使用属性元素和附加属性,已建立大部分基本 XAML 语法。 但是,有时需要以间接方式将属性设置为对象,例如,从资源字典中设置属性。 下一部分第 3 部分介绍了此方法 。XAML 标记扩展

  • XamlSamples
  • 第 1 部分。 XAML 入门
  • 第 3 部分。 XAML 标记扩展
  • 第 4 部分。 数据绑定基础知识
  • 第 5 部分。 从数据绑定到 MVVM
  •