Está en la página 1de 62

Visual C++

Aplicaciones para Win32


Fco. Javier Ceballos Sierra
Profesor titular de la
Escuela Politécnica Superior
Universidad de Alcalá

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.

RA-MA es una marca comercial registrada.

Se ha puesto el máximo empeño en ofrecer al lector una información completa y precisa.


Sin embargo, RA-MA Editorial no asume ninguna responsabilidad derivada de su uso,
ni tampoco por cualquier violación de patentes ni otros derechos de terceras partes que pudieran
ocurrir. Esta publicación tiene por objeto proporcionar unos conocimientos precisos y acreditados
sobre el tema tratado. Su venta no supone para el editor ninguna forma de asistencia legal,
administrativa ni de ningún otro tipo. Caso de precisarse asesoría legal u otra forma de ayuda
experta, deben buscarse los servicios de un profesional competente.

Reservados todos los derechos de publicación en cualquier idioma.

Ninguna parte de este libro puede ser reproducida, grabada en sistema


de almacenamiento o transmitida en forma alguna ni por cualquier procedimiento, ya
sea electrónico, mecánico, reprográfico, magnético o cualquier otro, sin autorización
previa y por escrito de RA-MA; según lo dispuesto en el artículo 534-bis del Código Penal
vigente serán castigados con la pena de arresto mayor y multa quienes intencionadamente,
reprodujeren o plagiaren, en todo o en parte, una obra literaria, artística o científica.

Editado por:
RA-MA Editorial
Ctra. Canillas, 144
28043 MADRID
Teléfono: (91) 381 03 00
Telefax: (91) 381 03 72
Correo electrónico: rama@arrakis.es
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

PARTE 1. PROGRAMACIÓN BÁSICA CON LAS MFC .... 1


CAPÍTULO 1. ¿QUÉ ES VISUAL C++? ............................................................. 3

ENTORNO DE DESARROLLO DE VISUAL C++ .......................................... 7


Ventana del proyecto ..................................................................................... 7
Ventana de edición ......................................................................................... 8
Ventana de salida ........................................................................................... 9
Utilidades ....................................................................................................... 9
COMO CREAR UNA APLICACIÓN ................................................................ 10
Creación del esqueleto de la aplicación ......................................................... 10
Desarrollo de la aplicación ............................................................................. 11
UN EJEMPLO SENCILLO ................................................................................ 12
OTRAS CARACTERÍSTICAS DEL ENTORNO DE DESARROLLO ............ 20
Proyectos y subproyectos ............................................................................... 20
Configuración del proyecto ............................................................................ 21
Recursos ......................................................................................................... 21
DOCUMENTOS Y VISTAS .............................................................................. 24

CAPÍTULO 2. MI PRIMERA APLICACIÓN .................................................... 29

PROGRAMANDO EN WINDOWS................................................................... 32
EL LENGUAJE DE WINDOWS........................................................................ 34
Mensajes......................................................................................................... 34
Independencia de hardware ............................................................................ 34
VIII VISUAL C++. APLICACIONES PARA WIN32.

Llamadas a funciones ..................................................................................... 35


Ventanas y procedimiento de ventana............................................................ 35
Handles........................................................................................................... 36
Cómo se ejecuta una aplicación Windows ..................................................... 36
APLICACIÓN MÍNIMA .................................................................................... 37
Crear un nuevo proyecto ................................................................................ 38
Preparar el fichero de recursos ....................................................................... 38
Escribir el código de la aplicación ................................................................. 39
Fichero de cabecera (.h) ............................................................................ 39
Fichero fuente (.cpp) ................................................................................. 41
¿Cómo se ejecuta ésta aplicación? ................................................................. 42
Construir el programa .................................................................................... 44
Fichero del proyecto ................................................................................. 45
Fichero ejecutable (.exe) ........................................................................... 45
Ejecutar la aplicación ................................................................................ 46
Depurar la aplicación ................................................................................ 46
Explorar una aplicación ............................................................................ 46
AÑADIR CONTROLES A LA APLICACIÓN.................................................. 47
COMUNICACIÓN CON WINDOWS ............................................................... 50
Mensajes......................................................................................................... 51
Mapa de mensajes .......................................................................................... 53
BUCLE DE MENSAJES .................................................................................... 56
Buscando un mensaje en los mapas de mensajes ........................................... 57
ASISTENTES PARA LA PROGRAMACIÓN .................................................. 59
Diseño de la ventana principal ....................................................................... 61
Mover y ajustar el tamaño del formulario ...................................................... 63
Dibujar los controles ...................................................................................... 63
Borrar un control ............................................................................................ 68
Propiedades de los objetos ............................................................................. 68
Unir el código a los objetos............................................................................ 70
Utilizando funciones miembro .................................................................. 72
Utilizando variables miembro ................................................................... 74
CAMBIO DE PROPIEDADES EN EJECUCIÓN.............................................. 76
PERSONALIZAR LA APLICACIÓN ............................................................... 77
VENTANA DE DIÁLOGO COMO VENTANA PRINCIPAL ......................... 79
Diseño de la ventana principal ....................................................................... 81
Unir el código a los objetos............................................................................ 82
Conclusiones .................................................................................................. 83

CAPÍTULO 3. CLASES BASE de Microsoft ...................................................... 85

JERARQUÍA DE LAS MFC .............................................................................. 86


ÍNDICE IX

La clase CObject ............................................................................................ 87


Clases de la arquitectura de una aplicación .................................................... 89
Objeto aplicación ...................................................................................... 90
Plantillas de documento ............................................................................ 90
Documentos .............................................................................................. 91
Elementos de documentos ......................................................................... 91
Otras clases ............................................................................................... 92
Soporte de ventanas .................................................................................. 92
Ventanas marco.................................................................................... 93
Barras de control .................................................................................. 93
Vistas ................................................................................................... 94
Hojas de propiedades ........................................................................... 94
Ventanas de diálogo ............................................................................. 95
Controles .............................................................................................. 95
Soporte gráfico ............................................................................................... 96
Dibujo de gráficos ..................................................................................... 97
Objetos de dibujo de gráficos ................................................................... 97
Controles gráficos ..................................................................................... 98
Soporte de sistema ......................................................................................... 98
Menús........................................................................................................ 98
Excepciones .............................................................................................. 99
Sincronización........................................................................................... 99
Conexión con bases de datos .................................................................... 100
Objetos de acceso a datos.......................................................................... 100
Programación de redes .............................................................................. 100
Servicios de internet .................................................................................. 101
Colecciones .................................................................................................... 102
Clases no derivadas de CObject ..................................................................... 102
Modelo de objeto y seriación .................................................................... 102
Tipos de datos varios ................................................................................ 103
API para un servidor de Internet ............................................................... 103
Estructuras................................................................................................. 103
Clases de soporte....................................................................................... 104
Plantillas para colecciones ........................................................................ 104
Soporte OLE ............................................................................................. 104
Sincronización........................................................................................... 104
Sumario .......................................................................................................... 105
NOTACIÓN HÚNGARA ................................................................................... 105
ARQUITECTURA DE UNA APLICACIÓN ..................................................... 106
Objeto aplicación ........................................................................................... 107
Objeto plantilla de documento ....................................................................... 107
Objeto documento .......................................................................................... 108
Objeto ventana marco .................................................................................... 108
X VISUAL C++. APLICACIONES PARA WIN32.

Objeto vista .................................................................................................... 108


ANÁLISIS DE UNA APLICACIÓN.................................................................. 109
La clase aplicación ......................................................................................... 110
La clase ventana marco .................................................................................. 111
La clase documento ........................................................................................ 112
La clase vista .................................................................................................. 114
LA JERARQUÍA DE VENTANAS.................................................................... 118

CAPÍTULO 4. CONTROLES MÁS COMUNES ................................................ 121

CONVERSIÓN DE TEMPERATURAS ............................................................ 121


DESARROLLO DE LA APLICACIÓN ............................................................. 122
Objetos ........................................................................................................... 122
Eventos ........................................................................................................... 122
Pasos a seguir durante el desarrollo ............................................................... 122
El formulario, los controles y sus propiedades .............................................. 123
Escribir el código ........................................................................................... 124
Enfocar un objeto ........................................................................................... 127
VISUALIZAR LA FECHA Y LA HORA .......................................................... 128
BOTONES DE PULSACION ............................................................................. 131
DISEÑO DE UNA CALCULADORA ............................................................... 133
Objetos ........................................................................................................... 133
Eventos ........................................................................................................... 134
Pasos a seguir durante el desarrollo ............................................................... 134
Diseño de la ventana y de los controles ......................................................... 134
Escribir el código ........................................................................................... 136
ACCESO A LAS FUNCIONES MIEMBRO DE UN CONTROL .................... 146
ESTABLECER UNA FUENTE PARA UN CONTROL ................................... 147
COLOR DE FONDO PARA UN CONTROL .................................................... 151
Macro RGB .................................................................................................... 152
Color de fondo para un control CEdit ............................................................ 153
CREAR CONTROLES DURANTE LA EJECUCION ...................................... 155
Pasos a seguir durante el desarrollo ............................................................... 156
Diseño de la ventana y de los controles ......................................................... 156
Escribir el código ........................................................................................... 157

CAPÍTULO 5. EL TECLADO Y LA SUBCLASIFICACIÓN........................... 165

CONCEPTOS BÁSICOS SOBRE EL TECLADO ............................................ 166


INTERCEPTANDO LA TECLA PULSADA .................................................... 168
CONVERSIÓN DE TEMPERATURAS ............................................................ 170
Subclasificación dinámica .............................................................................. 174
ÍNDICE XI

Subclasificación utilizando ClassWizard ....................................................... 179


MENSAJES REFLEJADOS ............................................................................... 180
Definir un controlador para un mensaje reflejado .......................................... 182

CAPÍTULO 6. TRABAJANDO CON MENÚS ................................................... 187

DISEÑO DE UN MENÚ .................................................................................... 188


