ListView delayed, controlando la sobrecarga de eventos en un VistView

Tenemos el siguiente escenario: Una aplicación con dos ListViews, al dispararse el evento OnSelectedIndexChanged de uno de ellos, el otro se actualiza. Esto provoca que cuando el usuario recorre las distintas entradas, hace una multiselección o en general interactúa con la aplicación, los ListView implicados, van a parpadear de manera bastante molesta, e indicando una sobrecarga de trabajo producida por nuestra aplicación, que, en el peor de los casos, podría llegar a bloquearla, dependiendo de que tipo de actualización se lleve a cabo en el ListView que está enlazado. Si este realizara un acceso a base de datos, podríamos llegar a bloquear al resto de usuarios que estén ejecutando esta aplicación en otras máquinas. Esto se puede evitar de múltiples maneras, y hoy voy a describir una bastante sencilla que he desarrollado.

Show me the code

Para evitar este problema, la idea es interceptar todos los eventos que nos lleguen y quedarnos solamente con el último. Por otro también nos hace falta tener un hilo que lance el último evento guardado al darse ciertas condiciones, de manera que podamos controlar perfectamente cuando se van a lanzar estos eventos. Para ello vamos a crear un UserControl, que en mi caso he llamado ListViewADV que herede de ListView
 
public partial class ListViewAdv : ListView
{
   private EventArgs delayedEvent;
   private int counter = 0;
}
Tenemos dos variables: delayedEvent que será la que almacene el último evento que me llegue, y counter, que como su nombre indica es un contador. Para poder interceptar todos los eventos, tendremos que hacer un override del método OnSelectIndexChange de la siguiente manera
 
protected override void OnSelectedIndexChanged(EventArgs e)
{
  delayedEvent = e;
  counter = 0;
}
Es importante remarcar que cada vez que me llega un nuevo evento, el contador se vuelve a poner a cero. Ya sólo nos falta el proceso que va a disparar el último evento cuando nos interese
 
private void ProcessselectedIndexChangedEventsStack()
{
 while (!IsDisposed)
 {
   counter++;

   if (!IsDisposed && delayedEvent != null && counter == 4)
   {
     SendOnSelectedIndexChanged();
     counter = 0;
   }

   Thread.Sleep(100);
 }
}
Veamos que se hace en esta función. Primeramente comprobamos que el ListView no esté disposed, ya que en ese caso su uso habrá finalizado, seguidamente incrementamos el contador y llegamos a la condición de disparo del evento; que el contador haya llegado a 4, que tengamos un evento y que el control siga en activo . Si se dan todas esas condiciones, llamaremos a la función que dispara el evento.
 
internal void SendOnSelectedIndexChanged()
 {
   if (this.Parent != null && this.Parent.InvokeRequired)
   {
     this.Invoke((MethodInvoker)delegate
     {
       SendOnSelectedIndexChanged();
     });
     return;
   }
   base.OnSelectedIndexChanged(delayedEvent);
   delayedEvent = null;
 }

Para evitar problemas tenemos en cuenta si se requiere utilizar Invoke, una vez hecho esto lanzamos el evento. El código completo de la clase quedaría de la siguiente manera.
 
public partial class ListViewAdv : ListView
 {
  private EventArgs delayedEvent;
  private int counter = 0;

  public ListViewAdv()
  : base()
  {
   new Thread(ProcessselectedIndexChangedEventsStack).Start();

   InitializeComponent();
  }

private void ProcessselectedIndexChangedEventsStack()
{
  while (!IsDisposed)
  {
   counter++;

   if (!IsDisposed && delayedEvent != null && counter == 4)
   {
     SendOnSelectedIndexChanged();
     counter = 0;
   }

   Thread.Sleep(100);
 }
protected override void OnSelectedIndexChanged(EventArgs e)
{
  delayedEvent = e;
  counter = 0;
}
internal void SendOnSelectedIndexChanged()
{
  if (this.Parent != null && this.Parent.InvokeRequired)
  {
    this.Invoke((MethodInvoker)delegate
    {
      SendOnSelectedIndexChanged();
    });
    return;
   }
  base.OnSelectedIndexChanged(delayedEvent);
  delayedEvent = null;
  }
}

Espero que sea de utilidad.

No hay comentarios:

Publicar un comentario