Partilhar via


Parte 2. Sintaxe essencial de XAML

O XAML foi projetado principalmente para instanciar e inicializar objetos. Mas, muitas vezes, as propriedades devem ser definidas como objetos complexos que não podem ser facilmente representados como cadeias de caracteres XML e, às vezes, as propriedades definidas por uma classe devem ser definidas em uma classe filho. Essas duas necessidades exigem os recursos essenciais de sintaxe XAML de elementos de propriedade e propriedades anexadas.

Elementos de propriedade

Em XAML, as propriedades das classes são normalmente definidas como atributos XML:

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

No entanto, há uma maneira alternativa de definir uma propriedade em XAML. Para tentar essa alternativa com TextColoro , primeiro exclua a configuração existente TextColor :

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

Abra a tag empty-element Label separando-a em tags de início e fim:

<Label Text="Hello, XAML!"
       VerticalOptions="Center"
       FontAttributes="Bold"
       FontSize="Large">

</Label>

Dentro dessas tags, adicione tags de início e fim que consistem no nome da classe e um nome de propriedade separados por um ponto:

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

    </Label.TextColor>
</Label>

Defina o valor da propriedade como conteúdo dessas novas tags, assim:

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

Essas duas maneiras de especificar a TextColor propriedade são funcionalmente equivalentes, mas não use as duas maneiras para a mesma propriedade porque isso seria efetivamente definir a propriedade duas vezes e pode ser ambíguo.

Com essa nova sintaxe, algumas terminologias úteis podem ser introduzidas:

  • Label é um elemento de objeto. É um Xamarin.Forms objeto expresso como um elemento XML.
  • Text, VerticalOptionse FontAttributes FontSize são atributos de propriedade. Xamarin.Forms São propriedades expressas como atributos XML.
  • Nesse trecho final, TextColor tornou-se um elemento de propriedade. É uma Xamarin.Forms propriedade, mas agora é um elemento XML.

A definição de elementos de propriedade pode parecer à primeira vista uma violação da sintaxe XML, mas não é. O ponto não tem nenhum significado especial em XML. Para um decodificador XML, Label.TextColor é simplesmente um elemento filho normal.

No XAML, no entanto, essa sintaxe é muito especial. Uma das regras para elementos de propriedade é que nada mais pode aparecer na Label.TextColor tag. O valor da propriedade é sempre definido como conteúdo entre as tags de início e término do elemento de propriedade.

Você pode usar a sintaxe property-element em mais de uma propriedade:

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

Ou você pode usar a sintaxe de elemento de propriedade para todas as propriedades:

<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>

A princípio, a sintaxe do elemento de propriedade pode parecer uma substituição desnecessária e prolixa para algo comparativamente bastante simples e, nesses exemplos, esse é certamente o caso.

No entanto, a sintaxe do elemento de propriedade torna-se essencial quando o valor de uma propriedade é muito complexo para ser expresso como uma cadeia de caracteres simples. Dentro das tags de elemento de propriedade, você pode instanciar outro objeto e definir suas propriedades. Por exemplo, você pode definir explicitamente uma propriedade, como VerticalOptions um LayoutOptions valor com configurações de propriedade:

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

Outro exemplo: O Grid tem duas propriedades chamadas RowDefinitions e ColumnDefinitions. Essas duas propriedades são do tipo RowDefinitionCollection e ColumnDefinitionCollection, que são coleções de RowDefinition e ColumnDefinition objetos. Você precisa usar a sintaxe do elemento de propriedade para definir essas coleções.

Aqui está o início do arquivo XAML para uma GridDemoPage classe, mostrando as marcas de elemento de propriedade para as RowDefinitions coleções e ColumnDefinitions :

<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>
        <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>

Observe a sintaxe abreviada para definir células dimensionadas automaticamente, células de larguras e alturas de pixel e configurações de estrela.

Propriedades Anexadas

Você acabou de ver que os Grid elementos de propriedade requires para as RowDefinitions coleções and ColumnDefinitions definirem as linhas e colunas. No entanto, também deve haver alguma maneira de o programador indicar a linha e a coluna onde cada filho do Grid reside.

Na tag de cada filho do Grid você especifica a linha e a coluna desse filho usando os seguintes atributos:

  • Grid.Row
  • Grid.Column

Os valores padrão desses atributos são 0. Você também pode indicar se um filho abrange mais de uma linha ou coluna com estes atributos:

  • Grid.RowSpan
  • Grid.ColumnSpan

Esses dois atributos têm valores padrão de 1.

Aqui está o arquivo GridDemoPage.xaml completo:

<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>
        <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>

As Grid.Row configurações e Grid.Column de 0 não são necessárias, mas geralmente são incluídas para fins de clareza.

Esta é a aparência dele:

Layout de grade

A julgar apenas pela sintaxe, esses Grid.Rowatributos , Grid.Column, Grid.RowSpan, e Grid.ColumnSpan parecem ser campos estáticos ou propriedades de Grid, mas, curiosamente, Grid não definem nada chamado Row, Column, RowSpan, ou ColumnSpan.

Em vez disso, Grid define quatro propriedades associáveis chamadas RowProperty, ColumnProperty, RowSpanProperty, e ColumnSpanProperty. Esses são tipos especiais de propriedades vinculáveis conhecidas como propriedades anexadas. Eles são definidos pela classe, Grid mas definidos em filhos do Grid.

