viernes, 19 de septiembre de 2014

El control BackgroudWorker de Visual Studio


El control BackgroundWorker

Una de las dificultades en la programación en capas es la de mostrar elementos en la interfaz gráfica de la aplicación mientras se realiza un proceso que lleva mucho tiempo en la capa de datos. Por ejemplo la clásica aplicación que muestra una barra de progreso de la tarea que se está ejecutando dentro de una clase que obtiene una gran información de una base de datos o que procesa una gran cantidad de datos. Cuando colocaba toda la funcionalidad dentro del formulario no era problema, pero ¿Cómo hacerlo ahora que mi clase está separada del formulario?. Primero lo resolví con un evento dentro de la clase que procesa la gran cantidad de información. Este evento se dispararía cada vez que quisiera que la barra de progreso se actualizara, pero me quedaba el problema de poder cancelar el proceso antes de que terminara. Entonces pensé en hilos (threads). Es complicado trabajar con hilos de ejecución, pero afortunadamente encontré un control que cubre esta funcionalidad en visual studio y se llama BackgroundWorker.

El control BackgroudWorker permite ejecutar una operación en un subproceso (hilo) dedicado e independiente. La figura 1 muestra el ícono del control.

Figura 1 Icono del backgroundWorker

El control BackgroundWorker no es visible al usuario por lo que al agregarse se coloca en la barra debajo del formulario, donde se colocan otros controles como el timer por ejemplo.

Los eventos que maneja el control BackGroundWorker son DoWork donde se coloca el código del proceso que se va a ejecutar, el ProgressChanged que sirve para actualizar los controles de avance, por ejemplo un progressbar y el RunWorkCompleted que es el evento que indica el final del proceso.

Para ejecutar el subproceso se utiliza el método RunWorkerAsync del control, que puede recibir cualquier objeto como argumento. Es importante definir este objeto con todos los parámetros necesarios para el método que va a realizar el proceso dentro de DoWork.

El método CancelAsync sirve para cancelar el proceso, siempre y cuando la propiedad WorkerSupportCancellation del control BackgroundWorker este en true. El objeto que realiza el proceso debe revisar la propiedad CancellationPending del objeto BackgroundWorker y poner a true la propiedad Cancel del objeto de argumentos del evento para que se realice la cancelación.

El siguiente código muestra un ejemplo de como usar el control BackgroundWorker:

Primero defino la clase clsParamsProceso que me servirá para pasar todos los parámetros que requiero para realizar el proceso. A continuación escribo el código que llevará cada manejador de eventos del BackgroudWorker.

public class clsParamsProceso
    {
        private string _NombreArchivo;

        public string NombreArchivo
        {
            get { return _NombreArchivo; }
            set { _NombreArchivo = value; }
        }

        private int _Nivel1;

        public int Nivel1
        {
            get { return _Nivel1; }
            set { _Nivel1 = value; }
        }

        private int _Nivel2;

        public int Nivel2
        {
            get { return _Nivel2; }
            set { _Nivel2 = value; }
        }

        private int _Nivel3;

        public int Nivel3
        {
            get { return _Nivel3; }
            set { _Nivel3 = value; }
        }

        private int _Nivel4;

        public int Nivel4
        {
            get { return _Nivel4; }
            set { _Nivel4 = value; }
        }

        private string _CuentaBanco;

        public string CuentaBanco
        {
            get { return _CuentaBanco; }
            set { _CuentaBanco = value; }
        }

        private int _FilaInicio;

        public int FilaInicio
        {
            get { return _FilaInicio; }
            set { _FilaInicio = value; }
        }

        private string _ArchivoRangos;

        public string ArchivoRangos
        {
            get { return _ArchivoRangos; }
            set { _ArchivoRangos = value; }
        }
    }

        private void btnProcCatalog_Click(object sender, EventArgs e)
        {
            clsParamsProceso parametros = new clsParamsProceso();
                if (txtNomArchivoCat.Text != "")
                {
                    if (!bgwProgreso.IsBusy)
                    {
                        tssEstado.Text = "Convirtiendo Catálogo...";
                        parametros.NombreArchivo = opdlgPricnipal.FileName;
                        parametros.Nivel1 = Convert.ToInt32(txtLonNivel1.Text);
                        parametros.Nivel2 = Convert.ToInt32(txtLonNivel2.Text);
                        parametros.Nivel3 = Convert.ToInt32(txtLonNivel3.Text);
                        parametros.Nivel4 = Convert.ToInt32(txtLonNivel4.Text);
                        parametros.CuentaBanco = txtCuentaBanco.Text;
                        parametros.FilaInicio = Convert.ToInt32(txtInicio.Text);
                        bgwProgreso.RunWorkerAsync(parametros);
                        HabilitarControles(false);
                    }
                }
                else
                    MessageBox.Show("No a seleccionado ningún archivo a convertir""Error captura de datos", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }

        private void bgwProgreso_DoWork(object sender, DoWorkEventArgs e)
        {
            ctr.GenerarCatalogo(bgwProgreso, e);
            }

        private void bgwProgreso_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            prbPrincipal.Value = e.ProgressPercentage;
        }

        private void bgwProgreso_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            prbPrincipal.Value = 0;
            HabilitarControles(true);
            if (e.Error != null)
            {
                tssEstado.Text = "Error: " + e.Error.Message;
            }
            else if (e.Cancelled)
            {
               tssEstado.Text = "Conversión de catálogo Cancelada";
            }
            else
            {
                        tssEstado.Text = "Conversión de catálogo finalizada exitósamente";
                }
            }

