Ottimizzazione delle prestazioni: associazione dati
Il data binding di Windows Presentation Foundation (WPF) consente alle applicazioni di presentare e interagire con i dati in modo semplice e coerente. Gli elementi possono essere collegati ai dati provenienti da una vasta gamma di origini dati, come oggetti CLR e XML.
In questo argomento vengono fornite indicazioni sulle prestazioni di data binding.
Come vengono risolti i riferimenti al data binding
Prima di discutere dei problemi di prestazioni del data binding, è utile esaminare in che modo il motore di data binding di Windows Presentation Foundation (WPF) risolve i riferimenti agli oggetti per l'associazione.
L'origine di un data binding di Windows Presentation Foundation (WPF) può essere qualsiasi oggetto CLR. È possibile eseguire il binding a proprietà, sottoproprietà o indicizzatori di un oggetto CLR. I riferimenti di associazione vengono risolti usando la reflection Microsoft .NET Framework o un ICustomTypeDescriptor. Ecco tre metodi per risolvere i riferimenti agli oggetti per l'associazione.
Il primo metodo prevede l'uso della riflessione. In questo caso, l'oggetto PropertyInfo viene usato per individuare gli attributi della proprietà e fornisce l'accesso ai metadati delle proprietà. Quando si usa l'interfaccia ICustomTypeDescriptor, il motore di associazione dati usa questa interfaccia per accedere ai valori delle proprietà. L'interfaccia ICustomTypeDescriptor è particolarmente utile nei casi in cui l'oggetto non dispone di un set statico di proprietà.
Le notifiche di modifica delle proprietà possono essere fornite implementando l'interfaccia INotifyPropertyChanged o usando le notifiche di modifica associate al TypeDescriptor. Tuttavia, la strategia preferita per l'implementazione delle notifiche di modifica delle proprietà consiste nell'usare INotifyPropertyChanged.
Se l'oggetto di origine è un oggetto CLR e la proprietà di origine è una proprietà CLR, il motore di data binding di Windows Presentation Foundation (WPF) deve prima utilizzare la reflection sull'oggetto di origine per ottenere il TypeDescriptore quindi eseguire una query per un PropertyDescriptor. Questa sequenza di operazioni di riflessione potrebbe essere molto dispendiosa in termini di tempo dal punto di vista delle prestazioni.
Il secondo metodo per la risoluzione dei riferimenti agli oggetti prevede un oggetto di origine CLR che implementa l'interfaccia INotifyPropertyChanged e una proprietà di origine che è una proprietà CLR. In questo caso, il motore di associazione dati utilizza la reflection direttamente sul tipo di origine per ottenere la proprietà necessaria. Questo non è ancora il metodo ottimale, ma costerà meno nei requisiti del working set rispetto al primo metodo.
Il terzo metodo per la risoluzione dei riferimenti agli oggetti implica un oggetto di origine che è un DependencyObject e una proprietà di origine che è un DependencyProperty. In questo caso, il motore di data binding non ha bisogno di utilizzare la reflection. Insieme, il motore delle proprietà e il motore di data binding risolvono in modo indipendente il riferimento alla proprietà. Questo è il metodo ottimale per la risoluzione dei riferimenti agli oggetti usati per il data binding.
La tabella seguente confronta la velocità del data binding della proprietà Text di un migliaio di elementi TextBlock usando questi tre metodi.
Binding della proprietà Text di un TextBlock | Tempo di associazione (ms) | Tempo di rendering -- incluso il binding (ms) |
---|---|---|
A una proprietà di un oggetto CLR | 115 | 314 |
Per una proprietà di un oggetto CLR che implementa INotifyPropertyChanged | 115 | 305 |
Per un DependencyProperty di un DependencyObject. | 90 | 263 |
Associazione a oggetti CLR di grandi dimensioni
Si verifica un impatto significativo sulle prestazioni quando si esegue il binding dei dati a un singolo oggetto CLR con migliaia di proprietà. È possibile ridurre al minimo questo impatto dividendo il singolo oggetto in più oggetti CLR con un minor numero di proprietà. La tabella mostra i tempi di associazione e rendering per il data binding a un singolo oggetto CLR di grandi dimensioni rispetto a diversi oggetti più piccoli.
Associazione dati di 1000 oggetti TextBlock | tempo di associazione (ms) | Tempo di rendering -- inclusi i tempi di binding (ms) |
---|---|---|
Per un oggetto CLR con 1000 proprietà | 950 | 1200 |
Per 1000 oggetti CLR con una proprietà | 115 | 314 |
Collegamento a ItemsSource
Si consideri uno scenario in cui si dispone di un oggetto CLR List<T> che contiene una lista di dipendenti da visualizzare in un ListBox. Per creare una corrispondenza tra questi due oggetti, associare l'elenco dei dipendenti alla proprietà ItemsSource del ListBox. Si supponga tuttavia di avere un nuovo dipendente che si unirà al gruppo. Si potrebbe pensare che per inserire questa nuova persona nei valori di ListBox associati, è sufficiente aggiungere questa persona all'elenco dei dipendenti e aspettarsi che questa modifica venga riconosciuta automaticamente dal motore di associazione dati. Tale presupposto sarebbe falso; in realtà, la modifica non verrà riflessa automaticamente nel ListBox. Questo perché l'oggetto CLR List<T> non genera automaticamente un evento di modifica della raccolta. Per far sì che il ListBox recepisca le modifiche, è necessario ricostruire l'elenco di dipendenti e riassociarlo alla proprietà ItemsSource dell'ListBox. Anche se questa soluzione funziona, introduce un impatto enorme sulle prestazioni. Ogni volta che si riassegna il ItemsSource di ListBox a un nuovo oggetto, il ListBox prima elimina gli elementi precedenti e rigenera l'intero elenco. L'impatto sulle prestazioni viene amplificato se il ListBox esegue il mapping a un DataTemplatecomplesso.
Una soluzione molto efficiente a questo problema consiste nel rendere l'elenco dei dipendenti un ObservableCollection<T>. Un oggetto ObservableCollection<T> genera una notifica di modifica che il motore di data binding può ricevere. L'evento aggiunge o rimuove un elemento da un ItemsControl senza la necessità di rigenerare l'intero elenco.
La tabella seguente mostra il tempo necessario per aggiornare il ListBox (con la virtualizzazione dell'interfaccia utente disattivata) quando viene aggiunto un elemento. Il numero nella prima riga rappresenta il tempo trascorso quando l'oggetto CLR List<T> è associato alla ItemsSourcedell'elemento ListBox . Il numero nella seconda riga rappresenta il tempo trascorso quando un ObservableCollection<T> è associato al ItemsSourcedell'elemento ListBox . Si noti il notevole risparmio di tempo usando la strategia di data binding ObservableCollection<T>.
Data binding di ItemsSource | ora di aggiornamento per 1 elemento (ms) |
---|---|
Ad un oggetto CLR List<T> | 1656 |
A un ObservableCollection<T> | 20 |
Associare IList a ItemsControl e non a IEnumerable
Se è possibile scegliere tra l'associazione di un IList<T> o un IEnumerable a un oggetto ItemsControl, scegliere l'oggetto IList<T>. L'associazione di IEnumerable a un ItemsControl costringe WPF a creare un oggetto wrapper IList<T>, il che implica un peggioramento delle prestazioni a causa del sovraccarico non necessario di un secondo oggetto.
Non convertire oggetti CLR in XML solo per il binding dei dati.
WPF consente di fare data binding al contenuto XML; tuttavia, il data binding al contenuto XML è più lento rispetto al data binding agli oggetti CLR. Non convertire i dati dell'oggetto CLR in XML se l'unico scopo è per il data binding.
Vedere anche
- Ottimizzazione delle prestazioni dell'applicazione WPF
- Pianificazione delle prestazioni dell'applicazione
- sfruttare i vantaggi dell'hardware
- Layout e Progettazione
- grafica 2D e imaging
- comportamento dell'oggetto
- Risorse dell'applicazione
- Testo
- Altre raccomandazioni sulle prestazioni
- Panoramica del Data Binding
- procedura dettagliata: Memorizzazione nella cache dei dati dell'applicazione in un'applicazione WPF
.NET Desktop feedback