Informazioni approfondite sul data binding

API importanti

Nota

In questo argomento vengono descritte in dettaglio le funzionalità di data binding. Per una breve introduzione pratica, vedere Panoramica del data binding.

Questo argomento riguarda il data binding per le API che risiedono nello spazio dei nomi Windows.UI.Xaml.Data.

Il data binding è un modo per consentire all'interfaccia utente dell'app di visualizzare i dati e, facoltativamente, di rimanere sincronizzati con tali dati. Con il data binding puoi tenere separati i dati dall'interfaccia utente, generando un modello concettuale più semplice e migliorando la leggibilità, la testabilità e la manutenibilità della tua app.

È possibile usare il data binding per visualizzare semplicemente i valori di un'origine dati quando l'interfaccia utente viene visualizzata per la prima volta, ma non per rispondere alle modifiche apportate a tali valori. Si tratta di una modalità di binding denominata una tantum che funziona bene per un valore che non cambia in fase di esecuzione. In alternativa, puoi scegliere di "osservare" i valori e di aggiornare l'interfaccia utente quando vengono modificati. Questa modalità è denominata unidirezionale ed è ideale per i dati di sola lettura. In definitiva, è possibile scegliere di osservare e aggiornare, in modo che le modifiche apportate dall'utente ai valori nell'interfaccia utente vengano automaticamente inserite nell'origine dati. Questa modalità è denominata bidirezionale ed è ideale per i dati di sola lettura. Di seguito sono riportati alcuni esempi.

  • Puoi usare la modalità una tantum per associare un Image alla foto dell'utente corrente.
  • Puoi usare la modalità unidirezionale per associare un ListView a una raccolta di articoli di notizie in tempo reale raggruppati in base alla sezione di un giornale.
  • Puoi usare la modalità bidirezionale per associare un TextBox al nome del cliente in un modulo.

A prescindere dalla modalità, esistono due tipi di binding ed entrambi in genere sono dichiarati nel markup dell'interfaccia utente. È possibile scegliere di usare l'estensione di markup {x:Bind} o l'estensione di markup {Binding}. E si può anche usare una combinazione dei due nella stessa app, anche nello stesso elemento dell'interfaccia utente. {x:Bind} è una novità per Windows 10 e offre prestazioni migliori. Tutti i dettagli descritti in questo argomento si applicano a entrambi i tipi di binding, a meno che non sia indicato esplicitamente il contrario.

App di esempio che illustrano {x:Bind}

App di esempio che illustrano {Binding}

Ogni associazione implica questi pezzi

  • Origine dell'associazione. Questa è l'origine dei dati per l'associazione e può essere un'istanza di qualsiasi classe, con membri di cui si desidera visualizzare i valori nell'interfaccia utente.
  • Una destinazione di associazione. Si tratta di dependencyProperty del FrameworkElement nell'interfaccia utente che visualizza i dati.
  • Oggetto di associazione. Questa è la parte che trasferisce i valori dei dati dall'origine alla destinazione e, facoltativamente, dalla destinazione all'origine. L'oggetto di associazione viene creato in fase di caricamento XAML dall'estensione di markup {x:Bind} o {Binding}.

Nelle sezioni seguenti verranno esaminate più in dettaglio l'origine di associazione, la destinazione di associazione e l'oggetto di associazione. Le sezioni verranno collegate insieme all'esempio di associazione del contenuto di un pulsante a una proprietà stringa denominata NextButtonText, che appartiene a una classe denominata HostViewModel.

Origine di associazione

Ecco un'implementazione molto rudimentale di una classe che è possibile usare come origine di associazione.

Se stai usando C++/WinRT, aggiungi al progetto nuovi elementi di file Midl (.idl), denominati come illustrato nell'elenco di esempi di codice C++/WinRT seguente. Sostituisci il contenuto dei nuovi file con il codice MIDL 3.0 mostrato nell'elenco, crea il progetto per generare HostViewModel.h e .cpp, quindi aggiungi il codice ai file generati per trovare la corrispondenza con l'elenco. Per altre informazioni sui file generati e su come copiarli nel progetto, vedi Controlli XAML, binding a una proprietà C++/WinRT.

public class HostViewModel
{
    public HostViewModel()
    {
        this.NextButtonText = "Next";
    }

    public string NextButtonText { get; set; }
}
// HostViewModel.idl
namespace DataBindingInDepth
{
    runtimeclass HostViewModel
    {
        HostViewModel();
        String NextButtonText;
    }
}

// HostViewModel.h
// Implement the constructor like this, and add this field:
...
HostViewModel() : m_nextButtonText{ L"Next" } {}
...
private:
    std::wstring m_nextButtonText;
...

// HostViewModel.cpp
// Implement like this:
...
hstring HostViewModel::NextButtonText()
{
    return hstring{ m_nextButtonText };
}

void HostViewModel::NextButtonText(hstring const& value)
{
    m_nextButtonText = value;
}
...

L'implementazione di HostViewModele la relativa proprietà NextButtonText sono appropriate solo per l'associazione una tantum. Tuttavia, le associazioni unidirezionale e bidirezionale sono estremamente comuni e in questi tipi di associazione l'interfaccia utente viene aggiornata automaticamente in risposta alle modifiche apportate ai valori dei dati dell'origine di associazione. Affinché tali tipi di binding funzionino correttamente, è necessario rendere l'origine di binding "osservabile" all'oggetto di associazione. Pertanto, nell'esempio, se si vuole eseguire l'associazione unidirezionale o bidirezionale alla proprietà NextButtonText, tutte le modifiche apportate in fase di esecuzione al valore di tale proprietà devono essere rese osservabili all'oggetto di associazione.

Un modo per eseguire questa operazione consiste nel derivare la classe che rappresenta l'origine di associazione da DependencyObject, ed esporre un valore di dati tramite DependencyProperty. Ecco come un FrameworkElement diventa osservabile. I FrameworkElements sono origini di binding valide fin dall'inizio.

Un modo più leggero per rendere osservabile una classe, e una classe necessaria per le classi che dispongono già di una classe base, consiste nell'implementare System.ComponentModel.INotifyPropertyChanged. Questo implica semplicemente l'implementazione di un singolo evento denominato PropertyChanged. Di seguito è riportato un esempio di utilizzo di HostViewModel.

...
using System.ComponentModel;
using System.Runtime.CompilerServices;
...
public class HostViewModel : INotifyPropertyChanged
{
    private string nextButtonText;

    public event PropertyChangedEventHandler PropertyChanged = delegate { };

    public HostViewModel()
    {
        this.NextButtonText = "Next";
    }

    public string NextButtonText
    {
        get { return this.nextButtonText; }
        set
        {
            this.nextButtonText = value;
            this.OnPropertyChanged();
        }
    }

    public void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        // Raise the PropertyChanged event, passing the name of the property whose value has changed.
        this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}
// HostViewModel.idl
namespace DataBindingInDepth
{
    runtimeclass HostViewModel : Windows.UI.Xaml.Data.INotifyPropertyChanged
    {
        HostViewModel();
        String NextButtonText;
    }
}

