2012-08-14 13 views
15

Tengo una configuración en la que se agregarán potencialmente miles de elementos (supongamos 3000-5000) a un ObservableCollection que está vinculado a alguna interfaz visual. Actualmente, el proceso de agregarlos es bastante lento (aproximadamente 4 segundos/1000 elementos) y, por supuesto, la GUI no responde durante ese tiempo. ¿Cuál es un buen método para mover muchos elementos a la vez en una colección sin preocuparse por el bloqueo del sistema? Miré DispatcherTimer pero no estoy seguro si proporcionará todo lo que necesito.Cómo agregar miles de elementos a una colección encuadernada sin bloquear GUI

Otra pregunta: ¿hay algo que pueda hacer para acelerar la creación de estos objetos para que no tarden tanto en agregarlos a la colección? Actualmente los uso así: Collection.Add(new Item(<params>)) ¿Generaría los elementos de antemano, probablemente en un hilo de fondo, disminuiría el tiempo que lleva agregarlos en una cantidad notable?

Editar: La virtualización no es posible. Los requisitos especifican un vistazo WrapPanel, por lo que la pantalla es en realidad un ListBox que tiene una plantilla de ItemsPanel

Edit2: De acuerdo con el cronómetro, el cuello de botella es en realidad poniendo artículos en mi ObservableCollection. Voy a tratar de cambiar ese tipo de colección y hacer mi propia notificación para ver si eso lo acelera sustancialmente.

Edit3: Así que la respuesta está en un solo lugar - He resuelto este problema (con la ayuda de abajo) creando una clase que hereda de ObservableCollection. Esta clase hizo dos cosas: exponer un método para agregar colecciones a la vez, y agregó la posibilidad de suprimir el evento CollectionChanged. Con estos cambios, el tiempo que lleva agregar 3000 elementos es aproximadamente .4 segundos (97% de mejora). This enlace detalla la implementación de estos cambios.

+1

