2009-09-09 14 views
7

En la actualidad tengo dos cuadros de lista WPF imitando la siguiente funcionalidadAumento del rendimiento ObservableCollection WPF

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

estoy usando 2 ObservableCollections para permitir a los usuarios seleccionar cualquier elemento que requieren (flexibilidad es la clave aquí). El principal problema es que tengo miles de artículos que se agrupan en los dos cuadros de lista. En general, el diseño funciona muy bien (con algunas docenas de elementos), pero mi obstáculo es cuando un usuario copia todos los elementos disponibles de izquierda a derecha a medida que la pantalla se congela (¿hora de ejecutar en un hilo diferente?).

En cuanto a ObservableCollection carece de un método AddRange y hay varias implementaciones disponibles en Internet. También sé que el evento CollectionChanged se dispara innecesariamente ya que cada elemento se copia horriblemente sobre el rendimiento de drenaje.

Es posible que tenga que permitir a los usuarios elegir entre grupos de más de 10 000 elementos en el futuro, lo cual suena como una mala idea, pero no es negociable ya que la agrupación en el listbox (CollectionViewSource) funciona realmente bien, pero tiene el efecto secundario de conmutación fuera la virtualización tanto de los cuadros de lista

¿Qué puedo hacer para mejorar el rendimiento cuando se carga un cuadro de lista con miles de artículos cuando enlace de datos a un ObservableCollection? ¿Hay implementaciones de tipo AddRange que recomendarías? ¿Es la única opción que tengo aquí para ejecutar esto en un hilo de fondo que parece caro porque no estoy cargando datos de una base de datos?

+0

Ver este http://stackoverflow.com/questions/1007691/observablecollection-databinding-performance – Sauron

Respuesta

2

He eliminado el CollectionViewSource y la agrupación y los elementos se copian en 1/2 segundo, pero con la agrupación puede tardar hasta un minuto porque la virtualización no funciona con la agrupación.

que tendrá que decidir si desea utilizar la CollectionViewSource

+0

El collectionviews funciona de manera eficiente cuando tiene lugar la vinculación inicial, pero es muy ineficiente en tiempo de ejecución. Ahora estoy haciendo la clasificación/filtrado en el código detrás de usar LINQ – Vault

+0

¿cómo es esta una respuesta? –

1

Probablemente se podría heredar de ObservableCollection<T> (o implementa directamente INotifyCollectionChanged) para añadir BeginUpdate y EndUpdate métodos. Los cambios realizados entre llamadas al BeginUpdate y EndUpdate se pondrían en cola, luego se combinarían en uno (o varios si hay rangos separados) NotifyCollectionChangedEventArgs objeto que se pasaría a los controladores del evento CollectionChanged cuando se llama al EndUpdate.

+1

Por lo que yo sé, los controles WPF no admiten actualizaciones de rango para la recopilación y lanzan una excepción cuando reciben más de 1 elemento en un evento CollectionChanged. –

+1

WTF?! ¿Por qué proporcionar la capacidad de especificar múltiples elementos en el evento args si no lo admiten? Implementé la colección descrita en mi respuesta, pero no tuve tiempo para probarla realmente ... Simplemente lo hice, y parece que tienes razón :(. Así que mi colección no se puede usar para escenarios vinculantes ... –

+0

No funciona en WPF 4.0 :( –

1

Puede encontrar una colección observable Thread safe here. Haga seguro su hilo de colección Observable y agréguelo al listbox.

+0

Ese es el enfoque que estoy usando y funciona bastante bien Puede utilizar un BackgroundWorker para llenar su ObservableCollection y ver que su ListBox se rellene sobre la marcha – japf

+0

Este enfoque sigue siendo arrojando un número excesivamente grande de eventos en el hilo de la interfaz de usuario porque cada elemento agregado arrojará su propio evento de colección cambiada. Esto no solucionará el problema. –

+0

ese enlace está muerto – JobaDiniz

2

no pude resistir responder a esta. No creo que ya no necesites esta respuesta, pero tal vez alguien más pueda usarla.

No lo piense demasiado (no se acerque a este multiproceso (esto hará que las cosas sean propensas a errores e innecesariamente complicadas. Solo utilice el subprocesamiento para cálculos difíciles/IO), todos esos diferentes tipos de acción dificultarán el almacenamiento. La parte más molesta es que si elimina o agrega 10000 elementos su aplicación (listboxes) estará muy ocupada manejando los eventos generados por ObservableCollection. El evento ya admite múltiples elementos. Así que .....

Usted podría almacenar en búfer los elementos hasta que cambie la acción. Por lo tanto, las acciones Agregar se almacenarán en búfer y se generarán como un lote si el "usuario" cambia la acción o la vacía. No han probarlo, pero se puede hacer algo como esto:

// 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; 
} 

Diviértete !, J3R03N

+0

Esto realmente no funciona. Cuando intente aumentar el evento de recolección de elementos múltiples, obtendrá una excepción NotSupportedException (las acciones de rango no son compatibles). Sugerir otras soluciones como: http: //binarysculpting.com/2012/04/03/adding-many-entries-to-an-observable-collection-in-a-performance-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-notify-for-each. – Charlie

Cuestiones relacionadas