// HostViewModel.h
// Add this field:
...
    winrt::event_token PropertyChanged(Windows::UI::Xaml::Data::PropertyChangedEventHandler const& handler);
    void PropertyChanged(winrt::event_token const& token) noexcept;

private:
    winrt::event<Windows::UI::Xaml::Data::PropertyChangedEventHandler> m_propertyChanged;
...

// HostViewModel.cpp
// Implement like this:
...
void HostViewModel::NextButtonText(hstring const& value)
{
    if (m_nextButtonText != value)
    {
        m_nextButtonText = value;
        m_propertyChanged(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"NextButtonText" });
    }
}

winrt::event_token HostViewModel::PropertyChanged(Windows::UI::Xaml::Data::PropertyChangedEventHandler const& handler)
{
    return m_propertyChanged.add(handler);
}

void HostViewModel::PropertyChanged(winrt::event_token const& token) noexcept
{
    m_propertyChanged.remove(token);
}
...

La proprietà NextButtonText è ora osservabile. Quando si crea un binding unidirezionale o bidirezionale a tale proprietà (verrà illustrato come in seguito), l'oggetto di associazione risultante sottoscrive l'evento PropertyChanged. Quando viene generato l'evento, il gestore dell'oggetto di associazione riceve un argomento contenente il nome della proprietà modificata. Questo è il modo in cui l'oggetto binding conosce il valore della proprietà da leggere di nuovo.

Non si deve quindi implementare diverse volte il modello illustrato sopra, se si usa C# è possibile semplicemente derivare dalla classe bass BindableBase che si trova nell'esempio QuizGame (nella cartella "Common"). Ecco un esempio di come appare.

public class HostViewModel : BindableBase
{
    private string nextButtonText;

    public HostViewModel()
    {
        this.NextButtonText = "Next";
    }

    public string NextButtonText
    {
        get { return this.nextButtonText; }
        set { this.SetProperty(ref this.nextButtonText, value); }
    }
}
// Your BindableBase base class should itself derive from Windows::UI::Xaml::DependencyObject. Then, in HostViewModel.idl, derive from BindableBase instead of implementing INotifyPropertyChanged.

Nota

Per C++/WinRT, qualsiasi classe di runtime dichiarata all'interno dell'applicazione che deriva da una classe di base è definita classe componibile. Le classi componibili sono soggette a limitazioni. Affinché un'applicazione superi i test del Kit di certificazione app Windows utilizzato da Visual Studio e da Microsoft Store per convalidare gli invii, e possa essere inserita correttamente nel sistema di Microsoft Store, una classe componibile in ultima analisi deve derivare da una classe di base di Windows. Vale a dire che la classe radice della gerarchia di ereditarietà deve essere un tipo che ha origine in uno spazio dei nomi Windows.*. Se è necessario derivare una classe di runtime da una classe di base, ad esempio per implementare una classe BindableBase da cui derivare tutti i modelli di visualizzazione, è possibile derivare da Windows.UI.Xaml.DependencyObject.

La generazione dell'evento PropertyChanged con un argomento String.Empty o null indica che tutte le proprietà non indicizzatore nell'oggetto devono essere rilette. È possibile generare l'evento per indicare che le proprietà indicizzatore dell'oggetto sono state modificate tramite un argomento "Itemindexer" per indicizzatori specifici (dove indexer è il valore indice) o un valore "Item[]" per tutti gli indicizzatori.

Un'origine di associazione può essere considerata come un singolo oggetto le cui proprietà contengono dati, oppure come raccolta di oggetti. Nel codice C# e Visual Basic puoi eseguire il binding una tantum a un oggetto che implementa List(Of T) per visualizzare una raccolta che non cambia in fase di esecuzione. Per una raccolta osservabile (osservando quando gli elementi vengono aggiunti e rimossi dalla raccolta), eseguire invece un’associazione unidirezionale a ObservableCollection(Of T). Nel codice C++/CX puoi eseguire il binding a Vector<T> per le raccolte osservabili e non osservabili, con C++/WinRT aventi i propri tipi. Per eseguire l'associazione alle proprie classi di raccolta, usare le indicazioni riportate nella tabella seguente.

Scenario C# e VB (CLR) C++/WinRT C++/CX
Eseguire l'associazione a un oggetto. Può essere qualsiasi oggetto. Può essere qualsiasi oggetto. L'oggetto deve avere BindableAttribute o implementare ICustomPropertyProvider.
Ottenere notifiche di modifica della proprietà da un oggetto associato. L'oggetto deve implementare INotifyPropertyChanged. L'oggetto deve implementare INotifyPropertyChanged. L'oggetto deve implementare INotifyPropertyChanged.
Eseguire l'associazione a una raccolta. List(Of T) IVector di IInspectable o IBindableObservableVector. Vedi Controlli di elementi XAML, binding a una raccolta C++/WinRT e Raccolte con C++/WinRT. Vector<T>
Ottenere le notifiche di modifica della raccolta da una raccolta associata. ObservableCollection(Of T) IObservableVector di IInspectable. Ad esempio, winrt::single_threaded_observable_vector<T>. IObservableVector<T>. Vector<T> implementa questa interfaccia.
Implementare una raccolta che supporta l'associazione. Estendere List(Of T) o implementare IList, IList(Of Object), IEnumerable o IEnumerable(Of Object). Il binding a IList(Of T) generico e IEnumerable(Of T) non è supportato. Implementa IObservableVector di IInspectable. Vedi Controlli di elementi XAML, binding a una raccolta C++/WinRT e Raccolte con C++/WinRT. Implementa IBindableVector, IBindableIterable, IVector<Object^>, IIterable<Object^>, IVector<IInspectable*>, o IIterable<IInspectable*>. L'associazione a IVector<T> generico e IIterable<T> non è supportata.
Implementare una raccolta che supporta le notifiche di modifica della raccolta. Estendere ObservableCollection(Of T) o implementare IList (non generico) e INotifyCollectionChanged. Implementa IObservableVector di IInspectable o IBindableObservableVector. Implementare IBindableVector e IBindableObservableVector.
Implementare una raccolta che supporta il caricamento incrementale. Estendere ObservableCollection(Of T) o implementare IList (non generico) e INotifyCollectionChanged. Implementa inoltre ISupportIncrementalLoading. Implementa IObservableVector di IInspectable o IBindableObservableVector. Implementa inoltre ISupportIncrementalLoading Implementare IBindableVector, IBindableObservableVector e ISupportIncrementalLoading.

È possibile associare controlli elenco a origini dati arbitrariamente di grandi dimensioni e ottenere comunque prestazioni elevate usando il caricamento incrementale. Ad esempio, è possibile associare controlli elenco ai risultati delle query di immagini Bing senza dover caricare tutti i risultati contemporaneamente. Al contrario, si caricano solo alcuni risultati immediatamente e si caricano risultati aggiuntivi in base alle esigenze. Per supportare il caricamento incrementale, devi implementare ISupportIncrementalLoading in un'origine dati che supporta le notifiche di modifica della raccolta. Quando il motore di data binding richiede più dati, l'origine dati deve effettuare le richieste appropriate, integrare i risultati e quindi inviare le notifiche appropriate per aggiornare l'interfaccia utente.

