Blog alskare

.Net y lo que surja

Archivos de la categoría ‘WinForms’

C# Mantener la configuración de tamaño y posición de los formularios

Publicado por alskare en 15/09/2009

No sería la primera vez que los clientes piden que, en los formularios WinForms, se guarde tanto la posición como el tamaño del formulario, incluso después de cerrar la aplicación. Bajo esta premisa, me inicio en un nuevo post que tiene como finalidad, precisamente esto, almacenar la información de tamaño del formulario y recuperarla cada vez que se abra de nuevo el mismo formulario.

Framework.Net nos ofrece una serie de posibilidades amplias para poder guardar información en el área de trabajo del usuario. Una de estas opciones es el namespace System.XML, el cual nos permite poder trabajar con ficheros XML, tanto a nivel de lectura como de escritura. Aprovechando estas características, se me ocurre la creación de un fichero en el que poder almacenar los datos de tamaño y posición del formulario. Ahora bien, el problema de una aplicación es la cantidad de formularios existentes, así que empiezo a pensar en un fichero con una estructura similar a la siguiente:

<?xml version="1.0" encoding="utf-8"?>
<Configuracion>
    <NombreForm1>
        <WindowState>ValorWindowStateForm1</WindowState>
        <Top>ValorTopForm1</Top>
        <Left>ValorTopForm1</Left>
        <Width>ValorTopForm1</Width>
        <Height>ValorTopForm1</Height>
    </NombreForm1>
    <NombreForm2>
        <WindowState>ValorWindowStateForm2</WindowState>
        <Top>ValorTopForm2</Top>
        <Left>ValorTopForm2</Left>
        <Width>ValorTopForm2</Width>
        <Height>ValorTopForm2</Height>
    </NombreForm2>
</Configuracion>

De esta manera, si los datos de cada formulario representa un nodo dentro del archivo XML, puede resultar más o menos sencilla la adaptación de la librería a cualquier otro valor.

Una vez está decidido cuál será el medio de almacenamiento, cabe comenzar a pensar en cómo se realizarán las operaciones de lectura y escritura sobre el fichero XML, así que, recordando las clases de Programación Orientada a Objeto, se me ocurre la idea de crear un formulario base del que heredarán todos aquellos formularios que necesiten almacenar dicha información.

Manos a la obra. Comenzamos creando un nuevo proyecto que será una Biblioteca de controles de WindowsForms, que contendrá un formulario denominado FormularioBase. Éste será un formulario estándar en el que primarán dos cosas:

  1. En el evento FormClosing se almacenarán los datos que interesen en el fichero de configuración
  2. En el evento Load se recuperarán, en caso de existir previamente, los datos de configuración y se aplicarán al formulario

Así, una vez creado el campo que contendrá el fichero de configuración,

private string ficheroXML = Path.Combine(Application.StartupPath, "ConfForm.xml");

nos ponemos a implementar el proceso de almacenamiento de los datos:

