[ contenidos | #winprog ]

Men�es e Iconos

Ejemplo: menu_one

Esta es s�lo una peque�a secci�n para mostrar como agregar men�es b�sicos en nuestra ventana. Generalmente utilizaremos un recurso men� pre-definido que estar� en un archivo .rc y que luego ser� compilado y enlazado con el archivo .exe. Esto suele depender de cada compilador, los compiladores comerciales cuentan con un editor de recursos que puedes utilizar para crear tus men�es, pero en este ejemplo voy a mostrar el texto del archivo .rc para que puedas agregar men�es manualmente. Generalmente tengo un archivo .h que incluyo dentro de mis archivos .c y .rc .Este archivo contiene los identificadores para controles, items de un men�, etc...

En este ejemplo puedes comenzar con el c�digo de la ventana que vimos en el ejemplo simple_window, e ir agregando el c�digo de esta secci�n.

Primero el archivo .h, generalmente llamado "resource.h"

#define IDR_MYMENU 101
#define IDI_MYICON 201

#define ID_FILE_EXIT 9001
#define ID_STUFF_GO 9002

No hay demasiado aqu�, pero nuestro men� ser� muy simple. Los nombres y valores puedes escogerlos arbitrariamente. Ahora escribamos nuesto archivo .rc

#include "resource.h"

IDR_MYMENU MENU
BEGIN
    POPUP "&File"
    BEGIN
        MENUITEM "E&xit", ID_FILE_EXIT
    END

    POPUP "&Stuff"
    BEGIN
        MENUITEM "&Go", ID_STUFF_GO
        MENUITEM "G&o somewhere else", 0, GRAYED
    END
END

IDI_MYICON ICON "menu_one.ico"

Deber�s agregar el archivo .rc a tu project o makefile dependiendo de que herramienta est�s usando.

Tambi�n debes agregar la linea #include "resource.h" en tu archivo fuente (.c) para que los identificadores de los comandos del men� y los del recurso men� est�n definidos.

La forma mas f�cil de asignar un men� y un �cono a nuestra ventana es especificar ambos cundo registramos la clase ventana, como esto:

    wc.lpszMenuName  = MAKEINTRESOURCE(IDR_MYMENU);
    wc.hIcon  = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_MYICON));
    wc.hIconSm  = (HICON)LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_MYICON), IMAGE_ICON, 16, 16, 0);

Cambia esto y observa que sucede. Tu ventana ahora deber�a tener un men� File y un men� Stuff, con los respectivos items dentro. Esto es asumiendo que nuestro archivo .rc fu� apropiadamente compilado y enlazado en nuestro programa. (nuevamente mira las notas sobre compiladores)

He usado LoadIcon( ) para cargar el �cono grande debido a que es mas simple, sin embargo solo cargar� los �conos en la resoluci�n por default de 32x32, por lo tanto para cargar la imagen peque�a necesitamos usar LoadImage( ) .Observa que los archivos de �conos y de recursos pueden contener m�ltiples im�genes y en este caso, la �nica imagen que hay, contiene los dos tama�os que estamos cargando.

Ejemplo: menu_two

Una alternativa cuando usamos el recurso men� es crear uno en tiempo de ejecuci�n. Esto lleva un poco mas de trabajo de programaci�n, pero agrega mas flexibilidad y a veces es necesario.

Tambi�n podemos usar �conos que no est�n almacenados como recursos, podr�amos elegir almacenar nuestros �conos en un archivo separado y cargarlos en tiempo de ejecuci�n. Esto tambi�n podr�a darnos la opci�n de permitir al usuario seleccionar un �cono con los dialogos comunes (common dialogs) que veremos luego, o alguna otra opci�n que tenga el mismo efecto.

Comenzaremos nuevamente con el ejemplo simple_window sin agregar el archivo .h y el archivo .rc. Ahora manejaremos el mensaje WM_CREATE y agregaremos un men� a nuestra ventana.

#define ID_FILE_EXIT 9001
#define ID_STUFF_GO 9002

Por esta vez pon estos dos ID's en el comienzo de tu archivo .c, debajo de los #include's. A continuaci�n agregaremos el siguiente c�digo dentro de nuestro manejador del mensaje WM_CREATE.

    case WM_CREATE:
    {
        HMENU hMenu, hSubMenu;
        HICON hIcon, hIconSm;

        hMenu = CreateMenu();

        hSubMenu = CreatePopupMenu();
        AppendMenu(hSubMenu, MF_STRING, ID_FILE_EXIT, "E&xit");
        AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT)hSubMenu, "&File");

        hSubMenu = CreatePopupMenu();
        AppendMenu(hSubMenu, MF_STRING, ID_STUFF_GO, "&Go");
        AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT)hSubMenu, "&Stuff");

        SetMenu(hwnd, hMenu);


        hIcon = LoadImage(NULL, "menu_two.ico", IMAGE_ICON, 32, 32, LR_LOADFROMFILE);
        if(hIcon)
            SendMessage(hwnd, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
        else
            MessageBox(hwnd, "Could not load large icon!", "Error", MB_OK | MB_ICONERROR);

        hIconSm = LoadImage(NULL, "menu_two.ico", IMAGE_ICON, 16, 16, LR_LOADFROMFILE);
        if(hIconSm)
            SendMessage(hwnd, WM_SETICON, ICON_SMALL, (LPARAM)hIconSm);
        else
            MessageBox(hwnd, "Could not load small icon!", "Error", MB_OK | MB_ICONERROR);
    }
    break;

