Nelle lezioni precedenti, sono stati esaminati gli elementi di una schermata e la loro definizione in C#. Non sempre, però, definire gli oggetti in C# risulta essere un buon approccio, specie in presenza di schermate articolate che rendono la struttura del codice C# più complessa.
Una soluzione alternativa è offerta dal linguaggio dichiarativo XAML, basato su XML, sviluppato da Microsoft e largamente impiegato nelle applicazioni Windows Presentation Foundation (WPF), Windows Phone e UWP. In particolare, la versione implementata in Xamarin.Forms offre la possibilità di definire Layout
e View
, dividendo la definizione dell’interfaccia grafica dalla sua implementazione specifica.
I vantaggi offerti riguardano sicuramente una maggiore leggibilità (grazie alla tipica struttura gerarchica padre-figlio) e manutenzione del codice, alleggerendo così il codice del costruttore della pagina. Di contro ritroviamo tutte le limitazioni tipiche dell'XML, come la necessità di definire in C# il comportamento di un oggetto View
sulla base di un evento.
Anatomia di una pagina XAML
Entriamo in confidenza con la definizione di XAML offerta da Xamarin.Forms e creiamo una nuova Page da aggiungere al progetto portable, aggiungendo una nuova Forms Xaml Page
e chiamandola XAMLPageXample
.
Come si può notare dalla struttura del progetto, la nuova pagina è stata creata con estensione .xaml e si compone di due file (uno contenente il codice XAML, e l'altro il codice C#) offrendo una rappresentazione compatta della Page
. I due file sono correlati tra loro ed è uso comune chiamare il file C# con il nome code-behind del file XAML, proprio perché permette la definizione di aspetti specifici della schermata. Esaminiamo lo XAML creato con la pagina.
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="HelloXamarin.XAMLPageXample">
</ContentPage>
In questa porzione di codice ritroviamo due dichiarazioni di namespace XML. La prima dichiara che tutti i tag ed elementi senza prefisso, come ContentPage
, sono relativi agli oggetti di Xamarin.Forms. La seconda dichiarazione fornisce il prefisso x
che è da utilizzare per elementi ed attributi propri di XAML, come x:Class
. Attraverso quest’ultimo, inoltre, si definisce la relazione tra il codice XAML e il relativo code-behind, definito in questo caso dalla classe XAMLPageXample
. Tale attributo è presente solo ed esclusivamente all’interno dell’elemento root del file XAML, che deve essere necessariamente di tipo Page
. Analizziamo ora il costruttore della Page
public XAMLPageXample()
{
InitializeComponent();
}
A differenza delle precedenti versioni, ritroviamo il metodo InitializeComponent
responsabile del caricamento dello XAML. Per rendere possibile quest’ultimo, è indispensabile compilare ed eseguire il codice, poiché durante la compilazione viene generato un file C# a partire dallo XAML, file contenuto all’interno della cartella .\HelloXamarin\HelloXamarin\HelloXamarin\obj\Debug e il cui nome è HelloXamarin.XAMLPageXample.xaml.g.cs. All’interno del file C# ritroviamo una classe partial che definisce al suo interno il metodo InitializeComponent
:
public partial class HelloXamlPage : ContentPage
{
private void InitializeComponent()
{
this.LoadFromXaml(typeof(HelloXamlPage));
}
}
In questa implementazione viene richiamato il metodo LoadFromXaml
che estrae e analizza il file XAML incorporato nel PCL durante la compilazione. Inoltre, vengono istanziati e inizializzati tutti gli elementi presenti nello XAML, definendo le relative dipendenze padre-figlio.
Definizione degli elementi di una Page in XAML
Compresi i meccanismi alla base del caricamento di una Forms Xaml Page, muoviamo i primi passi con XAML e definiamo una struttura che si compone di due StackLayout
annidati, contenenti diverse View
.
Per ottenere la medesima implementazione, è necessario apportare diverse modifiche alla pagina XAML definendo prima di tutto i due StackLayout
annidati e contenuti all’interno della ContentPage
.
<StackLayout Padding="20">
<StackLayout VerticalOptions="FillAndExpand" BackgroundColor="Gray" Spacing="30" Padding="10"/>
</StackLayout>
Tramite lo XAML, tale operazione risulta immediata e di facile lettura, offrendo la possibilità di definire diverse proprietà per ogni layout. In particolare, per lo StackLayout
padre è stata impostata la proprietà Padding
, mentre per lo StackLayout
figlio sono stati definiti anche il colore del background, la distanza tra gli elementi in esso contenuti e la sua disposizione nel layout padre. Analogamente, la definizione degli altri elementi risulta immediata. Vediamo ad esempio la Label
contenuta nel primo StackLayout
.
<Label x:Name="lbl"
VerticalOptions="Start"
HorizontalOptions="Center"
FontSize="20"
TextColor="Lime"/>
Come visto in C#, anche in XAML la definizione di una View
è immediata. In questo caso sono stati specificati la posizione dell’elemento nel nodo padre che lo contiene, la sua dimensione, colore e il nome. Grazie alla proprietà x:Name
possiamo richiamare e interagire con l’elemento nel code-behind modificando dinamicamente il contenuto della Label
. Infatti, la Label lbl
verrà visualizzata come campo del code-behind ed è visibile dalla finestra Esplora Soluzione, come si vede nella figura seguente.
A questo punto non resta che aggiornare il costruttore della pagina nel code-behind, aggiungendo dopo l’inizializzazione della pagina quanto segue:
lbl.Text = "XAML Example";
Altro aspetto interessante è il View-to-View binding, che collega le proprietà di due View
tra loro impostando la proprietà BindingContext
della View
di interesse con l'attributo x:Reference
. Ad esempio:
<Slider x:Name="sldr"
Maximum="100" />
<Label BindingContext="{x:Reference sldr}"
Text="{Binding Value, StringFormat='Value: {0:F0}'}"/>
Così facendo, è stata definita una Label il cui testo si modifica in accordo al valore assunto dall’attributo Value
dello Slider
.
Il codice completo della pagina XAML è allegato a questa lezione e disponibile su GitHub ai seguenti link:
Eseguendo il codice su ogni piattaforma, il risultato ottenuto sarà quello mostrato di seguito.