/// <summary>
 /// Almacena la información del formulario actual en fichero XML
 /// </summary>
 private void GuardaConfiguracionForm()
 {
     string nombreForm = this.Name;
     XmlNode nodoPrincipal;

     try
     {
         // Intancia del documento XML que permitirá guardar la configuración
         XmlDocument doc = new XmlDocument();

         // Se comprueba que exista el fichero
         if (!File.Exists(ficheroXML))
         {
             // No existe el fichero. Se añaden los nodos iniciales
             doc.AppendChild(doc.CreateComment("Configuración de formularios - älskare, Sep/09"));

             // Creación del nodo principal
             nodoPrincipal = doc.CreateNode(XmlNodeType.Element, "Configuracion", null);

             // Se añade el nodo principal al documento
             doc.AppendChild(nodoPrincipal);
         }
         else
         {
             // El fichero existe previamente. Se recupera la información
             doc.Load(ficheroXML);

             // Lectura del nodo principal
             nodoPrincipal = doc.SelectSingleNode("Configuracion");
         }

         // Se comprueba si existe un nodo con el nombre del form actual
         XmlNode nodoFormActual = nodoPrincipal.SelectSingleNode(nombreForm);
         if (nodoFormActual == null)
             nodoFormActual =
                   nodoPrincipal.AppendChild(
                     doc.CreateNode(XmlNodeType.Element, nombreForm, null));

         // Almacenamiento de los valores
         NodoValor(doc, nodoFormActual, "WindowState", this.WindowState);
         NodoValor(doc, nodoFormActual, "Top", this.Top);
         NodoValor(doc, nodoFormActual, "Left", this.Left);
         NodoValor(doc, nodoFormActual, "Width", this.Width);
         NodoValor(doc, nodoFormActual, "Height", this.Height);


         // Se guarda el fichero de configuración en disco
         XmlTextWriter tw = new XmlTextWriter(ficheroXML, Encoding.UTF8);
         tw.Indentation = 4;
         tw.IndentChar = " "[0];
         tw.Formatting = Formatting.Indented;

         doc.Save(tw);
         doc = null;

         tw.Flush();
         tw.Close();
     }
     catch { }
 }

 /// <summary>
 /// Crea un nuevo par clave-valor en el nodo actual en caso de que no exista
 /// <returns></returns>
 XmlNode NodoValor(XmlDocument doc, XmlNode nodoPadre, string Clave, Object Valor)
 {
     XmlNode nodoActual = nodoPadre.SelectSingleNode(Clave);
     if (nodoActual == null)
         nodoActual = nodoPadre.AppendChild(doc.CreateNode(XmlNodeType.Element, Clave, null));
     nodoActual.InnerText = Valor.ToString();
     return nodoActual;
 }

 

Una vez realizado el proceso de escritura del fichero, falta poder recuperarlo para aplicar los cambios en el formulario cuando se abra.

/// <summary>
/// Realiza una lectura del fichero de configuración y aplica los cambios al form
/// </summary>
private void LecturaConfiguracionForm()
{
    // No se controlan errores. En caso de existir cualquier error
    // no se modifica nada en el formulario actual
    try
    {
        string nombreForm = this.Name;
        XmlDocument doc = new XmlDocument();
        doc.Load(ficheroXML);

        // Lectura del nodo principal
        XmlNode nodoPrincipal = doc.SelectSingleNode("Configuracion");

        // Lectura del nodo del formulario actual
        XmlNode nodoActual = nodoPrincipal.SelectSingleNode(nombreForm);

        // Lectura del modo de presentación de la ventana
        this.WindowState = (FormWindowState)Enum.Parse(typeof(FormWindowState), GetValor(nodoActual, "WindowState").ToString(), false);

        if (this.WindowState == FormWindowState.Normal)
        {
            this.Top = Convert.ToInt32(GetValor(nodoActual, "Top"));
            this.Left = Convert.ToInt32(GetValor(nodoActual, "Left"));
            this.Width = Convert.ToInt32(GetValor(nodoActual, "Width"));
            this.Height = Convert.ToInt32(GetValor(nodoActual, "Height"));
        }
    }
    catch { }
}

/// <summary>
/// Obtiene el valor de la clave
/// </summary>
private object GetValor(XmlNode nodoPadre, string clave)
{
    XmlNode valor = nodoPadre.SelectSingleNode(clave);
    return valor.LastChild.Value;
}

Implementadas tanto la escritura como la lectura de los valores, quedará aplicar dichos cambios al formulario actual. En esta parte, debo reconocer que no he conseguido eliminar un efecto un tanto raro que se da al abrir el formulario, sobre todo, si el formulario a mostrar está contenido en otro en modo MDI. Seguiremos trabajando sobre el tema para ver si consigo eliminar dicho efecto antiestético.

public FormularioBase()
{
    this.SuspendLayout();
    InitializeComponent();
    this.ResumeLayout();

    this.Load += new EventHandler(FormularioBase_Load);
    this.FormClosing += new FormClosingEventHandler(FormularioBase_FormClosing);
}

