Panoramica del data binding (WPF .NET)

Il data binding in Windows Presentation Foundation (WPF) offre un modo semplice e coerente per le app di presentare e interagire con i dati. Gli elementi possono essere associati a dati di tipi diversi di origini dati sotto forma di oggetti .NET e XML. Qualsiasi, ad esempio e , ad esempio e , dispone di funzionalità incorporate per consentire l'applicazione di stili flessibili a singoli elementi di dati ContentControl o raccolte di elementi di Button ItemsControl ListBox ListView dati. In cima ai dati è possibile generare visualizzazioni di ordinamento, filtro e raggruppamento.

Il data binding in WPF offre diversi vantaggi rispetto ai modelli tradizionali, tra cui il supporto intrinseco per data binding tramite un'ampia gamma di proprietà, una rappresentazione flessibile dell'interfaccia utente dei dati e una netta separazione della logica di business dall'interfaccia utente.

Questo articolo illustra prima di tutto i concetti fondamentali per WPF data binding e quindi illustra l'utilizzo della classe e di altre funzionalità Binding di data binding.

Importante

La documentazione della Guida desktop per .NET 5 (e .NET Core) è in fase di costruzione.

Che cos'è il data binding?

Il data binding è il processo che stabilisce una connessione tra l'interfaccia utente dell'app e i dati visualizzati. Se l'associazione è impostata correttamente e i dati forniscono le notifiche appropriate, quando il valore dei dati cambia la modifica si riflette automaticamente negli elementi associati ai dati. Il data binding prevede anche che, se una rappresentazione esterna dei dati in un elemento viene modificata, i dati sottostanti possono essere automaticamente aggiornati in modo da riflettere la modifica. Ad esempio, se l'utente modifica il valore in un elemento , il valore dei dati sottostanti viene aggiornato automaticamente TextBox in base a tale modifica.

Un uso tipico di data binding è inserire i dati di configurazione locale o del server in form o altri controlli dell'interfaccia utente. In WPF questo concetto viene esteso per includere l'associazione di un'ampia gamma di proprietà a diversi tipi di origini dati. In WPF le proprietà di dipendenza degli elementi possono essere associate a oggetti .NET (inclusi oggetti ADO.NET o oggetti associati a servizi Web e proprietà Web) e a dati XML.

Concetti di data binding base

Indipendentemente dall'elemento da associare e dalla natura dell'origine dati, ogni associazione segue sempre il modello illustrato nella figura seguente.

Diagramma che mostra il modello di data binding base.

Come illustrato nella figura, data binding è essenzialmente il ponte tra la destinazione del binding e l'origine del binding. La figura illustra i concetti fondamentali seguenti relativi data binding WPF:

  • In genere, ogni associazione include quattro componenti:

    • Oggetto di destinazione dell'associazione.
    • Proprietà di destinazione.
    • Un'origine di binding.
    • Percorso del valore nell'origine dell'associazione da utilizzare.

    Ad esempio, se si associa il contenuto di un oggetto alla proprietà , si configura TextBox Employee.Name l'associazione come nella tabella seguente:

    Impostazione Valore
    Destinazione TextBox
    Proprietà di destinazione Text
    Oggetto di origine Employee
    Percorso del valore dell'oggetto di origine Name
  • La proprietà di destinazione deve essere una proprietà di dipendenza.

    La maggior parte delle proprietà sono proprietà di dipendenza e la maggior parte delle proprietà di dipendenza, ad eccezione di quelle di sola lettura, data binding UIElement per impostazione predefinita. Solo i tipi derivati da DependencyObject possono definire proprietà di dipendenza. Tutti UIElement i tipi derivano da DependencyObject .

  • Le origini di binding non sono limitate a oggetti .NET personalizzati.

    Anche se non è illustrato nella figura, si noti che l'oggetto di origine del binding non è limitato a essere un oggetto .NET personalizzato. WPF data binding supporta dati sotto forma di oggetti .NET, XML e persino oggetti elemento XAML. Per fornire alcuni esempi, l'origine di associazione può essere un oggetto , qualsiasi oggetto elenco, un oggetto ADO.NET o servizi Web o un oggetto XmlNode che contiene UIElement i dati XML. Per altre informazioni, vedere Panoramica delle origini di associazione.

È importante ricordare che quando si stabilisce un binding, si associa una destinazione di binding a un'origine di binding. Ad esempio, se si visualizzano alcuni dati XML sottostanti in un oggetto usando data binding, si associa ai ListBox ListBox dati XML.

Per stabilire un'associazione, usare Binding l'oggetto . Nella parte restante di questo articolo vengono illustrati molti dei concetti associati a e alcune delle proprietà e dell'utilizzo Binding dell'oggetto .

Contesto dati

Quando data binding viene dichiarato sugli elementi XAML, questi vengono risolti data binding esaminando la relativa proprietà DataContext immediata. Il contesto dati è in genere l'oggetto di origine dell'associazione per la valutazione del percorso del valore di origine dell'associazione. È possibile eseguire l'override di questo comportamento nell'associazione e impostare un valore dell'oggetto origine dell'associazione specifico. Se la proprietà per l'oggetto che ospita il binding non è impostata, la proprietà dell'elemento padre viene controllata e così via fino alla radice della struttura ad albero DataContext DataContext di oggetti XAML. In breve, il contesto dati usato per risolvere l'associazione viene ereditato dall'elemento padre, a meno che non venga impostato in modo esplicito sull'oggetto .

Le associazioni possono essere configurate per la risoluzione con un oggetto specifico, anziché usare il contesto dati per la risoluzione dell'associazione. La specifica diretta di un oggetto di origine viene usata quando, ad esempio, si associa il colore di primo piano di un oggetto al colore di sfondo di un altro oggetto. Il contesto dati non è necessario perché l'associazione viene risolta tra questi due oggetti. Al contrario, le associazioni non associate a oggetti di origine specifici usano la risoluzione del contesto dei dati.

Quando la proprietà viene modificata, vengono rivalutate tutte le associazioni che potrebbero essere interessate dal contesto DataContext dati.

Direzione del flusso di dati

Come indicato dalla freccia nella figura precedente, il flusso di dati di un'associazione può passare dalla destinazione del binding all'origine del binding (ad esempio, il valore di origine cambia quando un utente modifica il valore di un oggetto ) e/o dall'origine del binding alla destinazione del binding (ad esempio, il contenuto viene aggiornato con le modifiche nell'origine del binding) se l'origine del binding fornisce le notifiche TextBox TextBox appropriate.

È possibile consentire all'app di consentire agli utenti di modificare i dati e propagare nuovamente i dati all'oggetto di origine. oppure è possibile fare in modo che gli utenti non possano aggiornare i dati di origine. È possibile controllare il flusso di dati impostando Binding.Mode .

Questa figura illustra i diversi tipi di flusso di dati:

