Capítulo 50 Barra de progreso

Podemos considerar las barras de progreso como un control estático, dado que no sirven para obtener datos desde el usuario, sino sólo para informarle sobre el estado del progreso de una tarea, generalmente de una duración considerable.

Estilos visuales


Modo clásico

Estilo visual

A partir de la versión XP de Windows es posible seleccionar diferentes temas que definen muchos aspectos visuales del interfaz de Windows. Entre ellos, algunos afectan a la apariencia de las barras de progreso.

En nuestros programas podemos optar por una apariencia clásica (modo clásico) o por una apariencia más actual (estilos visuales).

Por defecto se usará el modo clásico, en el que las barras se suelen mostrar en color azul sobre fondo blanco, si efectos 3D. En modo clásico los mensajes para cambiar los colores de fondo y de la barra funcionan como se espera, de modo que aunque visualmente pueden ser menos atractivos, nos proporcionan mayor control sobre la apariencia de estos controles.

Si se activan los estilos visuales, la apariencia de los controles es más atractiva: se añaden efectos 3D, que aparentan relieve, y animaciones de color. Pero perdemos la posibilidad de modificar los colores desde la aplicación, ya que esos parámetros dependen sólo del estilo activo. Tampoco podemos diferenciar entre las barras de progreso de bloques o suaves, como en el modo clásico.

Para activar los estilos visuales tenemos dos alternativas, y algunas condiciones previas.

Las condiciones están relacionadas con la versión de sistema operativo y de Internet Explorer instaladas, o más concretamente, con la versión de la DLL de los controles comunes, que generalmente se asocia con una versión de Internet Explorer.

El sistema operativo debe ser Windows XP o posterior, y la de la DLL la 6.0 o posterior. Para que el compilador tenga esto en cuenta hay que definir estas macros:

#define _WIN32_WINNT  0x0501
#define  WINVER       0x0501
#define _WIN32_IE     0x0600

Aunque lo normal es que estén definidas si tu compilador fue instalado en Windows XP o un sistema operativo posterior, y no necesitemos declararlas.

La otra condición es agregar al programa un fichero de manifiesto, en el que se indican las versiones y dependencias de DLLs, entre otras cosas.

Aquí es donde aparecen dos opciones: incluir el fichero de manifiesto en un fichero independiente, cuyo nombre será <nombre_aplicación>.exe.manifest, donde <nombre_aplicación> debe ser el nombre del fichero ejecutable de la aplicación. Ese fichero debe estar en la misma carpeta que el programa ejecutable.

La segunda opción es incluir el manifiesto en el fichero de recursos, de modo que se enlace con el programa ejecutable, y formen un único fichero.

Cada opción tiene sus ventajas e inconvenientes. Si se usa un fichero independiente siempre tendremos la opción de borrarlo o renombrarlo, de modo que se active el modo clásico automáticamente. Sin embargo, si queremos mantener el control sobre el aspecto de la aplicación, esta opción tiene el inconveniente de que el usuario puede borrar el fichero o que puede resultar corrupto.

En cuanto a incluirlo en el fichero de recursos, la limitación es que el modo clásico deja de ser accesible, a no ser que se recompile la aplicación.

Fichero de manifiesto

Los ficheros de manifiesto son ficheros de texto en formato XML, con el siguiente contenido:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
    version="1.0.0.0"
    processorArchitecture="*"
    name="CompanyName.ProductName.YourApplication"
    type="win32"
/>
<description>Your application description here.</description>
<dependency>
    <dependentAssembly>
        <assemblyIdentity
            type="win32"
            name="Microsoft.Windows.Common-Controls"
            version="6.0.0.0"
            processorArchitecture="*"
            publicKeyToken="6595b64144ccf1df"
            language="*"
        />
    </dependentAssembly>
</dependency>
</assembly>

La parte que nos interesa, para activar los estilos visuales es la de <dependency>, que indica que se use la versión 6.0 de la DLL de controles comunes. Las claves de <assemblyIdentity> y <description> podemos ajustarlas dependiendo de nuestra aplicación, indicando la versión, los datos del programador o la empresa que realiza el programa, y la descripción de la aplicación.

Si optamos por la primera opción, bastará salvar un fichero con este contenido en la misma carpeta que el ejecutable, y con el nombre indicado.

Manifiesto en fichero de recursos

En el caso de incluir en manifiesto en el fichero de recursos, bastará con añadir estas líneas:

LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
1                  RT_MANIFEST    ".\\manifest.xml"

