Capítulo 47 Control animación

Diálogo copia ficheros
Diálogo de copia de ficheros

El control animación es un simple control estático que permite mostrar animaciones en formato AVI, sin sonido. Esto es importante, estos controles rechazarán cualquier AVI que contenga sonido.

Seguramente has visto estos controles, por ejemplo al copiar, mover o borrar ficheros desde el administrador de archivos de Windows. Se suelen usar para indicar que se está realizando una tarea, en lugar de mostrar un cuadro de diálogo estático. De este modo, el usuario tiene la sensación de que su petición está siendo atendida y no que ha quedado atascada, sobre todo cuando lleva algún tiempo completarla.

En realidad sólo sirven como adorno, no aportan mejoras de rendimiento, ni tienen correspondencia con los procesos que se están ejecutando en el programa. Pero los programas tienen mejor aspecto (siempre que las animaciones tengan cierta calidad), y se ven más profesionales.

Para poder usar los controles animación en nuestros programas es necesario incluir el fichero de cabecera "commctrl.h", enlazar con la librería "comctl32" y asegurarse de que la dll ha sido cargada invocando a la función InitCommonControls.

Nota: A menudo podemos olvidar invocar a InitCommonControls, y a pesar de ello, el programa funciona correctamente. Esto se debe a que cualquier otro programa que se esté ejecutando actualmente (por ejemplo el propio IDE del compilador), ya ha cargado la dll en memoria. Si no tenemos esto en cuenta es probable que nuestro ejecutable no funcione correctamente cuando se ejecute en solitario.

Ficheros de recursos

Como con los otros recursos que hemos visto con anterioridad, podemos integrar los recursos de animaciones dentro de un fichero de recursos.

En este caso tenemos dos recursos relacionados con los controles de animación. Uno es el propio recurso AVI, que contiene una animación en formato AVI sin sonido. El otro es el control que es una ventana de la clase "ANIMATE_CLASS".

Para poder incluir este control en nuestros cuadros de diálogo hay que incluir el fichero de cabecera "commctrl.h".

#include <windows.h>
#include <commctrl.h>

mundo AVI "Globe.avi"
reloj AVI "L_Hourglass.avi"

LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
"Dialogo" DIALOG 0, 0, 139, 50
STYLE DS_MODALFRAME | WS_CAPTION | WS_VISIBLE | WS_POPUP
CAPTION "Dialogo animación"
FONT 8, "Ms Shell Dlg"
{
    CONTROL         "reloj", -1, ANIMATE_CLASS, ACS_CENTER | ACS_TRANSPARENT | ACS_AUTOPLAY, 22, 12, 20, 20
    DEFPUSHBUTTON   "Cerrar", IDOK, 79, 16, 50, 14
}

En este ejemplo de fichero de recursos hemos definido dos recursos de tipo AVI, que se insertan desde un fichero externo.

Además, hemos definido un cuadro de diálogo en el que hemos insertado un control de la clase "ANIMATE_CLASS". En el texto se indica el recurso AVI que se mostrará en el control. También se puede usar un identificador entero, si el recurso AVI tiene un identificador entero en lugar de uno de cadena.

Hemos indicado además tres estilos propios de los controles de animación:

  • ASC_CENTER: que centrará la animación en el control.
  • ASC_TRANSPARENT: tomará el color del fondo de la animación AVI como transparente.
  • ASC_AUTOPLAY: reproducirá la animación tan pronto como se cree el control.

Estos son, prácticamente, todos los estilos disponibles para estos controles. Existe otro, ACS_TIMER, que indica que no se debe crear un hilo para reproducir la animación en segundo plano. Pero este es el estilo por defecto desde la aparición de Windows XP.

Para más detalles sobre los estilos de controles de animación ver estilos de animación.

Insertar durante la ejecución

Podemos insertar controles de animación durante la ejecución del programa, directamente en nuestras ventanas.

El proceso es algo diferente al que hemos visto con otros controles. Primero, usaremos la macro Animate_Create. Esta macro crea el control de animación, pero no tiene ningún parámetro que indique su posición o su tamaño. Tan sólo hay que suministrar un manipulador de su ventana padre, un identificador, los estilos y un manipulador de la instancia que crea el control.

Esta función invoca internamente a CreateWindow.

