Formulario Splash
[Una agenda realizada en C#]

Fecha: 30 de junio de 2004
Autor: Antonio Cuesta Garcia ACUESTA@terra.es

 

Nota 07/Jul/04:
También está la versión para VB .NET


Introducción:
Comencé este articulo con la intención de explicar la realización de un Formulario Splash, es decir un formulario inicial como el que aparece en la mayoría de las aplicaciones comerciales con la información referente a la empresa, etc. y además añadir a este un hilo (Thread) donde se ejecuten funciones de inicialización, sin que afecten a la visualización de dicho formulario.

Pero esta idea inicial en si era muy sosa, así que le añadí un poco de funcionalidad al proyecto para que el resultado fuera mas interesante a todos aquellos que quieran leer este articulo. Creando al final una pequeña agenda, que a pesar de no parecer muy vistosa en ejecución, si se puede observar en el código la aplicación de técnicas muy variadas como son la programación en tres capas, programación multiproceso, trabajo con ficheros XML y DataSets.

Por esta pequeña evolución del proyecto inicial he dividido el articulo en dos partes:

Creación de un Formulario Splash y ejecución de un subproceso

Desarrollo de una agenda aplicando la programación en tres capas (Interfaz de usuario, Capa de negocios, Capa de acceso a datos).



Bueno pues una vez que se ha explicado un poco la idea, lo mejor es ver el desarrollo.



Creación de un Formulario Splash y ejecución de un subproceso

Un formulario inicial de presentación en una aplicación es algo muy simple y que le da a la misma un toque muy profesional, esta se puede realizar de muchas maneras, así que yo simplemente intentare dar una breve introducción.

El Formulario Splash.

Primero creamos un nuevo proyecto de Aplicación para Windows (en mi caso esta realizado en C#), y que yo he llamado FormSplash, de este proyecto el Form1.cs (Formulario inicial creado por defecto), será el formulario inicial de la aplicación, que mas tarde se usara para contener la agenda (ya que solo necesitara un formulario).

A este proyecto le añadiremos un nuevo formulario que yo he llamado FrmSplash, y que será nuestro formulario inicial.

Después modifico las siguientes propiedades del formulario:

Pongo ControlBox a false y Text se deja en blanco para eliminar el titulo del formulario.

Pongo en StartPosition, el valor CenterScreen para que aparezca en el centro de la pantalla.

A ShowInTaskBar le doy el valor false, para que no aparezca el formulario en la barra de herramientas de Windows.

Como no queremos que el usuario modifique la ventana, ni que la minimice ni maximice, ya que queremos que este lo vea igual que nosotros (ya que puede ser nuestra pequeña obra de arte), ponemos el valor false en las propiedades MinimizeBox y MaximizeBox, y a FormBorderStyle le daremos el valor FixedSingle.

A partir de esto, lo mejor que yo puedo añadir es simplemente que se "decore" al gusto, en el fichero adjunto que es el código fuente completo se vera mejor la idea.

Una vez terminado el formulario, se le añade un control Timer, que será el encargado de que nuestro formulario este visible durante un cierto periodo de tiempo y desaparezca, dejando paso a la nuestra aplicación (que realmente será lo importante).

A la hora de indicar el intervalo de tiempo lo podemos hacer de dos formas, una es simplemente indicarlo en el tiempo de diseño, y otra puede ser indicada en tiempo de ejecución, yo que he inclinado por la segunda opción, ya que si queremos reutilizar esta pantalla lo mejor es hacerlo así.

Para indicar el intervalo la idea que a mi se me ocurre es modificar el constructor por defecto de nuestro formulario y añadirle un argumento que será un int que representara los segundos que queremos que permanezca nuestra pantalla visible, siendo así necesario indicar este dato al crear una nueva instancia.

El constructor quedaría así:

public FrmSplash(int segundos)
{
    //
    // Necesario para admitir el diseñador de Windows Forms
    //

    InitializeComponent();

    timer1.Interval = segundos * 1000;    // pasamos de segundos a milisegundos

    if (!timer1.Enabled)
       timer1.Enabled=true;    // Activamos el Timer si no esta Enabled (Activado)

    //
    // TODO: Agregar código de constructor después de llamar a InitializeComponent
    //
}

Después de esto ya solo nos quedaría controlar el evento Tick del control Timer, para cerrar el formulario cuando el tiempo indicado se cumpla:

private void timer1_Tick(object sender, System.EventArgs e)
{
    timer1.Stop();     // Se para el timer.
    this.Close();      // Cerramos el formulario.
}

Bueno pues nuestro formulario inicial ya esta finalizado, así que ahora simplemente tendríamos que llamarlo cuando cargamos el formulario principal (Form1.cs por defecto) de nuestra aplicación, quedando el código como sigue.

private void Form1_Load(object sender, System.EventArgs e)
{
    // -------  Cargamos y mostramos el formulario Splash durante 5 segundos  -----
    FrmSplash f1=new FrmSplash(5);
    f1.ShowDialog(this);     // Mostramos el formulario de forma modal.
    f1.Dispose();
}

El subproceso.

Como esto ya nos funciona, pasemos al tema del subproceso, ¿Por que un subproceso?, la razón es muy simple, para que nuestras aplicaciones sean mas rápidas (por lo menos ante el usuario), siempre intentamos realizar las operaciones muy costosas en tiempo el menor numero de veces posible, y una buena idea es realizarlas (siempre que sea posible) cuando nuestra aplicación se inicializa, así mejoraremos la respuesta ante el usuario, y este estará mas contento.

Un claro ejemplo de esto es la utilización de los DataSet, que contienen una representación de nuestros datos en la memoria y trabajan de forma desconectada del soporte de los mismos, (en el caso de la agenda yo cargo todos los datos de la misma en un DataSet al iniciar el programa, pero en un programa de facturación podrían ser los clientes y artículos que utilice un usuario, etc.), y para que esos procesos iniciales que pueden tardar cierto tiempo no desesperen al usuario, lo mejor es mostrar nuestro formulario Splash mientras estos se están ejecutando, de ahí la idea de crear un subproceso y ejecutarlo mientras el usuario disfruta de nuestro formulario Splash.

En el programa adjunto este subproceso (como ya he indicado antes), realiza la carga de datos en un DataSet, pero para las pruebas yo utilice un simple bucle while, y es este el que utilizare para explicar la implementación del subproceso.

Bueno lo mejor será ir al grano, la mejor solución de las que he probado es (para mi gusto) la mas simple también, consiste en crear y ejecutar el subproceso al cargar nuestro formulario principal (Form1), y después tal como hacíamos antes mostramos nuestro formulario Splash, quedando el programa tal y como sigue:

using System.Threading;

.
.
.

private void Form1_Load(object sender, System.EventArgs e)
{
    ProcesosInic pInic=new ProcesosInic();
    Thread tAux = new Thread(new ThreadStart(pInic.ProcInic));   // Creamos el subproceso
    tAux.Start();                           // Ejecutamos el subproceso
    while (!tAux.IsAlive);                  // Esperamos a que se este ejecutando

    // -------  Cargamos y mostramos el formulario Splash durante 5 minimo  -----
    FrmSplash f1=new FrmSplash(5,tAux);
    f1.ShowDialog(this);     // Mostramos el formulario de forma modal.
    f1.Dispose();
}

Esta es la clase ProcesosInic que utilice para las pruebas (es muy simple no la comento):

class ProcesosInic
{
    public ProcesosInic()  {}

    public void ProcInic()
    {
        int i=0;

        while (i<1000)
        {
            Console.WriteLine("--- Proceso inicial. Linea: {0} ---",i++);
        }

    }
}

Bueno, pues esto podría ser todo, pero el que sea observador se habrá dado cuenta de que la llamada al constructor de nuestro formulario Splash ha cambiado y ahora además del tiempo incluye como argumento nuestro subproceso, ¿Por que?, muy fácil nosotros no sabemos de antemano cuanto tiempo tardara en realizarse el proceso, y este puede ser inferior o superior al deseado (es decir el argumento segundos del constructor), pasando a ser ahora un tiempo mínimo de muestra, si el proceso tarda menos este se mostrara durante los segundos que nosotros le indiquemos, pero si tarda mas el usuario ya estará cansadito y querrá usar la el programa, por lo tanto lo mejor es esperar el tiempo mínimo y después cerrar inmediatamente el formulario cuando el subproceso termine, por este motivo he modificado el constructor del formulario Splash y dejándolo de la siguiente manera:

using System.Threading;

.
.
.

public class FrmSplash : System.Windows.Forms.Form
{
    .
    .
    .

    private Thread _subProceso;

    public FrmSplash(int segundos, Thread t)
    {
        //
        // Necesario para admitir el diseñador de Windows Forms
        //

        InitializeComponent();

        timer1.Interval = segundos * 1000;    // pasamos de segundos a milisegundos
        _subProceso=t;

        if (!timer1.Enabled)
           timer1.Enabled=true;    // Activamos el Timer si no esta Enabled (Activado)

        //
        // TODO: Agregar código de constructor después de llamar a InitializeComponent
        //
    }
}

Con la asignación del argumento "t" a nuestra variable privada "_subProceso", podremos acceder a ella desde la clase, y así simplemente con modificar el evento Tick de timer1 de la siguiente manera conseguiremos el efecto deseado.

private void timer1_Tick(object sender, System.EventArgs e)
{
    timer1.Stop();     // Se para el timer.

    if (_subProceso.IsAlive)
    {
        // Una vez transcurrido el tiempo inicialmente establecido
        // establezco un intervalo de un segundo para mirar si el proceso a terminado.

        if (timer1.Interval!=1000)
            timer1.Interval=1000;

        timer1.Start();
    }
    else
        this.Close();      // Cerramos el formulario.
}

Esto es todo lo que da de si la primera parte del articulo, que aunque parece mucho rollo si se mira el código fuente del ejemplo se ve que es algo muy sencillo.

Desarrollo de una agenda aplicando la programación en tres capas (Interfaz de usuario, Capa de negocios, Capa de acceso a datos).

Como he indicado al principio y llegados hasta este punto quise realizar algo mas, ya que un simple formulario que llama a otro y ejecuta un bucle, queda un poco pobre, así que pensé que se podía hacer con el formulario principal de la aplicación, y lo que se me ocurrió en el momento es realizar una agenda, y de hacerlo, hacerlo bien es decir realizar la programación en tres capas para que la persona que quiera ver un ejemplo tenga a mano algo sencillo.

La capa de acceso a datos.

Aquí es donde se me plantea la primera duda, y no es otra que utilizar, SQL Server o MSDE, un fichero de Access, un fichero plano, etc., pero al pensar en ello lo que mejor me pareció es utilizar un fichero XML, ya que no necesita de otro programa, y se puede cargar sin problemas en un DataSet.

Esta capa solo tiene una clase, que llamo FichAgenda, que contendrá los datos en un DataSet, y además proporcionara la funcionalidad básica, de cargar, borrar, modificar, crear y guardar los datos de la agenda, (la persona que este interesada, puede probar a modificarla, para que utilice SQL Server o ACCESS, etc.), como en si es muy simple, solamente realizo un comentario sobre el método estático "CargarDatos", que tiene un control de errores sobre la lectura del fichero que no realiza nada en caso de producirse el error, y realmente esto de no realizar nada tiene una razón, ya que el error mas probable es que no exista el fichero, pero al tener en la clase el DataSet, perfectamente construido (sin datos claro) y si no queremos grabar nada, no necesitamos crear el fichero en el disco duro, y si queremos grabar algo, al llamar al método "GuardarDatos" este será creado.

public static void CargarDatos()
{
    _ds=new DataSet();               // Creamos una nueva instancia de un DataSet
    _ds.Tables.Add(Estructura());    // Le Añadimos la estructura

    try (_subProceso.IsAlive)
    {
        _ds.ReadXml("datos.xml");     // Leemos los datos del fichero datos.xml
    }
    catch
    {
        Console.WriteLine("Error al leer el fichero de datos.")
    }
    _dtCargados=true;
}

La capa de negocios.

Esta capa solo tiene una clase Persona, ya que no quería que fuera mas extenso, por dejarlo muy simple (Es un pequeño ejemplo), y como me parece tan simple recomiendo consultar el código.

El interfaz de usuario.

El interfaz de usuario es el formulario principal de la aplicación, (como buen ejercicio se podría realizar otro utilizando Web Forms en vez de utilizar Windows Forms), como explicación lo mejor que se puede hacer ver directamente el código de la aplicación.

Bueno, pues por fin esto si es todo, espero que todo este bien, ya que es mi primer articulo, y me gustaría que fuera útil, solo añadir que en caso de quejas, dudas, etc. se pueden poner en contacto conmigo en el correo indicado, un saludo a todos.



ir al índice

Fichero con el código de ejemplo: ACUESTA_FormularioSplash.zip - Tamaño 96,4 KB

Índice de la sección dedicada a punto NET (en el Guille)