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.
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.
Versi�n en espa�ol: Federico Pizarro - 2003.