Destinazione di associazione

Nei due esempi seguenti la proprietà Button.Content è la destinazione del binding e il relativo valore è impostato su un'estensione di markup che dichiara l'oggetto binding. Viene visualizzato il primo {x:Bind} e poi {Binding}. La dichiarazione di associazioni nel markup è il caso comune (è pratico, leggibile e utilizzabile). Tuttavia, se necessario, è possibile evitare markup e creare in modo imperativo (a livello di programmazione) un'istanza della classe Binding.

<Button Content="{x:Bind ...}" ... />
<Button Content="{Binding ...}" ... />

Se stai usando le estensioni del componente C++/WinRT o Visual C++ (C++/CX), devi aggiungere l'attributo BindableAttribute a qualsiasi classe di runtime per cui vuoi usare l'estensione di markup {Binding}.

Importante

Se stai usando C++/WinRT, l'attributo BindableAttribute è disponibile se hai installato Windows SDK 10.0.17763.0 (Windows 10, versione 1809) o versioni successive. Senza tale attributo, è necessario implementare le interfacce ICustomPropertyProvider e ICustomProperty per poter usare l'estensione di markup {Binding}.

Oggetto binding dichiarato con {x:Bind}

È necessario eseguire un passaggio prima di creare il markup {x:Bind}. È necessario esporre la classe di origine dell'associazione dalla classe che rappresenta la pagina di markup. Puoi eseguire questa operazione aggiungendo una proprietà (di tipo HostViewModel in questo caso) alla classe della pagina MainPage.

namespace DataBindingInDepth
{
    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();
            this.ViewModel = new HostViewModel();
        }
    
        public HostViewModel ViewModel { get; set; }
    }
}
// MainPage.idl
import "HostViewModel.idl";

namespace DataBindingInDepth
{
    runtimeclass MainPage : Windows.UI.Xaml.Controls.Page
    {
        MainPage();
        HostViewModel ViewModel{ get; };
    }
}

// MainPage.h
// Include a header, and add this field:
...
#include "HostViewModel.h"
...
    DataBindingInDepth::HostViewModel ViewModel();

private:
    DataBindingInDepth::HostViewModel m_viewModel{ nullptr };
...

// MainPage.cpp
// Implement like this:
...
MainPage::MainPage()
{
    InitializeComponent();

}

DataBindingInDepth::HostViewModel MainPage::ViewModel()
{
    return m_viewModel;
}
...

A questo scopo, è ora possibile esaminare più in dettaglio il markup che dichiara l'oggetto di associazione. L'esempio seguente usa la stessa destinazione di associazione Button.Content usata nella sezione "Destinazione binding" precedente e mostra che è associata alla proprietà HostViewModel.NextButtonText.

<!-- MainPage.xaml -->
<Page x:Class="DataBindingInDepth.Mainpage" ... >
    <Button Content="{x:Bind Path=ViewModel.NextButtonText, Mode=OneWay}" ... />
</Page>

Si noti il valore specificato per Path. Questo valore viene interpretato nel contesto della pagina stessa e in questo caso il percorso ha inizio facendo riferimento alla proprietà ViewModel che abbiamo appena aggiunto alla pagina MainPage. Tale proprietà restituisce un'istanza di HostViewModel e quindi è possibile aggiungere un punto a tale oggetto per accedere alla proprietà HostViewModel.NextButtonText. Viene inoltre specificata la modalità per eseguire l'override del valore predefinito {x:Bind} una tantum.

La proprietà Path supporta un'ampia gamma di opzioni di sintassi per l'associazione a proprietà annidate, proprietà associate e indicizzatori di tipo integer e stringhe. Per altre info, vedi Property-path syntax. L'associazione agli indicizzatori di stringhe offre l'effetto dell'associazione alle proprietà dinamiche senza dover implementare ICustomPropertyProvider. Per altre impostazioni, vedere estensione di markup {x:Bind}.

Per illustrare che la proprietà HostViewModel.NextButtonText è effettivamente osservabile, aggiungi un gestore di eventi Click al pulsante e aggiorna il valore di HostViewModel.NextButtonText. Crea, esegui e fai clic sul pulsante per visualizzare il valore dell'aggiornamento del contenuto del pulsante.

// MainPage.xaml.cs
private void Button_Click(object sender, RoutedEventArgs e)
{
    this.ViewModel.NextButtonText = "Updated Next button text";
}
// MainPage.cpp
void MainPage::ClickHandler(IInspectable const&, RoutedEventArgs const&)
{
    ViewModel().NextButtonText(L"Updated Next button text");
}

Nota

Le modifiche a TextBox.Text vengono inviate a un'origine associata bidirezionale quando TextBox perde lo stato attivo e non dopo ogni pressione dei tasti da parte dell'utente.

DataTemplate e x:DataType

All'interno di un Oggetto DataTemplate (usato come modello di elemento, modello di contenuto o modello di intestazione), il valore di Path non viene interpretato nel contesto della pagina, ma nel contesto dell'oggetto dati basato su modelli. Quando si usa {x:Bind} in un modello di dati, in modo che i relativi binding possano essere convalidati (e generati codice efficiente) in fase di compilazione, DataTemplate deve dichiarare il tipo dell'oggetto dati usando x:DataType. L'esempio riportato di seguito può essere usato come ItemTemplate di un controllo elementi associato a una raccolta di oggetti SampleDataGroup .

<DataTemplate x:Key="SimpleItemTemplate" x:DataType="data:SampleDataGroup">
    <StackPanel Orientation="Vertical" Height="50">
      <TextBlock Text="{x:Bind Title}"/>
      <TextBlock Text="{x:Bind Description}"/>
    </StackPanel>
  </DataTemplate>

Oggetti con tipizzazione debole nel percorso

Si consideri, ad esempio, un tipo denominato SampleDataGroup, che implementa una proprietà stringa denominata Title. Inoltre, hai una proprietà MainPage.SampleDataGroupAsObject che è di tipo oggetto, ma che restituisce in realtà un'istanza di SampleDataGroup. L'associazione <TextBlock Text="{x:Bind SampleDataGroupAsObject.Title}"/> genererà un errore di compilazione perché la proprietà Title non viene trovata nell'oggetto type. Il rimedio per questo consiste nell'aggiungere un cast alla sintassi Path, come questo: <TextBlock Text="{x:Bind ((data:SampleDataGroup)SampleDataGroupAsObject).Title}"/>. Ecco un altro esempio in cui Element viene dichiarato come oggetto, ma in realtà è TextBlock: <TextBlock Text="{x:Bind Element.Text}"/>. E un cast risolve il problema: <TextBlock Text="{x:Bind ((TextBlock)Element).Text}"/>.

Se i dati vengono caricati in modo asincrono