void FormularioBase_Load(object sender, EventArgs e)
{
    LecturaConfiguracionForm();
}
void FormularioBase_FormClosing(object sender, FormClosingEventArgs e)
{
    GuardaConfiguracionForm();
}

Bueno, pues ya está. A partir de ahora, cada vez que cree un nuevo WinForms, tan sólo añadiré una referencia a la librería en la que tengo este formulario base y, por supuesto, haré que cada formulario herede de FormularioBase.

public partial class Form2 : jnSoftware.Utiles.FormularioBase 
    {
        public Form2()
        {
            InitializeComponent();
        }
    }

 

Si le interesa a alguien, dejo enlace en el que se puede descargar un ejemplo de uso: descargar.

Publicado en C#, Controles, WinForms | 2 Comentarios »

Validar Número de IVA VIES

Publicado por alskare en 07/09/2009

El otro día, hablando con la contable de la empresa, me comentó la posibilidad de añadir en las aplicaciones la validación de unos “NIF” un poco especiales. Digo un poco especiales porque en principio no se trata de un número de identificación sino más bien es un registro de aquellas empresas “legalizadas” para poder trabajar con IVAs a nivel intracomunitario. Digo legalizada entre comillas puesto que desconozco totalmente si el medio para entrar en esa base de datos se realiza automáticamente al trabajar con empresas intracomunitarias o hace falta algún tipo de registro especial.

Bueno, el caso es que mala validación podemos crear si el origen es una base de datos, así que no me queda más remedio que empezar a buscar y encuentro lo que andaba buscando en lo que creo que es la AEAT Europea: http://ec.europa.eu/taxation_customs/vies/lang.do?fromWhichPage=vieshome&selectedLanguage=ES.

Dentro de los servicios informáticos con los que he trabajado, quizás éste sea uno de los más originales. De hecho, no existe una base de datos centralizada. Se trata de bases de datos con un ámbito del país local que va “replicando” con el resto de países (igual me he pasado al escribir “replicando”). Por otro lado, resulta que no todos los países atienden las 24 horas del día (igual los ordenadores se van a tomar un café): http://ec.europa.eu/taxation_customs/vies/viesspec.do

Al fin y al cabo, somos programadores y nuestros profes siempre nos han enseñado que debemos desglosar un problema gordo en problemas más pequeños para poder afrontarlos con mayor destreza, así que eso voy a hacer en estos momentos. Imagino que al final pasará como ha ocurrido con otra entrada en el blog (Validar NIF, NIE, CIF ), que empezó con una pequeña validación y se ha ido ampliando y mejorando con el tiempo. Así, el objetivo inicial será el de comprobar si el NIF introducido es correcto o no, siempre según el servicio de Fiscalidad y Unión Aduanera.

La primera me la doy en la frente. Resulta que para solicitar la validez de un Número de IVA, necesitamos el código del país y… ¿cómo no?, no encuentro ningún servicio que devuelva los países aceptados, así que hay que hacer una lectura del código HTML de la página para poder tenerlos todos. Para poder trabajar con los países y poder ponerlos como origen de datos de los controles se me ocurre hacer lo más sencillo, una clase List<T>.

Paises

using System;
using System.Collections.Generic;

namespace ValidacionVies
{
    class Pais : IComparable 
    {
        public string CodigoPais { get; set; }
        public string NombrePais { get; set; }

        public Pais(string codigoPais, string pais)
        {
            this.CodigoPais = codigoPais;
            this.NombrePais  = pais;
        }


        // Se implementa la interfaz IComparable para poder ordenar por el país 
        public int CompareTo(object obj)
        {
            return this.NombrePais.CompareTo(((Pais)obj).NombrePais);
        }
    }