Finalmente, dentro de mi clase control, el metodo GenerarCatalogo quedaría de la siguiente forma:
public int GenerarCatalogo(BackgroundWorker trabajador,
                            DoWorkEventArgs e)
{
    int Avance = 0;
    for (int i = 0; i < 10000; i++)
    {
        //Lineas de codigo del proceso....
        Avance = (int)(i * 100.0 / 10000);
        trabajador.ReportProgress(Avance);
        if (trabajador.CancellationPending)
        {
            e.Cancel = true;
            break;
        }
    }
}

Cada ciclo calculo el avance y se lo envío al método ReportProgress del objeto BackgroundWorker

Espero que este control les sea de utilidad y comiencen a experimentar con hilos de procesos, un tema bastante interesante, sobre todo en la ejecución en paralelo.

viernes, 6 de junio de 2014

Respuesta al Reto 5 Clases Abstractas


Las clases abstractas son clases que sirven para definir las propiedades y el comportamiento de sus clases derivadas. Establecen el límite conceptual del objeto identificado en el diseño de la solución al problema a resolver. Es conocida como la super clase y no se puede instanciar un objeto de ella. La idea es heredar clases instanciables a partir de esta clase. Nos puede servir para especificar un comportamiento mínimo de una clase, para que en un grupo de programadores los que requieran este tipo deriven sus clases a partir de ella. Las clases abstractas existen en los lenguajes mas populares como C#, Java y PHP.

Es complicado diseñar los elementos que van a componer la solución al problema que se pretende resolver con un programa si no se tiene un amplio conocimiento del dominio de ese problema. Asi que proponer en base a solo al nombre de una clase, no es posible. La idea de este reto es motivar a los que deseen resolverlo a imaginarse y a pensar que es lo que representa el concepto de un producto y de un cliente en un sistema de punto de venta.


En el caso del producto podemos pensar en una lata de frijoles en una tienda de abarrotes, un par de zapatos en una zapatería o un juego de herramientas en una ferretería. Aqui lo importante es determinar por medio de una clase los límites de esa abstracción. En lo que a mi experiencia se refiere, pensar en un artículo o un producto, me hace pensar en todas las posibilidades que cada uno de mis clientes me ha planteado: que tenga descripción, que tenga una clave fácil de recordar por los empleados, que tenga un código de barras, que se sepa a cuanto se compro, es decir su costo, a cuánto se va a vender, su porcentaje de iva. Características asociadas con el inventario como la cantidad que se tiene en la tienda (existencias), el límite de esas existencias para pedir mas (cantidad en stock o punto de reorden), que tipo de costeo se va a manejar (costo promedio, primeras entradas, primeras salidas, identificado), cuál va a ser su ubicación en la tienda, su unidad de medida. Otras quizás mas específicas como a que categoría pertenece, si el producto se vende suelto o por paquete o si tiene una gama de colores. En realidad las posibilidades son enormes.

Lo que yo propongo es agrupar los atributos más básicos en la clase abstracta y después en las clases concretas ir generando clases mas complejas, que vayan incluyendo las características adicionales mas específicas.

Mi propuesta se muestra en la siguiente figura:



Observen el esterotipo de la clase encerrada entre <<>>. Solo se tiene una clase abstracta y las demás son clases concretas (clases que si se pueden instanciar).