Quando você deseja usar essas propriedades anexadas no código, a Grid classe fornece métodos estáticos chamados SetRow, GetColumn, e assim por diante. Mas em XAML, essas propriedades anexadas são definidas como atributos nos filhos dos nomes de Grid propriedades simples using.

As propriedades anexadas são sempre reconhecíveis em arquivos XAML como atributos que contêm uma classe e um nome de propriedade separados por um ponto. Eles são chamados de propriedades anexadas porque são definidos por uma classe (neste caso, Grid) mas anexados a outros objetos (neste caso, filhos do Grid). Durante o layout, o Grid pode interrogar os valores dessas propriedades anexadas para saber onde colocar cada filho.

A AbsoluteLayout classe define duas propriedades anexadas chamadas LayoutBounds e LayoutFlags. Aqui está um padrão quadriculado realizado usando os recursos proporcionais de posicionamento e dimensionamento de 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>

Aqui está:

Layout Absoluto

Para algo assim, você pode questionar a sabedoria de usar XAML. Certamente, a repetição e a LayoutBounds regularidade do retângulo sugerem que ele pode ser melhor realizado em código.

Essa é certamente uma preocupação legítima e não há problema em equilibrar o uso de código e marcação ao definir suas interfaces de usuário. É fácil definir alguns dos visuais em XAML e, em seguida, usar o construtor do arquivo code-behind para adicionar mais alguns visuais que podem ser melhor gerados em loops.

Propriedades de Conteúdo

Nos exemplos anteriores, os StackLayoutobjetos , Grid, e AbsoluteLayout são definidos como a Content propriedade do ContentPage, e os filhos desses layouts são, na verdade, itens na Children coleção. No entanto, essas Content propriedades e Children não estão em nenhum lugar no arquivo XAML.

Você certamente pode incluir as Content propriedades and Children como elementos de propriedade, como no exemplo 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>

A verdadeira questão é: por que esses elementos de propriedade não são necessários no arquivo XAML?

Os elementos definidos para Xamarin.Forms uso em XAML têm permissão para ter uma propriedade sinalizada ContentProperty no atributo na classe. Se você procurar a ContentPage classe na documentação online Xamarin.Forms , verá este atributo:

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

Isso significa que as Content tags de elemento de propriedade não são necessárias. Qualquer conteúdo XML que apareça entre as marcas de início e fim ContentPage é considerado atribuído à Content propriedade.

StackLayout, Grid, AbsoluteLayoute RelativeLayout todos derivam de Layout<View>, e se você procurar Layout<T> na Xamarin.Forms documentação, verá outro ContentProperty atributo:

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

Isso permite que o conteúdo do layout seja adicionado automaticamente à Children coleção sem marcas explícitas Children de elemento de propriedade.

Outras classes também têm ContentProperty definições de atributo. Por exemplo, a propriedade content de Label is Text. Verifique a documentação da API para outros.

Diferenças de plataforma com OnPlatform

Em aplicativos de página única, é comum definir a Padding propriedade na página para evitar a substituição da barra de status do iOS. No código, você pode usar a Device.RuntimePlatform propriedade para essa finalidade:

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

Você também pode fazer algo semelhante em XAML usando as OnPlatform classes and On . Primeiro, inclua elementos de propriedade para a Padding propriedade perto da parte superior da página:

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

    <ContentPage.Padding>

    </ContentPage.Padding>
    ...
</ContentPage>

Dentro dessas tags, inclua uma OnPlatform tag. OnPlatform é uma classe genérica. Você precisa especificar o argumento de tipo genérico, neste caso, Thickness, que é o tipo de Padding propriedade. Felizmente, há um atributo XAML especificamente para definir argumentos genéricos chamado x:TypeArguments. Isso deve corresponder ao tipo de propriedade que você está definindo:

<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 tem uma propriedade chamada Platforms que é um IList de On objetos. Use tags de elemento de propriedade para essa propriedade:

<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>

Agora adicione On elementos. Para cada um, defina a Platform propriedade e a Value propriedade a serem marcadas para a Thickness propriedade:

<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>

Essa marcação pode ser simplificada. A propriedade content de OnPlatform é Platforms, portanto, essas tags de elemento de propriedade podem ser removidas:

<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>

A Platform propriedade de On é do tipo IList<string>, portanto, você pode incluir várias plataformas se os valores forem os mesmos:

<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>

Como o Android e a UWP são definidos com o valor padrão de Padding, essa marca pode ser removida:

<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>

Essa é a maneira padrão de definir uma propriedade dependente Padding de plataforma em XAML. Se a Value configuração não puder ser representada por uma única string, você poderá definir elementos de propriedade para ela:

<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>
            </On>
        </OnPlatform>
    </ContentPage.Padding>
  ...
</ContentPage>

Observação

A OnPlatform extensão de marcação também pode ser usada em XAML para personalizar a aparência da interface do usuário por plataforma. Ele fornece a mesma funcionalidade das classes OnPlatform e On, mas com uma representação mais concisa. Para obter mais informações, consulte Extensão de marcação OnPlatform.

Resumo

Com elementos de propriedade e propriedades anexadas, grande parte da sintaxe XAML básica foi estabelecida. No entanto, às vezes você precisa definir propriedades para objetos de maneira indireta, por exemplo, de um dicionário de recursos. Essa abordagem é abordada na próxima parte, Parte 3. Extensões de marcação XAML.