Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Ceballos: Visual C++. Aplicaciones para Win32, 2ed
Ceballos: Visual C++. Aplicaciones para Win32, 2ed
http://www.fjceballos.es
Visual C++. Aplicaciones para Win32 (2ª edición).
© Fco. Javier Ceballos Sierra
© De la edición: RA-MA 1999
MARCAS COMERCIALES: Las designaciones utilizadas por las empresas para distinguir
sus productos suelen ser marcas registradas. RA-MA ha intentado a lo largo de este libro distinguir
las marcas comerciales de los términos descriptivos, siguiendo el estilo de mayúsculas que utiliza
el fabricante, sin intención de infringir la marca y sólo en beneficio del propietario de la misma.
Editado por:
RA-MA Editorial
Ctra. Canillas, 144
28043 MADRID
Teléfono: (91) 381 03 00
Telefax: (91) 381 03 72
Correo electrónico: [email protected]
Servidor Web: http://www.ra-ma.es
ISBN: 84-7897-302-8
Depósito Legal: M-43539-1997
Autoedición: Fco. Javier Ceballos
Filmación e impresión: Albadalejo, S.L.
Impreso en España
Primera impresión: Febrero 1999
ÍNDICE
PRÓLOGO.............................................................................................................. XIX
PROGRAMANDO EN WINDOWS................................................................... 32
EL LENGUAJE DE WINDOWS........................................................................ 34
Mensajes......................................................................................................... 34
Independencia de hardware ............................................................................ 34
VIII VISUAL C++. APLICACIONES PARA WIN32.
Posteriormente, en abril de 1993, Microsoft presentó OLE 2.0. Con OLE 2.0
los desarrolladores podían implementar objetos que interactuaban unos con otros
sin importar cómo actuaba cada objeto específicamente. Más aún, podían utilizar-
se aplicaciones enteras como componentes, lo que hacía más fácil la integración
de aplicaciones y como consecuencia la combinación de información. No obstan-
te, hasta que Visual C++ 1.5 y la biblioteca MFC 2.5 no estuvieron disponibles,
no fue cómodo desarrollar aplicaciones OLE 2.0. A partir de este momento, los
desarrolladores tuvieron asistentes para crear objetos OLE cliente, servidor o con-
tenedor con pocos esfuerzos. Asimismo, esta biblioteca también incluía soporte
XX VISUAL C++. APLICACIONES PARA WIN32.
Visual C++ 6.0 proporciona varias formas de trabajo con bases de datos. Por
ejemplo, utilizando la biblioteca de clases MFC, podemos recurrir a las clases
DAO (Data Access Objects - objetos de acceso a datos) o a las clases ODBC
(Open DataBase Connectivity - conectividad abierta de bases de datos). Pero las
tecnologías actuales de acceso a datos tienen que satisfacer los nuevos escenarios
demandados por las empresas, tales como los sistemas de información basados en
la Web. En esta línea, Microsoft ofrece utilizar OLE DB como un proveedor de
datos y objetos ADO (ActiveX Data Objects - objetos ActiveX para acceso a da-
tos), como tecnología de acceso a datos.
permanece inactivo hasta que se produzca el evento que lo activa (por ejemplo, un
clic del ratón).
Este libro, escrito con la versión 5.0 y actualizado con la versión 6.0 de Vi-
sual C++, está organizado en dos partes: programación básica con las MFC y
técnicas avanzadas. La primera parte está pensada para que usted aprenda a inte-
grar cualquier objeto Windows en una aplicación, lo que le permitirá desarrollar
sus primeras aplicaciones. Y para abordar esta primera parte, ¿qué necesita saber?
Pues necesita saber programación orientada a objetos. La segunda parte comple-
menta la primera y trata temas más específicos, como ficheros de datos, barras de
control, gráficos, el ratón, aplicaciones MDI, mapas de bits, impresión y presenta-
ción preliminar y acceso a una base de datos. Ambas partes se han documentando
con abundantes ejemplos resueltos, lo que le facilitará el aprendizaje. Cuando
complete ambas partes, todavía no sabrá todo lo que es posible hacer con Visual
C++, pero sí lo suficiente como para abordar muchas de las aplicaciones que nor-
malmente ve en el entorno que le rodea.
Agradecimientos
He recibido ideas y sugerencias de algunas personas durante la preparación de la
primera edición de este libro, entre las que se encuentran, como no, mis alumnos,
que con su interés por aprender me hacen reflexionar sobre objetivos que a prime-
ra vista parecen inalcanzables, pero que una vez logrados sirven para que todos
aprendamos; a todos ellos les estoy francamente agradecidos.
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
Orden
Separador
Menú Submenú
Cuando el usuario haga clic en un título de un menú, se desplegará una lista
visualizando los elementos que contiene el menú. Si no contiene elementos, se
ejecutará directamente una acción. Los elementos de un menú pueden ser órdenes,
188 VISUAL C++. APLICACIONES PARA WIN32
DISEÑO DE UN MENÚ
Para diseñar un menú, utilizaremos el editor de menús del entorno de desarrollo
de Visual C++ que se muestra en la figura siguiente:
1. Abrir el editor de menús. Para ello, abra el proyecto con el que va a trabajar y
haga clic en la pestaña ResourceView de la ventana Workspace. Seleccione el
tipo de recurso Menu, despliegue la lista de recursos de este tipo y haga doble
clic en el identificador del menú. Si lo que desea es editar un nuevo menú,
ejecute la orden Resource del menú Insert, seleccione el tipo de recurso Menu
y haga clic en el botón New, o bien haga clic en el botón New Menu de la ba-
rra de herramientas Resource, si está visible
6. Cerrar el editor de menús. Una vez que haya finalizado el diseño, ejecute la
orden Save del menú File y cierre la ventana correspondiente al menú. La
plantilla del menú se guarda en el fichero de recursos.
Para borrar un menú o uno de sus elementos (orden, submenú o separador) se-
lecciónelo y pulse la tecla Borrar (Del).
Utilizando el ratón y las teclas Ctrl o Shift (Mayús), puede copiar o mover los
menús o sus elementos de una posición a otra. Incluso, si tiene varios recursos de
este tipo, puede arrastrar menús o elementos individuales, de una ventana a otra.
190 VISUAL C++. APLICACIONES PARA WIN32
Como ejercicio, cree una aplicación SDI denominada Menus con una barra de
menús igual a la mostrada en las figuras anteriores. Después, guarde la aplicación,
compílela y ejecútela. Compruebe cómo haciendo clic sobre cualquier menú éste
se despliega mostrando sus elementos.
Para definir cómo debe responder cada orden de un menú al evento clic, hay
que escribir una función para cada una de ellas. Las órdenes de un menú sólo res-
ponden al evento clic; cuando un usuario selecciona una orden de un menú, se en-
vía un mensaje WM_COMMAND.
Para escribir una función para una orden de un menú ejecute ClassWizard y
en la ventana MFC ClassWizard seleccione la clase que va a manipular esta or-
den, en nuestro caso CMenusView, el identificador del menú, ID_EDIT_COPY, el
mensaje que desea manipular, COMMAND, y haga clic en el botón Add Function.
ClassWizard añadirá al fichero .cpp y le mostrará el esqueleto del controlador pa-
ra esa orden, lo que le permitirá escribir el código que tiene que ejecutarse en res-
puesta a ese mensaje. Por ejemplo, el controlador para la orden Copiar del menú
Edición de nuestra aplicación Menus, sería así:
void CMenusView::OnEditCopy()
{
// Añada el código del controlador aquí
}
Propiedades de un menú
Las propiedades que pueden seleccionarse en la ventana de diseño de menús son
ID, Separator, Checked, Pop-up, Grayed, Inactive, Help, Break, Prompt y Right-
to-left order and alignment.
propiedad (se pone a valor true) aparece una marca b a la izquierda del elemento
del menú.
Esta propiedad, igual que la anterior (Help) afecta al aspecto del menú sólo
durante la ejecución, no durante el diseño.
Observe cómo cada menú o submenú va encabezado por la palabra clave PO-
PUP y los elementos de cada uno de ellos por la palabra clave MENUITEM. Así
mismo puede observar que las órdenes Cortar y Copiar aparecerán inicialmente
desactivadas y en gris (GRAYED) y que el menú Ayuda aparecerá a la derecha en
la barra de menús (HELP).
Un editor de textos parece a simple vista una aplicación muy complicada, pe-
ro ahora veremos que no es así. Visual C++ tiene mecanismos de entrada que
hacen sumamente sencillas las aplicaciones como ésta.
Utilizando una definición sencilla, un editor de textos es una caja de texto con
múltiples líneas, y Visual C++ soporta este tipo de cajas.
CAPÍTULO 6: TRABAJANDO CON MENÚS 193
Para crear el editor que vemos en la figura, los pasos son muy sencillos:
1. Utilizando AppWizard, cree el esqueleto para una nueva aplicación SDI que
utilice un formulario como ventana principal. Denomínela Editor.
4. Ponga las propiedades Horizontal scroll y Vertical scroll a valor true. De esta
forma, la caja de texto queda dotada de barras de desplazamiento horizontal y
vertical. Por omisión, estas propiedades tienen valor false, lo cual indica que
no hay barras de desplazamiento.
5. Ponga la propiedad Want return a valor true. Esto le permitirá saltar a una
nueva línea cuando pulse Entrar.
Para hacer más operativo nuestro editor, vamos a añadirle sus propias órdenes
de Deshacer, Cortar, Copiar y Pegar. Estas órdenes nos permitirán seleccionar
un texto y moverlo o duplicarlo dentro del mismo documento, o llevarlo a otro
documento.
Una vez diseñada la interfaz, escribimos el código correspondiente para cada una
de las órdenes que no estén operativas (Salir y Acerca de, ya están operativas).
Estamos en el editor de diálogos visualizando el recurso menú IDR_MAINFRA-
ME (menú de la ventana principal). Para añadir el controlador (función miembro)
para una determinada orden, haga clic en el menú y después en la orden. A conti-
nuación, invoque a ClassWizard:
Para editar el código correspondiente a esta función pulse el botón Edit Code;
se visualiza el esquema siguiente:
void CEditorView::OnEditDeshacer()
{
// Añada aquí su código
}
Cuando el usuario de una aplicación despliega un menú, cada elemento del menú
necesita saber si se debe visualizar activado o desactivado. Una orden de un menú
puede ejecutarse, sólo si está activa. Quiere esto decir que una de las operaciones
que debe realizar la aplicación es activar y desactivar los elementos de un menú,
en función de que tengan o no actividad en el instante en el que el menú es des-
plegado. Cada vez que se despliega un menú, Windows envía los mensajes de ini-
ciación WM_INITMENU y WM_INITMENUPOPUP que en las MFC son
manipulados a través de la macro ON_UPDATE_COMMAND_UI. Por lo tanto,
un controlador para UPDATE_COMMAND_UI será invocado justo en el momen-
to en el que hay que actualizar el estado de las órdenes del menú. Utilizaremos
ClassWizard para examinar los objetos de la interfaz del usuario capaces de gene-
rar órdenes (elementos de un menú, aceleradores, y botones de una barra de
herramientas) y añadir una entrada en el mapa de mensajes, por cada controlador.
Por ejemplo, para determinar si la orden Deshacer debe activarse, hay que in-
vocar a la función miembro CanUndo de la clase CEdit. La llamada a CanUndo
devuelve un valor distinto de 0 si el control de edición puede realizar la acción de
deshacer, en cuyo caso la orden debe activarse; en caso contrario, debe permane-
cer inactiva (título Deshacer en gris apagado).
Siguiendo con nuestra aplicación, el menú Edición tiene tres órdenes más,
Cortar, Copiar y Pegar, que inicialmente están inactivas (su propiedad Grayed
está señalada). Las órdenes Cortar y Copiar estarán activas cuando haya selec-
cionado un bloque de texto, y la orden Pegar estará activa cuando haya texto en el
portapapeles. La selección del texto para Cortar y Copiar la realizamos utilizando
el teclado o el ratón.
¿Qué tiene que suceder cuando el usuario haga clic en la orden Deshacer? Tiene
que deshacerse la última operación realizada. Esto se consigue invocando a la
función miembro Undo de la clase CEdit. Por lo tanto, utilizando ClassWizard,
añada la función OnEditDeshacer, si aún no lo ha hecho, asociada con el objeto
CAPÍTULO 6: TRABAJANDO CON MENÚS 203
void CEditorView::OnEditDeshacer()
{
// Deshacer la última operación
m_ControlEdit1.Undo();
}
void CEditorView::OnEditCortar()
{
// Borrar la selección y copiarla en el portapapeles
m_ControlEdit1.Cut();
}
void CEditorView::OnEditCopiar()
{
// Copiar la selección en el portapapeles
m_ControlEdit1.Copy();
}
void CEditorView::OnEditPegar()
{
204 VISUAL C++. APLICACIONES PARA WIN32
void CEditorView::OnInitialUpdate()
{
CFormView::OnInitialUpdate();
GetParentFrame()->RecalcLayout();
ResizeParentToFit();
}
En una caja de texto solamente puede utilizarse una fuente, lo cual significa
que cuando se modifique el tipo de letra para la caja de texto, todo el texto cam-
biará a ese tipo de letra. Por lo tanto, para cambiar la fuente activa en el editor de
textos, simplemente tenemos que modificar la fuente actual de la caja de texto
(vea “Establecer una fuente para un control” en el Capítulo 4).
Las características del tipo de letra de la caja de texto las vamos a almacenar
en una variable miembro de la vista denominada m_lf, y la fuente a utilizar por la
caja de texto estará definida por otra variable miembro m_Fuente. Por lo tanto,
defina estas variables en la declaración de CEditorView, localizada en el fichero
editorview.h, como se indica a continuación:
CAPÍTULO 6: TRABAJANDO CON MENÚS 205
Para establecer el tipo de letra inicial, cree una fuente con las características
deseadas, m_Fuente, y haga que m_Fuente sea la fuente inicial, fuente por omi-
sión. Para ello, añada en la función OnInitialUpdate el siguiente código:
void CEditorView::OnInitialUpdate()
{
CFormView::OnInitialUpdate();
GetParentFrame()->RecalcLayout();
ResizeParentToFit();
// Establecer el tipo de letra por omisión.
// Un valor 0 significa utilizar los valores por omisión para los paráme-
tros
memset( &m_lf, 0, sizeof( LOGFONT ) );
m_Fuente.CreateFontIndirect( &m_lf );
m_ControlEdit1.SetFont( &m_Fuente );
}
Las órdenes del menú Fuentes realizan todas el mismo proceso: establecer la
fuente especificada por su título (propiedad Caption). Esto evita tener que escribir
una función para cada tipo de letra. Por lo tanto, el paso siguiente es ver cómo ac-
cedemos a las propiedades de una orden.
La pantalla para visualizar la hora será una caja de texto de sólo lectura con el
fin de que el usuario no pueda modificarla. Para que la hora varíe segundo a se-
gundo, el contenido de la caja de texto a la que hemos hecho referencia, que re-
presenta la hora, debe actualizarse a intervalos iguales o inferiores a un segundo.
Para realizar esto, las MFC proporcionan una función OnTimer miembro de la
clase CWnd.
Hay un segundo menú, denominado País, que permitirá al usuario añadir paí-
ses al propio menú, para después, haciendo clic sobre cualquiera de ellos, obtener
en una tercera caja, formada por otra caja de texto, la hora actual en ese país.
Temporizador
Un temporizador es una rutina interna de Visual C++ que notifica periódicamente
a una aplicación cuando ha transcurrido un período predeterminado de tiempo.
Cada vez que transcurre el período de tiempo especificado, Windows coloca un
mensaje WM_TIMER en la cola de mensajes de la aplicación. El procesamiento
de este mensaje hace que se ejecute la función OnTimer, lugar donde se especifi-
carán las acciones a ejecutar periódicamente. Los mensajes WM_TIMER, igual
CAPÍTULO 6: TRABAJANDO CON MENÚS 223
que los WM_PAINT, son de baja prioridad. Esto es, si la cola de una aplicación
contiene sólo mensajes WM_PAINT y WM_TIMER, y otra aplicación espera
para despachar mensajes distintos, Windows pasará el control a la otra aplicación.
El sistema genera un tic de reloj cada 54,925 msegs. (18,2 tics de reloj por se-
gundo) por ello, aunque el valor del parámetro de SetTimer se exprese en milise-
gundos, la precisión no puede ser mayor de 54,925 milésimas de segundo. Esto
significa que para períodos inferiores a 55 milisegundos, cada tic de reloj genera
un mensaje WM_TIMER.
Si su aplicación u otra aplicación está realizando una tarea que mantiene ocu-
pados los recursos del ordenador por un espacio largo de tiempo, tal como un bu-
cle largo, cálculos intensivos, acceso a los puertos, etc., puede ser que su
aplicación no responda de acuerdo con los intervalos de tiempo programados.
Añada una etiqueta encima del borde de la caja de texto. Asígnele el título
Hora:, ajuste el tamaño y sitúela como se indica en la figura siguiente:
Añada una etiqueta encima del borde de esta caja de texto, asígnele el título
Despertador:, ajuste su tamaño y sitúela como indica la figura.
void CRelojView::OnInitialUpdate()
{
CFormView::OnInitialUpdate();
// Ajustar el tamaño de la ventana marco al tamaño de la vista
GetParentFrame()->RecalcLayout();
ResizeParentToFit();
Utilizando ClassWizard añada una clase (botón Add Class) CMiEdit derivada
de CEdit. Elija el fichero de cabecera relojview.h como destino de la declaración
de la clase y el fichero de código relojview.cpp como destino de la definición de
las funciones miembro y del mapa de mensajes. Modifique la definición que hizo,
desde ClassWizard, de las variables m_CtrlHora y m_CtrlDespertador vinculadas
a las cajas IDC_HORA e IDC_DESPERTADOR, para que ahora su tipo sea
CMiEdit en lugar de CEdit (tiene que eliminarlas y volverlas a definir). Esto exi-
ge mover la declaración de CMiEdit antes de la de CRelojView. Con esto ha con-
cluido el proceso de subclasificación de los controles. En realidad, como lo que
pretendemos es controlar la entrada del usuario en IDC_DESPERTADOR, sólo se
necesitaría subclasificar con CMiEdit esta caja de texto.
Un control de edición tiene una propiedad Number que se puede poner duran-
te el diseño a true; por omisión es false. Cuando esta propiedad es true, el control
de edición sólo admite números. Nosotros no hemos utilizado esta propiedad por-
que necesitamos admitir también el carácter “dos puntos”.
La cuestión es que si estas sentencias se ejecutan una sola vez, la hora será la
presentada inicialmente y no variará. Es preciso, por lo tanto, ejecutarlas a inter-
valos de un segundo o menos. Para conseguir esto, haga que estas sentencias sean
parte del cuerpo de la función OnTimer miembro de CRelojView, la vista.
void CRelojView::OnInitialUpdate()
{
// ...
// Establecer un temporizador
SetTimer( ID_1SEGUNDO, 1000, NULL );
}
CFormView::OnTimer(nIDEvent);
}
CRelojView::CRelojView() : CFormView(CRelojView::IDD)
{
//{{AFX_DATA_INIT(CRelojView)
m_sDespertador = _T("00:00:00");
m_sHora = _T("");
//}}AFX_DATA_INIT
// TODO: add construction code here
// Despertador si/no
m_bDespertadorSi = false;
}
CFormView::OnTimer(nIDEvent);
Desarrollemos ahora el código para las órdenes del menú Despertador. La or-
den Salir está implementada por omisión.
Observe que cuando el usuario haga clic sobre la orden de activar y desactivar
el despertador, el título de la misma cambiará para indicar Despertador Sí si la va-
riable m_bDespertadorSi vale true, pasando ésta a valer false, y cambiará para
indicar Despertador No si la variable m_bDespertadorSi vale false, pasando ésta
a valer true.
void CRelojView::OnDespertadorSiNo()
{
m_bDespertadorSi = !m_bDespertadorSi;
}
Esto también podría haberse hecho diseñando el menú con las órdenes posi-
bles, Despertador Sí y Despertador No, estando una de ellas (la no activa) no vi-
sible. Por ejemplo, suponga que ha construido este menú asignando a sus
elementos las siguientes propiedades (los valores para las propiedades que no se
especifican son los dados por omisión):
Según la tabla anterior, el código siguiente hace que inicialmente sólo esté vi-
sible la orden Despertador No, eliminando Despertador Sí.
void CRelojView::OnInitialUpdate()
{
//...
CMenu *pMenu = GetParent()->GetMenu();
pMenu = pMenu->GetSubMenu(0);
pMenu->RemoveMenu(1, MF_BYPOSITION);
}
CRelojView::CRelojView() : CFormView(CRelojView::IDD)
{
// ...
// Despertador si/no
m_bDespertadorSi = false;
m_sDespertador = AfxGetApp()->GetProfileString(
"Settings", "Alarma", "00:00:00");
}
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
Técnicas avanzadas
• Ficheros de datos
• Barras de control
• Gráficos
• El ratón
• Aplicaciones MDI
• Mapas de bits
• Impresión y presentación preliminar
• Acceso a una base de datos
PARTE
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
Apéndices
• Códigos de caracteres
• Índice alfabético