Condividi tramite


Associazioni compilate

Sfogliare l'esempio. Esplorare l'esempio

I data binding dell'interfaccia utente dell'app multipiattaforma .NET (.NET MAUI) presentano due problemi principali:

  1. 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.
  2. 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 applicazioni MAUI .NET 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.

Importante

Le associazioni compilate sono necessarie invece di associazioni basate su stringhe nelle app NativeAOT e nelle app con taglio completo abilitato. Per altre informazioni, vedere Trim a .NET MAUI app and Native AOT deployment (Trim a .NET MAUI app and Native AOT deployment).

Binding compilati in XAML

Per usare le associazioni compilate in XAML, impostare un x:DataType attributo su un VisualElement sul tipo dell'oggetto a cui VisualElement verrà associato e ai relativi elementi figlio. È 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.

Importante

Le associazioni compilate richiedono l'uso della compilazione XAML, abilitata per impostazione predefinita in .NET MAUI. Se hai disabilitato la compilazione XAML, dovrai abilitarla. Per altre informazioni, vedere XAML Compilation (Compilazione XAML).

Per usare le associazioni compilate in XAML, 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

Le associazioni compilate sono disabilitate per tutte le espressioni di associazione XAML che definiscono la Source proprietà . 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.

Inoltre, le associazioni compilate in XAML non sono attualmente supportate su più associazioni.

Per impostazione predefinita, .NET MAUI non genera avvisi di compilazione per le associazioni XAML che non usano associazioni compilate. Tuttavia, puoi acconsentire esplicitamente agli avvisi di binding compilati generati impostando la proprietà di $(MauiStrictXamlCompilation) compilazione su true nel file di progetto dell'app (*.csproj):

<MauiStrictXamlCompilation>true</MauiStrictXamlCompilation>

Per impostazione predefinita, .NET MAUI genera avvisi di compilazione per le associazioni XAML che non usano associazioni compilate.

Per altre informazioni sugli avvisi dei binding compilati xaml, vedi Avvisi di binding compilati xaml.

Usare le associazioni compilate in XAML

L'esempio seguente illustra l'uso di associazioni compilate tra le viste MAUI .NET e le proprietà del modello di visualizzazione:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:DataBindingDemos"
             x:Class="DataBindingDemos.CompiledColorSelectorPage"
             x:DataType="local:HslColorViewModel"
             Title="Compiled Color Selector">
    <ContentPage.BindingContext>
        <local:HslColorViewModel Color="Sienna" />
    </ContentPage.BindingContext>
    ...
    <StackLayout>
        <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>

Crea ContentPage un'istanza di HslColorViewModel e inizializza la Color proprietà all'interno dei tag dell'elemento proprietà per la BindingContext proprietà . Definisce ContentPage anche l'attributo x:DataType come tipo viewmodel, che indica che tutte le espressioni di associazione nella gerarchia di ContentPage visualizzazione 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 ContentPage. 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 Slideroggetto .

Quando l'esempio viene eseguito per la prima volta, gli BoxViewelementi , 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. Man mano che i dispositivi di scorrimento vengono modificati, gli BoxView elementi e Label vengono aggiornati di conseguenza:

Selettore colori compilato.

Per altre informazioni su questo selettore di colori, vedere ViewModels e property-change notifications.

Usare le associazioni compilate in XAML 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. In caso contrario, l'ereditarietà di un oggetto non corretto DataTemplate dall'ambito x:DataType padre potrebbe comportare un errore:

<ContentPage ...
             x:DataType="local:AnimalsPageViewModel">
    <!-- Binding to AnimalsPageViewModel.Animals -->
    <CollectionView ItemsSource="{Binding Animals}">
        <CollectionView.ItemTemplate>
            <DataTemplate>
                <!-- incorrect: compiler thinks you want to bind to AnimalsPageViewModel.Name -->  
                <Label Text="{Binding Name}" />
            </DataTemplate>
        </CollectionView.ItemTemplate>
    </CollectionView>
</ContentPage>

Nell'esempio seguente viene illustrata l'impostazione corretta di su x:DataType un DataTemplateoggetto :

<ContentPage ...
             x:DataType="local:AnimalsPageViewModel">
    <!-- Binding to AnimalsPageViewModel.Animals -->
    <CollectionView ItemsSource="{Binding Animals}">
        <CollectionView.ItemTemplate>
            <DataTemplate x:DataType="local:Animal">
                <!-- correct: compiler knows you want to bind to Animal.Name -->
                <Label Text="{Binding Name}" />
            </DataTemplate>
        </CollectionView.ItemTemplate>
    </CollectionView>
</ContentPage>

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.

Compilare associazioni che specificano un tipo generico