Il codice per supportare {x:Bind} viene generato in fase di compilazione nelle classi parziali per le pagine. Questi file sono disponibili nella obj cartella, con nomi come (per C#) <view name>.g.cs. Il codice generato include un gestore per l'evento Loading della pagina e tale gestore chiama il metodo Initialize in una classe generata che rappresenta le associazioni della pagina. Inizializzare a sua volta chiama Update per iniziare a spostare i dati tra l'origine di associazione e la destinazione. Il caricamento viene generato poco prima del passaggio della prima misura della pagina o del controllo utente. Pertanto, se i dati vengono caricati in modo asincrono, potrebbero non essere pronti nel momento in cui viene richiamato Inizializzare. Dopo aver caricato i dati, è quindi possibile forzare l'inizializzazione delle associazioni una tantum chiamando this.Bindings.Update();. Se i binding una tantum sono necessari solo per i dati caricati in modo asincrono, è più economico inizializzarli in questo modo che non usare i binding unidirezionali ed essere in ascolto delle modifiche. Se i dati non subiscono modifiche con granularità fine e, se è probabile che vengano aggiornati come parte di un'azione specifica, è possibile apportare le associazioni una sola volta e forzare un aggiornamento manuale in qualsiasi momento con una chiamata a Aggiorna.

Nota

{x:Bind} non è adatto a scenari ad associazione tardiva, ad esempio l'esplorazione della struttura del dizionario di un oggetto JSON, né al duck typing. Il "duck typing" è una forma debole di digitazione basata sulle corrispondenze lessicali nei nomi delle proprietà (ad esempio, "Se cammina, nuota e starnazza come un'anatra, allora è un'anatra"). Con il duck typing, un binding alla proprietà Age verrebbe soddisfatto allo stesso modo con un oggetto Person o Wine (presupponendo che tali tipi abbiano entrambi una proprietà Age). Per questi scenari, usa l'estensione di markup {Binding}.

Oggetto binding dichiarato con {Binding}

Se si stanno usando le estensioni del componente C++/WinRT o Visual C++ (C++/CX), per usare l'estensione di markup {Binding} è necessario aggiungere l'attributo BindableAttribute a qualsiasi classe di runtime a cui si vuole eseguire l'associazione. Per usare {x:Bind}, questo attributo non è necessario.

// HostViewModel.idl
// Add this attribute:
[Windows.UI.Xaml.Data.Bindable]
runtimeclass HostViewModel : Windows.UI.Xaml.Data.INotifyPropertyChanged
...

Importante

Se stai usando C++/WinRT, l'attributo BindableAttribute è disponibile se hai installato Windows SDK 10.0.17763.0 (Windows 10, versione 1809) o versioni successive. Senza tale attributo, è necessario implementare le interfacce ICustomPropertyProvider e ICustomProperty per poter usare l'estensione di markup {Binding}.

{Binding} presuppone che, per impostazione predefinita, si sta eseguendo il binding del dataContext della pagina di markup. Il DataContext della pagina verrà quindi impostato come istanza della classe di origine dell'associazione (di tipo HostViewModel in questo caso). L'esempio seguente mostra il markup che dichiara l'oggetto di associazione. Usiamo la stessa destinazione di associazione Button.Content usata in precedenza nella sezione "Destinazione di associazione" ed eseguiamo il binding alla proprietà HostViewModel.NextButtonText.

<Page xmlns:viewmodel="using:DataBindingInDepth" ... >
    <Page.DataContext>
        <viewmodel:HostViewModel x:Name="viewModelInDataContext"/>
    </Page.DataContext>
    ...
    <Button Content="{Binding Path=NextButtonText}" ... />
</Page>
// MainPage.xaml.cs
private void Button_Click(object sender, RoutedEventArgs e)
{
    this.viewModelInDataContext.NextButtonText = "Updated Next button text";
}
// MainPage.cpp
void MainPage::ClickHandler(IInspectable const&, RoutedEventArgs const&)
{
    viewModelInDataContext().NextButtonText(L"Updated Next button text");
}

Si noti il valore specificato per Path. Questo valore viene interpretato nel contesto del DataContext della pagina, che in questo esempio è impostato su un'istanza di HostViewModel. Il percorso fa riferimento alla proprietà HostViewModel.NextButtonText. È possibile omettere la modalità perché l'impostazione predefinita {Binding} unidirezionale qui funziona.

Il valore predefinito di DataContext per un elemento dell'interfaccia utente è il valore ereditato del relativo elemento padre. È naturalmente possibile eseguire l'override di tale impostazione predefinita impostando DataContext in modo esplicito, che a sua volta viene ereditato dagli elementi figlio per impostazione predefinita. L'impostazione di DataContext in modo esplicito su un elemento è utile quando si desidera avere più associazioni che usano la stessa origine.

Un oggetto binding ha una proprietà Source che, per impostazione predefinita, corrisponde al DataContext dell'elemento dell'interfaccia utente in cui viene dichiarata l'associazione. È possibile eseguire l'override di questa impostazione predefinita impostando in modo esplicito Source, RelativeSourceo ElementName nell'associazione (vedere {Binding} per informazioni dettagliate).

All'interno di un DataTemplate, il DataContext è impostato automaticamente sull'oggetto dati usato come modello. L'esempio riportato di seguito può essere usato come ItemTemplate di un controllo elementi associato a una raccolta di qualsiasi tipo con proprietà stringa denominate Title e Description.

<DataTemplate x:Key="SimpleItemTemplate">
    <StackPanel Orientation="Vertical" Height="50">
      <TextBlock Text="{Binding Title}"/>
      <TextBlock Text="{Binding Description"/>
    </StackPanel>
  </DataTemplate>

Nota

Per impostazione predefinita, le modifiche a TextBox.Text vengono inviate a un'origine associata bidirezionale quando TextBox perde lo stato attivo. Per fare in modo che le modifiche vengano inviate dopo ogni sequenza di tasti utente, impostare UpdateSourceTrigger su PropertyChanged sull'associazione nel markup. È anche possibile controllare completamente quando le modifiche vengono inviate all'origine impostando UpdateSourceTrigger su Explicit. È quindi possibile gestire gli eventi nella casella di testo (in genere TextBox.TextChanged), chiamare GetBindingExpressionnella destinazione per ottenere un oggetto BindingExpression e infine chiamare BindingExpression.UpdateSource per aggiornare l'origine dati a livello di codice.

La proprietà Path supporta un'ampia gamma di opzioni di sintassi per l'associazione a proprietà annidate, proprietà associate e indicizzatori di tipo integer e stringhe. Per altre info, vedi Property-path syntax. L'associazione agli indicizzatori di stringhe offre l'effetto dell'associazione alle proprietà dinamiche senza dover implementare ICustomPropertyProvider. La proprietà ElementName è utile per l'associazione da elemento a elemento. La proprietà RelativeSource include diversi usi, uno dei quali è un'alternativa più potente all'associazione di modelli all'interno di un ControlTemplate. Per altre impostazioni, vedere Estensione di markup {Binding} e classe Binding.

Cosa accade se l'origine e la destinazione non sono dello stesso tipo?

Se vuoi controllare la visibilità di un elemento dell'interfaccia utente in base al valore di una proprietà booleana o se vuoi eseguire il rendering di un elemento dell'interfaccia utente con un colore che rappresenta una funzione dell'intervallo o della tendenza di un valore numerico oppure se vuoi visualizzare un valore di data e/o ora in una proprietà dell'elemento dell'interfaccia utente che prevede una stringa, sarà necessario convertire i valori da un tipo a un altro. Ci saranno casi in cui la soluzione corretta consiste nell'esporre un'altra proprietà del tipo corretto dalla classe di origine dell'associazione e mantenere la logica di conversione incapsulata e testabile. Ma questo non è flessibile né scalabile quando si hanno numeri elevati, o combinazioni di grandi dimensioni, di proprietà di origine e di destinazione. In questo caso sono disponibili due opzioni:

  • Se si usa {x:Bind} è possibile eseguire il binding direttamente a una funzione per eseguire tale conversione
  • In alternativa, è possibile specificare un convertitore di valori che è un oggetto progettato per eseguire la conversione

Convertitori di valori

Ecco un convertitore di valori, adatto per un binding una tantum o unidirezionale, che converte un valore DateTime in un valore stringa contenente il mese. La classe implementa IValueConverter.

public class DateToStringConverter : IValueConverter
{
    // Define the Convert method to convert a DateTime value to 
    // a month string.
    public object Convert(object value, Type targetType, 
        object parameter, string language)
    {
        // value is the data from the source object.
        DateTime thisdate = (DateTime)value;
        int monthnum = thisdate.Month;
        string month;
        switch (monthnum)
        {
            case 1:
                month = "January";
                break;
            case 2:
                month = "February";
                break;
            default:
                month = "Month not found";
                break;
        }
        // Return the value to pass to the target.
        return month;
    }

    // ConvertBack is not implemented for a OneWay binding.
    public object ConvertBack(object value, Type targetType, 
        object parameter, string language)
    {
        throw new NotImplementedException();
    }
}
// See the "Formatting or converting data values for display" section in the "Data binding overview" topic.

Ecco come utilizzare il convertitore di valori nel markup dell'oggetto di associazione.

<UserControl.Resources>
  <local:DateToStringConverter x:Key="Converter1"/>
</UserControl.Resources>
...
<TextBlock Grid.Column="0" 
  Text="{x:Bind ViewModel.Month, Converter={StaticResource Converter1}}"/>
<TextBlock Grid.Column="0" 
  Text="{Binding Month, Converter={StaticResource Converter1}}"/>

Il motore di associazione chiama i metodi Convert e ConvertBack se il parametro Converter è definito per l'associazione. Quando i dati vengono passati dall'origine, il motore di associazione chiama Convert e passa i dati restituiti alla destinazione. Quando i dati vengono passati dalla destinazione (per un'associazione bidirezionale), il motore di associazione chiama ConvertBack e passa i dati restituiti all'origine.

Il convertitore include anche parametri facoltativi: ConverterLanguage, che consente di specificare quale lingua usare nella conversione, e ConverterParameter, che consente di passare un parametro per la conversione logica. Per un esempio che usa un parametro del convertitore, vedere IValueConverter.

Nota

Se la conversione presenta un errore, non genera un'eccezione. Restituisce invece DependencyProperty.UnsetValue, che interromperà il trasferimento dei dati.

Per visualizzare un valore predefinito da utilizzare ogni volta che l'origine dell'associazione non può essere risolta, impostare la proprietà FallbackValue sull'oggetto binding nel markup. Ciò è utile per gestire gli errori di conversione e formattazione. È anche utile eseguire l'associazione alle proprietà di origine che potrebbero non esistere in tutti gli oggetti in una raccolta associata di tipi eterogenei.

Se si associa un controllo di testo a un valore che non è una stringa, il motore di data binding convertirà il valore in una stringa. Se il valore è un tipo riferimento, il motore di data binding recupererà il valore stringa chiamando ICustomPropertyProvider.GetStringRepresentation o IStringable.ToString, se disponibile, e chiamerà altrimenti Object.ToString. Si noti, tuttavia, che il motore di associazione ignorerà qualsiasi implementazione di ToString che nasconde l'implementazione della classe base. Le implementazioni della sottoclasse devono eseguire l'override del metodo ToString della classe base. Analogamente, nei linguaggi nativi, tutti gli oggetti gestiti sembrano implementare ICustomPropertyProvider e IStringable. Tuttavia, tutte le chiamate a GetStringRepresentation e IStringable.ToString vengono instradate a Object.ToString o a un override di tale metodo e non a una nuova implementazione ToString che nasconde l'implementazione della classe base.

Nota

A partire da Windows 10 versione 1607, il framework XAML fornisce un convertitore da valori booleani a Visibility. Il convertitore esegue il mapping true al valore di enumerazione Visible e false a Collapsed, quindi puoi associare una proprietà Visibility a un valore booleano senza creare un convertitore. Per usare il convertitore predefinito, la versione minima dell'SDK di destinazione dell'app deve essere 14393 o successiva. Non è possibile usarlo quando l'app è destinata alle versioni precedenti di Windows 10. Per altre info sulle versioni di destinazione, vedere Codice adattivo per la versione.

Associazione di funzioni in {x:Bind}

{x:Bind} consente al passaggio finale di un percorso di associazione di essere una funzione. Può essere usato per eseguire conversioni e per eseguire associazioni che dipendono da più di una proprietà. Vedi Funzioni in x:Bind

Binding da elemento a elemento

Puoi eseguire il binding della proprietà di un elemento XAML con la proprietà di un altro elemento XAML. Ecco un esempio di questa operazione nel markup.

<TextBox x:Name="myTextBox" />
<TextBlock Text="{x:Bind myTextBox.Text, Mode=OneWay}" />

Importante

Per il flusso di lavoro necessario per l'associazione da elemento a elemento usando C++/WinRT, vedi Binding da elemento a elemento.

Dizionari risorse con {x:Bind}

L'estensione di markup {x:Bind} dipende dalla generazione del codice, quindi richiede un file code-behind contenente un costruttore che chiama InitializeComponent (per inizializzare il codice generato). È possibile riutilizzare il dizionario risorse creando un'istanza del relativo tipo (in modo che venga chiamato InitializeComponent) anziché fare riferimento al nome del file. Ecco un esempio di operazioni da eseguire se si dispone di un dizionario risorse esistente e si vuole usare {x:Bind} in esso.

TemplatesResourceDictionary.xaml

<ResourceDictionary
    x:Class="ExampleNamespace.TemplatesResourceDictionary"
    .....
    xmlns:examplenamespace="using:ExampleNamespace">
    
    <DataTemplate x:Key="EmployeeTemplate" x:DataType="examplenamespace:IEmployee">
        <Grid>
            <TextBlock Text="{x:Bind Name}"/>
        </Grid>
    </DataTemplate>
</ResourceDictionary>

TemplatesResourceDictionary.xaml.cs

using Windows.UI.Xaml.Data;
 
namespace ExampleNamespace
{
    public partial class TemplatesResourceDictionary
    {
        public TemplatesResourceDictionary()
        {
            InitializeComponent();
        }
    }
}

MainPage.xaml

<Page x:Class="ExampleNamespace.MainPage"
    ....
    xmlns:examplenamespace="using:ExampleNamespace">

    <Page.Resources>
        <ResourceDictionary>
            .... 
            <ResourceDictionary.MergedDictionaries>
                <examplenamespace:TemplatesResourceDictionary/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Page.Resources>
</Page>

Associazione di eventi e ICommand

{x:Bind} supporta una funzionalità denominata associazione di eventi. Con questa funzionalità, è possibile specificare il gestore per un evento usando un'associazione, che è un'opzione aggiuntiva sulla gestione degli eventi con un metodo nel file code-behind. Si supponga di avere una proprietà RootFrame nella classe MainPage.

public sealed partial class MainPage : Page
{
    ...
    public Frame RootFrame { get { return Window.Current.Content as Frame; } }
}

È quindi possibile associare l'evento Click di un pulsante a un metodo sull'oggetto Frame restituito dalla proprietà RootFrame in questo modo. Si noti anche che la proprietà di IsEnabled del pulsante viene associata a un altro membro dello stesso Frame.

<AppBarButton Icon="Forward" IsCompact="True"
IsEnabled="{x:Bind RootFrame.CanGoForward, Mode=OneWay}"
Click="{x:Bind RootFrame.GoForward}"/>

I metodi di overload non possono essere utilizzati per gestire un evento con questa tecnica. Inoltre, se il metodo che gestisce l'evento ha parametri, devono essere tutti assegnabili rispettivamente dai tipi di tutti i parametri dell'evento. In questo caso, Frame.GoForward non è sottoposto a overload e non ha parametri (ma sarebbe ancora valido anche se ha preso due parametri oggetto). Frame.GoBack è invece in overload, quindi non possiamo usare tale metodo con questa tecnica.

La tecnica di associazione di eventi è simile all'implementazione e all'utilizzo dei comandi (un comando è una proprietà che restituisce un oggetto che implementa l'interfaccia ICommand). Sia {x:Bind} che {Binding} funzionano con i comandi. Pertanto non è necessario implementare più volte il modello di comando, è possibile usare la classe helper DelegateCommand disponibile nell'esempio QuizGame (nella cartella "Common").

Associazione a una raccolta di cartelle o file

È possibile usare le API nello spazio dei nomi Windows.Storage per recuperare i dati di file e cartelle. Tuttavia, i vari metodi GetFilesAsync, GetFoldersAsync e GetItemsAsync non restituiscono valori adatti per l'associazione ai controlli elenco. È invece necessario eseguire l'associazione ai valori restituiti dei metodi GetVirtualizedFilesVector, GetVirtualizedFoldersVector e GetVirtualizedItemsVector della classe FileInformationFactory. L'esempio di codice seguente dell'esempio StorageDataSource e GetVirtualizedFilesVector illustra il modello di utilizzo tipico. Ricordarsi di dichiarare la funzionalità picturesLibrary nel manifesto del pacchetto dell'app e verificare che nella cartella della raccolta immagini siano presenti immagini.

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    var library = Windows.Storage.KnownFolders.PicturesLibrary;
    var queryOptions = new Windows.Storage.Search.QueryOptions();
    queryOptions.FolderDepth = Windows.Storage.Search.FolderDepth.Deep;
    queryOptions.IndexerOption = Windows.Storage.Search.IndexerOption.UseIndexerWhenAvailable;

    var fileQuery = library.CreateFileQueryWithOptions(queryOptions);

    var fif = new Windows.Storage.BulkAccess.FileInformationFactory(
        fileQuery,
        Windows.Storage.FileProperties.ThumbnailMode.PicturesView,
        190,
        Windows.Storage.FileProperties.ThumbnailOptions.UseCurrentScale,
        false
        );

    var dataSource = fif.GetVirtualizedFilesVector();
    this.PicturesListView.ItemsSource = dataSource;
}

In genere si userà questo approccio per creare una visualizzazione di sola lettura delle informazioni su file e cartelle. È possibile creare associazioni bidirezionali alle proprietà del file e della cartella, ad esempio per consentire agli utenti di valutare un brano in una visualizzazione musicale. Tuttavia, tutte le modifiche non vengono mantenute finché non si chiama il metodo SavePropertiesAsync appropriato, ad esempio MusicProperties.SavePropertiesAsync. È consigliabile eseguire il commit delle modifiche quando l'elemento perde lo stato attivo perché attiva una reimpostazione della selezione.

Si noti che l'associazione bidirezionale che usa questa tecnica funziona solo con posizioni indicizzate, ad esempio Musica. È possibile determinare se una posizione viene indicizzata chiamando il metodo FolderInformation.GetIndexedStateAsync.

Si noti anche che un vettore virtualizzato può restituire null per alcuni elementi prima di popolarne il valore. Ad esempio, è necessario verificare la presenza di null prima di usare il valore SelectedItem di un controllo elenco associato a un vettore virtualizzato oppure utilizzare SelectedIndex.

Associazione a dati raggruppati in base a una chiave

Se prendi una raccolta semplice di elementi (ad esempio, libri rappresentati da una classe BookSku) e raggruppi gli elementi usando una proprietà comune come chiave (ad esempio, la proprietà BookSku.AuthorName), i dati risultanti sono denominati dati raggruppati. Quando si raggruppano i dati, non è più una raccolta flat. I dati raggruppati sono una raccolta di oggetti gruppo, in cui ogni oggetto gruppo ha

  • una chiave e
  • una raccolta di elementi la cui proprietà corrisponde alla chiave.

Tornando all'esempio dei libri, il risultato del raggruppamento dei libri in base al nome dell'autore genera una raccolta di gruppi di nomi di autore in cui ogni gruppo ha

  • una chiave, che è il nome dell'autore, e
  • una raccolta di BookSku la cui proprietà AuthorName corrisponde alla chiave del gruppo.

In generale, per visualizzare una raccolta, si associa ItemsSource di un controllo elementi (ad esempio ListView o GridView) direttamente a una proprietà che restituisce un insieme. Se si tratta di una raccolta semplice di elementi, non è necessario eseguire alcuna operazione speciale. Tuttavia, se si tratta di una raccolta di oggetti gruppo (così com'è quando si esegue l'associazione a dati raggruppati), sono necessari i servizi di un oggetto intermedio denominato CollectionViewSource che si trova tra il controllo elementi e l'origine dell'associazione. Si associa CollectionViewSource alla proprietà che restituisce dati raggruppati e si associa il controllo items a CollectionViewSource. Un ulteriore valore aggiunto di collectionViewSource è che tiene traccia dell'elemento corrente, in modo da poter mantenere sincronizzati più di un controllo elementi associandoli tutti allo stesso CollectionViewSource. È anche possibile accedere all'elemento corrente a livello di codice tramite la proprietà ICollectionView.CurrentItem dell'oggetto restituito dalla proprietà CollectionViewSource.View.

Per attivare la funzionalità di raggruppamento di un oggetto CollectionViewSource, impostare IsSourceGrouped su true. La necessità di impostare anche la proprietà ItemsPath dipende esattamente dalla modalità di creazione degli oggetti gruppo. Esistono due modi per creare un oggetto gruppo: il modello "is-a-group" e il modello "has-a-group". Nel modello "is-a-group", l'oggetto gruppo deriva da un tipo di raccolta (ad esempio, List<T>), quindi l'oggetto gruppo è in realtà il gruppo di elementi. Con questo modello non è necessario impostare ItemsPath. Nel modello "has-a-group", l'oggetto gruppo ha una o più proprietà di un tipo di raccolta (ad esempio List<T>), quindi il gruppo "ha un" gruppo di elementi sotto forma di proprietà (o diversi gruppi di elementi sotto forma di diverse proprietà). Con questo modello è necessario impostare ItemsPath sul nome della proprietà che contiene il gruppo di elementi.

L'esempio seguente illustra il modello "has-a-group". La classe page ha una proprietà denominata ViewModel, che restituisce un'istanza del modello di visualizzazione. CollectionViewSource viene associato alla proprietà Authors del modello di visualizzazione (Authors è la raccolta di oggetti gruppo) e specifica anche che è la proprietà Author.BookSkus che contiene gli elementi raggruppati. Infine, GridView è associato a CollectionViewSource e ha lo stile di gruppo definito in modo che possa eseguire il rendering degli elementi in gruppi.

<Page.Resources>
    <CollectionViewSource
    x:Name="AuthorHasACollectionOfBookSku"
    Source="{x:Bind ViewModel.Authors}"
    IsSourceGrouped="true"
    ItemsPath="BookSkus"/>
</Page.Resources>
...
<GridView
ItemsSource="{x:Bind AuthorHasACollectionOfBookSku}" ...>
    <GridView.GroupStyle>
        <GroupStyle
            HeaderTemplate="{StaticResource AuthorGroupHeaderTemplateWide}" ... />
    </GridView.GroupStyle>
</GridView>

È possibile implementare il modello "is-a-group" in uno dei due modi seguenti. Un modo consiste nel creare una classe di gruppo personalizzata. Derivare la classe da List<T> (dove T è il tipo degli elementi). Ad esempio, public class Author : List<BookSku>. Il secondo modo consiste nell'usare un'espressione LINQ per creare dinamicamente oggetti gruppo (e una classe di gruppo) da valori di proprietà simili degli elementi BookSku. Questo approccio, mantenendo solo un elenco semplice di elementi e raggruppandoli in tempo reale, è tipico di un'app che accede ai dati da un servizio cloud. Si ottiene la flessibilità di raggruppare i libri per autore o per genere (ad esempio) senza bisogno di classi di gruppo speciali, ad esempio Author e Genre.

L'esempio seguente illustra il modello "is-a-group" usando LINQ. Questa volta si raggruppano libri per genere, visualizzati con il nome del genere nelle intestazioni di gruppo. Ciò è indicato dal percorso della proprietà "Key" in riferimento al valore della chiave di gruppo.

using System.Linq;
...
private IOrderedEnumerable<IGrouping<string, BookSku>> genres;

public IOrderedEnumerable<IGrouping<string, BookSku>> Genres
{
    get
    {
        if (this.genres == null)
        {
            this.genres = from book in this.bookSkus
                          group book by book.genre into grp
                          orderby grp.Key
                          select grp;
        }
        return this.genres;
    }
}

Tenere presente che quando si usa {x:Bind} con i modelli di dati è necessario indicare il tipo a cui è associato impostando un valore x:DataType. Se il tipo è generico, non è possibile esprimerlo nel markup, quindi è necessario usare {Binding} nel modello di intestazione dello stile di gruppo.

    <Grid.Resources>
        <CollectionViewSource x:Name="GenreIsACollectionOfBookSku"
        Source="{x:Bind Genres}"
        IsSourceGrouped="true"/>
    </Grid.Resources>
    <GridView ItemsSource="{x:Bind GenreIsACollectionOfBookSku}">
        <GridView.ItemTemplate x:DataType="local:BookTemplate">
            <DataTemplate>
                <TextBlock Text="{x:Bind Title}"/>
            </DataTemplate>
        </GridView.ItemTemplate>
        <GridView.GroupStyle>
            <GroupStyle>
                <GroupStyle.HeaderTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding Key}"/>
                    </DataTemplate>
                </GroupStyle.HeaderTemplate>
            </GroupStyle>
        </GridView.GroupStyle>
    </GridView>

Un controllo SemanticZoom è un ottimo modo per consentire agli utenti di visualizzare e esplorare i dati raggruppati. L'app di esempio Bookstore2 illustra come usare SemanticZoom. In tale app è possibile visualizzare un elenco di libri raggruppati per autore (visualizzazione ingrandita) o per visualizzare un jump list di autori (visualizzazione ingrandita). La jump list offre una navigazione molto più rapida rispetto allo scorrimento nell'elenco dei libri. Le visualizzazioni ingrandite e rimpicciolite sono in realtà controlli ListView o GridView associati allo stesso CollectionViewSource.

An illustration of a SemanticZoom

Quando si esegue il binding a dati gerarchici, ad esempio sottocategorie all'interno delle categorie, è possibile scegliere di visualizzare i livelli gerarchici nell'interfaccia utente con una serie di controlli elementi. Una selezione in un controllo elementi determina il contenuto dei controlli elementi successivi. È possibile mantenere sincronizzati gli elenchi associando ogni elenco al proprio CollectionViewSource e associando le istanze CollectionViewSource in una catena. Si tratta di una visualizzazione master/dettagli (o elenco/dettagli). Per altre info, vedi Come eseguire il binding ai dati gerarchici e creare una visualizzazione master/dettagli.

Diagnosi e debug dei problemi di data binding

Il markup di associazione contiene i nomi delle proprietà (e, per C#, a volte campi e metodi). Pertanto, quando si rinomina una proprietà, sarà necessario modificare anche qualsiasi associazione che vi fa riferimento. Dimenticando di eseguire questa operazione, si verifica un tipico esempio di bug di data binding e l'app non verrà compilata o non verrà eseguita correttamente.

Gli oggetti di associazione creati da {x:Bind} e {Binding} sono in gran parte equivalenti a livello funzionale. Ma {x:Bind} contiene informazioni sul tipo per l'origine dell'associazione e genera il codice sorgente in fase di compilazione. Con {x:Bind} si ottiene lo stesso tipo di rilevamento dei problemi che si ottiene con il resto del codice. Ciò include la convalida in fase di compilazione delle espressioni di binding e il debug impostando punti di interruzione nel codice sorgente generato come classe parziale per la pagina. Queste classi sono disponibili nei file nella obj cartella, con nomi come (per C#) <view name>.g.cs). Se si verifica un problema con un'associazione, attivare Interrompi eccezioni non gestite nel debugger di Microsoft Visual Studio. Il debugger interromperà l'esecuzione a quel punto ed è quindi possibile eseguire il debug di ciò che è andato storto. Il codice generato da {x:Bind} segue lo stesso modello per ogni parte del grafico dei nodi di origine dell'associazione ed è possibile usare le informazioni nella finestra Stack di chiamate per determinare la sequenza di chiamate che hanno causato il problema.

{Binding} non dispone di informazioni sul tipo per l'origine dell'associazione. Tuttavia, quando si esegue l'app con il debugger collegato, eventuali errori di associazione vengono visualizzati nella finestra Output in Visual Studio.

Creazione di associazioni nel codice

Nota Questa sezione si applica solo a {Binding}, perché non è possibile creare binding {x:Bind} nel codice. Tuttavia, alcuni degli stessi vantaggi di {x:Bind} possono essere ottenuti con DependencyObject.RegisterPropertyChangedCallback, che consente di eseguire la registrazione per le notifiche di modifica in qualsiasi proprietà di dipendenza.

Puoi anche connettere gli elementi dell'interfaccia utente ai dati usando codice procedurale anziché XAML. A tale scopo, creare un nuovo oggetto Binding, impostare le proprietà appropriate, quindi chiamare FrameworkElement.SetBinding o BindingOperations.SetBinding. La creazione di associazioni a livello di codice è utile quando si desidera scegliere i valori delle proprietà di associazione in fase di esecuzione o condividere un'unica associazione tra più controlli. Si noti, tuttavia, che non è possibile modificare i valori delle proprietà di associazione dopo aver chiamato SetBinding.

Nell'esempio seguente viene illustrato come implementare un'associazione nel codice.

<TextBox x:Name="MyTextBox" Text="Text"/>
// Create an instance of the MyColors class 
// that implements INotifyPropertyChanged.
MyColors textcolor = new MyColors();

// Brush1 is set to be a SolidColorBrush with the value Red.
textcolor.Brush1 = new SolidColorBrush(Colors.Red);

// Set the DataContext of the TextBox MyTextBox.
MyTextBox.DataContext = textcolor;

// Create the binding and associate it with the text box.
Binding binding = new Binding() { Path = new PropertyPath("Brush1") };
MyTextBox.SetBinding(TextBox.ForegroundProperty, binding);
' Create an instance of the MyColors class 
' that implements INotifyPropertyChanged. 
Dim textcolor As New MyColors()

' Brush1 is set to be a SolidColorBrush with the value Red. 
textcolor.Brush1 = New SolidColorBrush(Colors.Red)

' Set the DataContext of the TextBox MyTextBox. 
MyTextBox.DataContext = textcolor

' Create the binding and associate it with the text box.
Dim binding As New Binding() With {.Path = New PropertyPath("Brush1")}
MyTextBox.SetBinding(TextBox.ForegroundProperty, binding)

Confronto tra funzionalità {x:Bind} e {Binding}

Funzionalità {x:Bind} vs {Binding} Note
Path è la proprietà predefinita {x:Bind a.b.c}
-
{Binding a.b.c}
Proprietà Path {x:Bind Path=a.b.c}
-
{Binding Path=a.b.c}
In x:Bind, Path è indicato nella Pagina per impostazione predefinita, non nel Datacontext.
Indicizzatore {x:Bind Groups[2].Title}
-
{Binding Groups[2].Title}
Restituisce l'elemento specificato nella raccolta. Sono supportati solo gli indici basati su numeri interi.
Proprietà associate {x:Bind Button22.(Grid.Row)}
-
{Binding Button22.(Grid.Row)}
Le proprietà associate vengono specificate usando parentesi. Se la proprietà non viene dichiarata in uno spazio dei nomi XAML, anteponerla a uno spazio dei nomi xml, che deve essere mappata a uno spazio dei nomi del codice all'inizio del documento.
Cast {x:Bind groups[0].(data:SampleDataGroup.Title)}
-
Non necessario per {Binding}.
I cast vengono specificati usando parentesi. Se la proprietà non viene dichiarata in uno spazio dei nomi XAML, anteponerla a uno spazio dei nomi xml, che deve essere mappata a uno spazio dei nomi del codice all'inizio del documento.
Convertitore {x:Bind IsShown, Converter={StaticResource BoolToVisibility}}
-
{Binding IsShown, Converter={StaticResource BoolToVisibility}}
I convertitori devono essere dichiarati nella radice di Page/ResourceDictionary o in App.xaml.
ConverterParameter, ConverterLanguage {x:Bind IsShown, Converter={StaticResource BoolToVisibility}, ConverterParameter=One, ConverterLanguage=fr-fr}
-
{Binding IsShown, Converter={StaticResource BoolToVisibility}, ConverterParameter=One, ConverterLanguage=fr-fr}
I convertitori devono essere dichiarati nella radice di Page/ResourceDictionary o in App.xaml.
TargetNullValue {x:Bind Name, TargetNullValue=0}
-
{Binding Name, TargetNullValue=0}
Utilizzato quando la foglia dell'espressione di associazione è Null. Usare virgolette singole per un valore stringa.
FallbackValue {x:Bind Name, FallbackValue='empty'}
-
{Binding Name, FallbackValue='empty'}
Utilizzato quando una parte del percorso per l'associazione (ad eccezione della foglia) è null.
ElementName {x:Bind slider1.Value}
-
{Binding Value, ElementName=slider1}
Con {x:Bind} si sta associando a un campo; il percorso è radicato nella pagina per impostazione predefinita, quindi è possibile accedere a qualsiasi elemento denominato tramite il relativo campo.
RelativeSource: Self <Rectangle x:Name="rect1" Width="200" Height="{x:Bind rect1.Width}" ... />
-
<Rectangle Width="200" Height="{Binding Width, RelativeSource={RelativeSource Self}}" ... />
Con {x:Bind}, denominare l'elemento e usarne il nome in Path.
RelativeSource: TemplatedParent Non necessario per {x:Bind}
-
{Binding <path>, RelativeSource={RelativeSource TemplatedParent}}
Con {x:Bind} TargetType su ControlTemplate indica il binding all'elemento padre del modello. Per {Binding} il normale binding dei modelli può essere usato nei modelli di controllo per la maggior parte dei casi. Usare tuttavia TemplatedParent dove è necessario usare un convertitore o un'associazione bidirezionale.<
Origine Non necessario per {x:Bind}
-
<ListView ItemsSource="{Binding Orders, Source={StaticResource MyData}}"/>
Per {x:Bind} puoi usare direttamente l'elemento denominato, usare una proprietà o un percorso statico.
Mode {x:Bind Name, Mode=OneWay}
-
{Binding Name, Mode=TwoWay}
La modalità può essere OneTime, OneWay o TwoWay. L'impostazione predefinita di {x:Bind} è OneTime; l'impostazione predefinita di {Binding} è OneWay.
UpdateSourceTrigger {x:Bind Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}
-
{Binding UpdateSourceTrigger=PropertyChanged}
UpdateSourceTrigger può essere Default, LostFocus o PropertyChanged. {X:Bind} non supporta UpdateSourceTrigger=Explicit. {x:Bind} usa il comportamento PropertyChanged per tutti i casi tranne TextBox.Text, in cui usa il comportamento LostFocus.