    class Paises : List<Pais>
    {
        public Paises()
        {
            this.Add(new Pais("AT", "Austria"));
            this.Add(new Pais("BE", "Bélgica"));
            this.Add(new Pais("BG", "Bulgaria"));
            this.Add(new Pais("CY", "Chipre"));
            this.Add(new Pais("CZ", "Chequia"));
            this.Add(new Pais("DE", "Alemania"));
            this.Add(new Pais("DK", "Dinamarca"));
            this.Add(new Pais("EE", "Estonia"));
            this.Add(new Pais("EL", "Grecia"));
            this.Add(new Pais("ES", "España"));
            this.Add(new Pais("FI", "Finlandia"));
            this.Add(new Pais("FR", "Francia"));
            this.Add(new Pais("GB", "Reino Unido"));
            this.Add(new Pais("HU", "Hungría"));
            this.Add(new Pais("IE", "Irlanda"));
            this.Add(new Pais("IT", "Italia"));
            this.Add(new Pais("LT", "Lituania"));
            this.Add(new Pais("LU", "Luxemburgo"));
            this.Add(new Pais("LV", "Letonia"));
            this.Add(new Pais("MT", "Malta"));
            this.Add(new Pais("NL", "Países Bajos"));
            this.Add(new Pais("PL", "Polonia"));
            this.Add(new Pais("PT", "Portugal"));
            this.Add(new Pais("RO", "Rumania"));
            this.Add(new Pais("SE", "Suecia"));
            this.Add(new Pais("SI", "Eslovenia"));
            this.Add(new Pais("SK", "Eslovaquia"));

            this.Sort();
        }
    }

}

 

El problema lo encontraré el día que haya que hacer alguna modificación, así que tomo nota de la mejora que podría hacerse creando un XML o un simple TXT para poder almacenar los países.

Una vez tenemos los países, conociendo los resultados que nos devuelve el WebService, es cuestión de empezar a preparar un entorno de interacción con el usuario:

- <element name="checkVatResponse">
- <complexType>
- <sequence>
  <element name="countryCode" type="xsd:string" /> 
  <element name="vatNumber" type="xsd:string" /> 
  <element name="requestDate" type="xsd:date" /> 
  <element name="valid" type="xsd:boolean" /> 
  <element name="name" type="xsd:string" /> 
  <element name="address" type="xsd:string" /> 
  </sequence>
  </complexType>
  </element>

Como que me hace gracia jugar un poco con las llamadas asíncronas al Web Service, opto por crear una aplicación WinForms que me permita realizar las primeras pruebas, así, me sale algo así como lo siguiente (ya dice mi jefe que lo mío no es el diseño):

image

 

De hecho, en el formulario no hay nada raro, un combo en el que se cargan los países (cboPaises), un textBox en el que deberá teclearse el número a validar (txtVatNumber) y los textBox de resultado (rstXXXX).

Al lío:

Código del formulario

using System;
using System.Windows.Forms;
using ValidacionVies.eu.europa.ec;


namespace ValidacionVies
{
    public partial class Form1 : Form
    {
        checkVatService cvs;

        public Form1()
        {
            InitializeComponent();

            // Asignación de eventos
            this.Load += new EventHandler(Form1_Load);
            this.cmdValida.Click += new EventHandler(cmdValida_Click);
        }

        void Form1_Load(object sender, EventArgs e)
        {
            lblProgreso.Text = string.Empty;

            // Carga de paises y asignación al combo
            Paises paises = new Paises();
            cboPaises.DataSource = paises;
            cboPaises.DisplayMember = "NombrePais";
            cboPaises.ValueMember = "CodigoPais";
            cboPaises.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
            cboPaises.AutoCompleteSource = AutoCompleteSource.ListItems;

            // Asignación del evento IndexChanged para el combo.
            cboPaises.SelectedIndexChanged += new EventHandler(cboPaises_SelectedIndexChanged);
            txtCountryCode.Text = cboPaises.SelectedValue.ToString();
        }


        void cboPaises_SelectedIndexChanged(object sender, EventArgs e)
        {
            ComboBox cbo = (ComboBox)sender;
            txtCountryCode.Text = cbo.SelectedValue.ToString();
        }