Propiedades de un menú ................................................................................ 190
DESARROLLO DE UN EDITOR DE TEXTOS ............................................... 192
Caja de texto multilínea ................................................................................. 193
Trabajar con texto seleccionado ..................................................................... 194
Utilización del portapapeles ........................................................................... 195
Diseño del editor ............................................................................................ 196
Añadir una función miembro para un elemento de un menú .................... 199
Activar los elementos de un menú ............................................................ 200
Procesar las órdenes de un menú .............................................................. 202
Seleccionar una orden de un menú ........................................................... 205
Marcar el menú seleccionado .................................................................... 209
CCmdUI .................................................................................................... 212
Colores de fondo y del texto ..................................................................... 212
Asociar un icono a la aplicación .................................................................... 216
Identificador de un elemento de un menú ...................................................... 216
Parámetros de la ventana ................................................................................ 219
Un editor con CEditView ............................................................................... 220
Un editor con CRichEditView ....................................................................... 221
DESARROLLO DE UN RELOJ DESPERTADOR ........................................... 222
Temporizador ................................................................................................. 222
Diseño de la ventana y de los controles ......................................................... 223
Unir el código a los controles y a la ventana ................................................. 224
Cambiar en ejecución una orden de un menú ................................................ 230
Otras órdenes para manipular menús ............................................................. 233
Añadir elementos a un menú .......................................................................... 234
Procedimiento común para las órdenes añadidas ........................................... 244
Borrar órdenes de un menú ............................................................................ 245
ACELERADORES DE TECLADO ................................................................... 247
MENÚS DESPLEGABLES FLOTANTES ........................................................ 249
SERIAR LOS DATOS ........................................................................................ 250
REGISTRO DE WINDOWS .............................................................................. 255
Establecer una clave en el registro ................................................................. 257
Guardar los valores configuración ................................................................. 258
Recuperar los valores de configuración ......................................................... 259
CAPÍTULO 7. CAJAS DE DIÁLOGO ................................................................ 261
XII VISUAL C++. APLICACIONES PARA WIN32.

CAJAS DE DIÁLOGO MODALES Y NO MODALES .................................... 261


Caja de diálogo modal.................................................................................... 262
Caja de diálogo no modal............................................................................... 262
CAJAS DE DIÁLOGO PARA E/S ..................................................................... 264
Visualizar datos .............................................................................................. 265
Salida con formato ......................................................................................... 267
Requerir datos ................................................................................................ 268
Recursos cadenas de caracteres ...................................................................... 270
CASILLAS DE VERIFICACIÓN ...................................................................... 271
BOTÓN DE OPCIÓN ......................................................................................... 279
MARCOS ............................................................................................................ 284
AGRUPANDO BOTONES DE OPCION .......................................................... 284
LISTAS Y LISTAS DESPLEGABLES .............................................................. 287
Acceso a los elementos de una lista ............................................................... 288
Utilización de listas ........................................................................................ 288
Eliminar un elemento de una lista .................................................................. 302
Inhabilitar controles ....................................................................................... 303
Utilización de listas desplegables................................................................... 305
Lista con múltiples columnas ......................................................................... 308
Seleccionar múltiples elementos en una lista ................................................. 309
Soporte para listas de imágenes ..................................................................... 309
BARRAS DE DESPLAZAMIENTO .................................................................. 309
UN EJEMPLO CON BARRAS DE DESPLAZAMIENTO ............................... 314
Color de fondo de la ventana principal .......................................................... 317
Tamaño de la ventana principal ..................................................................... 323
Título de la ventana principal......................................................................... 327
HOJAS DE PROPIEDADES .............................................................................. 327
Construir una página de propiedades ............................................................. 328
Construir una hoja de propiedades ................................................................. 332
Hoja de propiedades no modal ....................................................................... 339
Funciones miembro de CPropertySheet ......................................................... 342
Funciones miembro de CPropertyPage .......................................................... 343
CONTROL CON PESTAÑAS ........................................................................... 343
HOJAS DE PROPIEDADES ESTILO WINDOWS 9x/NT 5.0 ......................... 349

CAPÍTULO 8. CAJAS DE DIÁLOGO COMUNES ........................................... 357

AÑADIENDO UNA CAJA DE DIÁLOGO COMÚN ....................................... 357


Cajas de diálogo Abrir y Guardar como ........................................................ 359
Forma directa ............................................................................................ 360
Forma indirecta ......................................................................................... 363
Caja de diálogo Color .................................................................................... 364
ÍNDICE XIII

Caja de diálogo Fuente ................................................................................... 367


Caja de diálogo Buscar y Reemplazar............................................................ 370
Caja de diálogo Configurar página ................................................................ 374
Caja de diálogo Imprimir ............................................................................... 376
PLANTILLAS PERSONALIZADAS ................................................................ 381

CAPÍTULO 9. COMPONENTES SOFTWARE ................................................. 387

CONTROLES ACTIVOS ................................................................................... 388


EL CONTROL REJILLA ................................................................................... 390
UTILIZANDO UNA REJILLA .......................................................................... 391
Iniciar la ventana de la aplicación .................................................................. 397
Altura y la anchura de las celdas .................................................................... 398
Imágenes en un control activo........................................................................ 401
Manejo de la aplicación ................................................................................. 403
Transferir texto al portapapeles ...................................................................... 418
Obtener texto del portapapeles ....................................................................... 422
CONTROLES EN LA BIBLIOTECA MFC....................................................... 423
Controles comunes en la biblioteca MFC ...................................................... 424

PARTE 2. TÉCNICAS AVANZADAS ..................................... 427


CAPÍTULO 10. FICHEROS DE DATOS ............................................................ 429

ABRIENDO FICHEROS PARA ACCESO SECUENCIAL .............................. 430


Escribir en un fichero utilizando el acceso secuencial ................................... 431
Leer de un fichero utilizando el acceso secuencial ........................................ 432
LECTURA Y ESCRITURA DE DOCUMENTOS ............................................ 434
SERIACIÓN ....................................................................................................... 435
Inicio de la aplicación .................................................................................... 437
Conexión de la orden Abrir con Serialize ...................................................... 438
Conexión de Guardar y Guardar como con Serialize ..................................... 439
¿El documento ha sido modificado? .............................................................. 440
ACCESO ALEATORIO A UN FICHERO......................................................... 441
Nuevo fichero ................................................................................................. 444
Caja de diálogo Buscar registro ..................................................................... 444
Añadir un registro .......................................................................................... 445
Abrir un fichero .............................................................................................. 449
Mensajes definidos por el usuario............................................................. 449
Buscar un registro .......................................................................................... 453
Borrar un registro ........................................................................................... 454
XIV VISUAL C++. APLICACIONES PARA WIN32.

Guardar y Guardar como................................................................................ 455


Moverse por la base de datos ......................................................................... 457

CAPÍTULO 11. BARRAS DE CONTROL .......................................................... 461

BARRA DE HERRAMIENTAS......................................................................... 464


Diseño de la barra de herramientas ................................................................ 467
Estilos de un botón ......................................................................................... 468
Mensajes relacionados con un botón.............................................................. 469
BARRA DE ESTADO ........................................................................................ 470
Definición de la barra de estado ..................................................................... 470
Línea de mensajes e indicadores de estado .................................................... 472
Actualización de la barra de estado ................................................................ 473
Control de la barra de estado.......................................................................... 473
Modificar la barra de estado ........................................................................... 474
CONTROL BARRA DE ESTADO .................................................................... 477
BARRA DE DIÁLOGO...................................................................................... 483
BARRA DE BARRAS ........................................................................................ 487
Crear un objeto CReBar ................................................................................. 488
Objeto barra de diálogo .................................................................................. 490
Crear una banda utilizando una lista de imágenes ......................................... 491
Vincular un menú a un botón ......................................................................... 495

CAPÍTULO 12. GRÁFICOS ................................................................................. 499

EL CONTEXTO DE DISPOSITIVO.................................................................. 501


MFC Y CONTEXTOS DE DISPOSITIVO ........................................................ 502
Atributos del contexto de dispositivo............................................................. 503
Obtener información del contexto de dispositivo .......................................... 504
Objetos GDI ................................................................................................... 505
ASOCIACIÓN DE COLORES ........................................................................... 512
Paletas de colores ........................................................................................... 512
DIBUJAR CON VISUAL C++ ........................................................................... 514
UNIDADES EN UN SISTEMA DE COORDENADAS .................................... 515
Modo de proyección MM_TEXT .................................................................. 517
Modos de proyección métricos ...................................................................... 520
LPtoDP y DPtoLP .......................................................................................... 523
Modos de proyección personalizados ............................................................ 526
GRÁFICOS PERSISTENTES ............................................................................ 531
Repintar objetos ............................................................................................. 531
DIBUJAR PUNTOS ........................................................................................... 540
DIBUJAR LÍNEAS ............................................................................................. 542
ÍNDICE XV

Modos de dibujo ............................................................................................ 549


COLOREAR FIGURAS ..................................................................................... 550
ESTILOS DE LA PLUMA ................................................................................. 551
DIBUJAR FIGURAS .......................................................................................... 552
REGIONES ......................................................................................................... 557
Región de recorte ........................................................................................... 559

CAPÍTULO 13. EL RATÓN ................................................................................. 563

EVENTOS DEL RATÓN ................................................................................... 564


MENSAJES DEL RATÓN SOBRE EL ÁREA DE CLIENTE .......................... 564
ARGUMENTOS DE LAS FUNCIONES DEL RATÓN ................................... 565
EJEMPLO DE UN TABLERO DE DIBUJO ..................................................... 567
Cambiar el cursor del ratón ............................................................................ 576
Dibujar sobre la vista ..................................................................................... 578
Ocultar y visualizar la barra de dibujo ........................................................... 586
BARRAS DE DESPLAZAMIENTO EN UNA APLICACIÓN SDI ................. 587
Tamaño del documento .................................................................................. 587

CAPÍTULO 14. APLICACIONES MDI .............................................................. 591

INTERFAZ DE MÚLTIPLES DOCUMENTOS................................................ 592