I tipi generici possono essere specificati con l'attributo x:DataType specificando il vincolo generico come argomento stringa con prefisso tra parentesi:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:MyMauiApp"
             x:Class="MyMauiApp.MyPage"
             x:DataType="local:MyViewModel(x:Boolean)">
    ...
</ContentPage>

È possibile specificare più argomenti di tipo come argomenti stringa preceduti, delimitati da una virgola:

<DataTemplate x:DataType="local:MyType(local:MyObject,x:Boolean)">
    ...
</DataTemplate>

Per altre informazioni sui generics in XAML, vedere Generics.

Compilare associazioni che definiscono la Source proprietà

Prima di .NET MAUI 9, il compilatore XAML ignorava la compilazione di associazioni che definiscono la Source proprietà anziché .BindingContext Da .NET MAUI 9, queste associazioni possono essere compilate per sfruttare le prestazioni di runtime migliori. Tuttavia, questa ottimizzazione non è abilitata per impostazione predefinita per evitare l'interruzione del codice dell'app esistente. Per abilitare questa ottimizzazione, impostare la $(MauiEnableXamlCBindingWithSourceCompilation) proprietà di compilazione su true nel file di progetto dell'app:

<MauiEnableXamlCBindingWithSourceCompilation>true</MauiEnableXamlCBindingWithSourceCompilation>

Assicurarsi quindi che tutte le associazioni siano annotate con il corretto x:DataType e che non ereditano tipi di dati non corretti dall'ambito padre:

<HorizontalStackLayout BindingContext="{x:Reference slider}" x:DataType="Slider">
    <Label Text="{Binding Value}" />
    <Label Text="{Binding Text, Source={x:Reference entry}, x:DataType=Entry}" />
</HorizontalStackLayout>

Nota

Nei casi in cui è presente un'associazione con un Sourceoggetto , ma eredita dall'elemento x:DataType padre, può verificarsi una mancata corrispondenza tra e x:DataType il tipo di Source. In questo scenario verrà generato un avviso e verrà generato un fallback a un'associazione basata su reflection che risolve il percorso di associazione in fase di esecuzione.

Combinare associazioni compilate con associazioni classiche in XAML

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. L'esempio seguente illustra 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.

Avvisi di binding compilati XAML

Nella tabella seguente sono elencati gli avvisi del compilatore per le associazioni compilate e come risolverli:

Codice Messaggio Fix
XC0022 L'associazione può essere compilata per migliorare le prestazioni di runtime, se x:DataType specificato. Aggiungi x:DataType al codice XAML per specificare il tipo dell'oggetto corrente BindingContext. È consigliabile aggiungere x:DataType a tutti gli elementi in cui cambia il contesto di associazione.
XC0023 È possibile compilare l'associazione per migliorare le prestazioni di runtime se x:DataType non è esplicitamente null. Sostituire x:DataType="{x:Null}" con il tipo corretto.
Codice Messaggio
XC0022 L'associazione può essere compilata per migliorare le prestazioni di runtime, se x:DataType specificato.

Per correggere questo avviso, aggiungere x:DataType al codice XAML per specificare il tipo dell'oggetto corrente BindingContext. È consigliabile aggiungere x:DataType a tutti gli elementi in cui cambia il contesto di associazione.
XC0023 È possibile compilare l'associazione per migliorare le prestazioni di runtime se x:DataType non è esplicitamente null.

Per correggere questo avviso, sostituire x:DataType="{x:Null}" con il tipo corretto.
XC0024 L'associazione potrebbe essere compilata in modo non corretto perché l'annotazione x:DataType proviene da un ambito esterno. Assicurarsi di annotare tutti gli DataTemplate elementi XAML con il corretto x:DataType.

Per correggere questo avviso, assicurarsi che tutti gli DataTemplate elementi siano annotati con l'oggetto corretto x:DataType.
XC0025 L'associazione non è stata compilata perché dispone di una proprietà impostata Source in modo esplicito e la compilazione di associazioni con Source non è abilitata. Valutare la possibilità di abilitare questa ottimizzazione impostando nel <MauiEnableXamlCBindingWithSourceCompilation>true</MauiEnableXamlCBindingWithSourceCompilation> file di progetto e verificare che sia specificato il corretto x:DataType per questa associazione.

Per correggere questo avviso, abilitare la $(MauiEnableXamlCBindingWithSourceCompilation) proprietà di compilazione nel file di progetto e annotare tutte le associazioni con l'oggetto appropriato x:DataType.

Per assicurarsi che questi avvisi non vengano ignorati, prendere in considerazione la modifica di avvisi specifici per la compilazione degli errori con la $(WarningsAsErrors) proprietà di compilazione:

<WarningsAsErrors>$(WarningsAsErrors);XC0022;XC0023</WarningsAsErrors>

