Capítulo 5 Menús 1

Ahora que ya sabemos hacer el esqueleto de una aplicación Windows, veamos el primer medio para comunicarnos con ella.

Supongo que todos sabemos lo que es un menú: se trata de una ventana un tanto especial, del tipo pop-up, que contiene una lista de comandos u opciones entre las cuales el usuario puede elegir.

Cuando se usan en una aplicación, normalmente se agrupan varios menús bajo una barra horizontal, (que no es otra cosa que un menú), dividida en varias zonas o ítems.

Cada ítem de un menú, (salvo los separadores y aquellos que despliegan nuevos menús), tiene asociado un identificador. El valor de ese identificador se usará por la aplicación para saber qué opción se activó por el usuario, y decidir las acciones a tomar en consecuencia.

Existen varias formas de añadir un menú a una ventana, veremos cada una de ellas por separado.

También es posible desactivar o inhibir algunas opciones para que no estén disponibles para el usuario.

Usando las funciones para inserción ítem a ítem

Este es el sistema más rudimentario, pero como ya veremos en el futuro, en ocasiones puede ser muy útil. Empezaremos viendo este sistema porque ilustra mucho mejor la estructura de los menús.

Tomemos el ejemplo del capítulo anterior y definamos algunas constantes:

#define CM_PRUEBA 100
#define CM_SALIR  101

Y añadamos la declaración de una función en la zona de prototipos:

void InsertarMenu(HWND); 

Al final del programa añadimos la definición de esta función:

void InsertarMenu(HWND hWnd)
{
   HMENU hMenu1, hMenu2;
   
   hMenu1 = CreateMenu(); /* Manipulador de la barra de menú */
   hMenu2 = CreateMenu(); /* Manipulador para el primer menú pop-up */
   AppendMenu(hMenu2, MF_STRING, CM_PRUEBA, "&Prueba"); /* 1º ítem */
   AppendMenu(hMenu2, MF_SEPARATOR, 0, NULL);           /* 2º ítem (separador) */
   AppendMenu(hMenu2, MF_STRING, CM_SALIR, "&Salir");   /* 3º ítem */
   /* Inserción del menú pop-up */
   AppendMenu(hMenu1, MF_STRING | MF_POPUP, (UINT)hMenu2, "&Principal"); 
   SetMenu (hWnd, hMenu1);  /* Asigna el menú a la ventana hWnd */
}

Y por último, sólo nos queda llamar a nuestra función, insertaremos ésta llamada justo antes de visualizar la ventana.

...
   InsertarMenu(hWnd);
   ShowWindow(hWnd, SW_SHOWDEFAULT);
...

Veamos cómo funciona "InsertarMenu".

La primera novedad son las variables del tipo HMENU. HMENU es un tipo de manipulador especial para menús. Necesitamos dos variables de este tipo, una para manipular la barra de menú, hMenu1. La otra para manipular cada uno de los menús pop-up, en este caso sólo uno, hMenu2.

De momento haremos una barra de menú con un único elemento que será un menú pop-up. Después veremos como implementar menús más complejos.

Para crear un menú usaremos la función CreateMenu, que crea un menú vacío.

Para ir añadiendo ítems a cada menú usaremos la función AppendMenu. Esta función tiene varios argumentos:

El primero es el menú donde queremos insertar el nuevo ítem.

El segundo son las opciones o atributos del nuevo ítem, por ejemplo MF_STRING, indica que se trata de un ítem de tipo texto, MF_SEPARATOR, es un ítem separador y MF_POPUP, indica que se trata de un menú que desplegará un nuevo menú pop-up.

El siguiente parámetro puede tener distintos significados:

  • Puede ser un identificador de comando, este identificador se usará para comunicar a la aplicación si el usuario selecionó un determinado ítem.
  • Un manipulador de menú, si el ítem tiene el flag MF_POPUP, en este caso hay que hacer un casting a (UINT).
  • O también puede ser cero, si se trata de un separador.

El último parámetro es el texto del ítem, cuando se ha especificado el flag MF_STRING, más adelante veremos que los ítems pueden ser también bitmaps. Normalmente se trata de una cadena de texto. Pero hay una peculiaridad interesante, para indicar la tecla que activa un determinado ítem de un menú se muestra la letra correspondiente subrayada. Esto se consigue insertando un '&' justo antes de la letra que se quiere usar como atajo, por ejemplo, en el ítem "&Prueba" esta letra será la 'P'.

Por último SetMenu, asigna un menú a una ventana determinada. El primer parámetro es el manipulador de la ventana, y el segundo el del menú.

Prueba estas funciones y juega un rato con ellas. A continuación veremos cómo hacer que nuestra aplicación responda a los mensajes del menú.

Comentarios de los usuarios (9)

Gregor
2015-08-05 22:14:04

ESTUVE PORBANDO EL PROGRAMA DE EEJEMPLO DE LOS MENUS

Y ME CONSEGUI CON ESTE ERROR NO SE POR QUE RAZON EN LAS LINEAS:


   ...
    
   InsertarMenu(hWnd);
   ShowWindow(hWnd, SW_SHOWDEFAULT);

   ....

