Xamarin.Forms Binding compilati
Le associazioni compilate vengono risolte più rapidamente rispetto alle associazioni classiche, migliorando quindi le prestazioni del data binding nelle Xamarin.Forms applicazioni.
I data binding presentano due problemi principali:
- Non è prevista la convalida in fase di compilazione delle espressioni di binding. I binding vengono invece risolti in fase di runtime. Di conseguenza tutti i binding non validi non vengono rilevati fino alla fase di runtime, quando l'applicazione non si comporta come previsto o vengono visualizzati messaggi di errore.
- Non sono efficienti a livelli di costi. I binding vengono risolti in fase di runtime mediante l'ispezione oggetti di carattere generale (reflection) e il carico di lavoro necessario varia a seconda della piattaforma.
Le associazioni compilate migliorano le prestazioni del data binding nelle Xamarin.Forms applicazioni risolvendo le espressioni di associazione in fase di compilazione anziché in fase di esecuzione. Questa convalida delle espressioni di binding in fase di compilazione consente anche agli sviluppatori una risoluzione dei problemi più agevole, perché i binding non validi vengono segnalati come errori di compilazione.
Il processo per l'uso dei binding compilati è il seguente:
- Abilitare la compilazione XAML. Per altre informazioni sulla compilazione XAML, vedere Compilazione XAML.
- Impostare un attributo
x:DataType
in unVisualElement
sul tipo dell'oggetto al qualeVisualElement
e gli elementi figlio eseguiranno il binding.
Nota
È consigliabile impostare l'attributo x:DataType
sullo stesso livello della gerarchia di visualizzazione sul quale è impostato BindingContext
. Tuttavia, questo attributo può essere ridefinito in qualsiasi posizione in una gerarchia di visualizzazione.
Per usare le associazioni compilate, l'attributo x:DataType
deve essere impostato su un valore letterale stringa o su un tipo usando l'estensione di x:Type
markup. In fase di compilazione XAML, le espressioni di binding non valide vengono segnalate come errori di compilazione. Tuttavia il compilatore XAML segnala un errore di compilazione solo per la prima espressione di binding non valida rilevata. Tutte le espressioni di binding valido definite in VisualElement
o negli elementi figlio verranno compilate, indipendentemente dal fatto che BindingContext
sia impostato in XAML o nel codice. La compilazione di un'espressione di binding genera codice compilato che ottiene un valore da una proprietà in sourcee lo imposta sulla proprietà in target specificata nel markup. Inoltre a seconda dell'espressione di binding il codice generato potrebbe rilevare modifiche nel valore della proprietà source e aggiornare la proprietà target, nonché eseguire il push delle modifiche dalla proprietà target alla proprietà source.
Importante
I binding compilati sono attualmente disabilitati per le espressioni di binding che definiscono la proprietà Source
. Il motivo è che la proprietà Source
viene sempre impostata usando l'estensione di markup x:Reference
, che non può essere risolta in fase di compilazione.
Usare binding compilati
La pagina Selezione colori compilata illustra l'uso di associazioni compilate tra Xamarin.Forms le visualizzazioni e le proprietà del modello di visualizzazione:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:DataBindingDemos"
x:Class="DataBindingDemos.CompiledColorSelectorPage"
Title="Compiled Color Selector">
...
<StackLayout x:DataType="local:HslColorViewModel">
<StackLayout.BindingContext>
<local:HslColorViewModel Color="Sienna" />
</StackLayout.BindingContext>
<BoxView Color="{Binding Color}"
... />
<StackLayout Margin="10, 0">
<Label Text="{Binding Name}" />
<Slider Value="{Binding Hue}" />
<Label Text="{Binding Hue, StringFormat='Hue = {0:F2}'}" />
<Slider Value="{Binding Saturation}" />
<Label Text="{Binding Saturation, StringFormat='Saturation = {0:F2}'}" />
<Slider Value="{Binding Luminosity}" />
<Label Text="{Binding Luminosity, StringFormat='Luminosity = {0:F2}'}" />
</StackLayout>
</StackLayout>
</ContentPage>
L'elemento radice StackLayout
crea un'istanza di HslColorViewModel
e inizializza la proprietà Color
all'interno dei tag proprietà dell'elemento per la proprietà BindingContext
. Questa radice StackLayout
definisce anche l'attributo x:DataType
come tipo viewmodel, a indicare che tutte le espressioni di associazione nella gerarchia di visualizzazione radice StackLayout
verranno compilate. Questa operazione può essere verificata modificando una qualsiasi delle espressioni di associazione da associare a una proprietà viewmodel inesistente, che genererà un errore di compilazione. Anche se questo esempio imposta l'attributo x:DataType
su un valore letterale stringa, può anche essere impostato su un tipo con l'estensione x:Type
di markup. Per altre informazioni sull'estensione x:Type
di markup, vedere estensione di markup x:Type.
Importante
L'attributo x:DataType
può essere ridefinito in qualsiasi posizione di una gerarchia di visualizzazione.
Gli elementi BoxView
e Label
e le viste Slider
ereditano il contesto di binding da StackLayout
. Queste viste sono tutte destinazioni di associazione che fanno riferimento alle proprietà di origine nel modello di visualizzazione. Per la BoxView.Color
proprietà e la Label.Text
proprietà , i data binding sono OneWay
: le proprietà nella visualizzazione vengono impostate dalle proprietà nel modello di visualizzazione. Tuttavia la proprietà Slider.Value
usa un binding TwoWay
. In questo modo ogni Slider
elemento può essere impostato dal modello di visualizzazione e anche per impostare il modello di visualizzazione da ogni Slider
oggetto .
Quando l'applicazione viene eseguita per la prima volta, gli BoxView
elementi , Label
e Slider
gli elementi vengono impostati dal modello di visualizzazione in base alla proprietà iniziale Color
impostata quando è stata creata un'istanza del modello di visualizzazione. Ciò viene illustrato negli screenshot seguenti:
Se si azionano i dispositivi di scorrimento, gli elementi BoxView
e Label
vengono aggiornati di conseguenza.
Per altre informazioni su questo selettore colori, vedere ViewModels and Property-Change Notifications (ViewModel e notifiche di modifica delle proprietà).
Usare associazioni compilate in un oggetto DataTemplate
I binding in un DataTemplate
sono interpretati nel contesto dell'oggetto basato su modelli. Pertanto quando si usano binding compilati in un DataTemplate
, DataTemplate
deve dichiarare il tipo del relativo oggetto dati usando l'attributo x:DataType
.
La pagina Compiled Color List (Elenco colori compilato) visualizza come usare i binding compilati in un DataTemplate
:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:DataBindingDemos"
x:Class="DataBindingDemos.CompiledColorListPage"
Title="Compiled Color List">
<Grid>
...
<ListView x:Name="colorListView"
ItemsSource="{x:Static local:NamedColor.All}"
... >
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:NamedColor">
<ViewCell>
<StackLayout Orientation="Horizontal">
<BoxView Color="{Binding Color}"
... />
<Label Text="{Binding FriendlyName}"
... />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<!-- The BoxView doesn't use compiled bindings -->
<BoxView Color="{Binding Source={x:Reference colorListView}, Path=SelectedItem.Color}"
... />
</Grid>
</ContentPage>
La proprietà ListView.ItemsSource
è impostata sulla proprietà NamedColor.All
statica. La classe NamedColor
usa la reflection .NET per enumerare tutti i campi pubblici statici nella struttura Color
e archiviarli con i relativi nomi in una raccolta accessibile dalla proprietà All
statica. Di conseguenza l'elemento ListView
viene riempito con tutte le istanze NamedColor
. Per ogni elemento in ListView
il contesto di binding per l'elemento è impostato su un oggetto NamedColor
. Gli elementi BoxView
e Label
in ViewCell
sono associati a proprietà NamedColor
.
Si noti che DataTemplate
definisce l'attributo x:DataType
come tipo NamedColor
, a indicare che tutte le espressioni di binding nella gerarchia di visualizzazione DataTemplate
verranno compilate. È possibile verificare questa impostazione modificando le espressioni di binding per impostare il binding a una proprietà NamedColor
inesistente, che produrrà un errore di compilazione. Anche se questo esempio imposta l'attributo x:DataType
su un valore letterale stringa, può anche essere impostato su un tipo con l'estensione x:Type
di markup. Per altre informazioni sull'estensione x:Type
di markup, vedere estensione di markup x:Type.
Quando l'applicazione viene eseguita per la prima volta, ListView
viene popolata con istanze NamedColor
. Quando viene selezionato un elemento in ListView
, la proprietà BoxView.Color
viene impostata sul colore dell'elemento selezionato in ListView
:
Se si selezionano altri elementi in ListView
viene aggiornato il colore di BoxView
.
Combinare associazioni compilate con associazioni classiche
Le espressioni di binding vengono compilate solo per la gerarchia di visualizzazione in cui è definito l'attributo x:DataType
. Al contrario, tutte le viste in una gerarchia in cui l'attributo x:DataType
non è definito usa i binding classici. È pertanto possibile combinare binding compilati e classici in una pagina. Ad esempio nella sezione precedente le viste in DataTemplate
usano i binding compilati, mentre l'elemento BoxView
impostato sul colore selezionato in ListView
non usa tali binding.
Un'attenta strutturazione degli attributi x:DataType
può dare origine a una pagina che usa binding sia compilati che classici. In alternativa l'attributo x:DataType
può essere ridefinito in qualsiasi punto di una gerarchia di visualizzazione come null
usando l'estensione di markup x:Null
. Questa operazione indica che tutte le espressioni di binding all'interno della gerarchia di visualizzazione useranno i binding classici. La pagina Mixed Bindings (Binding misti) dimostra questo approccio:
<StackLayout x:DataType="local:HslColorViewModel">
<StackLayout.BindingContext>
<local:HslColorViewModel Color="Sienna" />
</StackLayout.BindingContext>
<BoxView Color="{Binding Color}"
VerticalOptions="FillAndExpand" />
<StackLayout x:DataType="{x:Null}"
Margin="10, 0">
<Label Text="{Binding Name}" />
<Slider Value="{Binding Hue}" />
<Label Text="{Binding Hue, StringFormat='Hue = {0:F2}'}" />
<Slider Value="{Binding Saturation}" />
<Label Text="{Binding Saturation, StringFormat='Saturation = {0:F2}'}" />
<Slider Value="{Binding Luminosity}" />
<Label Text="{Binding Luminosity, StringFormat='Luminosity = {0:F2}'}" />
</StackLayout>
</StackLayout>
L'elemento radice StackLayout
definisce l'attributo x:DataType
come tipo HslColorViewModel
, a indicare che tutte le espressioni di binding nella gerarchia di visualizzazione radice StackLayout
verranno compilate. Tuttavia l'elemento StackLayout
interno ridefinisce l'attributo x:DataType
come null
con l'espressione di markup x:Null
. Di conseguenza le espressioni di binding nell'elemento StackLayout
interno usano i binding classici. Solo BoxView
nell'ambito della gerarchia di visualizzazione radice StackLayout
usa i binding compilati.
Per altre informazioni sull'espressione di markup x:Null
, vedere Estensione di markup x:Null.
Prestazioni
I binding compilati migliorano le prestazioni di data binding e il vantaggio in termini di prestazioni può variare. Gli unit test rivelano che:
- Un binding compilato che usa la notifica di modifica delle proprietà (ovvero un binding
OneWay
,OneWayToSource
oTwoWay
) viene risolto circa 8 volte più rapidamente rispetto a un binding classico. - Un binding compilato che non usa la notifica di modifica delle proprietà (ovvero un binding
OneTime
) viene risolto circa 20 volte più rapidamente rispetto a un binding classico. - L'impostazione di
BindingContext
per un binding compilato che usa la notifica di modifica delle proprietà (ovvero un bindingOneWay
,OneWayToSource
oTwoWay
) è circa 5 volte più rapida rispetto all'impostazione diBindingContext
per un binding classico. - L'impostazione di
BindingContext
per un binding compilato che non usa la notifica di modifica delle proprietà (ovvero un bindingOneTime
) è circa 7 volte più rapida rispetto all'impostazione diBindingContext
per un binding classico.
Queste variazioni di prestazioni possono risultare più importanti nei dispositivi mobili a seconda della piattaforma usata, della versione del sistema operativo e del dispositivo in cui viene eseguita l'applicazione.