El menú es uno de los componentes más importantes en nuestra ventana. El menú presenta una lista de los servicios que un programa ofrece a un usuario. El usuario ya no tiene que leer el manual incluido con el programa para utilizarlo, ya que puede leerse cuidadosamente el menú para obtener una visión general de las capacidades de un programa particular y comenzar a trabajar con él inmediatamante. Como el menú es una herramienta para obtener el acercamiento del usuario y correr el programa rápidamente, se debería seguir siempre el estandard. Puesto sucintamente, los primeros dos elementos [items] del menú deberían ser Archivo [File] y Editar [Edit] y el último debería ser Ayuda [Help]. Puedes insertar tus propios elementos de menú entre Editar y Ayuda. Si un elemento de menú invoca una caja de diálogo, deberías anexar una ellipsis (...) a la cadena del menú.
El menú es un tipo de recurso. Hay varios tipos de recursos, tales como dialog box, string table, icon, bitmap, menú etc. Los recursos son descritos en un archivo separado llamado archivo de recursos, el cual generalmente tiene extensión .rc. Luego combinas los recursos cion el archivo fuente durante el estadío de enlace. El resultado final es un archivo ejecutable que contiene tanto instrucciones como recursos.
Puedes escribir guiones [scripts] de recursos usando un editor de texto. Estos guiones están compuestos por frases que describen la apariencia y otros atributos de los recursos usados en un programa particular. Aunque puedes escribir guiones de recursos con un editor de texto, esto resulta más bien embarazoso. Una mejor altrenativa es usar un editor de recursos que te permita visualizar con facilidad el diseño de los recursos. Usualmente los paquetes de compiladores como Visual C++, Borland C++, etc, incluyen editores de recursos
Describes
los recursos más o menos así:
Los programadores en lenguaje C pueden reconocer que es similar a la declaración de una estructura. MyMenu sería un nombre de menú seguido por la palabra clave MENU y una lista de menú entre llaves. Alternativamente, si quieres puedes usar BEGIN y END en vez de las llaves. Esta sintaxis es mas portable para los programadores en Pascal.
La lista de menú puede ser un enunciado MENUITEM o POPUP.
El enunciado MENUITEM define una barra de menú que no invoca un menú emetgente [popup] cuando es seleccionado. La sintaxis es como sigue:
Se comienza por la palabra clave MENUITEM seguida por el texto que quieres usar como cadena de texto de la barra de menú. Nota el ampersand (&). Hace que el carácter que le sigue sea subrayado.
Después de la cadena de texto está el ID del elemento de menú. El ID es un número que será usado para identificar el elemento de menú respectivo en el mensaje enviado al procedimiento de ventana cuando el elemento de menú es selccionado. Como tal, cada ID de menú debe ser único entre ellos.
Las opciones son 'opcionales'. Las disponibles son:
Puedes usar una de las opciones de arriba o combinarlas con el operador "or". Sólo recuerda que INACTIVE y GRAYED no pueden ser combinados simultáneamente.
El
enunciado POPUP tiene la siguiente
sintaxis:
El enunciado POPUP define una barra de menú que, cuando es seleccionada, despliega una lista de elementos de menú en una ventana emergente. La lista de menú puede ser una enunciado MENUTIEM o POPUP. Hay un tipo especial de enunciado MENUITEM, MENUITEM SEPARATOR, que dibuja una linea horizontal en la ventana emergente.
El paso siguiente, después de haber terminado con el guión de recursos, es hacer la referencia a él en el programa.
Esto se puede hacer de dos maneras.
En el miembro lpszMenuName de la estructura WNDCLASSEX. Es decir, si tienes un menú llamado "FirstMenu", puedes asignar este menú a tu ventana de la siguiente manera:
.DATA
MenuName db "FirstMenu",0
...........................
...........................
.CODE...........................
mov wc.lpszMenuName, OFFSET MenuName
...........................
.DATA
MenuName db "FirstMenu",0
hMenu HMENU ?
...........................
...........................
.CODE
...........................
invoke LoadMenu, hInst, OFFSET MenuName
mov hMenu, eax
invoke CreateWindowEx,NULL,OFFSET ClsName,\
OFFSET Caption, WS_OVERLAPPEDWINDOW,\
CW_USEDEFAULT,CW_USEDEFAULT,\
CW_USEDEFAULT,CW_USEDEFAULT,\
NULL,\
hMenu,\
hInst,\
NULL\
...........................
Entonces podrías preguntarte, ¿cuál es la diferencia entre estos dos métodos?
Cuando haces referencia al menú en la estructura WNDCLASSEX , el menú llega a ser el menú "por defecto" para la clase de ventana. Todas las ventanas de esa clase tendrán el mismo menú.
Si quieres que cada ventana creada a partir de la misma clase tenga diferente menú, debes elegir la segunda manera. En este caso, cualquier ventana que se le pase un manejador de menú en su función CreateWindowEx tendrá un menú que "reemplazará" el menú por defecto definido en la estructura WNDCLASSEX.
Ahora examinaremos como un menú notifica al procedimiento de ventana cuando el usuario selecciona un elemento del menú.
Cuando el usuario selecciona un elemento del menú, el procedimiento de ventana recibirá un mensaje WM_COMMAND. La palabra baja de wParam contendrá el ID del elemento del menú.
Ahora tienes suficiente información para usar el menú. Vamos a hacerlo.
Ejemplo:
El primer ejemplo muestra cómo crear y usar un menú especificando el nombre del menú en la clase de ventana.
.386
.model flat,stdcall
option casemap:noneWinMain proto :DWORD,:DWORD,:DWORD,:DWORD
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib.data
ClassName db "SimpleWinClass",0
AppName db "Our First Window",0
MenuName db "FirstMenu",0 ; Nombre de nuestro menú en el archivo .RC
Test_string db "You selected Test menu item",0
Hello_string db "Hello, my friend",0
Goodbye_string db "See you again, bye",0.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?.const
IDM_TEST equ 1 ; Menu IDs
IDM_HELLO equ 2
IDM_GOODBYE equ 3
IDM_EXIT equ 4.code
start:
invoke GetModuleHandle, NULL
mov hInstance,eax
invoke GetCommandLine
invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
invoke ExitProcess,eaxWinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
LOCAL hwnd:HWND
mov wc.cbSize,SIZEOF WNDCLASSEX
mov wc.style, CS_HREDRAW or CS_VREDRAW
mov wc.lpfnWndProc, OFFSET WndProc
mov wc.cbClsExtra,NULL
mov wc.cbWndExtra,NULL
push hInst
pop wc.hInstance
mov wc.hbrBackground,COLOR_WINDOW+1
mov wc.lpszMenuName,OFFSET MenuName ; Poner aquí el nombre del Menú
mov wc.lpszClassName,OFFSET ClassName
invoke LoadIcon,NULL,IDI_APPLICATION
mov wc.hIcon,eax
mov wc.hIconSm,eax
invoke LoadCursor,NULL,IDC_ARROW
mov wc.hCursor,eax
invoke RegisterClassEx, addr wc
invoke CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,\
WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\
CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,\
hInst,NULL
mov hwnd,eax
invoke ShowWindow, hwnd,SW_SHOWNORMAL
invoke UpdateWindow, hwnd
.WHILE TRUE
invoke GetMessage, ADDR msg,NULL,0,0
.BREAK .IF (!eax)
invoke DispatchMessage, ADDR msg
.ENDW
mov eax,msg.wParam
ret
WinMain endpWndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
.IF uMsg==WM_DESTROY
invoke PostQuitMessage,NULL
.ELSEIF uMsg==WM_COMMAND
mov eax,wParam
.IF ax==IDM_TEST
invoke MessageBox,NULL,ADDR Test_string,OFFSET AppName,MB_OK
.ELSEIF ax==IDM_HELLO
invoke MessageBox, NULL,ADDR Hello_string, OFFSET AppName,MB_OK
.ELSEIF ax==IDM_GOODBYE
invoke MessageBox,NULL,ADDR Goodbye_string, OFFSET AppName, MB_OK
.ELSE
invoke DestroyWindow,hWnd
.ENDIF
.ELSE
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.ENDIF
xor eax,eax
ret
WndProc endp
end start
**************************************************************************************************************************
#define
IDM_TEST 1
#define
IDM_HELLO 2
#define
IDM_GOODBYE 3
#define
IDM_EXIT 4
FirstMenu
MENU
{
POPUP
"&PopUp"
{
MENUITEM "&Say Hello",IDM_HELLO
MENUITEM "Say &GoodBye", IDM_GOODBYE
MENUITEM SEPARATOR
MENUITEM "E&xit",IDM_EXIT
}
MENUITEM
"&Test", IDM_TEST
}
Vamos a analizar
primero el guión de recursos.
#define IDM_TEST 1 /* equal to IDM_TEST equ 1*/
#define IDM_HELLO 2
#define IDM_GOODBYE 3
#define IDM_EXIT 4
Las líneas de arriba definen los IDs de menú usados por el guión de menú. Puedes asignar cualquier valor al ID siempre que sea único en el menú.
FirstMenu MENU
Declarar tu menú con la palabra clave MENU.
POPUP
"&PopUp"
{
MENUITEM "&Say Hello",IDM_HELLO
MENUITEM "Say &GoodBye", IDM_GOODBYE
MENUITEM SEPARATOR
MENUITEM "E&xit",IDM_EXIT
}
Definir un menú emergente con cuatro elementos, donde el tercer elemento es un separador.
MENUITEM "&Test", IDM_TEST
Definir una barra de menú en el menú principal.
Ahora examinaremos
el código fuente.
MenuName db "FirstMenu",0 ; Nombre del menú en el archivo de recursos.
Test_string db "You selected Test menu item",0
Hello_string db "Hello, my friend",0
Goodbye_string db "See you again, bye",0
MenuName es
el nombre del menú en el archivo de recursos. Nota que puedes definir
más de un menú en el archivo de recursos así que debes
especificar cual usar. Las restantes tres líneas definen la cadena de
texto a ser desplegada en las cajas de mensaje que son invocadas cuando el elemento
de menú apropiado es selecionado por el usuario.
IDM_TEST equ 1 ; Menu IDs
IDM_HELLO equ 2
IDM_GOODBYE equ 3
IDM_EXIT equ 4
Definir IDs de elementos de menú para usar en el procedimiento de ventana. Estos valores DEBEN ser idénticos a los definidos en el archivo de recursos.
.ELSEIF uMsg==WM_COMMAND
mov eax,wParam
.IF ax==IDM_TEST
invoke MessageBox,NULL,ADDR Test_string,OFFSET AppName,MB_OK
.ELSEIF ax==IDM_HELLO
invoke MessageBox, NULL,ADDR Hello_string, OFFSET AppName,MB_OK
.ELSEIF ax==IDM_GOODBYE
invoke MessageBox,NULL,ADDR Goodbye_string, OFFSET AppName, MB_OK
.ELSE
invoke DestroyWindow,hWnd
.ENDIF
En el procedimiento de ventana, procesamos mensajes WM_COMMAND. Cuando el usuario seleccione un elemento de menú, el ID de ese eemento de menú es enviado al procedimiento de ventana en la palabra baja de wParam junto con el mensaje WM_COMMAND. Así que cuando almacenamos el valor de wParam en eax, comparamos el valor en ax con los IDs de menú que definimos previamente y actuamos de acuerdo con ello. En los primeros tres casos, cuando el usuario selecciona los elementos de menú Test, Say Hello, y Say GoodBye, desplegamos una cadena de texto en la caja de mensaje.
Si el usuario selecciona el elemento de menú Exit, llamamos DestroyWindow con el manejador de nuestra ventana como su parámetro que cerrará nuestra ventana.
Como puedes
ver, especificar el nombre del menú en una clase de ventana es muy fácil
y directo. Sin embargo, también puedes
usar un método alternativo para cargar un menú en tu ventana.
No mostraré aquí todo el código fuente. El archivo de recursos
es el mismo en ambos métodos. Hay algunos cambios menores en el archivo
fuente que mostraré abajo.
.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?
hMenu HMENU ? ; handle of our menu
Definir un tipo de variable HMENU para almacenar nuestro manejador de menú.
invoke LoadMenu, hInst, OFFSET MenuName
mov hMenu,eax
INVOKE CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,\
WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\
CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,hMenu,\
hInst,NULL
Antes de llamar
a CreateWindowEx, llamamos a LoadMenu con el manejador de instancia del programa
y un puntero al manejador de nuestro menú. LoadMenu regresa el manejador
de nuestro menú en el archivo de recursos que se pasó a CreateWindowEx.
[Iczelion's Win32 Assembly HomePage]
n u M I T_o r's Programming Page
Este tutorial, original de Iczelion, ha sido traducido por: n u M I T_o r