Ejecución de una aplicación MDI .................................................................. 595
Ejemplo de una aplicación MDI .................................................................... 597
Aplicación MDI con dos tipos de documentos .............................................. 597
VENTANAS DIVISIBLES................................................................................. 600
Ventanas divisibles dinámicas ....................................................................... 601
Ventanas divisibles estáticas .......................................................................... 601
Barras de desplazamiento en ventanas divisibles ........................................... 602
Añadir ventanas divisibles a un editor de textos ............................................ 602

CAPÍTULO 15. MAPAS DE BITS ....................................................................... 609

MAPAS DE BITS DEPENDIENTES DEL DISPOSITIVO .............................. 609


Cargar un mapa de bits desde un recurso ....................................................... 612
Visualizar el mapa de bits .............................................................................. 614
Liberar el espacio de memoria asignado al DDB ........................................... 615
Ampliar o reducir un mapa de bits ................................................................. 616
Copiar un mapa de bits en el portapapeles ..................................................... 616
Pegar el mapa de bits del portapapeles........................................................... 620
Visualizar un mapa de bits en una caja de diálogo ........................................ 623
XVI VISUAL C++. APLICACIONES PARA WIN32.

Copiar el mapa de bits desde OnPaint ...................................................... 624


Copiar el mapa de bits en una caja de imagen .......................................... 626
Copiar el mapa de bits desde OnEraseBkgnd ........................................... 628
Copiar el mapa de bits en un botón ........................................................... 632
MAPAS DE BITS INDEPENDIENTES DEL DISPOSITIVO .......................... 636
Formato de un DIB ........................................................................................ 636
BITMAPFILEHEADER ........................................................................... 637
BITMAPINFOHEADER .......................................................................... 637
BITMAPINFO .......................................................................................... 639
Funciones de acceso a los DIB....................................................................... 640
CreateDIBitmap ........................................................................................ 640
SetDIBits y GetDIBits .............................................................................. 641
SetDIBitsToDevice ................................................................................... 644
StretchDIBits............................................................................................. 645
CreateDIBPatternBrush ............................................................................ 645
Colocar un DIB en el portapapeles ........................................................... 645
Clase CDIB .................................................................................................... 646
Manipulación de un DIB ................................................................................ 656
Inicio de la aplicación ............................................................................... 658
Nuevo y Abrir ........................................................................................... 659
Guardar y Guardar como .......................................................................... 659
Borrar todo ................................................................................................ 660
Abrir BMP ................................................................................................ 660
Nuevo, Abrir y Borrar todo (continuación) .............................................. 667
StretchBlt, BitBlt, StretchDlBits y SetDlBitsToDevice ............................ 668
Barras de desplazamiento.......................................................................... 669
Guardar BMP como .................................................................................. 670
Copiar........................................................................................................ 671
Cortar ........................................................................................................ 672
Pegar ......................................................................................................... 672
PALETAS DE COLORES .................................................................................. 674
El administrador de paletas de Windows ....................................................... 675
Paletas lógicas ................................................................................................ 676
SelectPalette .............................................................................................. 677
CreatePalette ............................................................................................. 677
RealizePalette ............................................................................................ 678
Paleta del sistema ........................................................................................... 678
Mensajes relacionados con las paletas ........................................................... 679
Clase CPALETA ............................................................................................ 681
Manipulación de una paleta ........................................................................... 684
CAPÍTULO 16. IMPRESIÓN Y PRESENTACIÓN PRELIMINAR ............... 693

IMPRESIÓN ....................................................................................................... 694


ÍNDICE XVII

Cómo se realiza una impresión ...................................................................... 695


Paginación ...................................................................................................... 696
Cabeceras y pies ............................................................................................. 698
Imprimir imágenes y texto ............................................................................. 698
PRESENTACIÓN PRELIMINAR...................................................................... 704
Arquitectura de la presentación preliminar .................................................... 704

CAPÍTULO 17. ACCESO A UNA BASE DE DATOS ....................................... 707

¿QUÉ ES UNA BASE DE DATOS? .................................................................. 707


PROGRAMACIÓN DE BASES DE DATOS .................................................... 708
Clases DAO.................................................................................................... 710
ACCESO A UNA BASE DE DATOS CON DAO ............................................. 716
Utilizando AppWizard ................................................................................... 717
Crear una base de datos con Microsoft Access ......................................... 717
Aplicación ................................................................................................. 718
Diseño de la plantilla de diálogo ............................................................... 721
Utilizando ClassWizard ................................................................................. 722
Diseño de la plantilla de diálogo ............................................................... 724
Abrir la base de datos ................................................................................ 727
Desplazarse por la base de datos ............................................................... 729
Añadir, modificar y borrar datos ............................................................... 731
MODELO DE OBJETO ADO ............................................................................ 740
Extensiones Visual C++ para ADO ............................................................... 741

PARTE 3. APÉNDICES ............................................................. 743


A. CÓDIGOS DE CARACTERES ....................................................................... 745

UTILIZACIÓN DE CARACTERES ANSI CON WINDOWS .......................... 745


JUEGO DE CARACTERES ANSI ..................................................................... 746
UTILIZACIÓN DE CARACTERES ASCII ....................................................... 747
JUEGO DE CARACTERES ASCII.................................................................... 748
CÓDIGOS EXTENDIDOS ................................................................................. 749
CÓDIGOS DEL TECLADO ............................................................................... 750

B. ÍNDICE ALFABÉTICO.................................................................................... 751


PRÓLOGO
Para facilitar el desarrollo de aplicaciones para Windows escritas en C, Microsoft
introduce en 1992, C/C++ 7.0 y la biblioteca de clases MFC 1.0 (Microsoft Foun-
dation Class). Las investigaciones demostraron que debido al nivel de dificultad
de aprender y utilizar no sólo C++, sino el conjunto de clases de las MFC, muchos
desarrolladores se quedaban en el intento de migrar a C++. Por este motivo fue
creado en febrero de 1993 Visual C++ 1.0, para facilitar a los desarrolladores la
migración a C++.

Con Visual C++ se introdujo una tecnología de desarrollo innovadora a base


de asistentes con una nueva versión de las MFC más potente; la 2.0. La biblioteca
MFC 2.0, compatible con la versión anterior, permitió implementar una arquitec-
tura de aplicación que facilitaba enormemente el desarrollo de aplicaciones. Los
asistentes que permitían generar esta nueva arquitectura basada en las clases
MFC, evitaban escribir muchas de las líneas de código necesarias para construir
una aplicación. Esto es, los asistentes generaban aplicaciones para Windows, sin
necesidad de escribir líneas y líneas de código. Por ello, Visual C++ se convirtió
en el camino más corto para el desarrollo de aplicaciones C++ para Windows,
combinando, además, un alto rendimiento con una comodidad en el uso.

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.

completo de ODBC para facilitar la programación y el acceso a bases de datos lo-


cales o remotas.

El fuerte interés de los desarrolladores por crear aplicaciones de 32 bits (basa-


das en Win32 y OLE) así como tener flexibilidad para dirigirse a múltiples plata-
formas (Windows y Windows NT para Intel y RISC, Windows 9x y Macintosh)
condujo a Microsoft a crear Visual C++ 2.0 y la biblioteca MFC 3.0 totalmente
compatible con las versiones anteriores. De esta forma los desarrolladores podían
continuar manteniendo sus aplicaciones de 16 bits con Visual C++ 1.5x y los des-
arrolladores de 32 bits las suyas con Visual C++ 2.x.

El compilador C++ de Visual C++ 2.0 ya incorporaba las últimas característi-


cas de C++; esto es, plantillas de C++ y los manipuladores de excepciones que sus
antecesores incorporaban a base de macros. Visual C++ 2.0 aportaba entre otras
características: la creación de aplicaciones de 32 bits, hilos (threads) y un espacio
de memoria plana (eliminando los segmentos de 64K). Asimismo, la biblioteca
MFC 3.0 añadía soporte OLE de 32 bits y ODBC. En realidad Visual C++ 2.0 fue
un trampolín para sus predecesores (Visual C++ 4.x-MFC 4.2, Visual C++ 5.0-
MFC 4.22, y Visual C++ 6.0-MFC 6.0).

Visual C++ 6.0 presenta la tecnología de compilación más avanzada para


producir aplicaciones de 32 bits más rápidas y de menor tamaño. También incor-
pora las características y palabras clave más actuales del estándar ANSI. Las nue-
vas MFC&T (MFC and Template Libraries) combinan la fiabilidad y productivi-
dad de la biblioteca MFC 6.0 con la biblioteca ATL (Active Template Library).
Hace más fácil el desarrollo de software basado en componentes (soporte COM).
Incorpora un gran número de nuevos elementos diseñados para explotar las tecno-
logías de Internet.

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.

En general, Microsoft Visual C++ es un entorno de desarrollo diseñado espe-


cialmente para crear aplicaciones gráficas orientadas a objetos. Para crear una
aplicación se crean ventanas y sobre ellas se dibujan controles (etiquetas, botones,
cajas de texto, listas desplegables, etc.) y a continuación se escribe el código fuen-
te relacionado con cada objeto. Esto es, cada objeto está ligado a un código que
PRÓLOGO XXI

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.

Este libro es el tercero de una colección de cuatro libros orientados al desarro-


llo de aplicaciones con C/C++. Entre los cuatro, y en el orden especificado, cu-
bren los siguientes aspectos: programación con C, programación orientada a obje-
tos con C++, desarrollo de aplicaciones para Windows basadas en objetos, y pro-
gramación avanzada en Windows incluyendo temas relativos a Internet.

El primero, Curso de programación C/C++, abarca todo lo relativo a la pro-


gramación estructurada con C. También incluye diversos algoritmos de uso
común así como estructuras dinámicas de datos.

El segundo, Programación orientada a objetos con C++, estudia como su


nombre indica el desarrollo de aplicaciones orientadas a objetos. Esta tecnología
es imprescindible conocerla si queremos desarrollar aplicaciones utilizando bi-
bliotecas de clases MFC y ATL de Microsoft Visual C++.

El tercero, Visual C++ - Aplicaciones para Win32, le enseña fundamental-


mente cómo desarrollar aplicaciones para Windows (aplicaciones con una interfaz
gráfica basada en ventanas).

Y el cuarto, Visual C++ - Programación avanzada en Win32, complementa