Por supuesto, necesitaremos un fichero externo, en este caso con el nombre "manifest.xml", y con un contenido análogo al anterior, aunque algo diferente:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <dependency>
    <dependentAssembly>
      <assemblyIdentity 
	     type="win32" 
		 name="Microsoft.Windows.Common-Controls" 
		 version="6.0.0.0" 
		 processorArchitecture="*" 
		 publicKeyToken="6595b64144ccf1df" 
		 language="*"
	  />
    </dependentAssembly>
  </dependency>
  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
    <security>
      <requestedPrivileges>
        <requestedExecutionLevel
          level="asInvoker"
          uiAccess="false"/>
      </requestedPrivileges>
    </security>
  </trustInfo>
</assembly>

Estilos

Hay dos estilos disponibles para las barras de progreso.

El primero, PBS_VERTICAL, afecta a la orientación. Si se especifica, la barra se rellenará de abajo a arriba, si no se especifica, se rellenará de izquierda a derecha.

El segundo, PBS_SMOOTH, afecta a la apariencia. Si se especifica el relleno de la barra será contínuo y no habrá cortes ni saltos. Si no se especifica, la barra se divide en rectángulos. Este estilo no tiene efecto cuando usamos estilos visuales.

Cómo crear barras de progreso

Como siempre, podemos insertar barras de progreso en nuestras ventanas mediante la función CreateWindowEx, especificando como clase de ventana el valor PROGRESS_CLASS:

        CreateWindowEx(0, PROGRESS_CLASS, (LPSTR)NULL, WS_CHILD | WS_VISIBLE, 10, 20, 200, 20, hwnd,
            (HMENU)ID_PROGRESSBAR, hInstance, NULL);

También podemos incluirlas en nuestros cuadros de diálogo, mediante ficheros de recursos, como un control de la clase PROGRESS_CLASS:

LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
IDD_DIALOG1 DIALOG 0, 0, 186, 47
STYLE DS_3DLOOK | DS_CENTER | DS_MODALFRAME | DS_SHELLFONT | WS_CAPTION | WS_VISIBLE | WS_POPUP | WS_SYSMENU
CAPTION "Dialog"
FONT 8, "Ms Shell Dlg"
{
    CONTROL         "", IDC_PROGRESO, PROGRESS_CLASS, PBS_SMOOTH, 8, 8, 169, 11
    PUSHBUTTON      "+", IDC_MAS, 7, 24, 14, 14
    PUSHBUTTON      "-", IDC_MENOS, 29, 24, 14, 14
    DEFPUSHBUTTON   "OK", IDOK, 127, 25, 50, 14
}

Entre los estilos, sólo hay dos disponibles: PBS_SMOOTH y PBS_VERTICAL. Se puede especificar uno de ellos, los dos o ninguno.

Rangos

Cada barra de progreso tiene ciertos parámetros que podemos modificar a nuestra conveniencia. Los principales son los que definen el rango: valor mínimo y máximo, y el valor actual.

Cuando el valor actual sea igual al valor mínimo del rango, la barra se mostrará vacía. Cuando el valor actual sea igual al valor máximo del rango, la barra se mostrará llena.

En nuestros programas haremos variar el valor actual desde el mínimo al máximo para reflejar el progreso de alguna tarea cuando nos interese informar al usuario a medida que esa tarea se va completando.

Por defecto, el rango de las barras de progreso es (0, 100), pero podemos variar ese rango, siempre que tengamos en cuenta dos reglas sencillas y lógicas:

  1. Que ambos valores deben ser positivos
  2. Que el valor máximo debe ser mayor que el mínimo

Para establecer un nuevo rango disponemos de dos mensajes: PBM_SETRANGE y PBM_SETRANGE32, dependiendo de si los valores máximo y mínimo son enteros de 16 ó 32 bits, respectivamente.

Generalmente, con la versión de 16 bits será suficiente, pero es bueno recordar que, en caso necesario, podemos usar valores de 32 bits para establecer los rangos.

Si optamos por la versión de 16 bits, usaremos el parámetro lParam para empaquetar los dos rangos, en la palabra de menor peso el rango mínimo, y en la de mayor peso, el máximo. Podemos usar la macro MAKELPARAM para empaquetar los dos valores:

           SendDlgItemMessage(hwnd, ID_PROGRESSBAR, PBM_SETRANGE, 0, MAKELPARAM(0, 200));

Si preferimos usar la versión de 32 bits, indicaremos el rango mínimo en wParam y el máximo en lParam.

           SendDlgItemMessage(hwnd, ID_PROGRESSBAR, PBM_SETRANGE32, 0, 200);

