Apunte de Visual C++
Por: Demian Panello demianpanello@yahoo.com.ar
Capítulo VIII
Indice rápido del capítulo 8:
Los menús, (o menúes), en las aplicaciones Windows son objetos, prácticamente omnipresentes. El Application Wizard que uno usa para generar la base de la aplicación permite agregarles a los diálogos menús, siempre y cuando la elección nuestra haya sido desarrollar un proyecto SDI o MDI, AppWizard no genera menús para los proyectos basados en diálogos.
Pero aquí haremos las cosas más rebuscadas, crearemos un proyecto basado en diálogo, con el nombre de menu y a "manopla" crearemos el menú.
El programa constará de un menú "Principal" con los submenús "Uno", "Dos", "Tres", una separación y "Salir". Además estará la opción "Ayuda" con el submenú "Acerca de..." que nos permitirá acceder al diálogo Acerca de que AppWizard nos generó.
Cuando se seleccione el ítem "Uno" se incrementará en 1 un campo de una estructura pública, si se selecciona "Dos" se incrementará en 2 otro campo de la misma estructura, si se selecciona "Tres" se incrementará en 3 y cuando se pulse el submenú "Sumar" se mostrará un mensaje con el resultado de la suma de los tres campos de la estructura.
Primero crearemos el menú, que se trata de un recurso.
Agregue los siguientes elementos al menú "Principal":

Solamente esto hace falta para crear el aspecto visual del menú; ahora hay que asociarlo al diálogo que lo presentará...¡algo sumamente complejo!.
Seleccione con dos click el diálogo principal y luego diríjase a sus propiedades. Como propiedades del diálogo, además de su ID y CAPTION, verá una lista desplegable que dice MENU, pues allí seleccione IDR_MENU1 que es el menú que acabamos de crear. ¡Listo el pollo cocinada la gallina, tenemos nuestra aplicación con menú y todo!. Al fin de cuentas no es para tanto VC++, ¿no?.
Puede probar el programa presionando Ctrl + F5, verá que el diálogo tiene ahora el menú que creamos, pero hay un problema, todavía no hace nada.
Habíamos quedado que de acuerdo al ítem seleccionado del menú Principal se irían incrementando campos de una estructura pública del diálogo. Bueno, lo primero que vamos a hacer es crear la estructura:
// CMenuDlg dialog
class CMenuDlg : public CDialog
{
// Construction
public:
struct numeros (1) //Se define la estructura numeros
{
int m_uno, m_dos, m_tres; (2)
}n; (3) //variable n de tipo numeros
CMenuDlg(CWnd* pParent = NULL); // standard constructor
.........
Se ha abreviado el código especificando sólo las líneas agregadas.
En (1) se declara una estructura con el nombre de numeros. Que contiene tres campos enteros, muno, m_dos y m_tres (2). Y en tres de declara la variable n de tipo numeros.
Ahora le daremos valores iniciales a los campos y el lugar indicado para esto es el constructor de la clase.
Pulse dos veces sobre el elemento CMenuDlg::CMenuDlg(CWnd* pParent =NULL) de la clase CMenuDlg.
Agregue las últimas tres líneas:
// CMenuDlg dialog
CMenuDlg::CMenuDlg(CWnd* pParent /*=NULL*/)
: CDialog(CMenuDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CMenuDlg)
// NOTE: the ClassWizard will add member initialization here
//}}AFX_DATA_INIT
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
n.m_uno =0; (1)
n.m_dos =0; (2)
n.m_tres =0; (3)
}
(1), (2) y (3) son las líneas agregadas, las que inicializan en 0 los campos de la estructura.
Finalmente hay que escribir el código por cada evento del menú, o sea por pulsar cada elemento:
void CMenuDlg::OnMuno()
{
// TODO: Add your command handler code here
n.m_uno = n.m_uno +1; (1)
}
Simplemente se incrementa en 1 el campo m_uno de la estructura.
Realice los mismos pasos anteriores para crear las funciones OnMdos(), OnMtres(), OnMsumar(), OnMsalir y OnMacerca(). Cuyos códigos son:
void CMenuDlg::OnMdos()
{
// TODO: Add your command handler code here
n.m_dos = n.m_dos +2;
}
void CMenuDlg::OnMtres()
{
// TODO: Add your command handler code here
n.m_tres = n.m_tres +3;
}
void CMenuDlg::OnMsumar()
{
// TODO: Add your command handler code here
CString strSuma;
strSuma.Format ("%s%i", strSuma, n.m_uno + n.m_dos +n.m_tres );
MessageBox(strSuma);
n.m_uno =0;
n.m_dos =0;
n.m_tres =0;
}
void CMenuDlg::OnMsalir()
{
// TODO: Add your command handler code here
EndDialog(IDOK);
}
void CMenuDlg::OnMacerca()
{
// TODO: Add your command handler code here
CAboutDlg dlgAcerca;
dlgAcerca.DoModal ();
}
Resumiendo:
Descargar archivos fuentes del ejemplo: menu.zip
ICONOS Y MAPAS DE BITS:
En Visual C++, además diseñar y escribir el código de una aplicación, puede crear los icono y archivos BMP (mapas de bits), que la aplicación utilice. Tanto los icono como los mapas de bits son recursos.
Crearemos una aplicación que manipulará dos controles Picture, que permitirán visualizar los iconos y archivos BMP que crearemos.
El diálogo debería quedar más o menos así:

Bien, ahora hay que diseñar los iconos e imágenes. Vaya a la solapa ResourceView de la ventana WorkSpace y sobre la carpeta ICON pulse con el botón derecho del mouse.
Una vez creados los iconos y los bitmaps, regresamos al díalogo para asociarle variables a los controles picture:
De esta forma se crearon dos variables de tipo CStatic relacionadas a los controles de imagen.
El programa consistirá en pulsar el icono o imagen que se esté visualizando y cambiará al otro icono o imagen. Tendremos que crear dos variables globales para poder saber que icono y/o que imagen se está visualizando al momento de hacer click sobre el control.
MostrarIcono = 1;
MostrarBmp = 1;
Una tercer variable global necesitaremos cuando carguemos los BMP. Entonces, aprovechamos y la creamos ahora.
Bueno, ahora hay que conectar por código los iconos e imágenes con sus respectivos controles.
Al iniciar la aplicación se cargará por defecto en uno de los controles Picture el icono IDI_ICON1 (el rectángulo) y en el otro la imagen IDB_BITMAP1 (el icono de RIVER).
Escriba entonces en el mensaje OnInitDialog(), las siguientes líneas numeradas:
BOOL CImagenesDlg::OnInitDialog()
{
CDialog::OnInitDialog();
HICON hIcono; (1)
//Tomo un puntero a la clase CWinApp
CWinApp* pApp = AfxGetApp(); (2)
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// TODO: Add extra initialization here
//Cargo la imagen por defecto del control imagen
VERIFY(m_bmp.LoadBitmap (IDB_BITMAP1)); (3)
m_imgBmp.SetBitmap (m_bmp); (4)
//Cargo el icono por defecto del otro control imagen
hIcono= pApp->LoadIcon (IDI_ICON1); (5)
m_imgIcono.SetIcon (hIcono); (6)
return TRUE; // return TRUE unless you set the focus to a control
}
En (1) se declara una variable de tipo HICON, esto es un manejador para el objeto icono, que será necesario luego.
En (2) se obtiene un puntero a la aplicación, portadora de la función para cargar un icono.
Primero cargamos el BitMap de River (IDB_BITMAP1) por medio de la función LoadBitmap() (3) del objeto CBitmap, se lo asignamos al manejador. Luego en (4) con la función SetBitmap() del control Picture le asignamos la imagen recién cargada.
En (5) y en (6) se cargar el icono por defecto y se lo asigna al control.
Listo, si ejecuta la aplicación verá que ahora los controles Picture vienen con una imagen por defecto.
Falta escribir el código que permita al pulsar el botón izquierdo del mouse sobre las imágenes cambiar los iconos y BitMaps. Entonces pulse dos veces sobre la primer imagen, (IDC_ICONOS), se creará el mensaje OnIconos(), allí escriba:
void CImagenesDlg::OnIconos()
{
// TODO: Add your control notification handler code here
HICON hIcono;
CWinApp* pApp = AfxGetApp();
if (MostrarIcono==1)
{
hIcono= pApp->LoadIcon (IDI_ICON2);
m_imgIcono.SetIcon (hIcono);
MostrarIcono=2;
}
else
{
hIcono= pApp->LoadIcon (IDI_ICON1);
m_imgIcono.SetIcon (hIcono);
MostrarIcono=1;
}
}
Básicamente se realiza el mismo procedimiento que en InitDialog() con la salvedad que aquí se verifica si la variable global MostrarIcono tiene un 1, porque de ser así se cargar el otro icono y se establece esa variable a 2. En caso de ser 2 se carga la primera y se establece MostrarIcono a 1.
Ahora pulse dos veces sobre el otro control Picture para generar el mensaje OnBmp(), allí escriba:
void CImagenesDlg::OnBmp()
{
// TODO: Add your control notification handler code here
m_bmp.DeleteObject (); //Elimino una referencia anterior
if (MostrarBmp==1)
{
VERIFY(m_bmp.LoadBitmap (IDB_BITMAP2));
m_imgBmp.SetBitmap (m_bmp);
MostrarBmp=2;
}
else
{
VERIFY(m_bmp.LoadBitmap (IDB_BITMAP1));
m_imgBmp.SetBitmap (m_bmp);
MostrarBmp=1;
}
}
También aquí es el mismo procedimiento que en InitDialog(), pero con dos salvedades.
Una, es la de verificar que BMP se está mostrando, esto se hace en la sentencia IF y la otra salvedad es que lo primero que se hace es eliminar la referencia anterior de la variable CBitmap m_bmp. La misma variable tiene el método DeleteObject() que se encarga de esto.
Resumiendo: