Condividi tramite


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, VerticalOptionsFontAttributes e FontSize 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 è riportato l'aspetto seguente:

Layout griglia

A giudicare esclusivamente dalla sintassi, questi Grid.Rowattributi , Grid.Column, Grid.RowSpane Grid.ColumnSpan sembrano essere campi statici o proprietà di Grid, ma abbastanza interessante, Grid non definisce nulla denominato Row, Column, RowSpano ColumnSpan.

Definisce invece Grid quattro proprietà associabili denominate RowProperty, ColumnProperty, RowSpanPropertye 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, GetColumne 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:

Layout assoluto

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 StackLayoutoggetti , Gride AbsoluteLayout vengono impostati sulla Content proprietà di ContentPagee 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, AbsoluteLayoute 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 , Thicknessche è 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 IListOn 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.