Part 2 (Distribuire macchine virtuali nel cloud: Parte 2). Sintassi XAML essenziale
XAML è progettato principalmente per la creazione di istanze e l'inizializzazione di oggetti. Spesso, tuttavia, le proprietà devono essere impostate su oggetti complessi che non possono essere facilmente rappresentati come stringhe XML e talvolta le proprietà definite da una classe devono essere impostate su una classe figlio. Queste due esigenze richiedono le funzionalità di sintassi XAML essenziali degli elementi delle proprietà e delle proprietà associate.
Elementi proprietà
In XAML le proprietà delle classi vengono in genere impostate come attributi XML:
<Label Text="Hello, XAML!"
VerticalOptions="Center"
FontAttributes="Bold"
FontSize="Large"
TextColor="Aqua" />
Esiste tuttavia un modo alternativo per impostare una proprietà in XAML. Per provare questa alternativa con TextColor
, eliminare prima l'impostazione esistente TextColor
:
<Label Text="Hello, XAML!"
VerticalOptions="Center"
FontAttributes="Bold"
FontSize="Large" />
Aprire il tag empty-element Label
separandolo in tag iniziale e finale:
<Label Text="Hello, XAML!"
VerticalOptions="Center"
FontAttributes="Bold"
FontSize="Large">
</Label>
All'interno di questi tag, aggiungere tag iniziale e finale costituiti dal nome della classe e da un nome di proprietà separati da un punto:
<Label Text="Hello, XAML!"
VerticalOptions="Center"
FontAttributes="Bold"
FontSize="Large">
<Label.TextColor>
</Label.TextColor>
</Label>
Impostare il valore della proprietà come contenuto di questi nuovi tag, come illustrato di seguito:
<Label Text="Hello, XAML!"
VerticalOptions="Center"
FontAttributes="Bold"
FontSize="Large">
<Label.TextColor>
Aqua
</Label.TextColor>
</Label>
Questi due modi per specificare la TextColor
proprietà sono equivalenti a livello funzionale, ma non usano i due modi per la stessa proprietà, perché ciò sarebbe effettivamente l'impostazione della proprietà due volte e potrebbe essere ambiguo.
Con questa nuova sintassi, è possibile introdurre una terminologia utile:
Label
è un elemento dell'oggetto. Si tratta di un Xamarin.Forms oggetto espresso come elemento XML.Text
,VerticalOptions
FontAttributes
eFontSize
sono attributi di proprietà. Sono Xamarin.Forms proprietà espresse come attributi XML.- In quel frammento finale è
TextColor
diventato un elemento di proprietà. Si tratta di una Xamarin.Forms proprietà, ma è ora un elemento XML.
La definizione degli elementi di proprietà potrebbe inizialmente sembrare una violazione della sintassi XML, ma non lo è. Il punto non ha alcun significato speciale in XML. Per un decodificatore XML, Label.TextColor
è semplicemente un elemento figlio normale.
In XAML, tuttavia, questa sintassi è molto speciale. Una delle regole per gli elementi della proprietà è che nessun altro elemento può essere visualizzato nel Label.TextColor
tag. Il valore della proprietà viene sempre definito come contenuto tra i tag iniziale e finale dell'elemento proprietà.
È possibile usare la sintassi dell'elemento proprietà in più di una proprietà:
<Label Text="Hello, XAML!"
VerticalOptions="Center">
<Label.FontAttributes>
Bold
</Label.FontAttributes>
<Label.FontSize>
Large
</Label.FontSize>
<Label.TextColor>
Aqua
</Label.TextColor>
</Label>
In alternativa, è possibile usare la sintassi dell'elemento proprietà per tutte le proprietà:
<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>
In un primo momento, la sintassi degli elementi di proprietà potrebbe sembrare una sostituzione eccessivamente a lungo vento per qualcosa relativamente semplice, e in questi esempi è certamente il caso.
Tuttavia, la sintassi dell'elemento proprietà diventa essenziale quando il valore di una proprietà è troppo complesso da esprimere come stringa semplice. All'interno dei tag property-element è possibile creare un'istanza di un altro oggetto e impostarne le proprietà. Ad esempio, è possibile impostare in modo esplicito una proprietà, VerticalOptions
ad esempio su un LayoutOptions
valore con le impostazioni delle proprietà:
<Label>
...
<Label.VerticalOptions>
<LayoutOptions Alignment="Center" />
</Label.VerticalOptions>
</Label>
Un altro esempio: ha Grid
due proprietà denominate RowDefinitions
e ColumnDefinitions
. Queste due proprietà sono di tipo RowDefinitionCollection
e ColumnDefinitionCollection
, che sono raccolte di RowDefinition
oggetti e ColumnDefinition
. Per impostare queste raccolte, è necessario usare la sintassi degli elementi delle proprietà.
Ecco l'inizio del file XAML per una GridDemoPage
classe, che mostra i tag degli elementi di proprietà per le RowDefinitions
raccolte 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>
Si noti la sintassi abbreviata per la definizione di celle ridimensionate automaticamente, celle di larghezze e altezze in pixel e impostazioni stella.
Proprietà associate
Si è appena visto che richiede Grid
elementi di proprietà per le RowDefinitions
raccolte e ColumnDefinitions
per definire le righe e le colonne. Tuttavia, è necessario che il programmatore indichi la riga e la colonna in cui risiede ogni elemento figlio dell'oggetto Grid
.
All'interno del tag per ogni elemento figlio di Grid
si specifica la riga e la colonna di tale elemento figlio usando gli attributi seguenti:
Grid.Row
Grid.Column
I valori predefiniti di questi attributi sono 0. È anche possibile indicare se un elemento figlio si estende su più righe o colonne con questi attributi:
Grid.RowSpan
Grid.ColumnSpan
Questi due attributi hanno valori predefiniti pari a 1.
Ecco il file 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>
Le Grid.Row
impostazioni e Grid.Column
di 0 non sono necessarie, ma sono generalmente incluse ai fini della chiarezza.
Di seguito è riportata un'immagine di tale finestra:
A giudicare esclusivamente dalla sintassi, questi Grid.Row
attributi , Grid.Column
, Grid.RowSpan
e Grid.ColumnSpan
sembrano essere campi statici o proprietà di Grid
, ma abbastanza interessante, Grid
non definisce nulla denominato Row
, Column
, RowSpan
o ColumnSpan
.
Definisce invece Grid
quattro proprietà associabili denominate RowProperty
, ColumnProperty
, RowSpanProperty
e ColumnSpanProperty
. Si tratta di tipi speciali di proprietà associabili note come proprietà associate. Sono definiti dalla Grid
classe ma impostati sugli elementi figlio di Grid
.
Quando si desidera usare queste proprietà associate nel codice, la Grid
classe fornisce metodi statici denominati SetRow
, GetColumn
e così via. In XAML, tuttavia, queste proprietà associate vengono impostate come attributi negli elementi figlio dell'oggetto Grid
usando nomi di proprietà semplici.
Le proprietà associate sono sempre riconoscibili nei file XAML come attributi contenenti sia una classe che un nome di proprietà separati da un punto. Vengono chiamate proprietà associate perché sono definite da una classe (in questo caso , Grid
) ma associate ad altri oggetti (in questo caso, elementi figlio di Grid
). Durante il layout, può Grid
interrogare i valori di queste proprietà associate per sapere dove posizionare ogni figlio.
La AbsoluteLayout
classe definisce due proprietà associate denominate LayoutBounds
e LayoutFlags
. Ecco un modello checkerboard realizzato usando le caratteristiche di posizionamento proporzionale e ridimensionamento di 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>
Ecco il risultato:
Per qualcosa di simile a questo, potresti mettere in dubbio la saggezza dell'uso di XAML. Certamente, la ripetizione e la regolarità del LayoutBounds
rettangolo suggeriscono che potrebbe essere meglio realizzato nel codice.
Questo è certamente un problema legittimo e non c'è alcun problema con il bilanciamento dell'uso del codice e del markup quando si definiscono le interfacce utente. È facile definire alcuni oggetti visivi in XAML e quindi usare il costruttore del file code-behind per aggiungere altri oggetti visivi che potrebbero essere generati meglio nei cicli.
Proprietà di contenuto
Negli esempi precedenti, gli StackLayout
oggetti , Grid
e AbsoluteLayout
vengono impostati sulla Content
proprietà di ContentPage
e gli elementi figlio di questi layout sono effettivamente elementi dell'insieme Children
. Tuttavia, queste Content
proprietà e Children
non sono in nessuna posizione nel file XAML.
Puoi certamente includere le Content
proprietà e Children
come elementi di proprietà, ad esempio nell'esempio 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>
La domanda reale è: Perché questi elementi di proprietà non sono necessari nel file XAML?
Gli elementi definiti in Xamarin.Forms per l'uso in XAML possono avere una proprietà contrassegnata nell'attributo ContentProperty
nella classe . Se si cerca la ContentPage
classe nella documentazione online Xamarin.Forms , verrà visualizzato questo attributo:
[Xamarin.Forms.ContentProperty("Content")]
public class ContentPage : TemplatedPage
Ciò significa che i tag dell'elemento Content
proprietà non sono obbligatori. Si presuppone che qualsiasi contenuto XML visualizzato tra i tag iniziale e ContentPage
finale venga assegnato alla Content
proprietà .
StackLayout
, Grid
, AbsoluteLayout
e RelativeLayout
tutti derivano da Layout<View>
e, se si cerca Layout<T>
nella Xamarin.Forms documentazione, verrà visualizzato un altro ContentProperty
attributo:
[Xamarin.Forms.ContentProperty("Children")]
public abstract class Layout<T> : Layout ...
Ciò consente l'aggiunta automatica del contenuto del layout alla Children
raccolta senza tag di elemento proprietà espliciti Children
.
Altre classi hanno ContentProperty
anche definizioni di attributi. Ad esempio, la proprietà content di Label
è Text
. Vedere la documentazione dell'API per altri utenti.
Differenze della piattaforma con OnPlatform
Nelle applicazioni a pagina singola, è comune impostare la Padding
proprietà nella pagina per evitare di sovrascrivere la barra di stato iOS. Nel codice è possibile usare la Device.RuntimePlatform
proprietà a questo scopo:
if (Device.RuntimePlatform == Device.iOS)
{
Padding = new Thickness(0, 20, 0, 0);
}
Puoi anche eseguire operazioni simili in XAML usando le OnPlatform
classi e On
. Includere innanzitutto gli elementi delle proprietà per la Padding
proprietà nella parte superiore della pagina:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="...">
<ContentPage.Padding>
</ContentPage.Padding>
...
</ContentPage>
All'interno di questi tag, includere un OnPlatform
tag. OnPlatform
è una classe generica. È necessario specificare l'argomento di tipo generico, in questo caso , Thickness
che è il tipo di Padding
proprietà . Fortunatamente, esiste un attributo XAML specifico per definire argomenti generici denominati x:TypeArguments
. Deve corrispondere al tipo della proprietà che si sta impostando:
<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
dispone di una proprietà denominata Platforms
di IList
On
oggetti . Usare i tag dell'elemento proprietà per tale proprietà:
<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
Aggiungere ora elementi. Per ognuno impostare la Platform
proprietà e la Value
proprietà su markup per la Thickness
proprietà :
<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>
Questo markup può essere semplificato. La proprietà content di OnPlatform
è Platforms
, in modo che i tag dell'elemento proprietà possano essere rimossi:
<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>
La Platform
proprietà di On
è di tipo IList<string>
, quindi è possibile includere più piattaforme se i valori sono uguali:
<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>
Poiché Android e UWP sono impostati sul valore predefinito di Padding
, tale tag può essere rimosso:
<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>
Questo è il modo standard per impostare una proprietà dipendente dalla Padding
piattaforma in XAML. Se l'impostazione Value
non può essere rappresentata da una singola stringa, è possibile definirne gli elementi di proprietà:
<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>
Nota
L'estensione OnPlatform
di markup può essere usata anche in XAML per personalizzare l'aspetto dell'interfaccia utente in base alla piattaforma. Offre le stesse funzionalità delle OnPlatform
classi e On
, ma con una rappresentazione più concisa. Per altre informazioni, vedere Estensione di markup OnPlatform.
Riepilogo
Con gli elementi della proprietà e le proprietà associate, è stata stabilita gran parte della sintassi XAML di base. Tuttavia, a volte è necessario impostare proprietà su oggetti in modo indiretto, ad esempio da un dizionario risorse. Questo approccio è trattato nella parte successiva, parte 3. Estensioni di markup XAML.