        void cmdValida_Click(object sender, EventArgs e)
        {
            lblProgreso.Text = "Consultando";

            // Llamada asíncrona al WebService
            cvs = new checkVatService();
            cvs.checkVatCompleted += new checkVatCompletedEventHandler(cvs_checkVatCompleted);
            cvs.checkVatAsync(txtCountryCode.Text, txtVatNumber.Text);
        }


        void cvs_checkVatCompleted(object sender, checkVatCompletedEventArgs e)
        {
            // Respuesta de la llamada asíncrona al WebService
            try
            {
                if (!e.Cancelled)
                {
                    lblProgreso.Text = string.Empty;
                    rstAddress.Text = e.address;
                    rstCountryCode.Text = e.countryCode;
                    rstName.Text = e.name;
                    rstResult.Text = e.Result.ToShortDateString();
                    rstCorrecto.Text = e.valid ? "Sí" : "No";
                }
                else
                {
                    lblProgreso.Text = string.Empty;
                    rstAddress.Text = string.Empty;
                    rstCountryCode.Text = string.Empty;
                    rstName.Text = string.Empty;
                    rstResult.Text = string.Empty;
                    rstCorrecto.Text = string.Empty;
                }
            }
            catch (System.Reflection.TargetInvocationException ex)
            {
                lblProgreso.Text = ex.Message;
            }
            catch (Exception ex)
            {
                lblProgreso.Text = "Se ha producido un error " + ex.GetType().ToString();

            }
        }
    }
}

Ya está. ¡Qué bonitas se ven las cosas cuando están acabadas!

Por cierto, se si alguien tiene interés, puede descargar el proyecto en VisualStudio2008: Descargar

Publicado en C#, Helpers, WebServices, WinForms | 3 Comentarios »

Redimensionar una imagen

Publicado por alskare en 24/02/2009

Cuando se trata de subir fotografías a una página Web, casi siempre nos encontramos que, la cámara más barata hace unas fotos de miedo. De miedo por calidad y por el tamaño.

Una solución es redimensionar la imagen, de tal manera que ocupe lo mínimo posible y tenga una calidad aceptable para mostrarse por Internet. Un tamaño que se está convirtiendo en estándar para fotos de productos en Internet es 640×480.

En el código que acompaña al post,  incluyo un ejemplo de cómo cambiar el tamaño y la resolución de una imagen