En los dos casos, el valor de retorno es un DWORD, cuya palabra de menor peso contiene el rango mínimo previo, y la de mayor peso el rango máximo previo. Si los valores de rango previos eran de 32 bits, sólo obtendremos las palabras de menor peso de cada rango.

También podemos obtener los valores actuales de los rangos mediante un mensaje PBM_GETRANGE. En wParam usaremos el valor TRUE para que el mensaje retorno el valor del rango mínimo, y FALSE para que retorne el máximo. En lParam podemos pasar un puntero a una estructura PBRANGE, que recibirá los dos valores del rango, o NULL si no queremos usar esa estructura.

    PBRANGE rango;
	
    min = SendDlgItemMessage(hwnd, ID_PROGRESSBAR, PBM_GETRANGE, TRUE, 0);
    max = SendDlgItemMessage(hwnd, ID_PROGRESSBAR, PBM_GETRANGE, FALSE, (LPARAM)&rango);

Posicion

Hay tres modos de modificar la posición actual de un control de barra de progreso, podremos elegir la que más se ajuste a nuestro problema particular.

La más sencilla es asignar la posición directamente al valor que queramos, para ello podemos usar el mensaje PBM_SETPOS, indicando en el parámetro wParam la nueva posición deseada.

    SendDlgItemMessage(hwnd, ID_PROGRESSBAR, PBM_SETPOS, (WPARAM)val, 0);

Otro modo es incrementar la posición actual en una cantidad. Esa cantidad puede ser positiva o negativa. Para hacer esto usaremos el mensaje PBM_DELTAPOS, indicando en wParam la cantidad a incrementar.

    SendDlgItemMessage(hwnd, ID_PROGRESSBAR, PBM_DELTAPOS, (WPARAM)1, 0);

La última forma es establecer un paso de incremento, y hacer incrementos en la posición usando ese valor. Las barras de progreso integran un mecanismo para hacer esto. Podemos establecer el paso mediante un mensaje PBM_SETSTEP, indicando en wParam el valor del paso, y posteriormente hacer incrementos de ese paso, enviando mensaje PBM_STEPIT.

    SendDlgItemMessage(hwnd, ID_PROGRESSBAR, PBM_SETSTEP, (WPARAM) 5, 0);
...
    SendDlgItemMessage(hwnd, ID_PROGRESSBAR, PBM_STEPIT, 0, 0);

Si necesitamos obtener el valor actual de la posición de una barra de progreso, podemos hacerlo usando un mensaje PBM_GETPOS, sin parámetros.

Colores

Si usamos barras de progreso en modo clásico, podemos modificar los colores para la barra y el fondo, usando los mensaje PBM_SETBARCOLOR y PBM_SETBKCOLOR, respectivamente. En controles con estilos visuales, tal como comentamos antes, estos mensajes no tienen efecto.

    SendDlgItemMessage(hwnd, ID_PROGRESSBAR, PBM_SETBARCOLOR, 0, (LPARAM)RGB(255,120,90));
    SendDlgItemMessage(hwnd, ID_PROGRESSBAR, PBM_SETBKCOLOR, 0, (LPARAM)RGB(0,0,0));

Ejemplo 86


  Nombre Fichero Fecha Tamaño Contador Descarga
D Ejemplo 86 win086.zip 2012-06-15 3881 bytes 298

Comentarios de los usuarios (3)

Ivan
2014-06-20 19:22:07

En todos los tutoriales que e leido sobre la WinApi32 siempre dice que se deven iniciar los controles antes de usar pero siempre mi compilador me dice que

InitCommonControlsEx();

esta indefinido

[Linker error] undefined reference to`InitCommonControlsEx'

y todo lo que tiene que ver con la funcion

INITCOMMONCONTROLSEX InitCtrlEx;

InitCtrlEx.dwSize = sizeof(INITCOMMONCONTROLSEX);

InitCtrlEx.dwICC = ICC_PROGRESS_CLASS;

InitCommonControlsEx(&InitCtrlEx);

pero si lo borro ya no hay problemas y funciona perfectamente esto me preocupa un poco, que es lo que no se puede hacer si no inicializo los controles o es la libreria? la verdad no entido este problema me podrian orientar

Salvador Pozo
2014-06-20 19:31:34

Hola:

El error se debe a que debes incluir la librería "comctl32" en las opciones del enlazador (linker).

Tampoco es necesario invocar a esta función siempre, sólo es imprescindible si tu programa usa controles comunes, si no lo hace, no es necesario.

Hasta pronto.

Ivan
2014-06-21 15:22:55

Te doy las gracias por ayudarme es usted muy amable ya tenia tiempo con ese problema