al libro anterior abordando temas más complejos como: el sistema de ayuda
HTML, hilos, comunicaciones RS-232, controles ActiveX, tecnología COM, bi-
blioteca ATL, dispositivos MCI, bibliotecas dinámicas, acceso a bases de datos,
Internet, Visual Interdev, aplicaciones de Internet, DirectDraw, Direct3DRM y
DirectSound.
XXII VISUAL C++. APLICACIONES PARA WIN32.

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.

En especial, quiero expresar mi agradecimiento Alfons González por las ide-


as que aportó y a Martín Knoblauch, José Daniel Castejón y a David Jurado
González por su participación en la corrección de esta obra.

Francisco Javier Ceballos Sierra


http://www.fjceballos.es/
PARTE

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯

Programación básica con las MFC


• ¿Qué es Visual C++?
• Mi primera aplicación
• Clases base de Microsoft
• Controles más comunes
• El teclado y la subclasificación
• Trabajando con menús
• Cajas de diálogo
• Cajas de diálogo comunes
• Componentes software
CAPÍTULO 6

TRABAJANDO CON MENÚS


Muchas aplicaciones simples tienen un formulario y varios controles, pero no ca-
be duda de que su aspecto puede ser mejorado utilizando menús. En este capítulo
aprenderemos a crear y a utilizar menús en una aplicación. Un menú es una forma
de proveer al usuario de un conjunto de órdenes, lógicamente relacionadas, agru-
padas bajo un mismo título. El conjunto de todos los títulos correspondientes a los
menús diseñados aparecerán en la barra de menús situada debajo del título del
formulario.

Título del menú Barra de menús

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

submenús y separadores. Cuando se hace clic en una orden o se selecciona y se


pulsa Entrar, se ejecuta una acción o se despliega una caja de diálogo. Por conve-
nio, una orden seguida de tres puntos (...) indica que se desplegará una caja de
diálogo. Cuando se hace clic en un submenú se despliega una nueva lista de ele-
mentos. Un separador tiene como finalidad separar grupos de órdenes de un mis-
mo menú, lógicamente en función de su actividad.

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:

Para crear un menú, los pasos a ejecutar son los siguientes:

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

2. Introducir datos en la ventana de propiedades. Para crear un nuevo menú se-


leccione el rectángulo vacío en la barra de menús; para modificarlo simple-
mente selecciónelo. Después edite sus propiedades. En la caja de texto
Caption se escribe el nombre del menú que se desea editar, el cual aparecerá
durante el diseño y la ejecución de la aplicación en la barra de menús. Inserte
CAPÍTULO 6: TRABAJANDO CON MENÚS 189

un ampersand (&) antes de la letra que da acceso al menú. El usuario podrá


seleccionar este menú pulsando la tecla Alt más la tecla que da acceso al
menú, la cual aparece subrayada. El nombre de un menú no lleva ID.

A continuación, para añadir elementos a un menú, seleccione el rectángulo


vacío debajo del nombre del mismo. Por ejemplo, en la figura siguiente, Ar-
chivo, Edición y Ayuda son menús.

3. Introducir los elementos que componen el menú. En la caja de texto Caption


se escribe el nombre del elemento del menú. Inserte un ampersand (&) antes
de la letra que da acceso al elemento. En la caja de texto ID se escribe el iden-
tificador utilizado en el código para referirse al elemento, o bien se elige uno
de la lista. Los ID de la lista, ya tienen funcionalidad asignada en una aplica-
ción construida con AppWizard. Por ejemplo, ID_APP_EXIT es el identifica-
dor de la orden Salir del menú Archivo, y ya está implementada.

4. Crear un submenú. Un elemento puede ser también un submenú. Para que un


elemento sea un submenú tiene que activar (poner a valor true) su propiedad
Pop-up. Por ejemplo, en la figura siguiente el elemento Guardar es un sub-
menú.

5. Añadir un separador. Utilizando separadores puede agrupar las órdenes en


función de su actividad. Para insertar un separador, seleccione el rectángulo
vacío y active la propiedad Separator. Por ejemplo, en el menú Archivo se ha
insertado un separador antes de la orden Salir.

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.

En general, los elementos que componen cada menú no estarán operativos


hasta no unir el código correspondiente. No obstante, AppWizard hace que algu-
nas órdenes como Salir ya estén operativas, por lo que no necesitaremos escribir
una función para ellas, a no ser que deseemos modificar su comportamiento.

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.

La propiedad ID permite identificar en el código a un menú o a uno de sus


elementos.

La propiedad Separator permite incluir una “línea gráfica” de separación en-


tre dos elementos de un menú.

La propiedad Checked es útil para indicar el estado (seleccionado, no selec-


cionado) de la opción a la que hace referencia la orden. Cuando se señala esta
CAPÍTULO 6: TRABAJANDO CON MENÚS 191

propiedad (se pone a valor true) aparece una marca b a la izquierda del elemento
del menú.

La propiedad Pop-up define un elemento como un submenú.

La propiedad Grayed es útil para desactivar una orden en un momento en el


cual no tiene sentido que esté activa. Por ejemplo, si estamos trabajando con un
editor y no tenemos seleccionado un bloque de texto, no tiene sentido que la or-
den Copiar del menú Edición esté activa. Cuando se hace clic en una orden de un
menú, ésta será ejecutada si su propiedad Grayed no está activada. Si la propiedad
Grayed está activada, la orden se muestra en tono gris y no puede ser ejecutada. Si
esta propiedad se activa durante el diseño, la orden correspondiente aparecerá en
tono gris (desactivada) cuando se ejecute la aplicación; lógicamente con la inten-
ción de activarla cuando sea preciso.

La propiedad Inactive es igual que la propiedad Grayed con la diferencia de


que la orden aparece con el texto normal. Si la propiedad Grayed es true la pro-
piedad Inactive es siempre true.

La propiedad Help permite que un menú, por ejemplo Ayuda, aparezca en el


margen derecho (sólo tiene efecto durante la ejecución). Esta acción, también
puede ser realizada por la propiedad Right-to-left order and alignment.

La propiedad Break puede tomar uno de los siguientes valores:

• None (valor por omisión): No tiene efecto.


• Column: En una barra de menús estática, este valor pone el elemento en una
nueva línea. En menús flotantes, este valor pone el elemento en una nueva co-
lumna sin línea divisoria entre las columnas.
• Bar: Igual que Column excepto para menús flotantes: el valor Bar separa la
nueva columna de la columna existente con una línea vertical.

Esta propiedad, igual que la anterior (Help) afecta al aspecto del menú sólo
durante la ejecución, no durante el diseño.

La propiedad Prompt contiene el texto que aparece en la barra de estado


cuando el elemento es seleccionado. El texto es colocado en la tabla de cadenas de
caracteres con el mismo identificador que el elemento del menú.

La plantilla del menú almacenada en el fichero de recursos (.rc) incorpora ca-


da una de las propiedades especificadas para cada uno de los elementos de la
misma. Por ejemplo, la barra de menús diseñada para ilustrar lo hasta ahora ex-
puesto se corresponde con la siguiente plantilla:
192 VISUAL C++. APLICACIONES PARA WIN32

IDR_MAINFRAME MENU PRELOAD DISCARDABLE


BEGIN
POPUP "&Archivo"
BEGIN
MENUITEM "&Abrir...\tCtrl+A", ID_FILE_OPEN
POPUP "&Guardar"
BEGIN
MENUITEM "&Mismo nombre", ID_ARCHIVO_GUARDAR_MISMONOMBRE
MENUITEM "&Otro nombre", ID_ARCHIVO_GUARDAR_OTRONOMBRE
END
MENUITEM SEPARATOR
MENUITEM "&Salir", ID_APP_EXIT
END
POPUP "&Editar"
BEGIN
MENUITEM "Cor&tar\tCtrl+X", ID_EDIT_CUT, GRAYED
MENUITEM "&Copiar\tCtrl+C", ID_EDIT_COPY, GRAYED
MENUITEM "&Pegar\tCtrl+V", ID_EDIT_PASTE
END
POPUP "Ay&uda", HELP
BEGIN
MENUITEM "&Acerca de Menús...", ID_APP_ABOUT
END
END

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).

DESARROLLO DE UN EDITOR DE TEXTOS


Nuestra próxima aplicación va a consistir en el desarrollo de un editor de textos.
Este editor, aunque de prestaciones muy limitadas, va a servir para poner en
práctica los menús y para incorporar nuevos conceptos como los aportados por las
cajas de texto multilínea y el portapapeles (denominado Clipboard en Windows).

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

Caja de texto multilínea


El aspecto y el comportamiento de una caja de texto está muy influenciado por las
propiedades, Multiline, Horizontal scroll, Vertical scroll y Want return, las cua-
les, cuando se trata de una caja de texto creada desde una plantilla, pueden ser es-
tablecidas solamente durante el diseño.

La propiedad Multiline permite utilizar una caja de texto capaz de contener


varias líneas. Cuando ponemos un control de este tipo en un formulario o en una
caja de diálogo y pulsamos la tecla Entrar, la respuesta por omisión está encami-
nada a activar el botón por omisión. Si, en este caso, lo que deseamos es que se
inserte en el texto un CR+LF (retorno de carro más avance de línea), tenemos que
establecer la propiedad Want return.

Las propiedad Horizontal scroll proporciona una barra de desplazamiento


horizontal y la propiedad Vertical scroll una barra de desplazamiento vertical. Es-
tas propiedades de una caja de texto no tienen que ser confundidas con el control
barra de desplazamiento que veremos más adelante.

Si en una caja de texto multilínea no hay una barra de desplazamiento hori-


zontal y su propiedad Auto HScroll no está activada, una línea de texto que alcan-
ce el extremo derecho de la caja, continuará automáticamente en la línea
siguiente.

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.

2. Utilizando el editor de diálogos, dibuje sobre el formulario, que va a ser la


vista de la ventana principal, una caja de texto y ajústela al tamaño que desee;
por ejemplo, al tamaño del formulario.
194 VISUAL C++. APLICACIONES PARA WIN32

3. Ponga la propiedad Multiline a valor true (señálela). Esto permite escribir en


