Ottimizzazione delle prestazioni: associazione dati

L'associazione dei dati di Windows Presentation Foundation (WPF) offre alle applicazioni un modo semplice e coerente di presentare i dati e interagire con essi. Gli elementi possono essere associati ai dati di un'ampia gamma di origini dati sotto forma di oggetti CLR e XML.

Questo argomento offre utili suggerimenti sulle prestazioni del data binding.

Risoluzione dei riferimenti di 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 oggetto ICustomTypeDescriptor. Di seguito vengono descritti i tre metodi disponibili per risolvere riferimenti a oggetti per il data binding.

Il primo metodo prevede l'uso della reflection. 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 data binding 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 a 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 usare reflection sull'oggetto di origine per ottenere e quindi eseguire TypeDescriptoruna query per un PropertyDescriptoroggetto . Questa sequenza di operazioni di reflection richiede potenzialmente molto tempo da un punto di vista delle prestazioni.

Il secondo metodo per la risoluzione dei riferimenti agli oggetti include 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 data binding usa direttamente la reflection sul tipo di origine e ottiene la proprietà necessaria. Sebbene presenti requisiti del working set inferiori rispetto al primo metodo, non si tratta ancora del metodo ottimale.

Il terzo metodo per la risoluzione dei riferimenti agli oggetti comporta un oggetto di origine che è un DependencyObject oggetto e una proprietà di origine che è un oggetto DependencyProperty. In questo caso, non è necessario che il motore di data binding usi la reflection: il motore della proprietà e il motore di data binding risolvono il riferimento alla proprietà in modo indipendente. Si tratta del metodo ottimale per la risoluzione dei riferimenti a oggetti usati per il data binding.

La tabella seguente confronta la velocità del data binding della Text proprietà di un migliaio TextBlock di elementi usando questi tre metodi.

Binding della proprietà Text di un TextBlock Tempo di binding (ms) Tempo di rendering: include il binding (ms)
A una proprietà di un oggetto CLR 115 314
Per una proprietà di un oggetto CLR che implementa INotifyPropertyChanged 115 305
A di DependencyProperty un oggetto .DependencyObject 90 263

Binding 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 più oggetti più piccoli.

Data binding di 1000 oggetti TextBlock Tempo di binding (ms) Tempo di rendering: include il binding (ms)
Per un oggetto CLR con 1000 proprietà 950 1200
A 1000 oggetti CLR con una proprietà 115 314

Binding a una proprietà ItemsSource

Si consideri uno scenario in cui si dispone di un oggetto CLR List<T> che contiene un elenco di dipendenti da visualizzare in un oggetto ListBox. Per creare una corrispondenza tra questi due oggetti, associare l'elenco ItemsSource dei dipendenti alla proprietà di ListBox. Si supponga ora che un nuovo dipendente si unisca al gruppo. Si potrebbe pensare che per inserire questa nuova persona nei valori associati ListBox , è 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.ListBox Ciò è dovuto al fatto che l'oggetto CLR List<T> non genera automaticamente un evento di modifica della raccolta. Per ottenere l'oggetto ListBox per raccogliere le modifiche, è necessario ricreare l'elenco dei dipendenti e ricollegarlo alla ItemsSource proprietà dell'oggetto ListBox. Questa soluzione funziona, ma incide considerevolmente sulle prestazioni. Ogni volta che si riassegna l'oggetto ItemsSource di ListBox a un nuovo oggetto, il ListBox primo elimina gli elementi precedenti e rigenera l'intero elenco. L'impatto sulle prestazioni viene amplificato se il mapping ListBox a un oggetto complesso DataTemplateè .

Una soluzione molto efficiente a questo problema consiste nel rendere l'elenco dei dipendenti come ObservableCollection<T>. Un ObservableCollection<T> oggetto genera una notifica di modifica che il motore di data binding può ricevere. L'evento aggiunge o rimuove un elemento da un oggetto ItemsControl senza la necessità di rigenerare l'intero elenco.

La tabella seguente mostra il tempo necessario per aggiornare 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 all'elemento ListBox .ItemsSource Il numero nella seconda riga rappresenta il tempo trascorso quando un oggetto ObservableCollection<T> è associato all'elemento ListBox .ItemsSource Si noti il notevole risparmio di tempo usando la strategia di ObservableCollection<T> data binding.

Data binding della proprietà ItemsSource Tempo di aggiornamento per 1 elemento (ms)
A un oggetto CLR List<T> 1656
A un oggetto ObservableCollection<T> 20

Binding di IList a ItemsControl non IEnumerable

Se si ha una scelta tra l'associazione di un IList<T> oggetto o IEnumerable a un ItemsControl oggetto, scegliere l'oggetto IList<T> . L'associazione IEnumerable a un ItemsControl forza WPF a creare un oggetto wrapper IList<T> , il che significa che le prestazioni sono influenzate dal sovraccarico non necessario di un secondo oggetto.

Non convertire oggetti CLR in XML solo per il data binding

WPF consente di associare dati al contenuto XML; Tuttavia, il data binding al contenuto XML è più lento rispetto all'associazione dati agli oggetti CLR. Non convertire i dati dell'oggetto CLR in XML se l'unico scopo è per il data binding.

Vedi anche