Optimizar busqueda en un datagridview

Iniciado por carlos7x, 1 Septiembre 2017, 03:51 AM

0 Miembros y 2 Visitantes están viendo este tema.

carlos7x

tengo un datagridview el cual se llena con un archivo de texto que contiene 14 mil registros, como puedo optimizar o hacer que la busqueda sea mas rapida y al escribir en el textbox no se alente demasiado

Serapis

Siempre hay que deshabilitar el objeto (listbox, datagridview, etc...) cuando se ingresan los datos (si son más de uno o 2). Y habilitarlo de nuevo al finalizar la introducción de datos.

La razón de que sea tan lento, es que con cada añadido, se empeña en actualizar la vista (que tarda enormemente más en eso que en añadir 10-100 elementos... entonces si vas a meter 14.000 registros, además de añadirlos tiene que actualizar la vista 14.000 veces (esto es, redibujar la vista cada vez). No es eficiente, por eso se dehabilita y deja de dibujarse y al término se habilita y se dibuja una sola y única vez.
Hay que reconocer que el sistema es un poco tonto, porque si se añade al final y el elemento no cabe en la vista, no hace falta en realidad actualizar la vista.... pero...

carlos7x

Disculpa mi ignorancia, pero como realizo tal operación que me indicas?

Eleкtro

#3
Hola.

Cita de: carlos7x en  3 Septiembre 2017, 17:19 PMcomo realizo tal operación que me indicas?

( Me tomo la libertad de responderte yo, que hace tiempo que no respondo nada en el foro, y el compañero NEBIRE parece estar un poco ausente a este thread. )

Al decir "deshabilitar un control" yo al menos me estoy refiriendo a deshabilitar las peticiones de redibujado del control. ¿Y cómo se hace esto?... pues depende del escenario...

Si es un control que realice un layout automático, por lo general un contenedor de controles como: TableLayoutPanel, FlowLayoutPanel, o Panel, podemos utilizar el método Control.SuspendLayout(), esto es útil sobre todo al añadir muchos controles al contenedor de controles, ahí queremos suprimir el layout para que el control no se redibuje hasta que hayamos terminado de insertar todos los controles. La supresión del layout afecta a propiedades tales como: Control.AutoSize, Control.Anchor y Control.Dock. Podemos ignorar llamar a SuspendLayout claro está, pero de hacerlo solo conseguiremos que el proceso de inserción /interacción con el control sea lento y feo, produciendo flickering...

Si por lo contrario hablamos de un un control de usuario entonces es mucho más facil "deshabilitar" el control ya que evidentemente tendriamos toda la capacidad de controlar el dibujado del control a nivel interno digamos. Por decir algo: podría ser tan simple como anular (override) la propiedad Control.Enabled e ignorar todo el dibujado del control mientras su valor sea False, por ejemplo.

Para un control de tipo lista, es decir: ListView, ListBox o ComboBox, lo que debemos hacer es llamar al método Control.Beginpdate. Los pasos a seguir serian los siguientes:

...Las operaciones de interacción con la colección (Control.Items) del control aquí...

Con el método Control.BeginUpdate() lo que conseguimos hacer es suprimir cualquier petición de redibujado durante las modificaciones que le hagamos a la colección del control. Cada vez que añadimos un elemento a la colección, se dispara un evento para que el control se redibuje, esto por cada 1 de los elementos que añadamos en el control, por lo tanto resulta evidente que pretender añadir varios elementos a la colección sin preocuparnos del redibujado... provocará una considerable disminuición en el rendimiento del control.

Sin embargo el control DataGridView no expone dicho miembro, pero en su lugar... (sigue leyendo aquí abajo)

Cita de: NEBIRE en  2 Septiembre 2017, 15:29 PMHay que reconocer que el sistema es un poco tonto, porque si se añade al final y el elemento no cabe en la vista, no hace falta en realidad actualizar la vista.... pero...

Así es, pero precisamente para solventar ese problema de raíz es que está la propiedad DataGridView.VirtualMode (con su posterior y tediosa implementación personal):


Ese es el modo más óptimo de administrar la respuesta o "responsividad" de un control DataGridView con exceso de elementos (como los +14.000 en este caso).




De todas formas, para mantener una velocidad de respuesta estable en un DataGridView en la carga de varios elementos, para evitar el redibujado constante y mantener un rendimiento decente, vaya, tampoco es tan dificil, por lo general no debería ser necesario recurrir a implementar el modo virtual, sino que simplemente debemos utilizar un DataSource en lugar de administrar manualmente la cantidad de columnas y filas, y la inserción o eliminación manual de elementos. Que de eso se encargue la lógica del control por si solo mediante el DataSource bindeado. Pero lo cierto es que no he trabajo en escenarios con +14.000 elementos de carga, así que no puedo hablar mucho de lo que no he experimentado...

Eso sí, si por el motivo que sea nos vemos obligados a tener que administrar de forma manual la inserción de elementos en el DataGridView (como parece ser tu caso), entonces para aumentar el rendimiento se debe utilizar el método DataGridView.Rows.AddRange en lugar de DataGridView.Rows.Add (recuerda que por cada elemento añadido el control se redibujará... y no quieres que eso ocurra, así que debes añadir todos los elementos de una sola vez, llamando a AddRange).




Para el tema de las búsquedas... una solución sería dejar un margen de tiempo de inactividad, un "timeout" por así decirlo. En lugar de buscar cada vez que presionas una tecla, lo que debes hacer es determinar (mediante un objeto Timer o StopWatch) cuanto tiempo hace que se pulsó una tecla por última vez (digamos 500 milisegundos, es un buen valor de inactividad), y entonces empezar a realizar la búsqueda en los elementos de la colección. O más sencillo: realizar la búsqueda solamente cuando se presione la tecla ENTER, o al presionar un botón que diga "Buscar".

Saludos.








Serapis

#4
mmmm... es que no suelo aparecer mucho por el foro de vb6 (tras responder, luego nadie suele volver)...

Al decir deshabilitar, pensaba más en sistemas de VB6. Pero en general si en otros controles resulta complejo, lo más sencillo es deshabilitar el propio control.
Código (vbnet) [Seleccionar]

Control.Enabled =FALSE
   Bucle de inserción de datos
       '....
   Fin bucle
Control.Enabled = TRUE


Un control deshabilitado simplemente deja de redibujarse y con ello (y básicamente), impedir al usuario su uso a través d ela interfaz grádica del mismo.

En definitiva deja de pintarse. Cuando se habilita de nuevo, el redibujado es siempre forzado.

En los controles de interfaz, si no están correctamente programados, suele pasar eso, sería acorde que poseyeran un método de entrada múltiple (en los controles que lo admiten) o bien disponer del par de métodos para que el programador desactive y active (a voluntad) la actualización de la interfaz gráfica del control.
Al menos si que todos los controles se comportan correctamente cuando se deshabilitan y habilitan. Primeor dejando de dibujarse si están deshabilitados y después redibujarse al cambiar su estado de Enabled desde FALSE a TRUE.

No obstante, deshabilitar y habilitar no es evidente... yo soy más partidario de estas opciones.
- Propiedad: FreezeRedraw o incluso de un
- Método: InsertMultiple(datos, UpdateOnlyToFinish:=TRUE), y cuando el parámetro fuera false, actualizaría con cada inserción.

Claramente la 2ª opción es válida solo para controles contenedores de datos que alteran la vista.
Incluso la 1ª opción nunca sobra en el resto de controles, si no se necesita no se invoca pero cuando se necesite, sin duda su utilidad queda manifiesta.

------
p.d.: El tema de actualización no indiqué nada, porque no me quedaba claro, si se refería a búsquedas de los datos, a la eficiencia dle método a usar, o si se refería lo que dices tú Elektro, a realizar la búsqueda al instante de que el usuario haga un cambio en la entrada de datos (caso típico de actualizar una vista de un listbox, haciento un 'TopItem' coincidente con el primer ítem que parece encajar con el ´termino de búsqueda 'actual' y que suele ser parcial mientras se va escribiendo.

Esperaba que tras una seguna entrada sobre lo cmentado, pidiera soluciones dando explicaciones más en detalle del caso... en fin según el interés del usuario, me extiendo más o menos en explicaciones...