la caja varias líneas de texto. Por omisión, esta propiedad tiene el valor false,
lo que indica que la caja de texto sólo puede contener una línea.

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.

6. Ponga la propiedad Border a valor false para eliminar el borde de la caja.

Guarde la aplicación, compílela y ejecútela. Pruebe a escribir texto, actúe so-


bre las barras de desplazamiento, modifique el texto, seleccione texto, inserte tex-
to y borre texto. Como podrá comprobar, todas estas operaciones están implícitas
sin escribir nada de código.

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.

Trabajar con texto seleccionado


En cualquier editor de texto el usuario puede seleccionar texto utilizando el ratón
o el teclado. Con el ratón, apunte al comienzo del texto a seleccionar y arrastre
con el botón izquierdo pulsado hasta haber seleccionado todo el texto. Con el te-
clado, sitúe el punto de inserción donde desea iniciar la selección, y manteniendo
pulsada la tecla Shift (mayúsculas) desplace el punto de inserción utilizando las
teclas de desplazamiento. Visual C++ da automáticamente esta capacidad a las ca-
jas de texto; no ocurre así con la funcionalidad de cortar, copiar y pegar.

Además de la capacidad de selección, la funcionalidad soportada por la clase


CEdit para las cajas de texto incluye una serie de funciones que permiten trabajar
con el texto seleccionado, a través del portapapeles (Clipboard). Algunas de estas
funciones son Undo, Clear, Copy, Cut, Paste, GetSel y SetSel.

La función Undo permite deshacer la última operación hecha en el control de


edición.
CAPÍTULO 6: TRABAJANDO CON MENÚS 195

La función Clear borra el texto seleccionado actualmente en el control de


edición.

La función Copy copia el texto actualmente seleccionado en el control de


edición, en el portapapeles.

La función Cut corta (borra) el texto actualmente seleccionado en el control


de edición y lo copia en el portapapeles.

La función Paste inserta el texto que contiene actualmente el portapapeles, en


el control de edición a partir del punto de inserción.

La función GetSel obtiene las posiciones del primer carácter seleccionado y


del primer carácter no seleccionado en el control de edición.

La función SetSel permite seleccionar un rango de caracteres en el control de


edición, similar a como lo hacemos con el ratón o con el teclado.

Utilización del portapapeles


El portapapeles de Windows permite transferir datos de unas aplicaciones a otras.
Muchas aplicaciones que manejan texto incluyen un menú de Edición con las
órdenes Cortar, Copiar y Pegar. Cuando el usuario selecciona texto y ejecuta
Cortar o Copiar, la aplicación transfiere los datos seleccionados en el documento
al portapapeles y cuando ejecuta la orden Pegar, la aplicación transfiere los datos
del portapapeles al documento. Los datos seguirán en el portapapeles hasta que se
ejecute la orden de Cortar o Copiar nuevamente.

Los datos en el portapapeles están en un formato particular, como puede ser


texto, mapa de bits o metaarchivo, entre otros. Estos formatos son identificados
por constantes:

• CF_TEXT: Una cadena de caracteres ANSI terminada en 0 que contiene un


retorno de carro y un salto de línea al final de cada línea.

• CF_BITMAP: Un mapa de bits. Un mapa de bits representa una imagen digi-


tal completa del dibujo. Dependiendo de que la imagen sea monocroma o de
color se utiliza uno o más bits para cada píxel.

• CF_METAFILEPICT: Una imagen de metaarchivo. Un metaarchivo guarda


información de la imagen como una serie de registros que se corresponden di-
rectamente con llamadas a la GDI (Rectangle, Ellipse, DrawText, etc.). En es-
te caso, el formato utilizado no es exactamente igual que un metaarchivo, más
196 VISUAL C++. APLICACIONES PARA WIN32

bien es un metaarchivo que contiene alguna información adicional según una


estructura de tipo METAFILEPICT.

Para entender las funciones miembro de CWnd relativas al portapapeles va-


mos a exponer algunos conceptos fundamentales (para más detalles, buscar en la
ayuda por “CWnd clipboard functions”). El portapapeles es esencialmente una
matriz de buzones de correo, uno por cada formato, lo que posibilita poder obte-
ner los datos en diferentes formatos. Cada buzón mantiene:

• El identificador del formato de datos.


• El handle de un objeto global de memoria que contenga los datos a pasar. Un
objeto global puede ser compartido por dos o más aplicaciones.
• El handle de la ventana que situó los datos en el portapapeles.

Una aplicación mantiene dos niveles de control con respecto al portapapeles:


a) abrir el portapapeles, que debe ser anterior a la recuperación de los datos de él,
y b) obtener la propiedad del portapapeles, que debe ser anterior a la colocación
de los datos en él. Según esto, una aplicación no puede abrir el portapapeles si ya
está abierto, y no puede ser propietaria del portapapeles a menos que lo haya
abierto. Esto es, el portapapeles es abierto, y consiguientemente poseído por una
ventana específica dentro de la aplicación, y no por la aplicación. Por lo tanto, una
vez que una aplicación ha situado los datos en el portapapeles o los ha recuperado
del portapapeles, debe cerrarlo.

Otro concepto para comprender las funciones miembro de CWnd referidas al


portapapeles es el de visor del portapapeles. Recibe este nombre cualquier pro-
grama al que se notifica de los cambios en el contenido del portapapeles. Pueden
funcionar al mismo tiempo cualquier número de visores. Windows ya suministra
un visor, pero también podemos escribir nuestro propio programa visor del porta-
papeles. Para que un programa forme parte de la cadena de visores del portapape-
les tiene que llamar a la función SetClipboardViewer; para retirar un programa
de la cadena, hay que llamar a la función ChangeClipboardChain.

Diseño del editor


Siguiendo con el diseño de la aplicación Editor, cree los menús Archivo, Edición,
Opciones y Ayuda. Para este planteamiento inicial, suponga que el menú Archivo
está formado sólo por la orden Salir, el menú Edición por las órdenes Deshacer,
Cortar, Copiar y Pegar, el menú Opciones por los submenús Texto y Colores y el
menú Ayuda por la orden Acerca de. Las propiedades para cada uno de los ele-
mentos de los menús se resumen en la tabla siguiente. Para las propiedades que no
figuran en la tabla se asume el valor por omisión. En esta tabla se han dispuesto
cada menú con sus elementos, distinguibles por el nivel de sangrado.
CAPÍTULO 6: TRABAJANDO CON MENÚS 197

Objeto Propiedad Valor


Archivo Caption &Archivo
Pop-up Sí
Salir ID ID_APP_EXIT
Caption &Salir
Edición Caption &Edición
Pop-up Sí
Deshacer ID ID_EDIT_UNDO
Caption &Deshacer\tCtrl+Z
Grayed Sí
Cortar ID ID_EDIT_CUT
Caption Cor&tar\tCtrl+X
Grayed Sí
Copiar ID ID_EDIT_COPY
Caption &Copiar\tCtrl+C
Grayed Sí
Pegar ID ID_EDIT_PASTE
Caption &Pegar\tCtrl+V
Grayed Sí
Opciones Caption &Opciones
Pop-up Sí
Texto Caption &Texto
Pop-up Sí
Fuentes Caption &Fuentes
Pop-up Sí
Courier New ID ID_OP_TEX_FU_COURIERNEW
Caption Courier New
Arial ID ID_OP_TEX_FU_ARIAL
Caption Arial
Por omisión ID ID_OP_TEX_FU_POROMISION
Caption Por omisión
Tamaño Caption &Tamaño
Pop-up Sí
16 ID ID_OP_TEX_TAM_16
Caption 16
24 ID ID_OP_TEX_TAM_24
Caption 24
Por omisión ID ID_OP_TEX_TAM_POROMISION
Caption Por omisión
Colores Caption &Colores
Pop-up Sí
198 VISUAL C++. APLICACIONES PARA WIN32

Fondo Caption &Fondo


Pop-up Sí
Blanco ID ID_OP_CO_FONDO_BLANCO
Caption &Blanco
Verde ID ID_OP_CO_FONDO_VERDE
Caption &Verde
Azul ID ID_OP_CO_FONDO_AZUL
Caption &Azul
Texto Caption &Texto
Pop-up Sí
Negro ID ID_OP_CO_TEXTO_NEGRO
Caption &Negro
Verde ID ID_OP_CO_TEXTO_VERDE
Caption &Verde
Azul ID ID_OP_CO_TEXTO_AZUL
Caption &Azul
Ayuda Caption &Ayuda
Pop-up Sí
Help Sí
Acerca de ID IDD_APP_ABOUT
Caption &Acerca del Editor...

Algunas órdenes como Cortar o Copiar especifican a la derecha de su título


el conjunto de teclas que hay que pulsar para ejecutar la orden directamente sin
recurrir al menú; por eso, estas teclas reciben el nombre de aceleradores, de los
que hablaremos más adelante. Finalizado el diseño, el resultado será similar al si-
guiente:
CAPÍTULO 6: TRABAJANDO CON MENÚS 199

Añadir una función miembro para un elemento de un menú

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:

Seleccione en la lista Class name la clase CEditorView con el fin de declarar


en ella las funciones miembro asociadas con cada una de las órdenes de los
menús. Por ejemplo, empecemos por la orden Deshacer. Cuando el usuario haga
clic en la orden Deshacer, Windows envía mensajes como WM_MENUSELECT
y WM_COMMAND. Para manipular WM_COMMAND, seleccione en la lista
Object IDs el objeto ID_EDIT_UNDO, y en la lista Messages el mensaje COM-
MAND. A continuación, haga clic en el botón Add Function y añada la función
miembro OnEditDeshacer.
200 VISUAL C++. APLICACIONES PARA WIN32

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
}

Activar los elementos de un menú

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).

Según lo expuesto, abra ClassWizard y añada la función OnUpdateEditDes-


hacer vinculada al objeto ID_EDIT_UNDO, para manipular el mensaje UPDA-
TE_COMMAND_UI. Después, escriba el código que se indica a continuación.
Previamente debe vincular el control IDC_EDIT1 con una variable de tipo CEdit
(categoría Control) denominada m_ControlEdit1.