VB.NET
''' <summary>Cambia la resolución de una imagen a 640x480</summary>
''' <param name="FicheroOrigen">Fichero con la foto original</param>
''' <param name="FicheroDestino">Fichero en el que se guardará la foto redimensionada</param>
Public Shared Sub Redimensiona(ByVal FicheroOrigen As String, ByVal FicheroDestino As String)
    Dim FotoOriginal As New Bitmap(FicheroOrigen)
    Dim FotoRetocada As New Bitmap(FotoOriginal, 640, 480)
    FotoRetocada.SetResolution(72, 72)

    FotoRetocada.Save(FicheroDestino, Imaging.ImageFormat.Jpeg)

    FotoOriginal = Nothing
    FotoRetocada = Nothing
End Sub
C#
/// <summary>Cambia la resolución de una imagen a 640x480</summary>
/// <param name="FicheroOrigen">Fichero con la foto original</param>
/// <param name="FicheroDestino">Fichero en el que se guardará la foto redimensionada</param>
public static void  Redimesiona(string FicheroOrigen, string FicheroDestino)
{
    Bitmap FotoOriginal = new Bitmap(FicheroOrigen);
    Bitmap FotoDestino = new Bitmap(FotoOriginal, new Size(640, 480));

    FotoDestino.SetResolution(72, 72);

    FotoDestino.Save(FicheroDestino, System.Drawing.Imaging.ImageFormat.Jpeg);

    FotoOriginal = null;
    FotoDestino = null;
}

Publicado en C#, NetFramework, Vb.Net, WinForms | Comentarios desactivados

Obtener información de una IP

Publicado por alskare en 25/01/2009

Jugando con el tema del consumo de servicios Web, me encuentro un servicio que permite obtener, en base a una dirección IP, su localización geográfica. La dirección de los servicios es : http://www.webservicex.net/geoipservice.asmx

Creación de la aplicación

Ya que trabajo poco con C#, opto por hacer las pruebas, precisamente con este lenguaje, así nos vamos familiarizando un poco con él. Siempre me ha apetecido dar el salto de VB a C#, pero mantengo demasiadas aplicaciones en VBA, VB6 y VB.Net como para empezar a habituarme.

Pues eso, creamos una aplicación de C# WinForms para ver los resultados:

image

Dentro de Proyecto | Agregar referencia Web haremos referencia al servicio que usaremos en la aplicación:

image

En este caso podemos apreciar como sólo tenemos un servicio disponible en el servidor, GetGeoIPContext y una clase, GetGeoIP que, como veremos más adelante, se encargará de encapsular la información devuelta por las operaciones del servicio.

Preparación del entorno

Creamos un formulario que tendrá doble utilidad. Por un lado, permitirá al usuario introducir la dirección IP que quiere consultar y, por otro, mostrará los datos que se extraigan de la clase GetGeoIP.

image

Los datos de respuesta son todos los que vienen incluidos en la clase GetGeoIP, salvo el DomainName, que lo he incluido de “cosecha propia” gracias a la inmensa librería de clases de .NetFramework.

El programa

Al final del artículo se incluye la aplicación completa, por tanto, sólo se hace mención especial a los fragmentos de código más característicos de la aplicación.

Comprobar que la Ip introducida es correcta

Creo una función que permite comprobar que la IP que introduce el usuario es una IP válida, haciendo uso del método TryParse de la clase System.Net.IPAddress:

/// <summary>
/// Confirma que la dirección IP sea válida. 
/// </summary>
/// <param name="Ip">string introducido por el usuario</param>
/// <returns>En caso de introducir 2 es válido, puesto que toma 0.0.0.2</returns>
private bool ConfirmIP(string Ip)
{
    System.Net.IPAddress ipTemp = null;
    return System.Net.IPAddress.TryParse(Ip, out ipTemp);
}

Es importante destacar, en el código anterior, que una dirección del tipo 12.12 será considerada como válida, puesto que la interpretará como 0.0.12.12.

Para obtener el nombre del servidor, hacemos uso de la clase System.Net.Dns, concretamente, del método estático GetHostEntry.

/// <summary>
/// Devuelve el nombre del servidor asociado a la IP.
/// </summary>
/// <returns></returns>
private string GetInfoServer()
{
    IPHostEntry ip = Dns.GetHostEntry(txtIpIn.Text);
    return ip.HostName;
}

 

Por tanto, una vez definidos los campos estáticos de la clase,

// Clase de WebService que contiene información de la localización de una IP.
private wsWhois.GeoIP IpInfo;

// Clase de WebService que permite consultar una Ip.
private wsWhois.GeoIPService wsService;

// Nombre del Servidor.
private string serverName = null;

Sólo quedará hacer una llamada al servicio GetGeoIPContext y escribir los datos resultantes:

private void cmdGo_Click(object sender, EventArgs e)
{
    IpInfo = null;
    WriteInfo();
    if (!ConfirmIP(this.txtIpIn.Text))
    {
        MessageBox.Show("Invalid IP Address");
    }
    else
    {
        //Get info from Country of Ip
        wsService = new wsWhois.GeoIPService();
        IpInfo = wsService.GetGeoIP(this.txtIpIn.Text);

        // GetInfo From Domain
        serverName = GetInfoServer();

        WriteInfo();
    }
}

Incluyo también el método writeInfo que, por lo menos a mí, me llama la atención por la sencillez y potencia del operador ternario para poder mostrar los datos en el caso que nos encontremos con resultados correctos o bien, una “limpieza” de los controles del formulario en el caso que no se haya recibido una respuesta correcta:

/// <summary>
/// Muestra la información resultante en pantalla o limpia los datos del formulario
/// </summary>
private void WriteInfo()
{
    bool resultOk = IpInfo != null;

    this.txtIp.Text = (resultOk) ? IpInfo.IP : string.Empty;
    this.txtCountryCode.Text = (resultOk) ? IpInfo.CountryCode.ToString() : string.Empty;
    this.txtCountryName.Text = (resultOk) ? IpInfo.CountryName : string.Empty;
    this.txtReturnCode.Text = (resultOk) ? IpInfo.ReturnCode.ToString() : string.Empty;
    this.txtReturnCodeDetails.Text = (resultOk) ? IpInfo.ReturnCodeDetails : string.Empty;
    this.txtDomainName.Text = (resultOk) ? serverName : string.Empty;
}

 

Dejo el enlace al proyecto completo. Yo supongo que sí que irá, pero es la primera vez que tenía que dejar un archivo, así que he optado por dejarlo en SkyDrive y colocar el enlace desde aquí. Ahora me queda probar si puedo subir ficheros directamente en WordPress.

Descarga del enlace: aquí

Publicado en C#, WebServices, WinForms | Comentarios desactivados

WinForms. Mostrar progreso con ProgressBar

Publicado por alskare en 13/01/2009

Uno de los atractivos de WinForms es que, cuando existe un procedimiento que tarda un rato en ejecutarse, se le puede ir mostrando al usuario un porcentaje aproximado de la duración que seguirá el sistema con el proceso. El control ProgressBar es una herramienta visual atractiva para realizar este tipo de acciones.

El uso de un control ProgressBar realmente es bien sencillo, puesto que tan sólo se trata de modificar un par de propiedades: Maximum, donde indicamos el valor entero máximo que admitirá el control y Value, que representará el progreso actual. Hasta aquí, todo es más o menos normal y sencillo. Un ejemplo de uso podría ser, aproximadamente el siguiente:

barraProgreso.Maximum = 100
For i As Integer = 0 To 100
    System.Threading.Thread.Sleep(500)
    barraProgreso.Value = i
Next

Uno de los problemas que surgen cuando se está ejecutando un proceso es que, si por ejemplo, el usuario minimiza la ventana de la aplicación o la coloca en segundo plano, nos encontramos con que, al situar la ventana de la aplicación de nuevo en un primer plano, la barra de progreso no sigue actualizándose, puesto que el proceso que se está ejecutando es el “cálculo” y no los “refrescos” de pantalla.

Una posible solución a este problema se encuentra en el procedimiento System.Windows.Forms.Application.DoEvents(), el cual, según la propia ayuda de .Net:

Al ejecutar un formulario Windows Forms, crea el nuevo formulario, que espera recibir eventos y controlarlos. Cada vez que el formulario controla un evento, procesa todo el código asociado al evento. Los demás eventos esperan en la cola. Mientras el código está controlando el evento, la aplicación no responde. Por ejemplo, la ventana no se vuelve a dibujar si se arrastra otra ventana encima de ella.

Viendo esto, tan sólo se trata de realizar una pequeña modificación en la rutina de presentación del progreso:

Private Sub PulsadoClick(ByVal sender As Object, ByVal e As EventArgs)
    barraProgreso.Maximum = 100
    For i As Integer = 0 To 100
        System.Threading.Thread.Sleep(500)
        barraProgreso.Value = i

        Application.DoEvents()
    Next
End Sub

Debo reconocer que antes de ver algo tan sencillo, como es habitual en mí, he pasado por hacer alguna cosa con el control BackgroundWorker, para que el proceso de presentación del progreso se ejecutase en segundo plano. No se me ha ocurrido mirar el rendimiento de ambos, pero es de suponer que la ejecución de tareas en segundo plano provoca un mayor consumo de recursos y, en consecuencia, un menor rendimiento.

 

Etiquetas de Technorati: ,

Publicado en C#, Controles, WinForms | 3 Comentarios »

 
Seguir

Get every new post delivered to your Inbox.