Flusso di dati dell'associazione dati

  • OneWay L'associazione fa sì che le modifiche apportate alla proprietà di origine aggiornerà automaticamente la proprietà di destinazione, ma le modifiche apportate alla proprietà di destinazione non vengono propagate alla proprietà di origine. Questo tipo di binding è appropriato se il controllo da associare è implicitamente di sola lettura. Ad esempio, è possibile eseguire l'associazione a un'origine, ad esempio un titolo azionario, oppure la proprietà di destinazione non ha un'interfaccia di controllo fornita per apportare modifiche, ad esempio un colore di sfondo associato a dati di una tabella. Se non è necessario monitorare le modifiche della proprietà di destinazione, l'uso della modalità di binding evita OneWay l'overhead della modalità di TwoWay binding.

  • TwoWay L'associazione fa sì che le modifiche apportate alla proprietà di origine o alla proprietà di destinazione aggiornerà automaticamente l'altra. Questo tipo di associazione è appropriato per i moduli modificabili o altri scenari di interfaccia utente completamente interattivi. Per impostazione predefinita, la maggior parte delle proprietà è binding, ma alcune proprietà di dipendenza ,in genere proprietà di controlli modificabili dall'utente, ad esempio e OneWay TextBox.Text CheckBox.IsChecked, sono associate per TwoWay impostazione predefinita. Un modo a livello di codice per determinare se una proprietà di dipendenza esegue l'associazione unidirettica o bidirettica per impostazione predefinita è ottenere i metadati della proprietà con e quindi controllare il valore DependencyProperty.GetMetadata booleano della FrameworkPropertyMetadata.BindsTwoWayByDefault proprietà.

  • OneWayToSource è l'inverso OneWay dell'associazione. Aggiorna la proprietà di origine quando la proprietà di destinazione viene modificata. Uno scenario di esempio è se è necessario solo rivalutare il valore di origine dall'interfaccia utente.

  • Non illustrato nella figura è l'associazione, che fa sì che la proprietà di origine inizializza la proprietà di destinazione, ma non OneTime propaga le modifiche successive. Se il contesto dati cambia o l'oggetto nel contesto dati cambia, la modifica non viene riflessa nella proprietà di destinazione. Questo tipo di associazione è appropriato se uno snapshot dello stato corrente è appropriato o se i dati sono realmente statici. Questo tipo di associazione è utile anche se si vuole inizializzare la proprietà di destinazione con un valore di una proprietà di origine e il contesto dati non è noto in anticipo. Questa modalità è essenzialmente una forma più semplice di associazione che offre prestazioni migliori nei casi in cui il valore di OneWay origine non cambia.

Per rilevare le modifiche all'origine (applicabili alle associazioni e ), l'origine deve implementare un meccanismo appropriato di notifica delle modifiche OneWay TwoWay delle proprietà, ad esempio INotifyPropertyChanged . Vedere Procedura: Implementare la notifica di modifica delle proprietà (.NET Framework) per un esempio di INotifyPropertyChanged implementazione.

La Binding.Mode proprietà fornisce altre informazioni sulle modalità di associazione e un esempio di come specificare la direzione di un'associazione.

Elementi che attivano gli aggiornamenti dell'origine

Le associazioni che sono o sono in ascolto delle modifiche nella proprietà di destinazione e le propagano nuovamente all'origine, nota come TwoWay OneWayToSource aggiornamento dell'origine. Può accadere ad esempio che si modifichi il testo di un oggetto TextBox per modificare il valore di origine sottostante.

Tuttavia, il valore di origine viene aggiornato durante la modifica del testo o dopo aver completato la modifica del testo e il controllo perde lo stato attivo? La Binding.UpdateSourceTrigger proprietà determina ciò che attiva l'aggiornamento dell'origine. I punti delle frecce a destra nella figura seguente illustrano il ruolo della Binding.UpdateSourceTrigger proprietà .

Diagramma che mostra il ruolo della proprietà UpdateSourceTrigger.

Se il valore è , il valore a cui punta la freccia destra di o le associazioni viene aggiornato non appena cambia UpdateSourceTrigger UpdateSourceTrigger.PropertyChanged la proprietà di TwoWay OneWayToSource destinazione. Tuttavia, se il valore è , tale valore viene aggiornato solo con il nuovo UpdateSourceTrigger valore quando la proprietà di destinazione perde lo stato LostFocus attivo.

Analogamente alla Mode proprietà , proprietà di dipendenza diverse hanno valori predefiniti UpdateSourceTrigger diversi. Il valore predefinito per la maggior parte delle proprietà di dipendenza è , che determina la modifica immediata del valore della proprietà di origine quando viene modificato il valore PropertyChanged della proprietà di destinazione. Sono necessarie modifiche immediate per CheckBox e altri controlli semplici. Tuttavia, per i campi di testo, l'aggiornamento dopo ogni pressione di tasto può ridurre le prestazioni e nega all'utente la consueta opportunità di eseguire il backspace e correggere gli errori di digitazione prima di eseguire il commit nel nuovo valore. Ad esempio, il valore predefinito della proprietà è , che determina la modifica del valore di origine solo quando l'elemento del controllo perde lo stato attivo, non quando la TextBox.Text UpdateSourceTrigger proprietà viene LostFocus TextBox.Text modificata. Per informazioni su come trovare il valore predefinito di una proprietà di dipendenza, vedere UpdateSourceTrigger la pagina delle proprietà.

Nella tabella seguente viene fornito uno scenario di esempio per UpdateSourceTrigger ogni valore utilizzando come esempio TextBox .

Valore di UpdateSourceTrigger Quando viene aggiornato il valore di origine Scenario di esempio per TextBox
LostFocus (impostazione predefinita per TextBox.Text ) Quando il controllo TextBox perde lo stato attivo. Oggetto TextBox associato alla logica di convalida (vedere Convalida dei dati più avanti).
PropertyChanged Durante la digitazione in TextBox . Controlli TextBox in una finestra della chat room.
Explicit Quando l'app chiama UpdateSource . Controlli TextBox in un modulo modificabile (aggiorna i valori di origine solo quando l'utente preme il pulsante di invio).

Per un esempio, vedere Procedura: Controllare quando il testo textBox aggiorna l'origine (.NET Framework).

Esempio di data binding

Per un esempio di data binding, esaminare l'interfaccia utente dell'app seguente dalla demo di data binding,che visualizza un elenco di articoli da non inserere.

Screenshot di esempio di data binding

L'app illustra le funzionalità di data binding:

  • Il contenuto dell'oggetto ListBox è associato a una raccolta di oggetti DisastabiliItem. Un oggetto DisabitazioneItem ha proprietà quali Description, StartPrice, StartDate, Category e SpecialFeatures.

  • I dati (oggetti DisastabiliItem) visualizzati in sono strutturati su modelli in modo che la descrizione e il prezzo corrente ListBox siano visualizzati per ogni articolo. Il modello viene creato usando un oggetto DataTemplate . L'aspetto di ogni articolo dipende inoltre dal valore di SpecialFeatures dell'oggetto AuctionItem visualizzato. Se il valore di SpecialFeatures dell'oggetto AuctionItem è Color, l'articolo avrà un bordo blu. Se il valore è Highlight, l'articolo sarà dotato di un bordo arancione e di una stella. La sezione Modelli di dati include informazioni sull'applicazione di modelli ai dati.

  • L'utente può raggruppare, filtrare o ordinare i dati usando CheckBoxes l'oggetto fornito. Nell'immagine precedente sono selezionate le opzioni Raggruppa per categoria e Ordina per categoria e CheckBoxes data. I dati sono raggruppati in base alla categoria del prodotto e i nomi delle categorie seguono l'ordine alfabetico. È difficile notare dall'immagine, ma gli elementi sono anche ordinati in base alla data di inizio all'interno di ogni categoria. L'ordinamento viene eseguito usando una visualizzazione di raccolta. La sezione Binding alle raccolte illustra le visualizzazioni raccolta.

  • Quando l'utente seleziona un elemento, visualizza ContentControl i dettagli dell'elemento selezionato. Questa esperienza è denominata scenario master-dettagli. La sezione Scenario master-dettagli fornisce informazioni su questo tipo di associazione.

  • Il tipo della proprietà StartDate è DateTime , che restituisce una data che include l'ora al millisecondo. In questa app è stato usato un convertitore personalizzato per visualizzare una stringa di data più breve. La sezione Conversione dati fornisce informazioni sui convertitori.