A continuación moveremos el control a la posición deseada, usando la función SetWindowPos. Esta función cambia la posición y tamaño de una ventana, pero tambien cambia su posición relativa en la lista de ventanas, lo que se conoce como orden-z. Ese orden, cuando se trata de controles, es en el que se activa cada uno de ellos cuando se pulsa la tecla TAB. Para establecer la posición del control en ese orden se usa el segundo parámetro, que indica el manipulador de la ventana anterior según ese orden. Además disponemos de algunas banderas.

Una vez colocado el control, abrimos una animación, usando la macro Animate_Open. Puede ser una animación procedente de un recurso, identificada por su cadena identificadora o un identificador entero, creado mediante la macro MAKEINTRESOURCE. En ese caso, se cargará el recurso desde el módulo especificado por el manipulador de instancia indicado en la llamada a Animate_Create.

También puede ser un fichero .avi externo, que se cargará desde esta macro. En ese caso hay que especificar el nombre completo, y el camino, si no está en la misma carpeta que el fichero ejecutable.

Por último, mostramos el control usando la función ShowWindow, indicando el modo SW_SHOW.

           hctrl = Animate_Create(hwnd, IDC_ANIMATE, WS_BORDER | WS_CHILD | ACS_TRANSPARENT | ACS_AUTOPLAY, hInstance);
           SetWindowPos(hctrl, 0, 10, 40, 48, 45, SWP_NOZORDER | SWP_DRAWFRAME);
           Animate_Open(hctrl, "mundo");
           ShowWindow(hctrl, SW_SHOW);

Cuando se destruya la ventana padre del control de animación deberemos cerrar esa animación, de modo que se borre de la memoria y se libere el recurso. Para ello usaremos la macro Animate_Close.

           Animate_Close(GetDlgItem(hwnd, IDC_ANIMATE));

Manipular la animación

El control de animación reproduce una animación, evidentemente. Pero esa animación puede estar incialmente parada, si no se indica el estilo ACS_AUTOPLAY, de modo que tendremos que reproducirla si queremos que no esté parada, o podemos pararla, o mostrar uno de sus fotogramas.

Para estas acciones disponemos de un juego de mensajes o macros (que lo único que hacen es enviar esos mensajes).

Abrir animación

Cuando se crea un control de animación no se le asigna una animación, este proceso hay que realizarlo de forma explícita mediante un mensaje ACM_OPEN o las macros Animate_Open o Animate_OpenEx.

La macro Animate_Open abre un fichero externo en formato avi o un recurso, identificado por una cadena o por un entero. En caso de ser un entero se debe usar la macro MAKEINTRESOURCE(id, 0).

El primer parámetro es un manipulador de ventana del control. Una vez abierta la animación, se muestra el primer fotograma en el control.

La macro Animate_OpenEx es similar, pero se añade un parámetro entre el manipulador de ventana y el identificador del recurso, que identifica el módulo desde el que se cargará el recurso, mediante un manipulador de instancia.

Alternativamente, podemos usar el mensaje {wm:ACM_OPEN), indicando en wParam el manipulador de instancia, o 0 si se quiere usar la instancia desde la que se creó el control, y en lParam el nombre o identificador del recurso o el nombre del fichero.

    Animate_Open(hctrl, "mundo");
    Animate_Open(hctrl, "Gears.avi");
    SendMessage(hctrl, ACM_OPEN, (WPARAM)0, (LPARAM)MAKEINTRESOURCE(101,0));

Reproducir

Para reproducir una animación podemos usar el mensaje ACM_PLAY o la macro Animate_Play.

Es más cómodo usar la macro, a la que deberemos pasar algunos parámetros. El primero, un manipulador del control animación en el que queramos reproducir la animación. El segundo es el número del primer fotograma que queramos reproducir, cero si es el primero. El tercer parámetro es el número del último parámetro a reproducir, -1 si es el último. El cuarto parámetro es el número de veces que queremos que se repita la animación, o -1 si queremos que se repita indefinidamente.

Si optamos por el mensaje, los parámetros son los mismos. Enviaremos el mensaje al control de animación deseado. En wParam indicaremos el número de repeticiones o el valor -1 para repetir indefinidamente. En lParam empaquetaremos el fotograma de inicio y final usando la macro MAKELONG(inicio, final):

    // Macro y mensaje equivalentes:
    Animate_Play(GetDlgItem(hwnd, IDC_ANIMATE), 0, -1, -1);
    SendMessage(GetDlgItem(hwnd, IDC_ANIMATE), ACM_PLAY, (WPARAM) (UINT) -1, (LPARAM) MAKELONG(0, -1));