Este crea un men� igual al que hemos definido en nuestro archivo de recursos y se lo asigna a nuestra ventana. Un men� que es asignado a una ventana es autom�ticamente removido cuando el programa termina, por lo tanto no necesitamos preocuparnos por librarnos de �l cuando el programa finalice.

El c�digo para los �conos es muy simple, llamamos a LoadImage( ) dos veces: primero para cargar el �cono de 16x16 y luego el �cono de 32x32. No podemos usar LoadIcon( ) en todo momento debido a que �ste s�lo carga recursos, no archivos. En el par�metro del handle a la instancia, especificamos el valor NULL debido a que no estamos cargando un recurso de nuestro m�dulo y en vez de pasar el ID de un recurso, pasamos el nombre del archivo donde se encuentra el �cono. Por �ltimo, pasamos el flag LR_LOADFROMFILE para indicar que la funci�n trate el string que le pasamos como un nombre de archivo y no como el nombre de un recurso.

Si esto tiene �xito, asignamos el handle al icono utilizando WM_SETICON y si falla mostramos un mensaje dici�ndonos que algo ha ocurrido.

NOTA: La llamada a LoadImage( ) fallar� si el archivo con el �cono no se encuentra en el directorio de trabajo del programa. Si estas usando VC++ y ejecutas el programa desde el IDE, el directorio de trabajo ser� aquel donde se encuentre almacenado el proyecto. Sin embargo si ejecutas desde el Debug, desde el explorer o la linea de comando, entonces necesitar�s copiar el archivo con los iconos dentro de dicho directorio para que el programa pueda encontrarlo. Si todo esto falla, especifica la ruta completa.

Ok, ahora que tenemos un men� necesitamos que haga algo. Esto es muy simple, todo lo que necesitmaos hacer es manejar el mensaje WM_COMMAND. Adem�s necesitaremos chequear que comando estamos recibiendo para poder actuar acordemente. Ahora nuestro WinProc( ) deber�a lucir como este:

LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
    switch(Message)
    {
        case WM_CREATE:
        {
            HMENU hMenu, hSubMenu;

            hMenu = CreateMenu();

            hSubMenu = CreatePopupMenu();
            AppendMenu(hSubMenu, MF_STRING, ID_FILE_EXIT, "E&xit");
            AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT)hSubMenu, "&File");

            hSubMenu = CreatePopupMenu();
            AppendMenu(hSubMenu, MF_STRING, ID_STUFF_GO, "&Go");
            AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT)hSubMenu, "&Stuff");

            SetMenu(hwnd, hMenu);


            hIcon = LoadImage(NULL, "menu_two.ico", IMAGE_ICON, 32, 32, LR_LOADFROMFILE);
            if(hIcon)
                SendMessage(hwnd, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
            else
                MessageBox(hwnd, "Could not load large icon!", "Error", MB_OK | MB_ICONERROR);

            hIconSm = LoadImage(NULL, "menu_two.ico", IMAGE_ICON, 16, 16, LR_LOADFROMFILE);
            if(hIconSm)
                SendMessage(hwnd, WM_SETICON, ICON_SMALL, (LPARAM)hIconSm);
            else
                MessageBox(hwnd, "Could not load small icon!", "Error", MB_OK | MB_ICONERROR);
        }
        break;
        case WM_COMMAND:
            switch(LOWORD(wParam))
            {
                case ID_FILE_EXIT:

                break;
                case ID_STUFF_GO:

                break;
            }
        break;
        case WM_CLOSE:
            DestroyWindow(hwnd);
        break;
        case WM_DESTROY:
            PostQuitMessage(0);
        break;
        default:
            return DefWindowProc(hwnd, Message, wParam, lParam);
    }
    return 0;
}

Como podemos ver, hemos procesado el mensaje WM_COMMAND y �ste tiene adentro otro switch( ). Este switch( ) distingue usando el valor de la palabra inferior de wparam: si es el caso de un mensaje WM_COMMAND, entonces contiene el ID del control o men� que envi� el mensaje.

Obviamente queremos que el item Exit cierre el programa. Por lo tanto en el procesador del mensaje WM_COMMAND, ID_FILE_EXIT usaremos el siguiente c�digo:

PostMessage(hwnd, WM_CLOSE, 0, 0);

El procesador del mensaje WM_COMMAND deber�a lucir como este:

    case WM_COMMAND:
        switch(LOWORD(wParam))
        {
            case ID_FILE_EXIT:
                PostMessage(hwnd, WM_CLOSE, 0, 0);
            break;
            case ID_STUFF_GO:

            break;
        }
    break;

Te dejo como tarea realizar el otro comando ID_STUFF_GO para que haga algo.

El �cono del Programa

Habr�s notado que ahora el archivo menu_one.exe muestra como �cono el �cono que agregamos como recurso, mientras que menu_two.exe no lo hace debido a que estamos cargando un archivo externo. El Windows Explorer simplemente muestra el primer icono (ordenados numericamente por ID) y debido a que s�lo tenemos un �cono, este es el que ser� mostrado. Si quieres asegurarte de que un cierto �cono sea mostrado por el Windows Explorer simplemente agrega este como recurso y as�gnale un numero bajo de ID... como 1. De aqu� en m�s, en tu programa, ya no necesitas referirte al archivo que contiene al �cono y puedes asignarle a cada ventana un �cono diferente si lo deseas.
Copyright © 1998-2003, Brook Miles (theForger). All rights reserved.

Versi�n en espa�ol: Federico Pizarro - 2003.