void CEditorView::OnUpdateEditDeshacer(CCmdUI* pCmdUI)


{
pCmdUI->Enable( m_ControlEdit1.CanUndo() );
}

Cuando el menú se despliega, el sistema busca y llama a cada controlador de


UPDATE_COMMAND_UI, el cual, a su vez, llama a las funciones miembro de
CCmdUI tal como Enable, SetCheck, SetRadio, SetText y ContinueRouting, y en-
tonces visualiza adecuadamente cada elemento del menú.
CAPÍTULO 6: TRABAJANDO CON MENÚS 201

La clase CCmdUI se utiliza solamente en las funciones miembro, manipula-


doras de mensajes relacionados con la macro ON_UPDATE_COMMAND_UI, de
clases derivadas de CCmdTarget.

Un elemento de un menú puede reemplazarse por un botón de una barra de


herramientas o por otros objetos de la interfaz del usuario, sin que se necesite mo-
dificar el código del controlador de UPDATE_COMMAND_UI.

La función Enable permite activar o desactivar un elemento de un menú, un


botón de la barra de herramientas, un botón o un control de CDialogBar, o bien
hacer visible o invisible el texto de un panel de la barra de estado. Para activar
una orden, hay que llamar a la función Enable pasando como argumento el valor
true, y para desactivarla, hay que pasar como argumento el valor false. El resto de
las funciones se comportan de forma análoga. La siguiente tabla resume el efecto
que las funciones miembro de CCmdUI tienen sobre diversos objetos de la inter-
faz del usuario:

Elemento de la Enable SetCheck SetRadio SetText


interfaz
Elemento de Lo activa o Pone o quita Pone o quita Le asigna el
un menú inactiva una señal b una señal i título
Botón de la barra Lo activa o Lo selecciona Igual que No aplicable
de herramientas inactiva o no SetCheck
Panel de la barra Texto visible borde normal Igual que Coloca texto
de estado o invisible o resaltado SetCheck en el panel
Botón de Lo activa o Casilla de v. Igual que Coloca texto
CDialogBar inactiva señalada o no SetCheck en el botón
Control de Lo activa o No aplicable No aplicable Coloca texto
CDialogBar inactiva en la ventana

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.

Según lo expuesto, la condición para activar las órdenes Cortar y Copiar es


que el usuario haya seleccionado un bloque de texto, lo que puede saberse inter-
rogando el valor devuelto por la función GetSel de la clase CEdit:

void GetSel( int& nPosInicial, int& nPosFinal ) const;


202 VISUAL C++. APLICACIONES PARA WIN32

El parámetro nPosInicial hace referencia al entero que recibirá la posición del


primer carácter seleccionado y nPosFinal hace referencia al entero que recibirá la
posición del primer carácter no seleccionado.

Realizando un proceso análogo al seguido para la orden Deshacer, añada las


siguientes funciones miembro:

void CEditorView::OnUpdateEditCortar(CCmdUI* pCmdUI)


{
// Activar/desactivar Cortar si hay/no hay texto seleccionado
int nPosInicial, nPosFinal;
m_ControlEdit1.GetSel( nPosInicial, nPosFinal );
pCmdUI->Enable( (nPosFinal - nPosInicial) > 0 );
}

void CEditorView::OnUpdateEditCopiar(CCmdUI* pCmdUI)


{
// Activar/desactivar Copiar si hay/no hay texto seleccionado
int nPosInicial, nPosFinal;
m_ControlEdit1.GetSel( nPosInicial, nPosFinal );
pCmdUI->Enable( (nPosFinal - nPosInicial) > 0 );
}

La orden Pegar estará activa cuando haya texto en el portapapeles, y no lo es-


tará cuando el portapapeles esté vació. Esto lo podemos determinar a través de
una llamada a la función de Windows IsClipboardFormatAvailable. Si hay da-
tos con el formato especificado la función devuelve un valor distinto de 0; en otro
caso devuelve un 0. Esta función típicamente se utiliza con aplicaciones que reco-
nocen un único formato del portapapeles. Cuando la aplicación reconoce más de
un formato la función que se utiliza es GetPriorityClipboardFormat. Según es-
to, añada la siguiente función:

void CEditorView::OnUpdateEditPegar(CCmdUI* pCmdUI)


{
// Activar/desactivar Pegar si hay/no hay texto en el portapapeles
IsClipboardFormatAvailable( CF_TEXT )
? pCmdUI->Enable( true )
: pCmdUI->Enable( false );
}

Procesar las órdenes de un menú

¿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

ID_EDIT_UNDO, para manipular el mensaje COMMAND. Después, escriba el


código que se indica a continuación:

void CEditorView::OnEditDeshacer()
{
// Deshacer la última operación
m_ControlEdit1.Undo();
}

Una vez seleccionado un bloque de texto, podemos copiarlo o cortarlo. El


bloque resultante de esta operación se almacenará en el portapapeles. La ventaja
de utilizar el portapapeles es que éste permite intercambiar información entre las
aplicaciones que lo utilicen. Esto es, podemos llevar bloques de información de
una aplicación a otra utilizando las órdenes de Cortar, Copiar y Pegar.

Cuando el usuario haga clic sobre la orden Cortar, se ejecutará el procedi-


miento asociado con ella. Por lo tanto, utilizando ClassWizard, añada la función
OnEditCortar asociada con el objeto ID_EDIT_CUT, para manipular el mensaje
COMMAND. Después, escriba el código que se indica a continuación:

void CEditorView::OnEditCortar()
{
// Borrar la selección y copiarla en el portapapeles
m_ControlEdit1.Cut();
}

La orden Copiar se diferencia de la orden Cortar en que no borra el texto se-


leccionado. Utilizando ClassWizard, añada la función OnEditCopiar asociada con
el objeto ID_EDIT_COPY, para manipular el mensaje COMMAND. Después, es-
criba el código que se indica a continuación:

void CEditorView::OnEditCopiar()
{
// Copiar la selección en el portapapeles
m_ControlEdit1.Copy();
}

Cuando el usuario haga clic sobre la orden Pegar se ejecutará la función


OnEditPegar, la cual, según se muestra a continuación, colocará el texto del por-
tapapeles sobre el texto seleccionado o en su defecto, a partir de la posición del
punto de inserción. Utilizando ClassWizard, añada la función OnEditPegar aso-
ciada con el objeto ID_EDIT PASTE para manipular el mensaje COMMAND.
Después escriba el código que se indica a continuación:

void CEditorView::OnEditPegar()
{
204 VISUAL C++. APLICACIONES PARA WIN32

// Poner en el control el texto del portapapeles


m_ControlEdit1.Paste();
}

Como hemos podido observar en los ejemplos anteriores, cuando se ejecuta


una aplicación, se realiza automáticamente la llamada UpdateData( false ) para
iniciar los controles de la vista. De no ser así, esta operación tendría que ser reali-
zada explícitamente. Análogamente, para una caja de diálogo, esta función es lla-
mada automáticamente por la aplicación, en la implementación por omisión de la
función OnInitDialog.

Así mismo, para ajustar el marco de la ventana al tamaño de la vista, la fun-


ción OnInitialUpdate llama a la función ResizeParentToFit:

void CEditorView::OnInitialUpdate()
{
CFormView::OnInitialUpdate();
GetParentFrame()->RecalcLayout();
ResizeParentToFit();
}

Guarde la aplicación, compílela y ejecútela. Escriba texto y pruebe las opera-


ciones de Deshacer, Cortar, Copiar y Pegar.

A continuación vamos a desarrollar el código para el menú Opciones. Este


menú visualiza dos submenús, Texto y Colores. A su vez, Texto visualiza otros
dos submenús, Fuentes y Tamaño, y Colores otros dos, Fondo y Texto.

Una fuente es un tipo de letra. El número de fuentes disponibles en Visual


C++ varía en función de la configuración de su sistema Windows. En nuestro edi-
tor sólo vamos a incluir tres tipos: Courier New, Arial y el tipo de letra estableci-
do por omisión.

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

class CEditorView : public CFormView


{
// ...
public:
LOGFONT m_lf; // características del tipo de letra
CFont m_Fuente; // fuente a utilizar por IDC_EDIT1
// ...
};

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.

Seleccionar una orden de un menú

Para acceder a las propiedades de un elemento de un menú, hay que conocer el


identificador del menú. Haciendo un pequeño análisis, cuando el usuario hace clic
en una orden del menú Fuentes, Windows envía a la ventana asociada con el
menú (en nuestro caso a la ventana marco) un mensaje WM_MENUSELECT
seguido de un mensaje WM_COMMAND. Según vimos en el capítulo 2,
WM_MENUSELECT será manejado por la ventana marco o por su ventana pa-
dre, por omisión. En cambio, WM_COMMAND no es procesado directamente
por la ventana marco, sino que ésta le da la primera oportunidad a su ventana hija,
la vista.

En general, el mensaje WM_COMMAND se envía cuando el usuario selec-