EL ENTORNO QUE USO ES DEV C/C++ Y AL CAMBIAR (hWnd) a (hwnd) corrio el programa sin problema.

Johnson
2015-08-05 22:28:09

Me gustaria saber si el nombre para un manipulador de ventana debe ser siempre " hwnd " es decir si se incializa la variable de tipo HWND esta debe llamarse hwnd siempre?. No podria colocarse otro nombre como:

   ....

     HWND manipventana;

    ....

para que no se preste a confusiones? Muchas gracias agradeceria me respondieran.

Gregor
2015-08-05 22:39:30

Otra duda que tengo es que, si se va a agregar un nuevo menú debe agregarse otra función en el área de prototipos? ejemplo:

void InsertarMenu2(HWND)

void InsertarMenu3(HWND)

    .
    .
    .

void InsertarMenuN(HWND)

Steven R. Davidson
2015-08-06 07:16:29

Hola Grégor,

En el código escrito en el capítulo 5, sí tenemos escrito 'hWnd'. Sin embargo, en el ejemplo completo en el fichero "ZIP", tenemos escrito correctamente, 'hwnd', en el código fuente.

Steven

Steven R. Davidson
2015-08-06 07:31:09

Hola Johnson,

Como se trata de una variable, puede tener cualquier nombre único que desees.

Steven

Steven R. Davidson
2015-08-06 07:54:40

Hola Grégor,

La función, 'InsertarMenu()', sirve de auxiliar para organizarnos, pero no es tan necesaria.

Si quieres crear otro menú en la misma barra de menú previamente creada, entonces sólo tienes que invocar 'CreateMenu()'; y luego, 'AppendMenu()'. Si quieres crear otro menú principal entonces, tendrías que realizar el proceso detallado en el ejemplo, que finaliza con 'SetMenu()', para establecer la creación de todos los menúes y submenúes.

Repito que 'InsertarMenu()' sirve para ayudarnos durante el diseño técnico de la programación. Si estás pensando en diseñar dos menúes principales, para usar uno u otro según convenga en algún momento dado, entonces es una buena idea crear dos funciones al estilo de 'InsertarMenu()' para organizarte mejor.

Espero haber aclarado la duda.

Steven

Gregor
2015-08-06 20:42:41

Hola buenas tardes soy yo, de nuevo, muchas gracias por haber respondido he estado apenas tres dias en esto y lo entiendo a la perfeccion con sus explicaciones. Excelente trabajo de verdad. Me trae de nuevo el hecho de que, he realizado lo que me habéis dicho. He creado un InsertarMenuDos(HWND). La parte donde se muestra me ha quedado:


     ....

     InsertarMenuDos(hwnd);
     InsertarMenu(hwnd);
     ShowWindow(hwnd,SW_SHOWDEFAULT);

     ....

lo que ocurre es que ahora solo muestra el InsertarMenuDos(hwnd), y a mi me gustaria que los mostrara los dos simultaneamente. Que debo hacer en este caso?

Steven R. Davidson
2015-08-07 20:21:51

Hola Grégor,

Como no sabemos la implementación de 'InsertarMenuDos()', no puedo ser muy preciso en mi respuesta. Si esta función es casi una copia de 'InsertarMenu()', entonces cada función crea su propia barra de menúes, junto con cada submenú. Como sólo puede haber una barra de menúes activa, tienes que elegir cuál quieres.

Si quieres agregar otro submenú en la barra (principal) de menú de la ventana, entonces necesitas acceder a ese manipulador de menú que representa la barra de menú para poder agregar otro (sub)menú. Podrías reescribir 'InsertarMenu()' para retornar ese HMENU; por ejemplo,

HMENU InsertarMenu( HWND hwnd )
{
  HMENU hMenu1, hMenu2;
  ...
  return hMenu1;
}

Hay que cambiar 'InsertarMenuDos()' para aceptar este 'HMENU'; por ejemplo,

HMENU InsertarMenuDos( HWND hwnd, HMENU hBarraMenu )
{
  ...
  return hBarraMenu;
}

Ahora puedes agregar el nuevo (sub)menú a 'hBarraMenu'. Y luego en 'WinMain()', puedes invocar cada función en cadena; esto es,

InsertarMenuDos( hwnd, InsertarMenu(hwnd) );
ShowWindow( hwnd, SW_SHOWDEFAULT );

Espero que esto te oriente.

Steven

fernando
2016-03-17 21:32:39

en el ejemplo 5 me da error al compilar(Dev c++)en la instruución siguiente:

DialogBoxParam(hInstance,"DialogoPrueba",hwnd,DlgProc,(LPARAM)&Datos);

[Error] invalid conversión from 'BOOL(*)(HWND,UINT,WPARAM,LPARAM){aka int(*)(HWND_*,unsigned int,long long unsigned int,long long int)}' to 'DLGPROC{aka long long int,(*))HWND_*,unsigned int,long long unsigned int,long long int)}' [-fpermissive

no comprendo el error