Clase con progress_Info
[Informar del progreso en una clase con C#]

Fecha: 18 de noviembre de 2004
Autor: Antonio Cuesta Garc�a [email protected]

 

.

Introducci�n:

Bueno la idea de este articulo es debida a que por regla general todos los programas, utilidades, etc. realiza ciertos procesos de larga duraci�n, y aunque nosotros pensemos (en mi caso particular), que el usuario no necesita una informaci�n visual (que pueda ir en contra de mas rapidez en dicho proceso), cuando somos nosotros quienes usamos un programa si que nos gusta saber si lo que esta haciendo tardara mas o menos, y en que punto se encuentra, cosa que queda muy bien reflejada con una barra de progreso.

No es la intenci�n de este articulo mostrar el uso del control ProgressBar, sino la de implementar en nuestros procesos (pesados) un mecanismo para que puedan transmitir una informaci�n de su progreso de forma �til y sencilla tanto si es para nuestro uso como si es para el uso y disfrute de terceros.

Evoluci�n de la idea inicial.

El resultado de las pruebas fueron tres posibles soluciones, ya que gracias a ellas se puede ver claramente una evoluci�n en sus resultados, para recoger las tres en este art�culo se crea un programa para Windows, compuesto por un solo formulario con tres botones para llamar a cada una de las soluciones propuestas y un ProgressBar.

Primera opci�n (primer bot�n).

Esta es la opci�n m�s sencilla, y realmente solo es una muestra del proceso elegido para demostrar la idea.

El proceso utilizado es muy sencillo, (ya que despu�s cada desarrollador utilizar el suyo, por ejemplo: copiar archivos, actualizar bases de datos con nuevos registros, etc.), lo �nico que si es importante, es que antes de comenzar el proceso tengamos informaci�n sobre el numero total de archivos, registros, etc. que tenemos que procesar, ya que sin esta informaci�n el conocimiento del punto actual de ejecuci�n de nuestro proceso no nos resultar�a �til.

A continuaci�n se muestra el proceso:

private void ProcesoTardon()
{
    int inicio=0;         // Valor de inicio
    int fin=200000;       // Valor de finalizaci�n

    progressBar1.Minimun=inicio;
    progressBar1.Maximun=fin;

    // El proceso en si mismo.

    for (int i=inicio;i<=fin;i++)
       this.progressBar1.Value=i;
}

Cono se puede ver es simplemente un bucle for, sin mayor importancia.

Ventajas de utilizar este m�todo:
* El c�digo a escribir es muy peque�o, buena idea para una soluci�n r�pida.

Desventajas de utilizar este m�todo:
* La principal, es que no se puede reutilizar, es decir, si queremos que este proceso se ejecute desde otro formulario, tendr�amos que rescribir todo el c�digo, ya que depende del control ProgressBar progressBar1, que esta en dicho formulario; de hecho por ese motivo se ha creado dentro de la clase del formulario principal y se le declara como private.

Segunda opci�n (segundo bot�n).

Bueno, pues esta opci�n, tambi�n es muy simple, lo que se pretende es reutilizar nuestro c�digo tard�n, ya que como se indica en las desventajas anteriores si queremos ejecutar dicho proceso desde dos formularios distintos, tendr�amos que copiar el c�digo con los problemas que puede traer esto.

Para solucionarlo, se crea una clase que yo llamo ClaseTardona (una ocurrencia r�pida), y que si nuestro proyecto es grande y lo desarrollamos en tres capas podr�a ir incluido en la Capa de Negocios, donde quedar�a muy bien.

La clase resultante seria la siguiente:

public class ClaseTardona
{
    private int _inicio;         // Valor de inicio
    private int _final;          // Valor de finalizaci�n

    public ClaseTardona()
    {
        _inicio=0;
        _final=200000;
    }

    public void Procesar(ProgressBar PBar)
    {
        PBar.Minimun=_inicio;
        PBar.Maximun=_final;

        // El proceso en si mismo.

        for (int i=_inicio;i<=_final;i++)
            PBar.Value=i;
    }

    public int Inicio
    {
        get { return _inicio; }
    }

    public int Final
    {
        get { return _final; }
    }
}

Y la llamada desde nuestro button2:

private void button2_Click(object sender, System.EventArgs e)
{
    ClaseTardona c1=new ClaseTardona();
    c1.Procesar(this.progressBar1);
}

Como ya indicaba es una clase muy sencilla, y solo aporta dos propiedades de solo lectura, Inicio y Final, que realmente no se utilizan en el ejemplo, pero dan un car�cter informativo a la clase.

En el constructor realizar�amos la asignaci�n de los valores tanto inicial como final, que en este caso simplemente es una asignaci�n num�rica fija (para el ejemplo), pero si nuestra clase fuera la encargada de facturar albaranes, en el constructor obtendr�amos el numero del albaranes que est�n pendientes de facturar, etc.

El m�todo Procesar, (que es el �nico de la clase), es el que se encarga de realizar el trabajo, y adem�s de informarnos del punto de avance donde se encuentra nuestro proceso, toma como argumento una instancia de un ProgressBar que utiliza para actualizar su valor, y que en nuestro caso es el progressBar1 de nuestro formulario, esto para mi personalmente es una chapuza, ya que en este punto tenemos algo que funciona y que es reutilizable, pero si en nuestro nuevo formulario no utilizamos un ProgressBar, no podremos facturar nuestros albaranes, (ni contar hasta 200000), etc. Esto lo podr�amos solucionar simplemente sobrecargando el m�todo Procesar sin argumentos, pero en este caso no tendr�amos la informaci�n que ha propiciado nuestro estudio, adem�s puede darse el caso de querer utilizar otra forma diferente para indicar al usuario el tiempo que queda de proceso y el punto donde se encuentra, por ejemplo: una barra de progreso creada por nosotros, o un simple control Label, etc., lo que limitara nuestra bonita clase.

Tercera opci�n (tercer bot�n).

Despu�s de estas dos opciones que simplemente nos dan una idea del problema y de como solucionarlo de forma r�pida (sobre todo la con la primera), comentare la idea inicial que yo tenia, y que no es otra que realizar una clase que realice el trabajo y que sea lo mas independiente posible, para que se pueda utilizar en diferentes partes de una soluci�n, reutilizarla en otro proyecto, e incluso dejarla lista para que pueda ser utilizada correctamente por otros programadores, sin que imponga a los mismos limitaciones a la hora de crear la interfaz de usuario, pudiendo mostrar o no la informaci�n del progreso, y si se quiere mostrar que se pueda hacer como quiera cada uno y no por imposici�n con un control ProgressBar.

�Como realizamos esto?, de forma muy sencilla, a�adiendo a nuestra clase un evento que notificara los cambios de posici�n en el progreso de nuestro proceso, así el usuario de dicha clase podrá optar por controlar dicho evento como el quiera, o no controlarlo.

Para esto, primero creo una clase que hereda de System.EventArgs, que yo llamo PosicEventArgs, que tendrá una propiedad publica Posic, donde se guardara el valor correspondiente a la posici�n actual del progreso, y un constructor con este valor como argumento, quedando como sigue:

public class PosicEventArgs:EventArgs
{
    public int Posic;         // Valor de la posici�n inicial

    public PosicEventArgs(int Pos):base()
    {
        this.Posic=Pos;
    }
}

Despu�s se declara el delegado publico CambioPosHandler, quedando de la siguiente manera:

public delegate void CambioPosHandler(objeto o,PosicEventArgs ev);

Y despu�s de todo esto nuestra nueva clase que en este caso (para poder convivir con la anterior ClaseTardona), he llamado ClaseTardona2:

public class ClaseTardona2
{
    private int _inicio;         // Valor de inicio
    private int _final;          // Valor de finalizaci�n

    public event CambioPosHandler CambioPosic;

    public ClaseTardona2()
    {
        _inicio=0;
        _final=200000;
    }

    public void Procesar()
    {
        // El proceso en si mismo.

        for (int i=_inicio;i<=_final;i++)
            OnCambioPosic(new PosicEventArgs(i));
    }

    protected virtual void OnCambioPosic(PosicEventArgs e)
    {
        if (CambioPosic!=null)
            CambioPosic(this,e);
    }

    public int Inicio
    {
        get { return _inicio; }
    }

    public int Final
    {
        get { return _final; }
    }
}

El desarrollo de la clase es tan simple como la anterior, lo �nico que tenemos un m�todo protegido OnCambioPosic, encargado de lanzar el evento CambioPosic, si es que esta instanciado, y este m�todo es llamado desde el m�todo Procesar cada vez que se modifica el estado del proceso.

Como remate una muestra del uso de nuestra clase en el formulario de prueba.

private void button_Click(object sender, System.EventArgs e)
{
    ClaseTardona c1=new ClaseTardona2();         // Creamos una instancia de ClaseTardona2

    // Actualizamos los valores de progressBar1
    this.progressBar1.Minimun = c1.Inicio;
    this.progressBar1.Maximun = c1.Final;

    // A�adimos un delegado al evento
    c1.CambioPosic+=new CambioPosHandler(c1_CambioPosic);
    c1.Procesar();                              // Ejecutamos el proceso
}
private void c1_CambioPosic(object o, PosicEventArgs ev)
{
    this.progressBar1.Value = ev.Posic;    // Refrescamos la informaci�n de progressBar1
}

Bueno con este articulo se ha intentado mostrar diferentes manera de realizar algo, se puede ver que con un poco de c�digo a�adido, y aprovechando t�cnicas como son los delegados y los eventos podemos crear nuestras clases dot�ndolas de una gran funcionalidad.

Me despido con un saludo a todos y esperando que este articulo sea �til a alguien.



ir al ndice

Fichero con el c�digo de ejemplo: ACUESTA_AppConProgreso.zip - Tama�o 9,13 KB