A ver que les parece, espero sus opiniones y críticas. La idea de poner las clases concretas es para mostrarles donde pongo las demás propiedades que no están definidas en la clase abstracta. En mi diseño intenté poner los atributos generales mínimos que definen a la clase productos y posteriormente fuí agregando características mas complejas, como el caso de la unidad de medida, que yo lo planteo como una clase y la categoría a la que pertenece el producto.

Con respecto a la clase abstracta de clientes, en un sistema de punto de venta, el cliente se define como la persona que compra en la tienda. Esta persona puede solicitar una factura, por lo que es importante tener sus datos fiscales, asi como los datos de su dirección. Ademas, un cliente puede ser una empresa, por lo que la persona que llega a comprar puede que sea la persona que hace el contacto.

Es por ello que mi propuesta de clase abstracta y sus clases concretas, es la siguiente:



Nuevamente espero sus comentarios.



Conclusión


En el diseño de clases es necesario contar con una vista jerárquica de interacción de clases que permita ir definiendo objetos de lo general a lo particular. Esto facilita la creación de nuevas clases y el reuso de las existentes en una forma ordenada. Anteriormente creaba clases con todos los atributos y derivar de ellas era un problema, pues existían atributos que no utilizaba. Las clases abstractas definen el inicio de la jerarquía de un grupo de objetos, que puede irse incrementando, dependiendo de las necesidades del sistema. Es pensar en una superclase que sirva como base para que los programadores de la misma empresa deriven sus clases de ella y no tengan que conocer el dominio completo del problema, pues en ella esta contenida la funcionalidad básica.

viernes, 18 de abril de 2014

Configuración de Ambiente de Desarrollo en Android

A pesar de que existe mucha documentación para desarrollo de aplicaciones para android, nada como la experiencia que te da hacerlo tu mismo. Te topas con varios detalles que se supone no te debes encontrar. Primero instalo el SDK de android. Yo esperaba que dentro de las herramientas estuviera un IDE, pero no es asi. La direccion URL de donde lo descargo es: http://developer.android.com/sdk/index.html Una vez que instale el SDK bajo la version mas reciente de Eclipse.
 Lo descargue de la ruta: http://www.eclipse.org/downloads/download.php?file=/eclipse/downloads/drops/R-3.7-201106131736/eclipse-SDK-3.7-win32-x86_64.zip pues mi computadora tiene Windows 7 de 64 bits.
Para Windows de 32 bits esta: http://www.eclipse.org/downloads/download.php?file=/eclipse/downloads/drops/R-3.7-201106131736/eclipse-SDK-3.7-win32.zip
 Finalmente descargo el Plugin ADT como me indica la pagina del sdk de android:
 1. inicio eclipse y entonces selecciono Help > Install New Software....
 2. doy clic en Add, en la esquina superior derecha.
3. En el Add Repository dialog que aparece pongo "ADT Plugin" en Name y la siguiente URL en Location: https://dl-ssl.google.com/android/eclipse/
 4. doy clic OK
 5. En el dialogo the Available Software, seleccione el checkbox Developer Tools y doy clic en Next.
 6. doy clic Next.
 7. acepto las licencia, entonces doy clic en Finish.
 8. Reinicio eclipse. En el SDK Manager tengo que descargar una plataforma, pues no se instala ninguna. Descargo la de la version 2.2 que es la que tiene mi celular.
 NOTA: El SDK Manager debe cargarse como administrador, pues como la carpeta temp esta dentro de la carpeta de instalacion del SDK y este esta en archivos de programa, marca error si no se abrio como administrador. Para abrir el SDK manager como administrador, se ubica en el icono de la aplicacion, dan clic derecho y seleccionan abrir como administrador. es importante bajar los ejemplos de la plataforma, para aprender de ellos. Finalmente en el AVD Manager hay que crear un nuevo device, para que se cargue el emulador al ejecutar un ejemplo. Se le da new, se agregan los parametros, como de que plataforma, el tamaño de la SD Card y el tamaño de la pantalla. Les recomiendo una SD Card de 1 a 2 GB, pues mas grande se tarda en crearlo.
 Para probar tu configuracion crea un nuevo proyecto del tipo android sample project y selecciona api demos, para que veas una serie de ejemplos que te pueden servir como ejemplo. No quiero terminar sin comentarles que deben tener conocimientos basicos de java para crear una aplicacion para android. Ademas saber manejar objetos y si tienen un poco de experiencia en applets o aplicaciones de windows, les puede ayudar a comprender el esquema.