ciona una orden de un menú, cuando un control envía un mensaje de notificación
a su ventana de padre, o cuando un acelerador (combinación de teclas de acceso a
Faltan páginas...
222 VISUAL C++. APLICACIONES PARA WIN32

DESARROLLO DE UN RELOJ DESPERTADOR


Este ejemplo le enseñará, entre otras cosas, a utilizar un temporizador, a cambiar
durante la ejecución una orden de un menú, a añadir o a borrar durante la ejecu-
ción órdenes en un menú, a visualizar menús emergentes y a guardar y a recuperar
del registro de Windows la configuración inicial para una aplicación.

Vamos a diseñar un reloj despertador digital como el que se muestra en la fi-


gura siguiente. El reloj tiene una pantalla para visualizar la hora y una caja de tex-
to denominada Despertador donde el usuario puede escribir la hora a la que
quiere ser avisado. Para activar o desactivar el despertador, el usuario dispone de
un menú denominado también Despertador.

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.

Para instalar un temporizador en una aplicación Windows, hay que llamar a la


función miembro SetTimer de la clase CWnd. Ésta tiene un parámetro que espe-
cifica el intervalo de tiempo en milisegundos que tiene que transcurrir para que la
función OnTimer se ejecute independientemente del usuario. El valor del pará-
metro puede oscilar entre 0 y 4.294.967.295 msegs. (50 días aproximadamente).

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.

Cuando una aplicación termine con el temporizador, es bueno llamar a la fun-


ción KillTimer miembro de CWnd para detener los mensajes WM_TIMER. A
partir de Windows 95, el número de temporizadores es teóricamente ilimitado. No
obstante, cuando SetTimer no puede instalar un temporizador, devuelve 0.

Una utilidad típica de un temporizador es verificar de alguna forma la hora


del sistema, para ver si es el momento de ejecutar alguna tarea.

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.

Diseño de la ventana y de los controles


Inicie una nueva aplicación SDI, denominada Reloj, que utilice un formulario co-
mo ventana principal y asigne a la ventana el título Reloj Despertador. A conti-
nuación añadiremos los controles. Lo primero que vamos a hacer es colocar la
pantalla del reloj. Haga un clic en la herramienta caja de texto de la caja de
herramientas y coloque la caja de texto en el lugar que se indica en la figura.
Ajuste su tamaño y asígnele las siguientes propiedades:

Tab stop: false la tecla Tab no colocará el punto de inserción


sobre este control.
ID: IDC_HORA Identificador del control.
Read-only: true Sólo lectura.
224 VISUAL C++. APLICACIONES PARA WIN32

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 continuación añadimos una caja de texto que va a dar lugar al despertador.


Haga un clic en el control caja de texto de la caja de herramientas y colóquela en
el lugar que se indica en la figura. Después, ajuste su tamaño y asígnele las si-
guientes propiedades:

ID: IDC_DESPERTADOR Identificador del control.

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.

Para activar o desactivar el despertador vamos a añadir un menú. Abra la ven-


tana de diseño de menús y diseñe el menú Despertador con las órdenes Desperta-
dor No y Salir:

Caption: Despertador No ID: ID_DESPERTADOR_SI_NO


Caption: Salir ID: ID_APP_EXIT

Conserve el menú Ayuda con la orden Acerca de Reloj y personalice la caja


de diálogo Acerca de... Es una buena idea guardar ahora el trabajo realizado. Eje-
cute la orden Save All. A continuación abra ClassWizard para vincular las varia-
bles miembro m_CtrlHora y m_CtrlDespertador, ambas de tipo CEdit (categoría
Control), a las cajas de texto IDC_HORA e IDC_DESPERTADOR, respectiva-
mente. El siguiente paso es unir el código a los controles.

Unir el código a los controles y a la ventana


En primer lugar vamos a editar la función OnInitialUpdate para que, además de
adaptar el tamaño de la ventana marco al contorno de la vista, establezca las fuen-
tes de las cajas IDC_HORA e IDC_DESPERTADOR. Antes, definiremos las va-
CAPÍTULO 6: TRABAJANDO CON MENÚS 225

riables m_FuenteHora y m_FuenteDespertador para crear las fuentes a las que


nos hemos referido:

class CRelojView : public CFormView


{
// ...
public:
//{{AFX_DATA(CRelojView)
enum { IDD = IDD_RELOJ_FORM };
CEdit m_CtrlHora;
CEdit m_CtrlDespertador;
//}}AFX_DATA
CFont m_FuenteDespertador; // fuente para IDC_DESPERTADOR
CFont m_FuenteHora; // fuente para IDC_HORA
// ...
};

Según lo expuesto, añada a la función OnInitialUpdate miembro de la clase


CRelojView, el código que se muestra a continuación:

void CRelojView::OnInitialUpdate()
{
CFormView::OnInitialUpdate();
// Ajustar el tamaño de la ventana marco al tamaño de la vista
GetParentFrame()->RecalcLayout();
ResizeParentToFit();

// Fuente para IDC_DESPERTADOR


LOGFONT lf;
memset( &lf, 0, sizeof(LOGFONT) );
lf.lfHeight = UnidadesLogicas(14);
m_FuenteDespertador.CreateFontIndirect( &lf );
m_CtrlDespertador.SetFont( &m_FuenteDespertador );

// Fuente para IDC_HORA


lf.lfHeight = UnidadesLogicas(24);
m_FuenteHora.CreateFontIndirect( &lf );
m_CtrlHora.SetFont( &m_FuenteHora );
}

Observe que para calcular la altura de la fuente en unidades lógicas invoca-


mos a la función UnidadesLogicas vista anteriormente en este mismo capítulo.

En segundo lugar redefiniremos la función PreCreateWindow para redefinir


el estilo de la ventana marco con la intención de que el usuario no disponga de un
botón de maximizar y tampoco de un borde que permita modificar el tamaño de la
ventana. Para más detalles, busque PreCreateWindow anteriormente en este
226 VISUAL C++. APLICACIONES PARA WIN32

mismo capítulo. Al tratarse de la ventana marco, la función PreCreateWindow


tiene que ser una función miembro de 1a clase CMainFrame:

BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)


{
if ( !CFrameWnd::PreCreateWindow(cs) )
return FALSE;
cs.style &= ~(FWS_ADDTOTITLE | WS_MAXIMIZEBOX | WS_THICKFRAME);
return TRUE;
}

El siguiente paso es unir el código correspondiente a cada uno de los contro-


les para un correcto funcionamiento del reloj.

Empecemos por el despertador. Nuestra intención es que el usuario escriba en


esta caja la hora a la que quiere ser avisado. Por lo tanto, los caracteres que él
pueda escribir los restringiremos a los dígitos “0” a “9”, “:” y BackSpace
(VK_BACK).

Puesto que los controles de un formulario o de una caja de diálogo creados


desde una plantilla no tienen un mapa de mensajes como lo tiene su ventana pa-
dre, necesitamos, para procesar el mensaje WM_CHAR enviado por el control,
aplicar la técnica de subclasificación de ventanas (vea en el Capítulo 5, “Subclasi-
ficación utilizando ClassWizard”).

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.

Una vez realizada la subclasificación, el paso siguiente es definir la función


OnChar para manipular el mensaje WM_CHAR recibido por el objeto m_Ctrl-
Despertador cada vez que se escribe un carácter sobre el mismo. Para ello, invo-
que a ClassWizard, seleccione el diálogo Message Maps de la ventana MFC
ClassWizard, elija la clase CMiEdit, el objeto CMiEdit de la lista de objetos, el
mensaje WM_CHAR de la lista de mensajes y pulse el botón Add Function. A
continuación edite la función como se indica a continuación:
CAPÍTULO 6: TRABAJANDO CON MENÚS 227

void CMiEdit::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)


{
if ((nChar<'0' || nChar>'9') && nChar != ':' && nChar != VK_BACK)
MessageBeep( MB_ICONEXCLAMATION );
else
CEdit::OnChar(nChar, nRepCnt, nFlags);
}

Esta función recibe como argumento el código de la tecla pulsada y lo alma-


cena en el parámetro nChar. A continuación verifica el carácter; si es uno de los
no permitidos, emite un pitido y lo deshecha; esto es, no lo pasa a la función On-
Char implementada por omisión, de la clase CEdit.

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”.

El paso siguiente es presentar la hora a través del control IDC_HORA. Para


visualizar la hora Visual C++ tiene la función GetCurrentTime miembro de la
clase CTime, que devuelve un objeto CTime con la fecha y la hora.

Entonces, para presentar la hora bastaría con escribir:

// Obtener y visualizar la hora


CTime Hora = CTime::GetCurrentTime();
CString HoraTxt = Hora.Format("%H:%M:%S");
SetDlgItemText( IDC_HORA, HoraTxt );

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.

Para instalar un temporizador invoque a la función SetTimer en el instante en


el que se inicie la aplicación; esto es, desde la función OnInitialUpdate.

void CRelojView::OnInitialUpdate()
{
// ...
// Establecer un temporizador
SetTimer( ID_1SEGUNDO, 1000, NULL );
}

El primer parámetro es el identificador del temporizador. El segundo paráme-


tro es el tiempo en milisegundos que transcurre hasta enviar un mensaje
228 VISUAL C++. APLICACIONES PARA WIN32

WM_TIMER. El valor especificado se redondea a un valor entero de tics de reloj


(por ejemplo, 1000/54,925=18 que son en realidad 989 msegs). El tercer paráme-
tro indica qué función debe procesar los mensajes WM_TIMER. Esta función
será una función call-back (una función dentro de la aplicación que puede ser
llamada por Windows). Cuando este parámetro es NULL, los mensajes WM_TI-
MER son colocados en la cola de mensajes de nuestra aplicación. Si no hay
ningún temporizador disponible, SetTimer devuelve 0. Se pueden detener los
mensajes WM_TIMER en cualquier momento llamando a la función KillTimer.

Antes de continuar defina el identificador ID_1SEGUNDO. Para ello, ejecute


la orden Resource Symbols del menú View, o haga clic en el botón [ID=] de la ba-
rra de herramientas Resource Symbols, y añada este nuevo identificador. A conti-
nuación, desde ClassWizard, añada la función OnTimer para procesar los
mensajes WM_TIMER que reciba el objeto CRelojView (en el diálogo Message
Maps, elija el objeto CRelojView, el mensaje WM_TIMER y pulse el botón Add
Function). Ahora edite la función así:

void CRelojView::OnTimer(UINT nIDEvent)


{
// Obtener y visualizar la hora
CTime Hora = CTime::GetCurrentTime();
CString HoraTxt = Hora.Format("%H:%M:%S");
SetDlgItemText( IDC_HORA, HoraTxt );

CFormView::OnTimer(nIDEvent);
}

La función OnTimer es invocada cada vez que transcurre el tiempo especifi-


cado en el temporizador ID_1SEGUNDO. Cuando se establecen varios tempori-
zadores, OnTimer puede saber de qué temporizador se trata porque el parámetro
nIDEvent contiene el ID del temporizador.

Cuando se ejecutan varias aplicaciones simultáneamente puede darse el caso,


por la prioridad que tienen asignada implícitamente los mensajes, de que no sea
posible que un mensaje WM_TIMER se despache en su momento. Cuando ocu-
rre esto, Windows no sigue poniendo mensajes WM_TIMER en la cola de la
aplicación; podríamos decir que los reduce a uno (vea el apartado “Temporizador”
descrito anteriormente en este mismo capítulo).