Quando l'utente seleziona il pulsante Aggiungi prodotto, viene visualizzato il modulo seguente.

Pagina Add Product Listing

L'utente può modificare i campi nel modulo, visualizzare in anteprima l'elenco dei prodotti usando i riquadri di anteprima brevi o dettagliati e selezionare per aggiungere il Submit nuovo elenco di prodotti. Tutte le impostazioni di raggruppamento, filtro e ordinamento esistenti verranno applicate alla nuova voce. In questo caso specifico, l'articolo immesso nella figura precedente verrà visualizzato come secondo articolo della categoria Computer.

Non illustrato in questa immagine è la logica di convalida fornita in Data di inizio TextBox . Se l'utente immette una data non valida (formattazione non valida o data passata), l'utente riceverà una notifica con un punto esclamativo rosso accanto a ToolTip TextBox . La sezione Convalida dei dati descrive come creare la logica di convalida.

Prima di esaminare le diverse funzionalità di data binding descritte in precedenza, verranno illustrati i concetti fondamentali fondamentali per la comprensione di WPF data binding.

Creare un'associazione

Per rievalutare alcuni dei concetti illustrati nelle sezioni precedenti, si stabilisce un'associazione usando l'oggetto e ogni associazione include in genere quattro componenti: una destinazione dell'associazione, una proprietà di destinazione, un'origine del binding e un percorso al valore di origine da Binding usare. Questa sezione illustra come impostare un binding.

Le origini di associazione sono collegate all'oggetto attivo DataContext per l'elemento . Gli elementi ereditano automaticamente se non ne è stato DataContext definito uno in modo esplicito.

Si consideri l'esempio seguente, in cui l'oggetto origine del binding è una classe denominata MyData definita nello spazio dei nomi SDKSample. A scopo dimostrativo, MyData ha una proprietà stringa denominata ColorName il cui valore è impostato su "Red". L'esempio genera quindi un pulsante con uno sfondo rosso.

<DockPanel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
           xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
           xmlns:c="clr-namespace:SDKSample">
    <DockPanel.Resources>
        <c:MyData x:Key="myDataSource"/>
    </DockPanel.Resources>
    <DockPanel.DataContext>
        <Binding Source="{StaticResource myDataSource}"/>
    </DockPanel.DataContext>
    <Button Background="{Binding Path=ColorName}"
            Width="150" Height="30">
        I am bound to be RED!
    </Button>
</DockPanel>

Per altre informazioni sulla sintassi della dichiarazione di associazione ed esempi su come configurare un'associazione nel codice, vedere Cenni preliminari sulle dichiarazioni di associazione.

Se si applica questo esempio al diagramma di base, la figura risultante sarà simile alla seguente. Questa figura descrive OneWay un'associazione perché la proprietà Background supporta l'associazione per impostazione OneWay predefinita.

Diagramma che mostra la data binding Background.

Ci si potrebbe chiedere perché questa associazione funzioni anche se la proprietà ColorName è di tipo stringa mentre la Background proprietà è di tipo Brush . Questa associazione usa la conversione dei tipi predefinita, descritta nella sezione Conversione dei dati.

Specifica dell'origine dell'associazione

Si noti che nell'esempio precedente l'origine del binding viene specificata impostando la proprietà DockPanel.DataContext. eredita Button quindi il valore da , che è il relativo elemento DataContext DockPanel padre. Come già detto, l'oggetto origine del binding è uno dei quattro componenti necessari di un binding. Pertanto, senza che venga specificato l'oggetto di origine dell'associazione, l'associazione non eseguirebbe alcuna operazione.

Esistono diversi modi per specificare l'oggetto origine del binding. L'uso della proprietà su un elemento padre è utile DataContext quando si associano più proprietà alla stessa origine. In alcuni casi, tuttavia, è preferibile specificare l'origine del binding in singole dichiarazioni di binding. Per l'esempio precedente, anziché usare la proprietà , è possibile specificare l'origine del binding impostando la proprietà direttamente nella dichiarazione di associazione del pulsante, come DataContext Binding.Source nell'esempio seguente.

<DockPanel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
           xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
           xmlns:c="clr-namespace:SDKSample">
    <DockPanel.Resources>
        <c:MyData x:Key="myDataSource"/>
    </DockPanel.Resources>
    <Button Background="{Binding Source={StaticResource myDataSource}, Path=ColorName}"
            Width="150" Height="30">
        I am bound to be RED!
    </Button>
</DockPanel>

