2009-09-09 11 views
7

Al momento ho due WPF listboxes imitando le seguenti funzionalitàAumentare WPF prestazioni ObservableCollection

Word 2007 customize screen http://tlt.its.psu.edu/suggestions/international/graphics/vista/WordCustomize.gif

Sto usando 2 ObservableCollections per consentire agli utenti di selezionare qualsiasi elemento di cui hanno bisogno (flessibilità è la chiave qui). Il problema principale è che ho migliaia di di elementi raggruppati in entrambi gli elenchi. Tutto sommato il design funziona molto bene (con poche decine di elementi), ma il mio ostacolo è quando un utente copia tutti gli elementi disponibili da sinistra a destra mentre lo schermo si blocca (tempo di esecuzione su un thread diverso?).

Guardando a ObservableCollection manca un metodo AddRange e ci sono varie implementazioni disponibili su Internet. So anche che l'evento CollectionChanged viene licenziato inutilmente dato che ogni elemento viene copiato in modo orribile su prestazioni drenanti.

È probabile che sia necessario consentire agli utenti di scegliere tra gruppi di oltre 10.000 elementi in futuro, il che suona come una cattiva idea, ma non è negoziabile in quanto il raggruppamento nella casella di riepilogo (CollectionViewSource) funziona davvero bene, ma ha l'effetto collaterale di commutazione off la virtualizzazione di entrambe le listboxes

Cosa posso fare per migliorare le prestazioni durante il caricamento di una casella di riepilogo con migliaia di elementi quando databound a un ObservableCollection? Esistono implementazioni di tipo AddRange che consiglieresti? È l'unica scelta che ho qui per eseguire questo su un thread in background che sembra costoso perché non sto caricando i dati da un database?

+0

Vedere questo http://stackoverflow.com/questions/1007691/observablecollection-databinding-performance – Sauron

risposta

2

Ho rimosso CollectionViewSource e il raggruppamento e gli elementi sono stati copiati in 1/2 secondo, ma il raggruppamento su di esso può richiedere fino a un minuto perché la virtualizzazione non funziona con il raggruppamento.

avrò bisogno di decidere se utilizzare il CollectionViewSource

+0

La raccoltaviewsource funziona in modo efficiente quando si verifica l'associazione iniziale, ma è molto inefficiente in fase di esecuzione Ora sto facendo lo smistamento/filtraggio nel codice dietro usando LINQ – Vault

+0

come è questa una risposta? –

1

Probabilmente si potrebbe ereditare da ObservableCollection<T> (o implementare direttamente INotifyCollectionChanged) per aggiungere BeginUpdate e EndUpdate metodi. Le modifiche apportate tra le chiamate a BeginUpdate e EndUpdate verranno messe in coda, quindi combinate in uno (o più se ci sono intervalli separati) NotifyCollectionChangedEventArgs oggetto che verrà passato ai gestori dell'evento CollectionChanged quando viene chiamato EndUpdate.

+1

Per quanto ne so, i controlli WPF non supportano gli aggiornamenti di intervallo per la raccolta e generano un'eccezione quando ricevono più di 1 elemento in un evento CollectionChanged. –

+1

WTF?! Perché fornire la possibilità di specificare più voci negli argomenti dell'evento se non lo supportano? Avevo implementato la raccolta descritta nella mia risposta, ma non avevo il tempo di testarlo realmente ... L'ho appena fatto, e sembra che tu abbia ragione :(Quindi la mia collezione non può essere utilizzata per gli scenari vincolanti .. . –

+0

non funziona in WPF 4.0 sia :( –

1

È possibile trovare una raccolta osservabile protetta da thread here. Rendi sicuro il tuo thread di raccolta Observable e collegalo a listbox.

+0

Questo è il metodo che sto utilizzando e funziona piuttosto bene. è possibile utilizzare un BackgroundWorker per riempire il vostro ObservableCollection e vedere il tuo ListBox viene popolato al volo. – japf

+0

Questo approccio è ancora lanciando un numero irragionevolmente elevato di eventi sul thread dell'interfaccia utente perché ogni elemento aggiunto genererà il proprio evento di modifica modificato, ma non risolverà il problema –

+0

il collegamento è morto – JobaDiniz

2

non ho potuto resistere rispondere a questa. Non penso che non avrai più bisogno di questa risposta, ma forse qualcun altro può usarla.

Non pensare troppo duro (non avvicinarsi a questo multithreaded (questo renderà le cose soggette a errori e inutili complicato. Utilizzare solo threading per i calcoli duri/IO), tutte queste diverse actiontypes sarà molto difficile da tampone. La parte più fastidiosa è che se rimuovi o aggiungi 10000 elementi della tua applicazione (caselle di elenco) sarà molto impegnato a gestire gli eventi generati da ObservableCollection.L'evento supporta già più elementi. Quindi .....

Tu potrebbe bufferizzare gli elementi fino a quando non cambia l'azione, quindi le azioni Aggiungi verranno memorizzate nel buffer e sollevate come batch se l'utente modifica l'azione o la svuota. Non hanno testarlo, ma si poteva fare qualcosa di simile:

// Written by JvanLangen 
public class BufferedObservableCollection<T> : ObservableCollection<T> 
{ 
    // the last action used 
    public NotifyCollectionChangedAction? _lastAction = null; 
    // the items to be buffered 
    public List<T> _itemBuffer = new List<T>(); 

    // constructor registeres on the CollectionChanged 
    public BufferedObservableCollection() 
    { 
     base.CollectionChanged += new NotifyCollectionChangedEventHandler(ObservableCollectionUpdate_CollectionChanged); 
    } 

    // When the collection changes, buffer the actions until the 'user' changes action or flushes it. 
    // This will batch add and remove actions. 
    private void ObservableCollectionUpdate_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) 
    { 
     // if we have a lastaction, check if it is changed and should be flush else only change the lastaction 
     if (_lastAction.HasValue) 
     { 
      if (_lastAction != e.Action) 
      { 
       Flush(); 
       _lastAction = e.Action; 
      } 
     } 
     else 
      _lastAction = e.Action; 

     _itemBuffer.AddRange(e.NewItems.Cast<T>()); 
    } 

    // Raise the new event. 
    protected void RaiseCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) 
    { 
     if (this.CollectionChanged != null) 
      CollectionChanged(sender, e); 
    } 

    // Don't forget to flush the list when your ready with your action or else the last actions will not be 'raised' 
    public void Flush() 
    { 
     if (_lastAction.HasValue && (_itemBuffer.Count > 0)) 
     { 
      RaiseCollectionChanged(this, new NotifyCollectionChangedEventArgs(_lastAction.Value, _itemBuffer)); 
      _itemBuffer.Clear(); 
      _lastAction = null; 
     } 
    } 

    // new event 
    public override event NotifyCollectionChangedEventHandler CollectionChanged; 
} 

Buon divertimento !, J3R03N

+0

Questo in realtà non funziona. Quando provi ad aumentare la raccolta dell'evento modificato con più elementi, otterrai NotSupportedException (le azioni Range non sono supportate). Suggerire di guardare altre soluzioni come: http: //binarysculpting.com/2012/04/03/adding-many-entries-to-an-observable-collection-in-a-formformance-friendly-way/, o http: //peteohanlon.wordpress.com/2008/10/22/bulk-loading-in-observablecollection/ o http://stackoverflow.com/questions/670577/observablecollection-doesnt-support-addrange-method-so-i- get notificato-for-each. – Charlie