Per ignorare questi avvisi, usare la $(NoWarn) proprietà di compilazione con codici di avviso specifici:

<NoWarn>$(NoWarn);XC0022;XC0023</NoWarn>

Importante

XC0022 e XC0023 gli avvisi verranno sempre eliminati a meno che la proprietà di $(MauiStrictXamlCompilation) compilazione non sia impostata su true.

Se imposti la $(TreatWarningsAsErrors) proprietà di compilazione su true nel file di progetto dell'app, ma vuoi ignorare determinati avvisi del compilatore XAML, usa la $(NoWarn) proprietà di compilazione per disattivare questi avvisi o la $(WarningsNotAsErrors) proprietà di compilazione per ridurre la gravità di alcuni codici specifici.

Per impostazione predefinita, .NET MAUI genera avvisi di compilazione per le associazioni XAML che non usano associazioni compilate. È possibile acconsentire esplicitamente agli avvisi di binding compilati considerati come errori impostando le $(MauiStrictXamlCompilation) proprietà $(TreatWarningsAsErrors) di compilazione e true su nel file di progetto dell'app (*.csproj):

<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<MauiStrictXamlCompilation>true</MauiStrictXamlCompilation>

Nota

Per impostazione predefinita, la $(MauiStrictXamlCompilation) proprietà di compilazione è false a meno che non si stia pubblicando l'app usando il taglio completo o NativeAOT.

Associazioni compilate nel codice

Le associazioni scritte nel codice usano in genere percorsi stringa risolti in fase di esecuzione con reflection. Tuttavia, il SetBinding metodo di estensione include anche un overload che definisce le associazioni usando un Func argomento anziché un percorso stringa:

MyLabel.SetBinding(Label.TextProperty, static (Entry entry) => entry.Text);

Non tutti i metodi possono essere usati per definire un'associazione compilata. L'espressione deve essere un'espressione di accesso alle proprietà semplice. Gli esempi seguenti mostrano espressioni di associazione valide e non valide:

// Valid: Property access
static (PersonViewModel vm) => vm.Name;
static (PersonViewModel vm) => vm.Address?.Street;

// Valid: Array and indexer access
static (PersonViewModel vm) => vm.PhoneNumbers[0];
static (PersonViewModel vm) => vm.Config["Font"];

// Valid: Casts
static (Label label) => (label.BindingContext as PersonViewModel).Name;
static (Label label) => ((PersonViewModel)label.BindingContext).Name;

// Invalid: Method calls
static (PersonViewModel vm) => vm.GetAddress();
static (PersonViewModel vm) => vm.Address?.ToString();

// Invalid: Complex expressions
static (PersonViewModel vm) => vm.Address?.Street + " " + vm.Address?.City;
static (PersonViewModel vm) => $"Name: {vm.Name}";

Avvertimento

Se la funzione di accesso set per una proprietà o un indicizzatore non è accessibile, si verificherà un errore del compilatore CS0272. In questo caso, aumentare l'accessibilità dell'accessore.

Inoltre, il BindingBase.Create metodo imposta l'associazione direttamente sull'oggetto con un Funcoggetto e restituisce l'istanza dell'oggetto di associazione:

myEntry.SetBinding(Entry.TextProperty, new MultiBinding
{
    Bindings = new Collection<BindingBase>
    {
        Binding.Create(static (Entry entry) => entry.FontFamily, source: RelativeBindingSource.Self),
        Binding.Create(static (Entry entry) => entry.FontSize, source: RelativeBindingSource.Self),
        Binding.Create(static (Entry entry) => entry.FontAttributes, source: RelativeBindingSource.Self),
    },
    Converter = new StringConcatenationConverter()
});

Questi approcci di associazione compilati offrono i vantaggi seguenti:

  • Miglioramento delle prestazioni del data binding risolvendo le espressioni di binding in fase di compilazione anziché in fase di esecuzione.
  • Un'esperienza migliore per la risoluzione dei problemi per gli sviluppatori perché le associazioni non valide vengono segnalate come errori di compilazione.
  • IntelliSense durante la modifica.

Prestazioni

Le associazioni compilate migliorano le prestazioni del data binding, con il vantaggio delle prestazioni variabile:

  • Un binding compilato che usa la notifica di modifica delle proprietà (ovvero un binding OneWay, OneWayToSource o TwoWay) 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 binding OneWay, OneWayToSource o TwoWay) è circa 5 volte più rapida rispetto all'impostazione di BindingContext per un binding classico.
  • L'impostazione di BindingContext per un binding compilato che non usa la notifica di modifica delle proprietà (ovvero un binding OneTime) è circa 7 volte più rapida rispetto all'impostazione di BindingContext 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.