Oltre a impostare direttamente la proprietà su un elemento, ereditando il valore da un predecessore (ad esempio il pulsante nel primo esempio) e specificando in modo esplicito l'origine del binding impostando la proprietà sull'associazione (ad esempio il pulsante nell'ultimo esempio), è anche possibile usare la proprietà o la proprietà per specificare l'origine DataContext DataContext del Binding.Source Binding.ElementName Binding.RelativeSource binding. La proprietà è utile quando si esegue il binding ad altri elementi nell'app, ad esempio quando si usa un dispositivo di scorrimento per regolare la ElementName larghezza di un pulsante. La RelativeSource proprietà è utile quando l'associazione viene specificata in un oggetto o ControlTemplate Style . Per altre informazioni, vedere Panoramica delle origini di associazione.

Specifica del percorso del valore

Se l'origine del binding è un oggetto , usare la Binding.Path proprietà per specificare il valore da utilizzare per l'associazione. Se si sta associando dati XML, si usa la Binding.XPath proprietà per specificare il valore. In alcuni casi, può essere applicabile usare la Path proprietà anche quando i dati sono XML. Ad esempio, se si desidera accedere alla proprietà Name di un XmlNode restituito (come risultato di una query XPath), è necessario usare la proprietà Path oltre alla XPath proprietà .

Per altre informazioni, vedere le Path proprietà XPath e .

Anche se è stato evidenziato che per il valore da usare è uno dei quattro componenti necessari di un'associazione, negli scenari che si desidera associare a un intero oggetto, il valore da usare sarebbe lo stesso dell'oggetto di origine del Path binding. In questi casi, è applicabile non specificare un Path . Si consideri l'esempio seguente.

<ListBox ItemsSource="{Binding}"
         IsSynchronizedWithCurrentItem="true"/>

L'esempio precedente usa la sintassi di binding vuota: {Binding}. In questo caso, ListBox eredita DataContext da un elemento DockPanel padre (non illustrato in questo esempio). Quando il percorso non è specificato, l'impostazione predefinita è l'associazione all'intero oggetto. In altre parole, in questo esempio il percorso è stato osato perché si associa la ItemsSource proprietà all'intero oggetto. Per una discussione approfondita, vedere la sezione Binding alle raccolte.

Oltre al binding a una raccolta, questo scenario è utile anche in caso di binding a un intero oggetto anziché a una singola proprietà di un oggetto. Ad esempio, se l'oggetto di origine è di tipo , è possibile eseguire semplicemente String l'associazione alla stringa stessa. Un altro scenario comune riguarda il binding di un elemento a un oggetto con numerose proprietà.

Potrebbe essere necessario applicare la logica personalizzata in modo che i dati siano significativi per la proprietà di destinazione associata. La logica personalizzata può essere sotto forma di convertitore personalizzato se la conversione del tipo predefinito non esiste. Per informazioni sui convertitori, vedere Conversione dei dati.

Binding e BindingExpression

Prima di iniziare ad accedere ad altre funzionalità e data binding, è utile introdurre la BindingExpression classe . Come si è visto nelle sezioni precedenti, la classe è la classe di alto livello per la dichiarazione di un'associazione. Fornisce molte proprietà che consentono di specificare le caratteristiche di Binding un'associazione. Una classe correlata, BindingExpression , è l'oggetto sottostante che gestisce la connessione tra l'origine e la destinazione. Un binding contiene tutte le informazioni condivisibili tra diverse espressioni di binding. Un BindingExpression oggetto è un'espressione di istanza che non può essere condivisa e contiene tutte le informazioni sull'istanza di Binding .

Si consideri l'esempio seguente, dove è un'istanza della classe , è l'oggetto di origine ed è una classe definita che contiene una proprietà myDataObject MyData stringa denominata myBinding Binding MyData ColorName . In questo esempio viene associato il contenuto di testo myText di , un'istanza di TextBlock , a ColorName .

// Make a new source
var myDataObject = new MyData();
var myBinding = new Binding("ColorName")
{
    Source = myDataObject
};

// Bind the data source to the TextBox control's Text dependency property
myText.SetBinding(TextBlock.TextProperty, myBinding);
' Make a New source
Dim myDataObject As New MyData
Dim myBinding As New Binding("ColorName")
myBinding.Source = myDataObject

' Bind the data source to the TextBox control's Text dependency property
myText.SetBinding(TextBlock.TextProperty, myBinding)

È possibile usare lo stesso oggetto myBinding per creare altri binding. Ad esempio, è possibile usare l'oggetto myBinding per associare il contenuto di testo di una casella di controllo a ColorName. In questo scenario saranno presenti due istanze della condivisione BindingExpression dell'oggetto myBinding.

Un BindingExpression oggetto viene restituito chiamando su un oggetto GetBindingExpression associato a dati. Gli articoli seguenti illustrano alcuni utilizzi della BindingExpression classe :

Conversione dei dati

Nella sezione Crea un binding il pulsante è rosso perché la relativa proprietà è associata a una proprietà stringa con il valore Background "Red". Questo valore stringa funziona perché nel tipo è presente un convertitore di tipi per Brush convertire il valore stringa in un oggetto Brush .

L'aggiunta di queste informazioni alla figura nella sezione Creare un'associazione è simile alla seguente.

Diagramma che mostra la data binding default.

Tuttavia, cosa succede se invece di avere una proprietà di tipo stringa l'oggetto di origine del binding ha una proprietà Color di tipo Color ? In tal caso, per consentire il funzionamento dell'associazione, è prima necessario trasformare il valore della proprietà Color in un elemento accettato Background dalla proprietà. È necessario creare un convertitore personalizzato implementando IValueConverter l'interfaccia , come nell'esempio seguente.

[ValueConversion(typeof(Color), typeof(SolidColorBrush))]
public class ColorBrushConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        Color color = (Color)value;
        return new SolidColorBrush(color);
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return null;
    }
}
<ValueConversion(GetType(Color), GetType(SolidColorBrush))>
Public Class ColorBrushConverter
    Implements IValueConverter
    Public Function Convert(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements IValueConverter.Convert
        Dim color As Color = CType(value, Color)
        Return New SolidColorBrush(color)
    End Function

    Public Function ConvertBack(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements IValueConverter.ConvertBack
        Return Nothing
    End Function
End Class

Per altre informazioni, vedere IValueConverter.

A questo punto viene usato il convertitore personalizzato anziché la conversione predefinita e il diagramma ha un aspetto simile al seguente.

Diagramma che mostra il data binding convertitore personalizzato.

Come già detto, le conversioni predefinite possono essere disponibili o meno a seconda dei convertitori presenti nel tipo a cui si esegue il binding. Questo comportamento dipenderà dai convertitori di tipi disponibili nella destinazione. In caso di dubbio, creare un convertitore personalizzato.

Di seguito sono riportati alcuni scenari tipici in cui è opportuno implementare un convertitore di dati:

  • I dati devono essere visualizzati in modo diverso, a seconda delle impostazioni cultura. Ad esempio, è possibile implementare un convertitore di valuta o un convertitore di data/ora del calendario in base alle convenzioni usate in determinate impostazioni cultura.

  • I dati usati non sono necessariamente destinati a modificare il valore di testo di una proprietà, ma sono invece destinati a modificare un altro valore, ad esempio l'origine di un'immagine o il colore o lo stile del testo visualizzato. I convertitori possono essere usati in questo caso per convertire il binding di una proprietà considerata poco appropriata, ad esempio il binding di un campo di testo alla proprietà Background della cella di una tabella.

  • Più di un controllo o più proprietà dei controlli sono associate agli stessi dati. In questo caso, il binding primario potrebbe semplicemente visualizzare il testo, mentre gli altri binding gestiscono problemi di visualizzazione specifici, usando comunque lo stesso binding come informazione di origine.

  • Una proprietà di destinazione ha una raccolta di associazioni, ovvero MultiBinding . Per MultiBinding , si usa un oggetto personalizzato per produrre un valore finale dai valori delle IMultiValueConverter associazioni. È possibile ad esempio calcolare il colore dai valori rosso, blu e verde, ovvero valori che possono provenire da oggetti origine del binding identici o diversi. Per MultiBinding esempi e informazioni, vedere .

Binding a raccolte

Un oggetto di origine dell'associazione può essere considerato come un singolo oggetto le cui proprietà contengono dati o come una raccolta di dati di oggetti polimorfici spesso raggruppati insieme, ad esempio il risultato di una query in un database. Finora è stato illustrato solo il binding a singoli oggetti. Tuttavia, l'associazione a una raccolta dati è uno scenario comune. Ad esempio, uno scenario comune consiste nell'usare un, ad esempio , o per visualizzare una raccolta di dati, ad esempio nell'app illustrata nella sezione ItemsControl ListBox Informazioni ListView TreeView data binding.

Anche in questo caso è possibile applicare il diagramma di base. Se si associa un oggetto ItemsControl a una raccolta, il diagramma è simile al seguente.

Diagramma che mostra l'data binding ItemsControl.

Come illustrato in questo diagramma, per associare ItemsControl un oggetto a un oggetto raccolta, la proprietà è la proprietà da ItemsControl.ItemsSource usare. È possibile pensare a ItemsSource come al contenuto di ItemsControl . L'associazione è OneWay perché la proprietà supporta ItemsSource OneWay l'associazione per impostazione predefinita.

Come implementare le raccolte

È possibile enumerare su qualsiasi raccolta che implementa IEnumerable l'interfaccia . Tuttavia, per configurare associazioni dinamiche in modo che gli inserimenti o le eliminazioni nella raccolta aggiornerà automaticamente l'interfaccia utente, la raccolta deve implementare INotifyCollectionChanged l'interfaccia . Questa interfaccia espone un evento che deve essere generato a ogni modifica della raccolta sottostante.

WPF fornisce la ObservableCollection<T> classe , che è un'implementazione predefinita di una raccolta di dati che espone l'interfaccia INotifyCollectionChanged . Per supportare completamente il trasferimento dei valori dei dati dagli oggetti di origine alle destinazioni, ogni oggetto nella raccolta che supporta le proprietà associabili deve implementare anche INotifyPropertyChanged l'interfaccia . Per altre informazioni, vedere Panoramica delle origini di associazione.

Prima di implementare una raccolta, è consigliabile usare o una delle classi di raccolte esistenti, ad esempio ObservableCollection<T> , e , tra molte List<T> Collection<T> BindingList<T> altre. Se si ha uno scenario avanzato e si vuole implementare una raccolta personale, è consigliabile usare , che fornisce una raccolta non generica di oggetti a cui l'indice può accedere singolarmente e che offre quindi prestazioni IList ottimali.

Viste di raccolte

Dopo aver associato a una raccolta dati, è possibile ItemsControl ordinare, filtrare o raggruppare i dati. A tale scopo, si usano le visualizzazioni raccolta, ovvero classi che implementano ICollectionView l'interfaccia .

Che cosa sono le visualizzazioni di raccolta?

Una visualizzazione di raccolta rappresenta il livello superiore di una raccolta di origine di binding che consente di esplorare e visualizzare la raccolta di origine in base a query di ordinamento, filtro e raggruppamento, il tutto senza modificare la raccolta di origine sottostante. Una visualizzazione di raccolta mantiene inoltre un puntatore all'elemento corrente nella raccolta. Se la raccolta di origine implementa INotifyCollectionChanged l'interfaccia , le modifiche generate dall'evento CollectionChanged vengono propagate alle visualizzazioni.

Poiché le visualizzazioni non modificano le raccolte di origine sottostanti, ogni raccolta di origine può avere più visualizzazioni associate. Si consideri ad esempio una raccolta di oggetti Task. Grazie alle visualizzazioni è possibile visualizzare gli stessi dati in modi diversi. È possibile ad esempio visualizzare le attività ordinate in base alla priorità nella parte sinistra della pagina e, contemporaneamente nella parte destra, visualizzare le stesse attività raggruppate in base all'area.

Come creare una visualizzazione

Per creare e usare una visualizzazione, è possibile creare direttamente un'istanza dell'oggetto visualizzazione e usare tale istanza come origine del binding. Si consideri ad esempio l'app demo Data binding illustrata nella sezione Informazioni data binding app. L'app viene implementata in modo che l'oggetto sia associato a una visualizzazione sulla raccolta ListBox dati anziché direttamente sulla raccolta dati. L'esempio seguente viene estratto dall'app demo Data binding. La CollectionViewSource classe è il proxy XAML di una classe che eredita da CollectionView . In questo particolare esempio, la proprietà della visualizzazione è associata alla raccolta Distogliere Source gli oggetti (di tipo ) ObservableCollection<T> dell'oggetto app corrente.

<Window.Resources>
    <CollectionViewSource 
      Source="{Binding Source={x:Static Application.Current}, Path=AuctionItems}"   
      x:Key="listingDataView" />
</Window.Resources>

La risorsa listingDataView funge quindi da origine di associazione per gli elementi nell'app, ad esempio ListBox .

<ListBox Name="Master" Grid.Row="2" Grid.ColumnSpan="3" Margin="8" 
         ItemsSource="{Binding Source={StaticResource listingDataView}}" />

Per creare un'altra visualizzazione per la stessa raccolta, è possibile creare un'altra CollectionViewSource istanza e assegnargli un nome x:Key diverso.

Nella tabella seguente vengono illustrati i tipi di dati della vista creati come visualizzazione raccolta predefinita o in base CollectionViewSource al tipo di raccolta di origine.

Tipo di raccolta di origine Tipo di visualizzazione di raccolta Note
IEnumerable Tipo interno basato su CollectionView Non è possibile raggruppare gli elementi.
IList ListCollectionView Il più veloce.
IBindingList BindingListCollectionView

Uso di una visualizzazione predefinita

Specificare una visualizzazione di raccolta come origine di binding rappresenta un modo per creare e usare una visualizzazione di raccolta. WPF crea inoltre una visualizzazione di raccolta predefinita per ogni raccolta usata come origine di binding. Se si esegue il binding direttamente a una raccolta, WPF esegue il binding alla relativa visualizzazione predefinita. Questa visualizzazione predefinita è condivisa da tutte le associazioni alla stessa raccolta, pertanto una modifica apportata a una visualizzazione predefinita da un controllo associato o da un codice (ad esempio l'ordinamento o una modifica al puntatore all'elemento corrente, descritto più avanti) si riflette in tutte le altre associazioni alla stessa raccolta.

Per ottenere la visualizzazione predefinita, usare il GetDefaultView metodo . Per un esempio, vedere Ottenere la visualizzazione predefinita di una raccolta dati (.NET Framework).

Visualizzazioni raccolta con ADO.NET DataTable

Per migliorare le prestazioni, le visualizzazioni raccolta per ADO.NET o gli oggetti delegano l'ordinamento e il filtro a , che fa sì che l'ordinamento e il filtro siano condivisi tra tutte le visualizzazioni raccolta DataTable DataView DataView dell'origine dati. Per consentire a ogni visualizzazione di raccolta di ordinare e filtrare in modo indipendente, inizializzare ogni visualizzazione di raccolta con il proprio DataView oggetto .

Ordinamento

Come detto in precedenza, le visualizzazioni possono applicare un ordinamento a una raccolta. Poiché esistono nella raccolta sottostante, i dati possono avere o non avere un ordine intrinseco. La visualizzazione della raccolta consente di imporre un ordine o di modificare l'ordine predefinito in base ai criteri di confronto che si forniscono. Poiché si tratta di una visualizzazione dei dati basata su client, uno scenario comune è che l'utente potrebbe voler ordinare le colonne di dati tabulari in base al valore a cui corrisponde la colonna. Grazie alle visualizzazioni, è possibile applicare questo ordinamento gestito dall'utente senza apportare alcuna modifica alla raccolta sottostante né dover ripetere la query nel contenuto della raccolta. Per un esempio, vedi Ordinare una colonna GridView quando si fa clic su un'intestazione (.NET Framework).

L'esempio seguente illustra la logica di ordinamento di "Ordina per categoria e data" dell'interfaccia utente dell'app nella sezione Elementi CheckBox data binding app.

private void AddSortCheckBox_Checked(object sender, RoutedEventArgs e)
{
    // Sort the items first by Category and then by StartDate
    listingDataView.SortDescriptions.Add(new SortDescription("Category", ListSortDirection.Ascending));
    listingDataView.SortDescriptions.Add(new SortDescription("StartDate", ListSortDirection.Ascending));
}
Private Sub AddSortCheckBox_Checked(sender As Object, e As RoutedEventArgs)
    ' Sort the items first by Category And then by StartDate
    listingDataView.SortDescriptions.Add(New SortDescription("Category", ListSortDirection.Ascending))
    listingDataView.SortDescriptions.Add(New SortDescription("StartDate", ListSortDirection.Ascending))
End Sub

Filtro

Le visualizzazioni possono anche applicare un filtro a una raccolta, in modo che la visualizzazione mostra solo un determinato subset della raccolta completa. I dati possono essere filtrati in base a una condizione. Ad esempio, come avviene per l'app nella sezione Che cos'è data binding, "Mostra solo le erre" contiene la logica per filtrare gli elementi che costano CheckBox $ 25 o più. Il codice seguente viene eseguito per impostare ShowOnlyBargainsFilter come gestore Filter eventi quando viene selezionato CheckBox .

private void AddFilteringCheckBox_Checked(object sender, RoutedEventArgs e)
{
    if (((CheckBox)sender).IsChecked == true)
        listingDataView.Filter += ListingDataView_Filter;
    else
        listingDataView.Filter -= ListingDataView_Filter;
}
Private Sub AddFilteringCheckBox_Checked(sender As Object, e As RoutedEventArgs)
    Dim checkBox = DirectCast(sender, CheckBox)

    If checkBox.IsChecked = True Then
        AddHandler listingDataView.Filter, AddressOf ListingDataView_Filter
    Else
        RemoveHandler listingDataView.Filter, AddressOf ListingDataView_Filter
    End If
End Sub

Il gestore dell'evento ShowOnlyBargainsFilter ha l'implementazione seguente.

private void ListingDataView_Filter(object sender, FilterEventArgs e)
{
    // Start with everything excluded
    e.Accepted = false;

    // Only inlcude items with a price less than 25
    if (e.Item is AuctionItem product && product.CurrentPrice < 25)
        e.Accepted = true;
}
Private Sub ListingDataView_Filter(sender As Object, e As FilterEventArgs)

    ' Start with everything excluded
    e.Accepted = False

    Dim product As AuctionItem = TryCast(e.Item, AuctionItem)

    If product IsNot Nothing Then

        ' Only include products with prices lower than 25
        If product.CurrentPrice < 25 Then e.Accepted = True

    End If

End Sub

Se si usa direttamente una delle CollectionView classi anziché , è necessario usare la proprietà per specificare un CollectionViewSource Filter callback. Per un esempio, vedere Filtrare i dati in una vista (.NET Framework).

Raggruppamento

Ad eccezione della classe interna che visualizza una raccolta, tutte le visualizzazioni di raccolta supportano il raggruppamento , che consente all'utente di partizionare la raccolta nella visualizzazione raccolta IEnumerable in gruppi logici. Se l'utente fornisce un elenco di gruppi, questi saranno espliciti. Se invece i gruppi vengono generati in modo dinamico in base ai dati, si avranno gruppi impliciti.

Nell'esempio seguente viene illustrata la logica di "Raggruppa per CheckBox categoria".

// This groups the items in the view by the property "Category"
var groupDescription = new PropertyGroupDescription();
groupDescription.PropertyName = "Category";
listingDataView.GroupDescriptions.Add(groupDescription);
' This groups the items in the view by the property "Category"
Dim groupDescription = New PropertyGroupDescription()
groupDescription.PropertyName = "Category"
listingDataView.GroupDescriptions.Add(groupDescription)

Per un altro esempio di raggruppamento, vedi Raggruppare elementi in un controllo ListView che implementa un controllo GridView (.NET Framework).

Puntatori all'elemento corrente

Le visualizzazioni supportano anche la nozione di elemento corrente. In una visualizzazione di raccolta è possibile spostarsi da un oggetto all'altro. Mentre ci si sposta, si sposta un puntatore a un elemento che consente di recuperare l'oggetto presente in quella posizione specifica nella raccolta. Per un esempio, vedi Spostarsi tra gli oggetti in un controllo CollectionView (.NET Framework)di dati.

Poiché WPF esegue il binding a una raccolta solo tramite una visualizzazione, che può essere sia una visualizzazione specificata dall'utente che la visualizzazione predefinita della raccolta, tutti i binding alle raccolte contengono un puntatore dell'elemento corrente. Quando si esegue il binding a una visualizzazione, il carattere barra ("/") in un valore di Path definisce l'elemento corrente della visualizzazione. Nell'esempio seguente il contesto di dati è una visualizzazione di raccolta. La prima riga viene associata alla raccolta. La seconda riga viene associata all'elemento corrente della raccolta. La terza riga viene associata alla proprietà Description dell'elemento corrente nella raccolta.

<Button Content="{Binding }" />
<Button Content="{Binding Path=/}" />
<Button Content="{Binding Path=/Description}" />

La barra e la sintassi della proprietà possono inoltre essere sovrapposte per attraversare una gerarchia di raccolte. Nell'esempio seguente viene eseguito il binding all'elemento corrente di una raccolta denominata Offices, che è una proprietà dell'elemento corrente della raccolta di origine.

<Button Content="{Binding /Offices/}" />

Il puntatore dell'elemento corrente può essere influenzato da un'operazione di ordinamento o filtro applicata alla raccolta. L'ordinamento mantiene il puntatore dell'elemento corrente sull'ultimo elemento selezionato, ma la visualizzazione di raccolta viene ristrutturata in base a tale elemento. È possibile che l'elemento selezionato si trova all'inizio dell'elenco prima, ma ora l'elemento selezionato potrebbe essere in una posizione centrale. Il filtro mantiene l'elemento selezionato se la selezione rimane nella visualizzazione dopo il filtro. Il puntatore dell'elemento corrente viene altrimenti impostato sul primo elemento della visualizzazione di raccolta filtrata.

Scenario di associazione master-dettagli

La nozione di elemento corrente è utile non solo per l'esplorazione degli elementi in una raccolta, bensì anche per lo scenario di binding master-dettagli. Si consideri di nuovo l'interfaccia utente dell data binding app nella sezione Elementi di lavoro. In tale app, la selezione all'interno ListBox di determina il contenuto visualizzato in ContentControl . Per metterlo in un altro modo, quando viene selezionato un elemento, visualizza ListBox i dettagli ContentControl dell'elemento selezionato.

Per implementare lo scenario master-dettagli, occorre semplicemente avere due o più controlli associati alla stessa visualizzazione. L'esempio seguente della demo data binding mostra il markup di e dell'elemento visualizzato nell'interfaccia utente dell'app nella sezione ListBox ContentControl Data binding.

<ListBox Name="Master" Grid.Row="2" Grid.ColumnSpan="3" Margin="8" 
         ItemsSource="{Binding Source={StaticResource listingDataView}}" />
<ContentControl Name="Detail" Grid.Row="3" Grid.ColumnSpan="3"
                Content="{Binding Source={StaticResource listingDataView}}"
                ContentTemplate="{StaticResource detailsProductListingTemplate}" 
                Margin="9,0,0,0"/>

Si noti che entrambi i controlli sono associati alla stessa origine, la risorsa statica listingDataView (vedere la definizione di questa risorsa nella sezione Come creare una visualizzazione). Questa associazione funziona perché quando un oggetto singleton ( in questo caso ) è associato a una visualizzazione raccolta, viene associato automaticamente ContentControl CurrentItem all'oggetto della visualizzazione. Gli CollectionViewSource oggetti sincronizzano automaticamente la valuta e la selezione. Se il controllo elenco non è associato a un oggetto come in questo esempio, è necessario impostarne la proprietà su per il CollectionViewSource IsSynchronizedWithCurrentItem true funzionamento.

Per altri esempi, vedere Eseguire l'associazione a una raccolta e visualizzare le informazioni in base alla selezione (.NET Framework) e Usare il modello master-dettagli con dati gerarchici (.NET Framework).

Come si può notare, l'esempio precedente usa un modello. In realtà, i dati non verrebbero visualizzati nel modo desiderato senza l'uso di modelli (quello usato in modo esplicito da e quello usato in modo ContentControl implicito da ListBox ). Le sezione seguente illustra i modelli di dati.

Modelli di dati

Senza l'uso di modelli di dati, l'interfaccia utente dell'app nella sezione Elementi data binding sarà simile alla seguente.

Data Binding Demo senza Data Templates

Come illustrato nell'esempio nella sezione precedente, sia il controllo che l'oggetto sono associati all'intero oggetto raccolta (o, più in particolare, alla visualizzazione sull'oggetto raccolta) di ListBox ContentControl OggettiAstoitem. Senza istruzioni specifiche su come visualizzare la raccolta dati, visualizza la rappresentazione di stringa di ogni oggetto nella raccolta sottostante e visualizza la rappresentazione di stringa dell'oggetto a cui ListBox ContentControl è associato.

Per risolvere il problema, l'app definisce DataTemplates . Come illustrato nell'esempio nella sezione precedente, usa in modo esplicito il modello di dati ContentControl detailsProductListingTemplate. Il ListBox controllo usa in modo implicito il modello di dati seguente quando si visualizzano gli oggetti DisastabiliItem nell'insieme.

<DataTemplate DataType="{x:Type src:AuctionItem}">
    <Border BorderThickness="1" BorderBrush="Gray"
            Padding="7" Name="border" Margin="3" Width="500">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition/>
                <RowDefinition/>
                <RowDefinition/>
                <RowDefinition/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="20"/>
                <ColumnDefinition Width="86"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>

            <Polygon Grid.Row="0" Grid.Column="0" Grid.RowSpan="4"
                     Fill="Yellow" Stroke="Black" StrokeThickness="1"
                     StrokeLineJoin="Round" Width="20" Height="20"
                     Stretch="Fill"
                     Points="9,2 11,7 17,7 12,10 14,15 9,12 4,15 6,10 1,7 7,7"
                     Visibility="Hidden" Name="star"/>

            <TextBlock Grid.Row="0" Grid.Column="1" Margin="0,0,8,0"
                       Name="descriptionTitle"
                       Style="{StaticResource smallTitleStyle}">Description:</TextBlock>
            
            <TextBlock Name="DescriptionDTDataType" Grid.Row="0" Grid.Column="2"
                       Text="{Binding Path=Description}"
                       Style="{StaticResource textStyleTextBlock}"/>

            <TextBlock Grid.Row="1" Grid.Column="1" Margin="0,0,8,0"
                       Name="currentPriceTitle"
                       Style="{StaticResource smallTitleStyle}">Current Price:</TextBlock>
            
            <StackPanel Grid.Row="1" Grid.Column="2" Orientation="Horizontal">
                <TextBlock Text="$" Style="{StaticResource textStyleTextBlock}"/>
                <TextBlock Name="CurrentPriceDTDataType"
                           Text="{Binding Path=CurrentPrice}" 
                           Style="{StaticResource textStyleTextBlock}"/>
            </StackPanel>
        </Grid>
    </Border>
    <DataTemplate.Triggers>
        <DataTrigger Binding="{Binding Path=SpecialFeatures}">
            <DataTrigger.Value>
                <src:SpecialFeatures>Color</src:SpecialFeatures>
            </DataTrigger.Value>
            <DataTrigger.Setters>
                <Setter Property="BorderBrush" Value="DodgerBlue" TargetName="border" />
                <Setter Property="Foreground" Value="Navy" TargetName="descriptionTitle" />
                <Setter Property="Foreground" Value="Navy" TargetName="currentPriceTitle" />
                <Setter Property="BorderThickness" Value="3" TargetName="border" />
                <Setter Property="Padding" Value="5" TargetName="border" />
            </DataTrigger.Setters>
        </DataTrigger>
        <DataTrigger Binding="{Binding Path=SpecialFeatures}">
            <DataTrigger.Value>
                <src:SpecialFeatures>Highlight</src:SpecialFeatures>
            </DataTrigger.Value>
            <Setter Property="BorderBrush" Value="Orange" TargetName="border" />
            <Setter Property="Foreground" Value="Navy" TargetName="descriptionTitle" />
            <Setter Property="Foreground" Value="Navy" TargetName="currentPriceTitle" />
            <Setter Property="Visibility" Value="Visible" TargetName="star" />
            <Setter Property="BorderThickness" Value="3" TargetName="border" />
            <Setter Property="Padding" Value="5" TargetName="border" />
        </DataTrigger>
    </DataTemplate.Triggers>
</DataTemplate>

Con l'uso di questi due DataTemplate, l'interfaccia utente risultante è quella visualizzata nella sezione Elementi data binding. Come si può vedere dallo screenshot, oltre a consentire di inserire i dati nei controlli, i datatemplate consentono di definire oggetti visivi accattivanti per i dati. Ad esempio, nell'esempio precedente vengono usati gli elementi in modo che gli oggetti Disastacciati con valore DataTrigger DataTemplate SpecialFeatures di HighLight siano visualizzati con un bordo arancione e una stella.

Per altre informazioni sui modelli di dati, vedere Panoramica dei modelli di dati (.NET Framework).

Convalida dei dati

La maggior parte delle app che accettano l'input dell'utente deve avere una logica di convalida per assicurarsi che l'utente abbia immesso le informazioni previste. I controlli di convalida possono essere basati su tipo, intervallo, formato o altri requisiti specifici dell'app. Questa sezione illustra il funzionamento della convalida dei dati in WPF.

Associazione di regole di convalida a un'associazione

Il modello data binding WPF consente di ValidationRules associare Binding all'oggetto . Nell'esempio seguente viene associato un oggetto a una proprietà denominata e TextBox viene aggiunto un oggetto alla proprietà StartPrice ExceptionValidationRule Binding.ValidationRules .

<TextBox Name="StartPriceEntryForm" Grid.Row="2"
         Style="{StaticResource textStyleTextBox}" Margin="8,5,0,5" Grid.ColumnSpan="2">
    <TextBox.Text>
        <Binding Path="StartPrice" UpdateSourceTrigger="PropertyChanged">
            <Binding.ValidationRules>
                <ExceptionValidationRule />
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox>

Un ValidationRule oggetto controlla se il valore di una proprietà è valido. WPF include due tipi di oggetti ValidationRule predefiniti:

È anche possibile creare una regola di convalida personalizzata derivando dalla ValidationRule classe e implementando il Validate metodo . L'esempio seguente illustra la regola usata dalla sezione "Data di inizio" dell'aggiunta TextBox dell'elenco data binding prodotto.

public class FutureDateRule : ValidationRule
{
    public override ValidationResult Validate(object value, CultureInfo cultureInfo)
    {
        // Test if date is valid
        if (DateTime.TryParse(value.ToString(), out DateTime date))
        {
            // Date is not in the future, fail
            if (DateTime.Now > date)
                return new ValidationResult(false, "Please enter a date in the future.");
        }
        else
        {
            // Date is not a valid date, fail
            return new ValidationResult(false, "Value is not a valid date.");
        }

        // Date is valid and in the future, pass
        return ValidationResult.ValidResult;
    }
}
Public Class FutureDateRule
    Inherits ValidationRule

    Public Overrides Function Validate(value As Object, cultureInfo As CultureInfo) As ValidationResult

        Dim inputDate As Date

        ' Test if date is valid
        If Date.TryParse(value.ToString, inputDate) Then

            ' Date is not in the future, fail
            If Date.Now > inputDate Then
                Return New ValidationResult(False, "Please enter a date in the future.")
            End If

        Else
            ' // Date Is Not a valid date, fail
            Return New ValidationResult(False, "Value is not a valid date.")
        End If

        ' Date is valid and in the future, pass
        Return ValidationResult.ValidResult

    End Function

End Class

StartDateEntryForm TextBox usa questo oggetto FutureDateRule, come illustrato nell'esempio seguente.

<TextBox Name="StartDateEntryForm" Grid.Row="3"
         Validation.ErrorTemplate="{StaticResource validationTemplate}" 
         Style="{StaticResource textStyleTextBox}" Margin="8,5,0,5" Grid.ColumnSpan="2">
    <TextBox.Text>
        <Binding Path="StartDate" UpdateSourceTrigger="PropertyChanged" 
                 Converter="{StaticResource dateConverter}" >
            <Binding.ValidationRules>
                <src:FutureDateRule />
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox>

Poiché il valore è , il motore di associazione aggiorna il valore di origine a ogni pressione di tasto, ovvero controlla anche ogni regola nella raccolta a UpdateSourceTrigger PropertyChanged ogni pressione di ValidationRules tasto. L'argomento verrà ulteriormente trattato nella sezione Processo di convalida.

Fornire commenti e suggerimenti visivi

Se l'utente immette un valore non valido, è possibile fornire commenti e suggerimenti sull'errore nell'interfaccia utente dell'app. Un modo per fornire tale feedback è impostare la Validation.ErrorTemplate proprietà associata su un oggetto ControlTemplate personalizzato. Come illustrato nella sottosezione precedente, StartDateEntryForm TextBox usa un elemento ErrorTemplate validationTemplate denominato. L'esempio seguente illustra la definizione di validationTemplate.

<ControlTemplate x:Key="validationTemplate">
    <DockPanel>
        <TextBlock Foreground="Red" FontSize="20">!</TextBlock>
        <AdornedElementPlaceholder/>
    </DockPanel>
</ControlTemplate>

AdornedElementPlaceholderL'elemento specifica dove deve essere posizionato il controllo da adornare.

È inoltre possibile utilizzare un oggetto per ToolTip visualizzare il messaggio di errore. Sia startDateEntryForm che StartPriceEntryForm usano lo stile TextBox textStyleTextBox, che crea un oggetto ToolTip che visualizza il messaggio di errore. L'esempio seguente illustra la definizione di textStyleTextBox. La proprietà associata è quando una o più associazioni sulle Validation.HasError true proprietà dell'elemento associato sono erre.

<Style x:Key="textStyleTextBox" TargetType="TextBox">
    <Setter Property="Foreground" Value="#333333" />
    <Setter Property="MaxLength" Value="40" />
    <Setter Property="Width" Value="392" />
    <Style.Triggers>
        <Trigger Property="Validation.HasError" Value="true">
            <Setter Property="ToolTip" 
                    Value="{Binding (Validation.Errors).CurrentItem.ErrorContent, RelativeSource={RelativeSource Self}}" />
        </Trigger>
    </Style.Triggers>
</Style>

Con ErrorTemplate l'oggetto personalizzato e , ToolTip StartDateEntryForm è simile al seguente TextBox quando si verifica un errore di convalida.

Errore di convalida del data binding per la data

Se all'oggetto sono associate regole di convalida ma non si specifica un oggetto nel controllo associato, verrà usato un valore predefinito per notificare agli utenti quando si verifica Binding ErrorTemplate un errore di ErrorTemplate convalida. Il valore ErrorTemplate predefinito è un modello di controllo che definisce un bordo rosso nel livello dello strumento decorativo visuale. Con il valore predefinito e , l'interfaccia utente di ErrorTemplate ToolTip StartPriceEntryForm è simile alla seguente quando si verifica TextBox un errore di convalida.

Errore di convalida del data binding per il prezzo

Per un esempio di come fornire la logica per convalidare tutti i controlli in una finestra di dialogo, vedere la sezione Finestre di dialogo personalizzate in Cenni preliminari sulle finestre di dialogo.

Processo di convalida

La convalida avviene solitamente quando si trasferisce un valore di destinazione alla proprietà di origine del binding. Questo trasferimento si verifica TwoWay nelle OneWayToSource associazioni e . Come si è detto, la causa di un aggiornamento dell'origine dipende dal valore della proprietà , come descritto nella sezione Che cosa attiva gli UpdateSourceTrigger aggiornamenti dell'origine.

Gli elementi seguenti descrivono il processo di convalida. Se si verifica un errore di convalida o un altro tipo di errore in qualsiasi momento durante questo processo, il processo viene interrotto:

  1. Il motore di associazione controlla se sono presenti oggetti personalizzati definiti la cui proprietà è impostata su per tale oggetto , nel qual caso chiama il metodo su ognuno di essi fino a quando uno di essi non restituisce un errore o fino a quando non vengono passati ValidationRule ValidationStep RawProposedValue Binding Validate ValidationRule tutti.

  2. Il motore di binding chiama quindi il convertitore, se presente.

  3. Se il convertitore ha esito positivo, il motore di associazione controlla se sono presenti oggetti personalizzati definiti la cui proprietà è impostata su per tale oggetto , nel qual caso chiama il metodo su ogni oggetto impostato su finché uno di essi non viene in esecuzione in un errore o fino a quando non vengono passati ValidationRule ValidationStep ConvertedProposedValue Binding Validate ValidationRule ValidationStep ConvertedProposedValue tutti.

  4. Il motore di binding imposta la proprietà di origine.

  5. Il motore di associazione verifica se sono definiti oggetti personalizzati il cui valore è impostato su per , nel qual caso chiama il metodo su ogni oggetto impostato su fino a quando uno di essi non viene in esecuzione in un errore o fino a quando non vengono passati ValidationRule ValidationStep UpdatedValue Binding Validate ValidationRule ValidationStep UpdatedValue tutti. Se un oggetto è associato a un'associazione e la proprietà è impostata sul valore DataErrorValidationRule ValidationStep UpdatedValue predefinito, l'oggetto DataErrorValidationRule viene controllato a questo punto. A questo punto viene verificata qualsiasi associazione ValidatesOnDataErrors con la proprietà true impostata su .

  6. Il motore di associazione verifica se sono definiti oggetti personalizzati il cui valore è impostato su per , nel qual caso chiama il metodo su ogni oggetto impostato su fino a quando uno di essi non viene in esecuzione in un errore o fino a quando non vengono passati ValidationRule ValidationStep CommittedValue Binding Validate ValidationRule ValidationStep CommittedValue tutti.

Se un oggetto non passa in qualsiasi momento durante questo processo, il motore di associazione crea un oggetto e lo aggiunge alla ValidationRule ValidationError raccolta Validation.Errors dell'elemento associato. Prima di eseguire gli oggetti in un determinato passaggio, il motore di associazione rimuove tutti gli elementi aggiunti alla proprietà associata dell'elemento ValidationRule ValidationError associato durante tale Validation.Errors passaggio. Ad esempio, se un oggetto la cui proprietà è impostata su ha esito negativo, alla successiva esecuzione del processo di convalida, il motore di associazione lo rimuove immediatamente prima di chiama qualsiasi oggetto impostato ValidationRule ValidationStep su UpdatedValue ValidationError ValidationRule ValidationStep UpdatedValue .

Quando Validation.Errors non è vuoto, la proprietà Validation.HasError associata dell'elemento è impostata su true . Inoltre, se la proprietà di è impostata su , il motore di NotifyOnValidationError associazione genera Binding true Validation.Error l'evento associato sull'elemento.

Si noti anche che un trasferimento di valori valido in entrambe le direzioni (da destinazione a origine o da origine a destinazione) cancella Validation.Errors la proprietà associata.

Se all'associazione è associato un oggetto o se la proprietà è impostata su e viene generata un'eccezione quando il motore di associazione imposta ExceptionValidationRule ValidatesOnExceptions true l'origine, il motore di associazione verifica se è presente un oggetto UpdateSourceExceptionFilter . È possibile usare il UpdateSourceExceptionFilter callback per fornire un gestore personalizzato per la gestione delle eccezioni. Se non è specificato in , il motore di associazione crea un oggetto con l'eccezione e lo aggiunge alla UpdateSourceExceptionFilter Binding raccolta ValidationError Validation.Errors dell'elemento associato.

Meccanismo di debug

È possibile impostare la proprietà associata su un oggetto correlato all'associazione per ricevere informazioni sullo PresentationTraceSources.TraceLevel stato di un'associazione specifica.

Vedi anche