Hay algunas cosas que podrías hacer. Uno podría ser crear todos los objetos antes de crear su ObservableCollection, de ese modo, una vez que lo haya creado, puede inicializarlo con la colección de elementos que ya tiene. Otra opción es usar [ReactiveExtensions] (http://msdn.microsoft.com/en-us/data/gg577609.aspx) que le permitirá "observar" su fuente de datos y agregar elementos a su ObservableCollection de forma asincrónica. – Thelonias

+0

Además, tiene que ser la creación de su artículo que es el sumidero del tiempo, no el "agregar". Entonces, ¿no puedes empujar la creación a otro hilo, coleccionarlos en alguna colección intermedia y, una vez que se hayan creado, agregarlos al ObservableCollection en el hilo de la GUI? – Thelonias

+0

@Ryan Estos son algunos puntos buenos. Moveré la creación al fondo e informaré mi velocidad entonces. – steveg89

Respuesta

7

Usted ha dicho 1000, así que me quedo con que número solo por ejemplo.

IIRC, la colección observable tiene un pequeño inconveniente: si agrega los elementos uno por uno, aumenta las notificaciones una vez por cada elemento. Eso significa que tiene 1000 notificaciones para 1000 de elementos y el hilo de la interfaz de usuario se ejecutará a una velocidad mortal para mantenerse al día con el rediseño de la pantalla.

¿Necesita volver a dibujar lo antes posible? ¿Tal vez puedes combinar las adiciones? Divida los 1000 elementos en unos pocos paquetes de 100 artículos o un poco más en paquetes de 50 o 20 artículos. Luego, en lugar de poner todos los elementos uno por uno, colóquelos en paquetes. Pero ten cuidado: tienes que usar algunos métodos como AddRange implementado por la colección en sí mismo, no por LINQ, o de lo contrario volverás a tener una inserción uno a uno.Si encuentra dicho método, debería reducir el número de eventos de manera significativa, ya que la recopilación solo debería generar el evento Modificado una vez por cada llamada AddRange.

Si la colección observable no tiene AddRange, use una colección diferente o escriba la suya, bastará con un contenedor. El objetivo es NO aumentar el evento modificado en cada uno de los Add(), pero después de un conteo razonable de ellos, o - tal vez solo omitir la elevación. ¿Se modificó cuando se agregaron elementos y se cambiaron en algunos intervalos de tiempo regulares? Esto sería beneficioso especialmente si sus datos "fluyen" indefinidamente a una velocidad constante.

Por supuesto, en la cantidad de elementos que aparecen en la pantalla, es posible que también sea retenido en el propio renderizado. Si sus Plantillas de elementos son complicadas, un 1000 de objetos multiplicado por 1000 instancias de capas/propiedades visuales puede simplemente matar la experiencia del usuario. ¿Ha simplificado las plantillas de elementos al mínimo?

Última cosa: considere el uso de la virtualización de StackPanels como los paneles de elementos en sus ItemsControl/ListBoxes. Puede reducir en gran medida la huella de memoria y la cantidad de elementos dibujados en un único punto de tiempo. Esto no necesariamente ayudará en el número o eventos planteados, ¡pero puede ser de gran ayuda cuando tiene plantillas de elementos complejas!

Edición: se está utilizando ObservableCollection, por lo que he asumido WPF/Silverlight .. actualizar la pregunta de si esto no es correcto

+0

Sí, OP debería crear su propia colección ya que ObservableCollection no implementa AddRange. Es posible utilizar un CollectionView, IIRC, que le daría al usuario el control sobre cuándo "Actualizar" la colección a la vista. – Thelonias

+0

Me veo obligado a usar un WrapPanel, por lo que la virtualización no es posible a menos que use algunos intentos de proyectos de código en VirtualizedWrapPanel (que en realidad está en mi lista para probar), así que por ahora estoy atascado con elementos no virtualizados. Debido a esto, mi plantilla es tan simple como podría hacerlo para estos artículos. – steveg89

8

WPF Binding admite concurrencia por este motivo. Intente configurar Binding.IsAsync en verdadero. En adición.

  • No utilice ObservableCollection<T>, es lento para esto porque cada vez que se agrega un elemento que provoca eventos. Use algo más rápido como List<T> y levante su notificación de cambio de propiedad después de todos sus artículos son agregados.
  • Precrear sus elementos en una cadena de fondo y luego insertarlos en su colección.
  • Compruebe otras partes del código involucrado para ver si hay hinchazón y recorte.
+0

Incluso con ese conjunto de banderas continúo recibiendo aproximadamente la misma cantidad de retraso. ~ 12 segundos para mientras se agregan los elementos a la colección – steveg89

+0

Actualicé mi respuesta. –

+0

Buenos puntos aquí también. Tengo varias cosas que probar ahora. Gracias. – steveg89

0

para la segunda pregunta si en su interfaz gráfica de usuario que está utilizando Technologie WPF, puede aumentar el rendimiento mediante VirualizingStackPanel que le permite crear sólo los elementos visibles

+0

Editaré mi publicación original para explicar por qué no es posible. – steveg89

4

Otra cosa que puede probar: ObservableCollection subclase y hacer que soporta la carga a granel (AddRange) . Aquí hay un artículo: AddRange and ObservableCollection

+0

Esto puede terminar siendo mi ruta. Ya he subclasificado 'ObservableCollection', pero el agregado base (que se llama 1000 veces) se está comiendo la mayor parte del tiempo que se pasa en el ciclo de agregar. – steveg89

+0

También puede intentar llamar a AddRange desde una cadena de fondo y hacer que su implementación de envío de ObservableColeccione la llamada OnCollectionChanged al subproceso de interfaz de usuario. (El evento AFAIR CollectionChanged NO se distribuye automáticamente en el hilo de la interfaz de usuario mediante el enlace de WPF) – Volma

+0

Su comentario me indicó la dirección correcta y me permitió encontrar una solución que se implementó por completo. Ver mi edición para más detalles. Todavía tengo que dar crédito a la persona que mencionó la idea primero. – steveg89

4

Por solicitud, así es como he resuelto este problema. Empecé creando una clase que hereda de ObservableCollection. Esta clase hizo dos cosas: exponer un método para agregar colecciones completas a la vez, y agregó la capacidad de suprimir el evento CollectionChanged. Con estos cambios, el tiempo que lleva agregar 3000 elementos es aproximadamente .4 segundos (97% de mejora). This enlace detalla la implementación de estos cambios.

Cuestiones relacionadas