Continuando con la aplicación, supongamos ahora que el usuario escribe una


hora en el despertador. Para que se produzcan pitidos de aviso cuando se alcance
esa hora, tienen que darse dos condiciones: una, que el despertador esté activado,
y otra, que la hora actual sea igual o mayor, o simplemente mayor, que la hora es-
pecificada en el despertador. La condición mayor es para que el pitido continúe
hasta que el usuario desactive el despertador o bien hasta que pasen 5 minutos.
CAPÍTULO 6: TRABAJANDO CON MENÚS 229

Nota: Puesto que compararemos cadenas de caracteres, utilice ocho caracteres


para escribir la hora del despertador. Se deja como ejercicio para el lector automa-
tizar este proceso para los formatos de hora que no tengan ocho caracteres.

Para saber el estado del despertador, defina una variable m_bDespertadorSi


miembro de la clase CRelojView de tipo bool y asígnele un valor inicial false. Un
valor false indica despertador desactivado y un valor true indica despertador acti-
vado. Para acceder de una forma fácil a la hora actual y a la hora del despertador,
abra ClassWizard y vincule las variables miembro m_sHora y m_sDespertador,
ambas de categoría Valor y de tipo CString, a las cajas de texto IDC_HORA e
IDC_DESPERTADOR, respectivamente (un control puede tener vinculada una o
más variables diferentes). Así mismo, inicie el despertador al valor “00:00:00”.

class CRelojView : public CFormView


{
// ...
public:
//{{AFX_DATA(CRelojView)
enum { IDD = IDD_RELOJ_FORM };
CMiEdit m_CtrlHora;
CMiEdit m_CtrlDespertador;
CString m_sDespertador;
CString m_sHora;
//}}AFX_DATA
CFont m_FuenteDespertador; // fuente para IDC_DESPERTADOR
CFont m_FuenteHora; // fuente para IDC_HORA
bool m_bDespertadorSi;
// ...
};

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

Realizamos las iniciaciones especificadas en el constructor de la clase CReloj-


View. De esta forma cuando se ejecute la función OnInitialUpdate la caja de tex-
to IDC_DESPERTADOR aparecerá iniciada. Recuerde que esta función ejecuta
automáticamente una llamada UpdateData( false ).
230 VISUAL C++. APLICACIONES PARA WIN32

A continuación y de acuerdo con lo expuesto, modifique la función OnTimer


de la forma siguiente:

void CRelojView::OnTimer(UINT nIDEvent)


{
// Obtener y visualizar la hora
CTime Hora = CTime::GetCurrentTime();
CString HoraTxt = Hora.Format("%H:%M:%S");
SetDlgItemText( IDC_HORA, HoraTxt );

CFormView::OnTimer(nIDEvent);

UpdateData( true ); // actualizar las variables


// Activar la alarma si procede
if ( m_sDespertador < m_sHora && m_bDespertadorSi )
{
// La alarma suena durante 5 minutos
CTimeSpan Diferencia(0, 0, 5, 0);
Hora -= Diferencia;
HoraTxt = Hora.Format("%H:%M:%S");
if ( m_sDespertador > HoraTxt )
{
MessageBeep(MB_OK);
} }
}

Desarrollemos ahora el código para las órdenes del menú Despertador. La or-
den Salir está implementada por omisión.

El título de la orden Despertador No ya indica que el despertador no está ac-


tivado, lo que implica que la variable m_bDespertadorSi tenga un valor false.
Cuando el usuario haga clic sobre esta orden, el título de la misma deberá cambiar
para indicar Despertador Si, que quiere decir que el despertador está activado, y
la variable m_bDespertadorSi tomará el valor true. En definitiva, un clic en esta
orden activa o desactiva el despertador.

Cambiar en ejecución una orden de un menú


La orden para activar y desactivar el despertador que acabamos de comentar, tiene
que ser cambiada durante la ejecución del programa. Para hacer esto, no tiene más
que asignar el título deseado al control denominado ID_DESPERTADOR_SI_NO.
Recuerde que cuando el usuario hace clic en un menú, Windows envía mensajes
de iniciación como WM_INITMENU y WM_INITMENUPOPUP, antes de que
el menú sea desplegado. Para manipular estos mensajes, el mapa de mensajes uti-
liza la macro ON_UPDATE_COMMAND_UI.
CAPÍTULO 6: TRABAJANDO CON MENÚS 231

Por lo tanto, desde el editor de menús, haga clic en el menú Despertador y


después en la orden Despertador No. A continuación abra ClassWizard, seleccio-
ne la clase CRelojView, el objeto ID_DESPERTADOR_SI_NO, el mensaje UP-
DATE_COMMAND_UI y añada la función:

void CRelojView::OnUpdateDespertadorSiNo(CCmdUI* pCmdUI)


{
pCmdUI->SetText(m_bDespertadorSi ?
"&Despertador Sí" :
"&Despertador No");
}

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.

Para cambiar el valor de la variable m_bDespertadorSi, utilizando ClassWi-


zard, añada la función OnDespertadorSiNo asociada con el objeto ID_DESPER-
TADOR_SI_NO para manipular el mensaje COMMAND, y escriba el código que
se indica a continuación:

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

Caption Despertador No Despertador Sí


ID ID_DESPERTADOR_NO ID_DESPERTADOR_SI
posición 0 1
232 VISUAL C++. APLICACIONES PARA WIN32

Supongamos también que ambas órdenes están vinculadas a las funciones


OnDespertadorSiNo y OnUpdateDespertadorSiNo.

El primer menú desplegable de la barra de menús tiene índice 0, el siguiente 1


y así sucesivamente. A su vez, cada elemento dentro del menú desplegable tiene
índice 0 el primero, 1 el segundo, y así sucesivamente. Por ejemplo:

Barra de menús Índices; menús Elementos Índices; elementos


Despertador 0
Despertador No 0
Despertador Sí 1
(separador) 2
Salir 3
Ayuda 1
Acerca de Reloj... 0

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);
}

La función GetMenu obtiene un puntero al menú principal o barra de menús


y la función GetSubMenu de la clase CMenu devuelve un puntero al menú des-
plegable especificado. La constante MF_BYPOSITION especifica que el acceso a
los elementos del menú se va a hacer por posición (por índice).

La función RemoveMenu de CMenu quita un elemento existente de un


menú. En nuestro ejemplo, eliminamos el elemento que está en la posición 1. El
resultado es:

Barra de menús Índices; menús Elementos Índices; elementos


Despertador 0
Despertador No 0
(separador) 1
Salir 2
Ayuda 1
Acerca de Reloj... 0
CAPÍTULO 6: TRABAJANDO CON MENÚS 233

Cuando el usuario haga clic sobre el menú Despertador, se invocará la fun-


ción OnUpdateDespertadorSiNo. La ejecución de esta función se desarrolla de la
forma siguiente: se elimina la orden 0 existente y seguidamente se inserta la orden
relacionada con el valor de la variable m_bDespertadorSi. Esto es:

void CRelojView::OnUpdateDespertadorSiNo(CCmdUI* pCmdUI)


{
CMenu *pMenu = GetParent()->GetMenu();
pMenu = pMenu->GetSubMenu(0);
pMenu->RemoveMenu(0, MF_BYPOSITION);
if (m_bDespertadorSi)
pMenu->InsertMenu(0, MF_BYPOSITION, ID_DESPERTADOR_SI, "&Despertador
Sí");
else
pMenu->InsertMenu(0, MF_BYPOSITION, ID_DESPERTADOR_NO, "&Despertador
No");
}

La función InsertMenu de CMenu inserta un nuevo elemento en un menú.


El primer parámetro, dependiendo del valor del segundo, MF_BYCOMMAND o
MF_BYPOSITION, indica el identificador o la posición, respectivamente, del
elemento a insertar. El tercer parámetro es el ID del elemento a insertar y el cuarto
es el título del elemento.

También, si lo prefiere, podríamos poner la orden Despertador No con una


marca a cuando esté activa y sin marca cuando no esté activa; proceso que fue
expuesto en la aplicación Editor realizada anteriormente en este mismo capítulo.

Para continuar, utilizaremos la versión inicialmente expuesta.

Otras órdenes para manipular menús


La clase CMenu de las MFC proporciona la funcionalidad necesaria para manipu-
lar menús. Ya hemos visto cómo se puede utilizar la función InsertMenu para in-
sertar un elemento en un menú y la función RemoveMenu para quitar un
elemento de un menú. Otras funciones de interés son:

• AppendMenu. Añade un elemento nuevo al final del menú.

• DeleteMenu. Elimina y destruye un elemento existente del menú. La diferen-


cia entre DeleteMenu y RemoveMenu, es que la primera destruye el elemen-
to del menú y la segunda no. Esto significa que si el elemento se corresponde
con un submenú, éste no se podrá reutilizar porque DeleteMenu elimina el
handle.
Faltan páginas...
CAPÍTULO 6: TRABAJANDO CON MENÚS 259

Recuperar los valores de configuración


Normalmente una aplicación recupera una configuración para incorporar las ca-
racterísticas que tenía cuando fue cerrada la última vez. Para realizar este trabajo
disponemos de las funciones:

BOOL GetProfileInt( LPCTSTR lpszSección, LPCTSTR lpszEntrada,


int nValorPredeterminado );

BOOL GetProfileString( LPCTSTR lpszSección, LPCTSTR lpszEntrada,


LPCTSTR lpszValorPredeterminado = NULL );

Por ejemplo, el siguiente código recupera la configuración registrada en el


apartado anterior:

CRelojView::CRelojView() : CFormView(CRelojView::IDD)
{
// ...
// Despertador si/no
m_bDespertadorSi = false;
m_sDespertador = AfxGetApp()->GetProfileString(
"Settings", "Alarma", "00:00:00");
}

El último valor, lpszValorPredeterminado, es opcional y es el valor retornado


por la función cuando no hay un valor registrado con la clave especificada.
Faltan páginas...
PARTE

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯

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

También podría gustarte