Site hosted by Angelfire.com: Build your free website today!

Tutorial 8: Menú

En este tutorial aprenderemos como incorporar un menú a nuestra ventana.
Bajar el ejemplo 1 y el ejemplo 2.

Teoría:

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:
 

POPUP "&text" [,options]
{
  [menu list]
}
 

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:

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:none

WinMain 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,eax

WinMain 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 endp

WndProc 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
**************************************************************************************************************************

 

 

Menu.rc
**************************************************************************************************************************

#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
}
 

Análisis:

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.


Índice

Siguiente

[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