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 TextColor
o , 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
,VerticalOptions
eFontAttributes
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:
A julgar apenas pela sintaxe, esses Grid.Row
atributos , 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á:
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 StackLayout
objetos , 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
, AbsoluteLayout
e 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.