Detener

Para detener una animación usaremos el mensaje ACM_STOP o la macro Animate_Stop.

Dado que para detener la animación no se necesitan parámetros, las dos formas son igualmente simples, y sólo necesitamos un manipulador de ventana del control:

    // Macro y mensaje equivalentes:
    Animate_Stop(GetDlgItem(hwnd, IDC_ANIMATE));
    SendMessage(GetDlgItem(hwnd, IDC_ANIMATE), ACM_STOP, 0, 0);

Mostrar un fotograma

A veces podemos querer mostrar un único fotograma. Por ejemplo, podemos crear una animación con varios fotogramas que, aunque no se comporten como una película, cada fotograma indique el estado de determinadas condiciones. Para mostrar ese estado nos bastará con mostrar uno de los fotogramas, y nunca necesitaremos reproducir la animación completa. Podríamos hacer lo mismo con varios mapas de bits o con varios iconos, por supuesto.

Para colocar la animación en un fotograma concreto podemos usar la misma macro o mensaje que para reproducirla, pero indicando el mismo fotograma de inicio y final. También disponemos de una macro específica para ello, Animate_Seek, que requiere dos parámetros: un manipulador del control animación, y el número del fotograma. Hay que tener en cuenta que los fotogramas empiezan a numerarse en 0, por lo tanto el tercer fotograma, por ejemplo, sería el número 2. Por ejemplo:

    // Segundo fotograma:
    Animate_Seek(GetDlgItem(hwnd, IDC_ANIMATE), 1);
    // Sexto fotograma:
    SendMessage(GetDlgItem(hwnd, IDC_ANIMATE), ACM_PLAY, (WPARAM) (UINT) 0, (LPARAM) MAKELONG(5, 5));

Verificar reproducción

Según la documentación de Microsoft, podemos comprobar si una animación se está repoduciendo en un control animación usando el mensaje ACM_ISPLAYING o la macro Animate_IsPlaying. Sin embargo, en la versión del API de la que dispongo actualmente, esta opción no está disponible.

En su lugar, podemos usar los mensajes de notificación, que veremos más abajo.

Cerrar animación

Cada animación asignada a un control debe ser liberada o cerrada para liberar recursos o cerrar el fichero que la contiene. Esto se hace mediante la macro Animate_Close o el mensaje ACM_OPEN.

La macro sólo necesita como parámetro un manipulador de ventana del control animación.

El mensaje es el mismo que para abrir la animación, pero usando el valor 0 como identificador de recurso.

    Animate_Close(hctrl);
    SendDlgItemMessage(hwnd, IDC_ANIMATE2, ACM_OPEN, 0, 0);

Mensajes de notificación

Exiten dos mensajes de notificación para los controles de animación. El mensaje de notificación ACN_START se envía a la ventana padre del control cuando la animación empieza a reproducirse. El mensaje ACN_STOP se envía cuando la animación se detiene.

Estos mensajes de notificación se reciben en el parámetro wParam de un mensaje CM_COMMAND. En la palabra de menor peso se envía el identificador del control, en la de mayor peso el mensaje de notificación.

En este ejemplo usamos los mensajes de notificación para mostrar un mensaje en un control estático y para activar o desactivar los botones de marcha y paro de la animación:

        case WM_COMMAND:
           switch(LOWORD(wParam)) {
                case IDC_ANIMATE:
                    switch(HIWORD(wParam)) {
                        case ACN_START:
                            SetWindowText(GetDlgItem(hwnd, IDC_MENSAJE), "Marcha");
                            EnableWindow(GetDlgItem(hwnd, CM_PLAY), FALSE);
                            EnableWindow(GetDlgItem(hwnd, CM_STOP), TRUE);
                            break;
                        case ACN_STOP:
                            SetWindowText(GetDlgItem(hwnd, IDC_MENSAJE), "Parado");
                            EnableWindow(GetDlgItem(hwnd, CM_PLAY), TRUE);
                            EnableWindow(GetDlgItem(hwnd, CM_STOP), FALSE);
                            break;
                    }
                    break;
        ...

Ejemplo 80


  Nombre Fichero Fecha Tamaño Contador Descarga
D Ejemplo 80 win080.zip 2012-05-02 23603 bytes 196