Beruflich Dokumente
Kultur Dokumente
. .
Agradecimientos ....................................................................................................................... 6
Contactar con el autor .............................................................................................................. 7
.
2 El lenguaje de programaci6n Delphi ..........................................................................89
Caracteristicas centrales del lenguaje .................................................................................. 90
Clases y objetos ....................................................................................................................... 91
Mas sobre metodos ............................................................................................................ 93
Creacion de componentes de forma dinamica ............................................................ 94
Encapsulado ............................................................................................................................ 95
Privado, protegido y public0 ............................................................................................ 96
Encapsulado con propiedades ......................................................................................... 97
Propiedades de la clase TDate ................................................................................... 99
Caracteristicas avanzadas de las propiedades ........................................................ 100
Encapsulado y formularios ............................................................................................. 101
Aiiadir propiedades a formularios ........................................................................... 101
Constructores ......................................................................................................................... 103
Destructores y el metodo Free ....................................................................................... 104
El modelo de referencia a objetos de Delphi ..................................................................... 104
Asignacion de objetos ..................................................................................................... 105
Objetos y memoria ........................................................................................................... 107
Destruir objetos una sola vez ................................................................................... 108
Herencia de 10s tipos existentes .......................................................................................... 109
Campos protegidos y encapsulado ................................................................................ 111
Herencia y compatibilidad de tipos ............................................................................... 113
Enlace posterior y polimorfismo ......................................................................................... 114
Sobrescribir y redefinir metodos ................................................................................... 115
Metodos virtuales frente a metodos dinamicos ............................................................ 117
Manejadores de mensajes ......................................................................................... 117
Metodos abstractos .......................................................................................................... 118
Conversion descendiente con seguridad de tipos .............................................................. 119
Uso de interfaces ................................................................................................................... 121
Trabajar con excepciones ..................................................................................................... 124
Flujo de programa y el bloque finally ........................................................................... 125
Clases de excepciones ..................................................................................................... 127
Registro de errores .......................................................................................................... 129
Referencias de clase .............................................................................................................. 130
Crear. componentes usando referencias de clase ....................................................... 132
.
3 La biblioteca en tiempo de ejecuc~on
.. ......................................................................... 135
Las unidades de la RTL ........................................................................................................ 136 .
Las unidades System y SysInit ...................................................................................... 139
Cambios recientes en la unidad System .................................................................. 140
Las unidades SysUtils y SysConst ................................................................................. 141
Nuevas hnciones de SysUtils .................................................................................. 142
Rutinas extendidas de formato de cadenas en Delphi 7 ........................................ 144
La unidad Math ............................................................................................................... 145
Nuevas funciones matematicas ................................................................................ 145
Redondeo y dolores de cabeza .................................................................................. 147
Las unidades ConvUtils y StdConvs .............................................................................148
La unidad DateUtils ........................................................................................................ 148
La unidad StrUtils ........................................................................................................... 149
De Pos a PosEx .......................................................................................................... 150
La unidad Types .............................................................................................................. 151
La unidad Variants y VarUtils ....................................................................................... 151
Variantes personalizadas y numeros complejos ..................................................... 152
Las unidades DelphiMM y ShareMem ......................................................................... 154
Unidades relacionadas con COM .................................................................................. 154
Convertir datos ...................................................................................................................... 154
iConversiones de divisas? ................................................................................................... 158
Gestion de archivos con SysUtils ........................................................................................162
La clase TObject ................................................................................................................... 163
Mostrar information de clase ........................................................................................ 167
.
4 La biblioteca de clases principales .............................................................................169
El paquete RTL. VCL y CLX ..............................................................................................170
Partes tradicionales de la VCL ...................................................................................... 171
La estructura de CLX ..................................................................................................... 172
Partes especificas de VCL de la biblioteca ................................................................... 173
La clase TPersistent ..............................................................................................................173
La palabra clave published .............................................................................................176
Acceso a las propiedades por su nombre ...................................................................... 177
La clase TComponent ...........................................................................................................180
Posesion ............................................................................................................................ 180
La matriz Components ......................................................................................... 181
Cambio de propietario .............................................................................................. 182
La propiedad Name ......................................................................................................... 184
. .
Elimination de campos del formulario ......................................................................... 185
Ocultar campos del formulario ...................................................................................... 186
La propiedad personalizada Tag .................................................................................... 188
Eventos ...................................................................................................... :............................ 188
Eventos en Delphi ........................................................................................................... 188
Punteros a metodo ....................................................................................................... 189
Los eventos son propiedades ......................................................................................... 190
Listas y clases contenedores ............................................................................................... 193
Listas y listas de cadena ............................................................................................... 193
Pares nombre-valor (y extensiones de Delphi 7) ................................................... 194
Usar listas de objetos ................................................................................................. 195
Colecciones ...................................................................................................................... 196
Clases de contenedores ............................................................................................. 196
. . . .
Listas asociativas de verification ............................................................................ 198
Contenedores y listas con seguridad de tipos .......................................................... 199
Streaming ............................................................................................................................... 202
La clase TStream ............................................................................................................. 202
Clases especificas de streams ......................................................................................... 204
Uso de streams de archivo .............................................................................................. 205
Las clases TReader y TWriter ........................................................................................ 206
Streams y permanencia ................................................................................................... 207
Compresion de streams con ZLib .................................................................................. 213
Resumen sobre las unidades principales de la VCL y la unidad BaseCLX ................... 215
La unidad Classes ........................................................................................................... 215
Novedades en la unidad Classes .............................................................................. 216
. .
Otras unidades prlncipales ............................................................................................. 217
.
5 Controles visuales ........................................................................................................... 219
VCL frente a VisualCLX ...................................................................................................... 220
Soporte dual de bibliotecas en Delphi .......................................................................... 222
Clases iguales, unidades diferentes ......................................................................... 223
DFM y XFM ............................................................................................................... 224
Sentencias uses .......................................................................................................... 226
Inhabilitar el soporte de ayuda a la biblioteca dual ............................................... 226
Eleccion de una biblioteca visual .................................................................................. 226
Ejecucion en Linux ................................................................................................... 227
Compilacion condicional de las bibliotecas ........................................................... 228
Conversion de aplicaciones existentes .......................................................................... 229
Las clases TControl y derivadas ......................................................................................... 230
Parent y Controls ............................................................................................................ 231
Propiedades relacionadas con el tamafio y la posicion del control ........................... 232
Propiedades de activation y visibilidad ........................................................................ 232
Fuentes ............................................................................................................................. 233
Colores ............................................................................................................................. 233
La clase TWinControl (VCL) ........................................................................................ 235
La clase TWidgetControl (CLX) ................................................................................... 236
Abrir la caja de herramientas de componentes ................................................................. 236
Los componentes de entrada de texto ........................................................................... 237
El componente Edit ................................................................................................... 237
El control LabeledEdit ............................................................................................. 238
El componente MaskEdit .......................................................................................... 238
Los componentes Memo y RichEdit ........................................................................ 239
El control CLX Textviewer ...................................................................................... 240
Seleccion de opciones ..................................................................................................... 240
Los componentes CheckBox y RadioButton ........................................................... 241
Los componentes GroupBox ..................................................................................... 241
El componente RadioGroup ..................................................................................... 241
Listas .................'............................................................................................................... 242
El componente ListBox ............................................................................................. 242
El componente ComboBox ....................................................................................... 243
El componente CheckListBox .................................................................................. 244
Los cuadros combinados extendidos: ComboBoxEx y ColorBox ......................... 245
Los componentes Listview y TreeView .................................................................. 246
El componente ValueListEditor ............................................................................... 246
Rangos .............................................................................................................................. 248
El componente ScrollBar .......................................................................................... 248
Los componentes TrackBar y ProgressBar ............................................................. 249
El componente UpDown ........................................................................................... 249
El componente PageScroller .................................................................................... 249
El componente ScrollBox ......................................................................................... 250
Comandos ......................................................................................................................... 250
Comandos y acciones ................................................................................................ 251
Menu Designer .......................................................................................................... 251
Menus contextuales y el evento OncontextPopup .............................................. 252
Tecnicas relacionadas con 10s controles ............................................................................ 254
Gestion del foco de entrada ............................................................................................ 254
Anclajes de control ......................................................................................................... 257
Uso del componente Splitter .......................................................................................... 258
Division en sentido horizontal ................................................................................. 260
Teclas aceleradoras ......................................................................................................... 261
Sugerencias flotantes ...................................................................................................... 262
Personalizacion de las sugerencias .......................................................................... 263
Estilos y controles dibujados por el propietario .......................................................... 264
Elementos del menu dibujados por el usuario ........................................................ 265
Una ListBox de colores ............................................................................................. 267
Controles ListView y TreeView ........................................................................................... 270
Una lista de referencias grafica ..................................................................................... 270
Un arb01 de datos ............................................................................................................ 275
La version adaptada de DragTree ............................................................................ 278
Nodos de arb01 personalizados ...................................................................................... 280
. ..
6 Creac~onde la interfaz de usuario ..............................................................................283
Formularios de varias paginas ............................................................................................ 284
Pagecontrols y Tabsheets .............................................................................................. 285
Un visor de imagenes con solapas dibujadas por el propietario ................................ 290
La interfaz de usuario de un asistente .......................................................................... 294
El control ToolBar ................................................................................................................ 297
El ejemplo RichBar ......................................................................................................... 298
Un menu y un cuadro combinado en una barra de herramientas .............................. 300
Una barra de estado simple ............................................................................................ 301
Temas y estilos ...................................................................................................................... 304
Estilos CLX ..................................................................................................................... 305
Temas de Windows XP ................................................................................................... 305
El Componente ActionList .................................................................................................. 308
Acciones predefinidas en Delphi ................................................................................... 310
Las acciones en la practica ............................................................................................ 312
La barra de herramientas y la lista de acciones de un editor ..................................... 316
Los contenedores de barra de herramientas .......................................................................318
ControlBar ....................................................................................................................... 320
Un menu en una barra de control ............................................................................323
Soporte de anclaje en Delphi ......................................................................................... 323
Anclaje de barras de herramientas en barras de control ............................................ 324
Control de las operaciones de anclaje ..................................................................... 325
Anclaje a un Pagecontrol ..............................................................................................329
La arquitectura de ActionManager ..................................................................................... 331
Construir una sencilla demostracion ............................................................................ 332
Objetos del menu utilizados con menos frecuencia .....................................................336
Modificar un programa existente .................................................................................. 339
Emplear las acciones de las listas ................................................................................. 340
.
7 Trabajo con formularios ................................................................................................345
La clase TForm ..................................................................................................................... 346
Usar formularios normales ............................................................................................. 346
El estilo del formulario .................................................................................................. 348
El estilo del borde ........................................................................................................... 349
Los iconos del borde .......................................................................................................352
Definicion de mas estilos de ventana ............................................................................ 354
Entrada directa en un formulario ........................................................................................ 356
Supervision de la entrada del teclado ........................................................................... 356
Obtener una entrada de raton ........................................................................................ 358
Los parametros de 10s eventos de raton ............................................................... 359
Arrastrar y dibujar con el raton ..................................................................................... 359
Pintar sobre formularios ...................................................................................................... 364
Tecnicas inusuales: Canal Alpha, Color Key y la API Animate ..................................... 366
Posicion, tamaiio, desplazamiento y ajuste de escala ....................................................... 367
..
La posicion del formulario ............................................................................................. 368
Ajuste a la ventana (en Delphi 7) ................................................................................. 368
El tamafio de un formulario y su zona de cliente ........................................................ 369
Restricciones del formulario .......................................................................................... 370
Desplazar un formulario ................................................................................................ 370
Un ejemplo de prueba de desplazamiento ............................................................... 371
Desplazamiento automatico ..................................................................................... 373
Desplazamiento y coordenadas del formulario ...................................................... 374
Escalado de formularios ................................................................................................. 376
Escalado manual del formulario .............................................................................. 377
Ajuste automatic0 de la escala del formulario ............................................................. 378
Crear y cerrar formularios ................................................................................................... 379
Eventos de creacion de formularios .............................................................................. 381
Cerrar un formulario ...................................................................................................... 382
Cuadros de dialog0 y otros formularios secundarios ........................................................ 383
Afiadir un formulario secundario a un programa ........................................................ 384
Crear formularios secundarios en tiempo de ejecucion .............................................. 385
Crear un unica instancia de formularios secundarios ........................................... 386
. .
Creacion de un cuadro de d~alogo....................................................................................... 387
El cuadro de dialogo del ejemplo RefList .................................................................... 388
Un cuadro de dialog0 no modal ..................................................................................... 390
. .
Cuadros de dialog0 predefinidos ......................................................................................... 393
Dialogos comunes de Windows ................................................................................... 394
Un desfile de cuadros de mensaje ................................................................................. 395
Cuadros "Acerca den y pantallas iniciales ......................................................................... 396
.,
Creacion de una pantalla inicial ................................................................................... 397
.
Parte I1 Arquitecturas orientadas a objetos en Delphi ...............................................401
8. La arquitectura de las aplicaciones Delphi ...............................................................403
. .
El objeto Application ............................................................................................................ 404
Mostrar la ventana de la aplicacion .............................................................................. 406
Activacion de aplicaciones y formularios .................................................................... 407
Seguimiento de formularios con el objeto Screen ..................................................... 407
De eventos a hilos ................................................................................................................. 412
Programacion guiada por eventos ................................................................................. 412
Entrega de mensajes Windows ...................................................................................... 414
Proceso secundario y multitarea .................................................................................... 414
Multihilo en Delphi ........................................................................................................ 415
Un ejemplo con hilos ................................................................................................ 416
Verificando si existe una instancia previa de una aplicacion .......................................... 418
Buscando una copia de la ventana principal ................................................................ 418
Uso de un mutex .............................................................................................................. 419
Buscar en una lista de ventanas .................................................................................... 420
Controlar mensajes de ventana definidos por el usuario ............................................ 421
Creacion de aplicaciones MDI ............................................................................................ 422
MDI en Windows: resumen tecnico ............................................................................. 422
Ventanas marco y ventanas hijo en Delphi ........................................................................ 423
Crear un menu Window completo ................................................................................. 424
El ejemplo MdiDemo ...................................................................................................... 426
Aplicaciones MDI con distintas ventanas hijo .................................................................. 428
Formularios hijo y mezcla de menus ............................................................................ 428
El formulario principal ................................................................................................... 429
Subclasificacion de la ventana MdiClient .................................................................... 430
Herencia de formularios visuales ........................................................................................ 432
Herencia de un formulario base .................................................................................... 433
Formularios polimorficos ............................................................................................... 436
Entender 10s marcos ............................................................................................................. 439
Marcos y fichas ............................................................................................................... 442
Varios marcos sin fichas ................................................................................................. 444
Formularios base e interfaces .............................................................................................. 446
Uso de una clase de formulario base ............................................................................. 447
Un truco adicional: clases de interposition ............................................................ 450
Uso de interfaces ............................................................................................................. 451
El gestor de memoria de Delphi .......................................................................................... 452
. ..
9 Creac~onde componentes Delphi ................................................................................. 455
Ampliacion de la biblioteca de Delphi ............................................................................... 456
Paquetes de componentes .............................................................................................. 4 5 6
Normas para escribir componentes ............................................................................... 458
Las clases basicas de componentes ............................................................................... 459
..
Creacion de nuestro primer componente ........................................................................... 460
El cuadro combinado Fonts ............................................................................................ 460
Creacion de un paquete .................................................................................................. 465
~ Q u Chay detras de un paquete? ............................................................................... 466
Uso del cuadro combinado Fonts ................................................................................... 469
Los mapas de bits de la Component Palette ................................................................. 469
Creacion de componentes compuestos ............................................................................... 471
Componentes internos .................................................................................................... 471
Publicacion de subcomponentes .................................................................................... 472
Componentes externos .................................................................................................... 475
Referencias a componentes mediante interfaces .......................................................... 477
Un componente grafico complejo ........................................................................................ 481
Definition de una propiedad enumerada ...................................................................... 482
Escritura del metodo Paint ............................................................................................. 484
Adicion de las propiedades TPersistent ........................................................................ 486
Definition de un nuevo evento personalizado ............................................................. 488
:
Uso de llamadas de bajo nivel a la API de Windows ..... ....................................... 489
La version CLX: Llamadas a funciones Qt nativas ............................................... 490
Registro de las categorias de propiedades .................................................................... 490
Personalizacion de 10s controles de Windows ................................................................... 492
El cuadro de edicion numeric0 ...................................................................................... 494
Un editor numeric0 con separador de millares ...................................................... 495
El boton Sound ................................................................................................................ 496
Control de mensaje internos: El boton Active ........................................................... 498
Mensajes de componente y notificaciones .................................................................... 499
Mensajes de componentes ........................................................................................ 499
Notificaciones a componentes .................................................................................. 503
Un ejemplo de mensajes de componente ................................................................. 503
Un cuadro de dialog0 en un componente ........................................................................... 504
Uso del componente no visual ....................................................................................... 508
Propiedades de coleccion ............................................................................................... 508
Definicion de acciones personalizadas ........................................................................ 512
Escritura de editores de propiedades .................................................................................. 516
Un editor para las propiedades de sonido ................................................................ 517
Instalacion del editor de propiedades ..................................................................... 520
Creacion de un editor de componentes ............................................................................... 521
Subclasificacion de la clase TComponentEditor ......................................................... 522
Un editor de componentes para ListDialog .................................................................. 522
Registro del editor de componentes ........................................................................ 524
.
10 Bibliotecas y paquetes ................................................................................................. 527
La funcion de las DLL en Windows ............................................................................ 528
El enlace dinamico .......................................................................................................... 528
Uso de las DLL ................................................................................................................ 529
Normas de creacion de DLL en Delphi ....................................................................... 530
Uso de las DLL existentes .................................................................................................... 531
Usar una DLL de C++ .................................................................................................... 532
Creacion de una DLL en Delphi ......................................................................................... 534
La primera DLL en Delphi ...................................................................................... 535
Funciones sobrecargadas en las DLL de Delphi ................................................... 537
Exportar cadenas de una DLL ................................................................................. 537
Llamada a la DLL de Delphi ...................................................................................... 539
Caracteristicas avanzadas de las DLL en Delphi ............................................................ 540
Cambiar nombres de proyecto y de biblioteca ............................................................. 540
Llamada a una funcion DLL en tiempo de ejecucion .................................................. 542
Un formulario de Delphi en una DLL .......................................................................... 544
Bibliotecas en memoria: codigo y datos ............................................................................. 546
Compartir datos con archivos proyectados en memoria ............................................. 548
Uso de paquetes Delphi ........................................................................................................ 550
Versiones de paquetes .................................................................................................... 551
Formularios dentro de paquetes .......................................................................................... 553
Carga de paquetes en tiempo de ejecucion ................................................................ 555
Uso de interfaces en paquetes .................................................................................. 558
Estructura de un paquete ..................................................................................................... 561
.
11 Modelado y programacih orientada a objetos (con ModelMaker) ...................567
Comprension del modelo interno de ModelMaker ............................................................ 568
Modelado y UML .................................................................................................................. 569
Diagramas de clase ........................................................................................................ 569
Diagramas de secuencia ............................................................................................. 571
Casos de uso y otros diagramas ..................................................................................... 572
Diagramas no W ........................................................................................................ 574
Elementos comunes de 10s diagramas ........................................................................... 575
Caracteristicas de codification de ModelMaker .......................................................... 576
Integracion Delphi / ModelMaker ................................................................................. 576
Gestion del modelo de codigo ........................................................................................ 578
El editor Unit Code Editor ............................................................................................. 580
El editor Method Implementation Code Editor ........................................................... 582
La vista de diferencias .................................................................................................... 582
La vista Event Types View ............................................................................................. 584
Documentacion y macros ..................................................................................................... 585
Documentacion frente a comentarios ............................................................................ 585
Trabajo con macros ......................................................................................................... 587
Reingenieria de codigo ......................................................................................................... 587
..
Aplicacion de patrones de diseiio .................................................................................. 590
Plantillas de codigo ......................................................................................................... 593
Detallitos poco conocidos ................................................................................................... 595
.
12 De COM a COM+ ..................................................................................................... 597
Una breve historia de OLE y COM ..................................................................................... 598
Implementacion de IUnknow .............................................................................................. 599
Identificadores globalmente unicos ............................................................................... 601
El papel de las fabricas de clases .................................................................................. 603
Un primer sewidor COM ..................................................................................................... 604
Interfaces y objetos COM ............................................................................................... 605
Inicializacion del objeto COM ....................................................................................... 608
Prueba del sewidor COM ............................................................................................... 609
Uso de las propiedades de la interfaz ........................................................................... 610
Llamada a metodos virtuales ......................................................................................... 611
Automatization ..................................................................................................................... 612
Envio de una llamada Automatizacion ......................................................................... 614
Creacion de un sewidor de Automatizacion ...................................................................... 617
El editor de bibliotecas de tipos .................................................................................... 618
El codigo del sewidor ..................................................................................................... 619
Registro del sewidor de autornatizacion ...................................................................... 621
Creacion de un cliente para el sewidor ........................................................................ 622
El alcance de 10s objetos de automatizacion ................................................................ 624
El senidor en un componente ...................................................................................... 626
Tipos de datos COM ....................................................................................................... 627
Exponer listas de cadenas y fuentes ....................................................................... 627
Us0 de programas Office ................................................................................................ 628
Uso de documentos compuestos .......................................................................................... 629
El componente Container ............................................................................................... 630
Uso del objeto interno .................................................................................................... 633
Controles ActiveX ................................................................................................................. 633
Controles ActiveX frente a componentes Delphi ........................................................ 635
Uso de controles ActiveX en Delphi ............................................................................. 636
Uso del control WebBrowser .................................................................................... 636
Creacion de controles ActiveX ............................................................................................ 638
Creacion de una flecha ActiveX .................................................................................... 639
Afiadir Nuevas Propiedades ........................................................................................... 640
Adicibn de una ficha de propiedades ............................................................................ 642
ActiveForms ..................................................................................................................... 644
Interioridades de ActiveForm ................................................................................... 644
El control ActiveX XClock ...................................................................................... 645
ActiveX en paginas Web ................................................................................................ 646
COM+ .................................................................................................................................... 648
Creacion de un componente COM+ .............................................................................. 649
Modulos de datos transaccionales ................................................................................. 651
Eventos COM+ ................................................................................................................ 653
COM y .NET en Delphi 7 .................................................................................................... 656
.
Parte I11 Arquitecturas orientadas a bases de datos en Delphi ................................ 659
13. Arquitectura de bases de datos Delphi .....................................................................661
Acceso a bases de datos: dbExpress. datos locales y otras alternativas .......................... 662
La biblioteca dbExpress .................................................................................................. 662
Borland Database Engine (BDE) .................................................................................. 664
InterBase Express (IBX) ................................................................................................ 664
MyBase y el componente ClientDataSet ....................................................................... 665
dbGo para ADO ............................................................................................................... 665
MyBase: ClientDataSet independiente ............................................................................... 666
Conexion a una tabla local ya existente ....................................................................... 667
De la DLL Midas a la unidad MidasLib ....................................................................... 669
Formatos XML y CDS .................................................................................................... 669
Definition de una tabla local nueva .............................................................................. 670
Indexado ........................................................................................................................... 671
Filtrado ............................................................................................................................. 672
Busqueda de registros ..................................................................................................... 673
Deshacer y Savepoint ................................................................................................ 674
Activar y desactivar el registro ................................................................................ 675
Uso de controles data-aware ................................................................................................ 675
Datos en una cuadricula ................................................................................................. 676
DBNavigator y acciones sobre el conjunto de datos ................................................... 676
Controles data-aware de texto ....................................................................................... 677
Controles data-aware de lista ........................................................................................ 677
El ejemplo DbAware ................................................................................................. 678
Uso de controles de busqueda ........................................................................................ 679
Controles grAficos data-aware ....................................................................................... 681
El componente DataSet ........................................................................................................ 681
El estado de un Dataset .................................................................................................. 686
Los campos de un conjunto de datos .................................................................................. 687
Uso de objetos de campo ................................................................................................ 690
Una jerarquia de clases de campo ................................................................................. 692
.,
Adicion de un campo calculado ..................................................................................... 695
Campos de busqueda ....................................................................................................... 699
Control de 10s valores nulos con eventos de campo .................................................... 701
Navegacion por un conjunto de datos ................................................................................. 702
El total de una columna de tabla ...................................................................................703
Uso de marcadores .......................................................................................................... 704
Edicion de una columna de tabla ................................................................................. 707
Personalizacion de la cuadricula de una base de datos .................................................... 707
Pintar una DBGrid ...................................................................................................... 708
Una cuadricula que permite la seleccion multiple ................................................. 710
Arrastre sobre una cuadricula ........................................................................................ 712
Aplicaciones de bases de datos con controles estandar .................................................... 713
Imitacion de 10s controles data-aware de Delphi ....................................................... 713
Envio de solicitudes a la base de datos ......................................................................... 716
Agrupacion y agregados ....................................................................................................... 718
Agrupacion ...................................................................................................................... 718
Definicion de agregados ................................................................................................. 719
Estructuras maestroldetalles ................................................................................................ 721
Maestro/detalle con 10s ClientDataSet ..................................................................... 722
Control de errores de la base de datos ............................................................................ 723
.
14 Clientelsemidor con dbExpress ............................................................................ 727
La arquitectura clientelservidor .......................................................................................... 728
Elementos del disefio de bases de datos .......................................................................... 730
Entidades y relaciones .................................................................................................... 730
Reglas de normalizacion ........................................................................................ 731
De las claves primarias a 10s OID ................................................................................. 731
Claves externas e integridad referencial ................................................................. 733
. .
Mas restricciones ............................................................................................................ 734
Cursores unidireccianales ....................................................................................... 734
Introduccion a InterBase ...................................................................................................... 736
Uso de IBConsole ............................................................................................................ 738
Programacion de servidor en InterBase ........................................................................ 740
Procedimientos almacenados ................................................................................. 740
Disparadores (y generadores) ................................................................................... 741
La biblioteca dbExpress ....................................................................................................... 743
Trabajo con cursores unidireccionales ..................................................................... 743
Plataformas y bases de datos ........................................................................................ 744
Problemas con las versiones de controladores e inclusion de unidades .................... 745
Los componentes dbExpress ................................................................................................ 746
El componente SQLConnection .................................................................................... 747
Los componentes de conjuntos de datos de dbExpress ............................................... 751
El componente SimpleDataSet de Delphi 7 ........................................................... 752
El componente SQLMonitor .......................................................................................... 753
Algunos ejemplos de dbExpress .................................................................................... 754
Uso de un componente unico o de varios ..................................................................... 755
Aplicacion de actualizaciones .................................................................................. 755
. .
Seguimiento de la conexion ..................................................................................... 756
Control del codigo SQL de actualizacion ............................................................... 757
Acceso a metadatos de la base de datos con SetSchemaInfo ................................ 758
Una consulta parametrica ............................................................................................... 760
Cuando basta una sola direccion: imprimir datas ....................................................... 762
Los paquetes y la cache ........................................................................................................ 765
Manipulacion de actualizaciones .................................................................................. 766
El estado de 10s registros ....................................................................................... 766
Acceso a Delta ........................................................................................................... 767
Actualizar 10s datos .................................................................................................... 768
Uso de transacciones ....................................................................................................... 771
Uso de InterBase Express ............................................................................................... 774
Componentes de conjunto de datos IBX ..................................................................... 776
Componentes administrativos IBX .......................................................................... 777
Creacion de un ejemplo IBX ....................................................................................... 777
Creacion de una consulta en vivo .................................................................................. 779
Control en InterBase Express ........................................................................................ 783
Obtencion de mas datos de sistema ............................................................................... 784
Bloques del mundo real ....................................................................................................... 785
Generadores e identificadores ........................................................................................ 786
Busquedas sin distincion entre mayusculas y minusculas .......................................... 788
Manejo de ubicaciones y personas ........................................................................... 790
Creacion de una interfaz de usuario .......................................................................... 792
Reserva de clases ............................................................................................................. 795
Creacion de un dialogo de busqueda ............................................................................. 798
Adicion de un formulario de consulta libre ................................................................ 800
.
15 Trabajo con ADO .......................................................................................................... 803
Microsoft Data Access Componentes (MDAC) ............................................................. 805
Proveedores de OLE DB .............................................................................................805
Uso de componentes dbGo ................................................................................................... 807
Un ejemplo practico ................................................................................................... 808
El componente ADOConnection ............................................................................. 811
Archivos de enlace de datos ......................................................................................... 811
Propiedades dinamicas ....................................................................................................... 812
Obtencion de information esquematica ............................................................................ 813
Uso del motor Jet ............................................................................................................. 815
Paradox a traves de Jet ................................................................................................... 816
Excel a traves de Jet ....................................................................................................... 817
Archivos de texto a traves de Jet ............................................................................. 819
.,
Importaclon y exportation ............................................................................................ 821
Trabajo con cursores ............................................................................................................. 822
. .
Ubicacion de cursor ................................................................................................... 822
Tipo de cursor .................................................................................................................. 823
Pedir y no recibir ............................................................................................................. 825
Sin recuento de registros ................................................................................................ 826
Indices de cliente ............................................................................................................. 826
. . .
Repllcaclon ...................................................................................................................... 827
Procesamiento de transacciones .................................................................................... 829
Transacciones anidadas ........................................................................................... 830
Atributos de ADOConnection ................................................................................... 830
Tipos de bloqueo ............................................................................................................. 831
. .
El bloqueo peslmlsta ............................................................................................. 832
Actualizacion de 10s datos ................................................................................................... 832
Actualizaciones por lotes ............................................................................................... 834
Bloqueo optimists ........................................................................................................... 836
Resolution de conflictos de actualizacion .................................................................... 839
Conjuntos de registros desconectados ................................................................................ 840
Pooling de conexiones .......................................................................................................... 841
Conjuntos de registros permanentes ............................................................................. 843
El modelo de maletin ...................................................................................................... 844
Unas palabras sobre ALIO.NET........................................................................................... 845
.
16 Aplicaciones DataSnap multicapa ............................................................................. 847
Niveles uno. dos y tres en la historia de Delphi ................................................................ 848
Fundamento tecnico de DataSnap ................................................................................. 850
La interfaz AppSener .................................................................................................... 850
Protocolo de conexion ..................................................................................................... 851
Proporcionar paquetes de datos ..................................................................................... 853
Componentes de soporte Delphi (entorno cliente) .................................................... 854
Componentes de soporte Delphi (entorno senidor) .................................................... 856
Construction de una aplicacion de ejemplo ...................................................................... 856
El primer senidor de aplicacion ................................................................................... 856
El primer cliente ligero .................................................................................................. 858
Adicion de restricciones a1 senidor .................................................................................... 860
Restricciones de campo y conjuntos de datos .............................................................. 860
Inclusion de propiedades de campo .............................................................................. 862
Eventos de campo y tabla ............................................................................................... 862
Adicion de caracteristicas a1 cliente ................................................................................... 863
Secuencia de actualization ............................................................................................ 864
Refresco de datos ............................................................................................................. 865
Caracteristicas avanzadas de DataSnap ............................................................................. 867
Consultas por parametros ............................................................................................... 868
Llamadas a metodos personalizados ............................................................................. 868
Relaciones maestroldetalle ............................................................................................. 870
Uso del agente de conexion ............................................................................................ 871
Mas opciones de proveedor ............................................................................................ 872
Agente simple de objetos ................................................................................................ 873
Pooling de objetos ........................................................................................................... 874
Personalizacion de paquetes de datos ...........................................................................874
. ..
18 Generation de informes con Rave ............................................................................. 931
.
20 Programacidn Web con WebBroker y WebSnap ....................................................997
Paginas Web dinarnicas .................................................................................................. 998
Un resumen de CGI ........................................................................................................ 999
Uso de bibliotecas dinamicas ....................................................................................... 1000
Tecnologia WebBroker de Delphi ..................................................................................... 1001
Depuracion con Web App Debugger ...................................................................... 1004
Creacion de un WebModule multiproposito ............................................................... 1007
Informes dinamicos de base de datos .......................................................................... 1009
Consultas y formularios ................................................................................................ 1010
Trabajo con Apache ...................................................................................................... 1014
Ejemplos practices .............................................................................................................. 1016
Un contador Web grafico de visitas ............................................................................ 1017
Busquedas con un motor Web de busquedas .............................................................. 1019
WebSnap ............................................................................................................................ 1021
., , .
Gestion de varias paglnas ........................................................................................ 1025
Guiones de servidor ...................................................................................................... 1027
Adaptadores ................................................................................................................... 1030
Campos de adaptadores .......................................................................................... 1030
Componentes de adaptadores ................................................................................. 1031
Uso del Adapterpageproducer ........................................................................... 1031
Guiones en lugar de codigo .................................................................................... 1034
Encontrar archivos ........................................................................................................ 1035
WebSnap y bases de datos .................................................................................................. 1036
Un modulo de datos WebSnap ..................................................................................... 1036
El DataSetAdapter ........................................................................................................ 1036
Edicion de 10s datos en un formulario ........................................................................ 1039
Maestro/Detalle en WebSnap ................................................................................... 1041
Sesiones, usuarios y permisos ........................................................................................... 1043
Uso de sesiones .............................................................................................................. 1043
Peticion de entrada en el sistema ............................................................................ 1045
Derechos de acceso a una unica pagina .............................................................. 1047
.
21 Programacibn Web con IntraWeb ...........................................................................1049
Introduccion a IntraWeb ............................................................................................... 1050
De sitios Web a aplicaciones Web ........................................................................... 1051
Un primer vistazo interior ...................................................................................... 1054
Arquitecturas IntraWeb .......................................................................................... 1057
Creacion del aplicaciones IntraWeb ............................................................................ 1058
Escritura de aplicaciones de varias paginas .......................................................... 1060
Gestion de sesiones ................................................................................................. 1064
Integracion con WebBroker (y WebSnap) .............................................................. 1066
Control de la estructura ................................................................................................ 1068
Aplicaciones Web de bases de datos ................................................................................. 1070
Enlaces con detalles ...................................................................................................... 1072
Transporte de datos a1 cliente ...................................................................................... 1076
.
22 Uso de tecnologias XML ............................................................................................ 1079
Presentacion de XML ......................................................................................................... 1080
Sintaxis XML basica .................................................................................................. 1080
XML bien formado ........................................................................................................ 1082
Trabajo con XML .......................................................................................................... 1083
Manejo de documentos XML en Delphi .............................................................. 1084
Programacion con DOM .................................................................................................... 1085
Un documento XML en una TreeView ................................................................... 1087
Creacion de documentos utilizando DOM ................................................................. 1090
Interfaces de enlace de datos XML ......................................................................... 1094
Validacion y esquemas ............................................................................................ 1098
Uso de la API de SAX .................................................................................................. 1099
Proyeccion de XML con transformaciones ................................................................. 1103
XML e Internet Express ..................................................................................................... 1108
El componente XMLBroker ......................................................................................... 1109
Soporte de JavaScript ................................................................................................... 1110
Creacion de un ejemplo ........................................................................................... 1111
Uso de XSLT ....................................................................................................................... 1116
Uso de XPath ................................................................................................................. 1117
XSLT en la practica ...................................................................................................... 1118
XSLT con WebSnap ...................................................................................................... 1119
Transformaciones XSL directas con DOM ................................................................. 1121
Procesamiento de grandes documentos XML ........................................................... 1123
De un ClientDataSet a un documento XML ............................................................ 1123
De un documento XML a un ClientDataSet ............................................................ 1125
.
23 Semicios Web y SOAP ............................................................................................... 1129
Servicios Web ................................................................................................................... 1130
SOAP y WSDL .............................................................................................................. 1130
Traducciones BabelFish ........................................................................................ 1131
Creacion de un servicio Web ....................................................................................... 1134
Un servicio Web de conversion de divisas ............................................................... 1135
Publicacion del WSDL ............................................................................................ 1136
Creacion de un cliente personalizado ............................................................... 1137
Peticion de datos de una base de datos ................................................................... 1139
Acceso a 10s datos ................................................................................................... 1139
Paso de documentos XML ...................................................................................... 1140
El programa cliente (con proyeccion XML) ......................................................... 1142
Depuracion de las cabeceras SOAP ............................................................................ 1143
Exponer una clase ya existente como un servicio Web ............................................. 1144
DataSnap sobre SOAP ........................................................................................................ 1145
Creacion del semidor SOAP DataSnap ...................................................................... 1145
Creacion del cliente SOAP DataSnap ......................................................................... 1148
SOAP frente a otras conexion con DataSnap ............................................................. 1148
Manejo de adjuntos ............................................................................................................. 1149
Soporte de UDDI ................................................................................................................. 1151
~ Q u Ces UDDI? .............................................................................................................. 1151
UDDI en Delphi 7 ......................................................................................................... 1153
.
Parte V ApCndices............................................................................................................ 1157
ApCndice A. Herramientas Delphi del autor ............................................................... 1159
CanTools Wizards ............................................................................................................... 1159
Programa de conversion VclToClx ................................................................................... 1162
Object Debugger ................................................................................................................. 1162
Memory Snap ...................................................................................................................... 1163
Licencias y contribuciones ................................................................................................. 1164
.
ApCndice B Contenido del CD-ROM ........................................................................... 1165
lntroduccion
La primera vez que Zack Urlocker me enseiio un product0 aun sin publicar
denominado Delphi, me di cuenta de que cambiaria mi trabajo (y el trabajo de
muchos otros desarrolladores de software). Solia pelearme con bibliotecas de
C++ para Windows y, Delphi era, y todavia es, la mejor combinacion de progra-
macion orientada a objetos y programacion visual no solo para este sistema ope-
rativo sino tambien para Linux y pronto para .NET.
Delphi 7 simplemente se suma a esta tradicion, sobre las solidas bases de la
VCL, para proporcionar otra impresionante herramienta de desarrollo de soft-
ware que lo coordina todo. iEsta buscando soluciones de bases de datos, clientel
servidor, multicapa (multitier), Intranet o Internet? iBusca control y potencia?
~ B U SunaC ~rapida productividad? Con Delphi y la multitud de tecnicas y trucos
que se presentan en este libro, sera capaz de conseguir todo eso.
Ediciones de Delphi
Antes de pasar a 10s pormenores del entorno de programacion de Delphi, resal-
taremos dos ideas clave. En primer lugar, no hay una unica edicion de Delphi,
sino muchas. En segundo lugar, cualquier entorno Delphi se puede personalizar.
Por dichas razones, las pantallas de Delphi que aparecen en este capitulo pueden
ser distintas a las que vea en su ordenador. Las ediciones de Delphi actuales son
las siguientes:
La edicion "Personal": Dirigida a quienes empiezan a utilizar Delphi y a
programadores esporadicos. No soporta programacion de bases de datos ni
ninguna de las caracteristicas avanzadas de Delphi.
La edicion "Professional Studio": Dirigida a desarrolladores profesiona-
les. Posee todas las caracteristicas basicas, mas soporte para programa-
cion de bases de datos (corno soporte ADO), soporte basico para servidores
Web (WebBroker) y algunas herramientas externas como ModelMaker e
IntraWeb. En el libro se asume que el lector trabaja como minimo con la
edicion Professional.
La edici6n "Enterprise Studio": Esta dirigida a desarrolladores que crean
aplicaciones para empresas. Incluye todas las tecnologias XML y de servi-
cios Web avanzados, soporte de CORBA, internacionalizacion, arquitec-
tura en tres niveles y muchas otras herramientas. Algunos capitulos del
libro tratan sobre caracteristicas que solo posee esta version de Delphi y
asi se ha especificado en esos casos.
La edicihn "Architect Studio": Aiiade a la edicion Enterprise el soporte
de Bold, un entorno para la creacion de aplicaciones dirigidas en tiempo de
ejecucion por un modelo UML y capaces de proyectar sus objetos tanto
sobre una base de datos como sobre una interfaz de usuarios, gracias a una
gran cantidad de componentes avanzados. El soporte de Bold no se trata en
este libro.
Ademas de las distintas versiones disponibles, existen varias formas de perso-
nalizar el entorno Delphi. En las capturas de pantalla presentadas a lo largo del
libro, se ha intentado utilizar una interfaz estandar (corno la que resulta de la
instalacion tal cual). Sin embargo, en ciertos ejemplos, pueden aparecer refleja-
das algunas preferencias del autor como la instalacion de muchos aiiadidos, que
pueden reflejarse en el aspect0 de las pantallas. La version Professional y supe-
riores de Delphi 7 incluyen una copia funcional de Kylix 3, en la edicion de
lenguaje Delphi. Ademas de referencias a la biblioteca CLX y a las caracteristi-
cas multiplataforma de Delphi, este libro no trata Kylix ni el desarrollo sobre
Linux. Puede buscar otras obras para conseguir mas informacion sobre este tema.
(No hay muchas diferencias entre Kylix 2 y Kylix 3 en la version de lenguaje
Delphi. La caracteristica nueva mas importante de Kylix 3 es su soporte del
lenguaje C++.)
rn
El editor de codigo es donde se escribe el codigo. El mod0 mas obvio de
escribir codigo en un entorno visual implica responder a eventos, comenzando por
10s eventos enlazados con las operaciones realizadas por 10s usuarios del progra-
ma, como hacer clic sobre un boton o escoger un elemento de un cuadro de lista.
Puede usarse el mismo enfoque para manejar eventos internos, como 10s eventos
que implican cambios en bases de datos o notificaciones del sistema operativo.
A medida que 10s programadores adquieren un mayor conocimiento sobre
Delphi, suelen comenzar escribiendo basicamente codigo gestor de eventos y des-
pues escriben sus propias clases y componentes y, normalmente, acaban invir-
tiendo la mayor parte de su tiempo en el editor. Ya que este libro trata mas
conceptos que la programacion visual e intenta ayudar a dominar toda la potencia
de Delphi, a medida que el testo avance se vera mas codigo y menos formularios.
[Alignmentpalette]
Create=l
Visible=O
Environment Options
Unas cuantas de las ultimas mejoras tienen que ver con el habitual cuadro de
dialogo Environment Options. Las paginas de este cuadro de dialogo se reorga-
nizaron en Delphi 6, desplazando las opciones del Form Designer de la pagina
Preferences a la nueva pagina Designer. En Delphi 6 tambien existian unas
cuantas opciones y paginas nuevas:
La pagina Preferences del cuadro de dialogo: Time una casilla de veri-
ficacion que impide que las ventanas de Delphi se acoplen automaticamente
entre si.
La pagina Environment Variables: Permite inspeccionar las variables
del entorno del sistema (como las rutas predefinidas y parametros del SO)
y establecer variables definidas por el usuario. Lo bueno es que se pueden
utilizar ambos tipos de variable en cada uno de 10s cuadros de dialogo del
IDE (por ejemplo, se puede evitar escribir explicitamente rutas usadas
habitualmente, sustituyendolas por una variable). En otras palabras, las
variables del entorno funcionan de manera similar a la variable $DELPHI,
que hace referencia a1 directorio base de Delphi per0 puede ser definida
por el usuario.
L a pagina Internet: En ella se pueden escoger cuales son las extensiones
de archivo predefinidas para 10s archivos HTML y SML (basicamente por
el marco de trabajo WebSnap) y tambien asociar un editor externo con
cada extension.
TO-DOList
Otra caracteristica aiiadida en Delphi 5 pero que aun sigue sin usarse como
deberia es la lista de tareas pendientes. Se trata de una lista de tareas que aun se
debe realizar para completar un proyecto (es un conjunto de notas para el progra-
mador o programadores, que resulta una herramienta muy util en un equipo).
Aunque la idea no es novedosa, el concept0 clave de la lista de tareas pendientes
en Delphi es que funciona como una herramienta de dos vias.
Figura 1.2. La pagina Preferences del cuadro de dialogo Environment Options.
Delphi trata todo lo que aparezca tras 10s dos puntos (hasta el final de la linea
o hasta la Have de cierre, segun el tipo de comentario), como el texto del elemento
de tarea pendiente.
---.
_.
1 *
I!
A c m llcm
.-
I ~ o a ~ e 10-
--'
IWWY
-A
7 Check comp~lerfelhngs 1 Marco
Figura 1.3. La ventana Edit To-Do Item puede usarse para modificar un elemento de
tarea pendiente, una operacion que tambien puede realizarse directamente en el
codigo fuente.
Se pueden utilizar las combinaciones Alt-Av Pag y Alt-Re Pag para recorrer
de manera ciclica las pestaiias de esta ventana. (Los mismos comandos sirven
para otras vistas con pestaiias.)
Si suceden errores de compilador, puede activarse otra ventana nueva median-
te el comando View>Additional Message Info. A medida que se compila un
programa, esta ventana Message Hints proporcionara informacion adicional para
algunos mcnsajes de error frecuentes, proporcionando sugerencias sobre como
solucionar estos errores:
Este tipo de ayuda esta destinada mas a programadores novatos, per0 podria
ser practico tener presente esta ventana. Es importante darse cuenta de que esta
informacion cs bastante facil de personalizar: un director de desarrollo de un
proyccto pucdc introducir descripciones apropiadas de errores comunes en un
formulario que signifiquen algo especifico para nuevos desarrolladores. Para ha-
cer esto, siga las instrucciones del archivo que guarda los parametros de esta
caracteristica, el archivo msginfo70,ini que se encuentra en la carpeta b i n de
Delphi.
El editor de Delphi
Aparentemente el editor de Delphi no ha cambiado mucho en la version 7 del
IDE. Sin embargo, en el fondo, se trata de una herramienta completamente nueva.
Ademas de emplearlo para trabajar con archivo escritos en lenguaje Pascal orien-
tad0 a objetos (o-en lenguaje Delphi, como prefiere llamarlo ahora Borland), se
puede usar ahora para trabajar con otros archivos relacionados con el desarrollo
en Delphi (como archivos SQL, XML, HTML y XSL), al igual que con archivos
de otros lenguajes (entre 10s que se incluyen C++ y C#). La edicion de XML y
HTML ya estaba disponible en Delphi 6, per0 10s cambios en esta version son
importantes. Por ejemplo, durante la edicion de un archivo HTML se tiene sopor-
te tanto para resaltado de sintaxis como para acabado de codigo.
Las configuraciones el editor para cada archivo (incluido el comportamiento
de teclas como Tab) dependen de la estension del archivo que se abra. Se pueden
configurar estos parametros mediante la nueva pagina Source Options del cua-
dro de dialogo Editor Properties, que muestra la figura 1.4. Esta caracteristica
se ha ampliado y abierto aun mas para que incluso pueda configurarse el editor
mediante un DTD para formatos de archivo basados en XML o mediante un
asistente personalizado que proporcione el resaltado de sintaxis para otros len-
guajes de programacion. Otra caracteristica del editor, las plantillas de codigo,
son ahora especificas del lenguaje (las plantillas predefinidas para Delphi tendran
poco sentido en HTML o C # ) .
Figura 1.4. Los diversos lenguajes soportados por el IDE de Delphi se pueden
asociar con varias extensiones de archivo mediante la pagina Source Options del
cuadro de dialogo Editor Properties.
El Code Explorer
L a ventana Code Explorer, que por lo general esta anclada en el lateral del
editor, lista sencillamente todos 10s tipos, variables y rutinas definidas en una
unidad, mas otras unidades que aparecen en sentencias u s e s . En el caso de tipos
complejos, como las clases, el Code Explorer puede listar informacion
pormenorizada, como una lista de campos, propiedades y metodos. Cuando co-
menzamos a teclear en el editor, toda la informacion se actualizara.
Podemos usar el Code Explorer para desplazarnos por el editor. A1 hacer
doble clic sobre una de las entradas del Code Explorer, el editor pasa a la
declaracion correspondiente. Tambien podemos modificar nombres de variables,
propiedades y m6todos directamente en el Code Explorer. Sin embargo, si se
desea utilizar una herramienta visual para trabajar con las clases, ModelMaker
ofrece muchas mas caracteristicas.
Aunque todo esto resulta bastante obvio a 10s cinco minutos de comenzar a
usar Delphi, algunas caracteristicas del Code Explorer no se pueden utilizar de
una forma tan intuitiva. Lo importante es que el usuario tiene control total sobre
el mod0 en que aparece dispuesta la informacion y que se puede reducir la profun-
didad del arbol que aparece en esta ventana cuando se personaliza el Code
Explorer. Si reducimos el arbol, podremos realizar las elecciones con mayor
rapidez. Podemos configurar el Code Explorer mediante la pagina de Environment
Options correspondiente, como se muestra en la figura 1.5.
Exploracion en el editor
Otra caracteristica del editor es la Tooltip symbol insight (Ventanas de suge-
rencia sobre simbolos). A1 mover el raton sobre un simbolo del editor, una venta-
na de sugerencia nos mostrara el lugar en el que se declara el identificador. Esta
caracteristica puede resultar especialmente importante para realizar el seguimien-
to de identificadores, clases y funciones de una aplicacion que estamos escribien-
do y tambien para consultar el codigo fuente de la biblioteca de componentes
visuales (VCL).
-
ADVERTENCIA: Aunque pueda parecer buena idea en principio, no po-
demos usar la ventana de sugerencia sobre simbolos para averiguar que
unidad declara un identificador que queremos emplear. En realidad. la ven-
tana de sugerencia no aparece, si no se ha incluido todavia la unidad corres-
pondiente.
Class Completion
El editor de Delphi tambien puede generar parte del codigo fuente, completan-
do lo que ya se haya escrito. Esta caracteristica se llama Class Completion, y se
activa a1 pulsar la combinacion de teclas Control-Mayus-C. Aiiadir un controla-
dor de eventos a una aplicacion es una operacion rapida, porque Delphi aiiade
automaticamente la declaracion de un nuevo metodo que controle el evento y nos
proporciona el esquema del metodo en la seccion de implementacion de la unidad.
Esto forma parte del soporte para programacion visual de Delphi.
De un mod0 similar, las ultimas versiones de Delphi han conseguido facilitar
el trabajo de 10s programadores que escriben codigo extra detras de 10s
controladores de evento. De hecho, la nueva caracteristica de creacion de codigo
afecta a 10s metodos generales, a 10s metodos de control de mensajes y a las
propiedades. Por ejemplo, si tecleamos el siguiente codigo en la declaracion de
clase:
public
procedure Hello (MessageText: string) ;
Esto resulta mas comodo que copiar y pegar una o mas declaraciones, aiiadir
10s nombres de clase y por ultimo duplicar el codigo begin. . . end en cada
metodo copiado. La funcion C 1ass C omp1etio n tambien puede funcionar a
la inversa: podemos escribir la implementacion del metodo directamente con su
codigo y despues pulsar Control-Mayus-C para crear la entrada necesaria en la
declaracion de clase.
El ejemplo mas importante y util de esta funcion de completitud de clases es la
generacion automatica de codigo para dar soporte a las propiedades declaradas en
las clases. Por ejemplo, si en una clase se escribe
p r o p e r t y Value: Integer;
Code Insight
Ademas del Code Explorer, la funcion de completitud de clases y las funcio-
nes de desplazamiento, el editor de Delphi soporta la tecnologia Code Insight. En
conjunto, las tecnicas Code Insight se basan en un analisis sintactico continuo en
segundo plano, tanto del codigo fuente que escribimos como del codigo fuente de
las unidades del sistema a las que se refiere nuestro codigo.
La funcion Code Insight implica cinco capacidades: Code Completion, Code
Templates, Code Parameters, Tooltip Expression Evaluation y Tooltip Symbol
Insight. Esta ultima caracteristica se trato durante la seccion sobre la exploracion
en el editor. Todas estas caracteristicas se pueden habilitar, inhabilitar y configu-
rar en la pagina Code Insight del cuadro de dialog0 Editor Properties.
Code Completion
La funcion Code Completion permite escoger la propiedad o metodo de un
objeto simplemente buscandolo en una lista o escribiendo sus letras iniciales.
Para activar esta lista, solo hay que teclear el nombre de un objeto, como Buttonl,
aiiadir el punto y esperar. Para que forzar la aparicion de la lista, hay que pulsar
Control-Barra espaciadora; para quitarla cuando no queramos verla, hay que
pulsar Esc. La funcion Code Completion permite ademas buscar un valor adecua-
do en una sentencia de asignacion.
Cuando comenzamos a teclear, la lista va filtrando su contenido de acuerdo
con la parte inicial del elemento que hemos escrito. La lista Code Completion
emplea colores y muestra mas detalles para ayudarnos a distinguir elementos
diferentes. En Delphi, se pueden personalizar estos colores mediante la pagina
Code Insight del cuadro de dialogo Editor Properties. Otra caracteristica en el
caso de funciones con parametros es la inclusion de parentesis en el codigo creado
y la aparicion inmediata de la ventana de sugerencia de la lista de parametros.
Cuando se escribe := despues de una variable o propiedad, Delphi listara
todas las demas variables u objetos del mismo tipo, ademas de 10s objetos que
tengan propiedades de ese tipo. Mientras la lista permanece visible, podemos
hacer clic con el boton derecho del raton sobre ella para modificar el orden de 10s
elementos, clasificandolos por alcance o por nombre y tambien podemos adaptar
el tamaiio de la ventana.
Desde Delphi 6, Code Completion funciona ademas en la parte de interfaz de
una unidad. Si pulsamos Control-Barra espaciadora mientras el cursor esta
dentro de la definition de clase, obtendremos una lista de 10s metodos virtuales
que se pueden sobrescribir (como por ejemplo, 10s metodos abstractos), 10s meto-
dos de las interfaces implementadas, las propiedades de clase basica y, por ulti-
mo, 10s mensajes del sistema que se pueden controlar. A1 seleccionar uno de ellos,
aiiadiremos sencillamente el metodo adecuado a la declaracion de clase. En este
caso concreto, la lista Code Completion permite la seleccion multiple.
L.
TRgC(r:,l&fpG 7 b capaoidad de explorar la d e c h r w i h de ele-
mentos de la fista.de campletitud de codigo a1 mantener pul'sadd la teela
Control y'hacer chc sohre cualquier identificador de la Ikta.
Code Templates
Esta caracteristica permite insertar una de las plantillas de codigo predefinidas,
como una declaracion compleja con un bloque interior b e g i n . . . . e n d . Las
plantillas de codigo deben activarse de forma manual, usando Control-J para
obtener una lista de todas ellas. Si tecleamos unas cuantas letras (como una pala-
bra clave) antes de pulsar Control-J, Delphi listara solo las plantillas que
comiencen por dichas letras.
Tambien se pueden aiiadir plantillas de codigo personalizadas, para crear me-
todos abreviados para 10s bloques de codigo que usemos normalmente. Por ejem-
plo, si empleamos con frecuencia la funcion M e s s a g e D l g , podemos aiiadir una
plantilla para la misma.
Para modificar plantillas, mediante la pagina Source Options del cuadro de
dialogo Editor Options, hay que seleccionar Pascal en la lista Source File Type
y hacer clic sobre el boton Edit Code Templates. A1 hacer esto, aparecera el
nuevo cuadro de dialogo Code Templates de Delphi 7.
En este momento, si hacemos clic sobre el boton Add, escribimos un nuevo
nombre de plantilla (por ejemplo, d e s o r d e n ) , escribimos tambien una descrip-
cion y, a continuacion, aiiadimos el siguiente texto a1 cuerpo de la plantilla en el
control memo Code:
MessageDlg (' I' , mtInformation, [mbOK] , 0) ;
Ahora, cada vez que necesitemos crear un cuadro de dialogo de mensaje, sim-
plemente escribiremos d e s o r d e n y, a continuacion, pulsaremos Control-J para
obtener el texto completo. El caracter de linea vertical indica la posicion dentro
del codigo fuente en la que estara el cursor en el editor despues de haber desplega-
do la plantilla. Deberiamos escoger la posicion en la que queremos comenzar a
teclear para completar el codigo producido por la plantilla.
Aunque pueda parecer que las plantillas de codigo, a primera vista, se corres-
ponden con palabras clave del lenguaje, estas son en realidad un mecanismo mas
general. Se guardan en el archivo DELPHI32.DC1, un archivo de texto en un
formato bastante simple que puede editarse con facilidad. Delphi 7 tambien per-
mite exportar la configuracion para un lenguaje a un archivo e importarla, lo que
facilita que 10s desarrolladores intercambien sus propias plantillas personalizadas.
Code Parameters
La funcion Code Parameters muestra, en una ventana de sugerencia, el tip0 de
datos de 10s parametros de un metodo o funcion mientras 10s tecleamos. A1 escri-
bir el nombre de la funcion o metodo y abrir el parentesis, apareceran inmediata-
mente 10s nombres y tipos de parametro en una ventana de sugerencia contextual.
Para que forzar a que aparezcan 10s parametros de codigo, podemos pulsar Con-
trol-Maylis-Barra espaciadora. Ademas, el parametro en uso aparece resaltado
en negrita.
Tooltip Expression Evaluation
La funcion Tooltip Expression Evaluation es una caracteristica en tiempo de
depuracion. Muestra el valor del identificador, la propiedad o expresion que esta
bajo el cursor del raton. En el caso de una expresion, normalmente necesitara
seleccionarla en el editor y despues mover el cursor sobre el texto seleccionado.
ti-
h e
Q+fcrolim
J
Ths IS a simple version
r-
M base
Form Designer
Otra ventana de Delphi con la que vamos a interactuar muy a menudo es el
Form Designer; una herramienta visual para colocar componentes en 10s formu-
larios. En el Form Designer, se puede seleccionar directamente un componente
con cl raton o a traves dcl Object Inspector o la Object Treeview, mttodos
ultimos utiles en caso de quc un control quede oculto. Si un control cubre a otro
por complete, se puedc emplear la tecla Esc para seleccionar el control padre del
control actual. Se pucdc pulsar la tecla Esc una o mas veces para seleccionar el
formulario o pulsar y mantener pulsada la tecla Mayus mientras sc hace clic
sobre cl componcntc scleccionado. Esto desactivara la selection del componente
en uso y seleccionara el formulario por defecto.
Esisten dos alternativas al uso del raton para fijar la posicion de un compo-
ncnte. Se pueden definir valores para las propiedades L e f t y TOP, o bien usar
las teclas de cursor mientras se mantiene pulsada la tech Control. El uso de las
teclas de cursor resulta util sobre todo para precisar la posicion de un elemento
(cuando la opcion Snap To Grid se encuentra activada), a1 igual que mantener
pulsada la tecla Alt y utilizar el raton para mover el control. Si se pulsa la
combinacion Control-Mayus junto con una tecla de cursor, el componente se
mover6 solo a intervalos de cuadricula.
Del mismo modo, a1 pulsar las teclas de cursor mientras se mantiene pulsada
la tecla Mayus, podemos precisar el tamaiio de un componente, algo que tambien
se puede hacer mediante la tecla Alt y el raton.
Para alinear diversos componentes o hacer que tengan el mismo tamaiio, se
pueden sclcccionar todos ellos y establecer las propiedades Top, L e f t , Width
o H e i g h t de todos a1 mismo tiempo. Para seleccionar varios componentes, po-
demos haccr clic sobre ellos con el raton mientras mantenemos pulsada la tecla
Mayus o. si todos 10s componentes se encuentran en zonas rectangulares, se
puede arrastrar el raton hasta "dibujar" un rectangulo que 10s rodee. Para selec-
cionar 10s controles hijo (por ejemplo, 10s botones que se encuentran dentro de un
panel), arrastraremos el raton dcntro del panel mientras que mantendremos pulsa-
da la tecla Control, ya que de no ser asi desplazaremos el panel. Cuando ya esten
seleccionados 10s diversos componentes, tambien se puede fijar su posicion rela-
tiva utilizando el cuadro de dialog0 Alignment (con la ordcn A l i g n del menu de
metodo abreviado del formulario) o Alignment Palette (a la que accedemos
mediante la orden del menu View>Alignment Palette).
Cuando este terminado el diseiio de un formulario, podemos emplear la orden
L o c k C o n t r o l s del menu Edit para evitar cambiar por equivocacion la posi-
cion de una componente en un formulario. Esto resulta util, sobre todo teniendo
en cuenta que las operaciones Undo en 10s formularios son limitadas (solo se
puede recuperar elementos eliminados), per0 la definicion no es permanente.
Entre otras de sus caracteristicas, el Form Designer ofrece diversas ventanas
de sugerencia:
A1 mover el punter0 sobre un componente, en la sugerencia aparece el
nombre y el tipo del componente. Desde la version 6, Delphi ofrece suge-
rencias extendidas, con datos sobre la posicion del control, el tamaiio, el
orden de tabulacion y mas. Esta es una mejora de la configuracion del
entorno Show Component Captions que se puede mantener activada.
Cuando adaptamos el tamaiio de un control, en la sugerencia aparece el
tamaiio actual (las propiedades W i d t h y H e i g h t ) . Por supuesto, estas
caracteristicas estan disponibles solo para controles, no para componentes
no visuales (que estan indicados en el Form Designer mediante iconos).
A1 mover un componente, la sugerencia indica la posicion actual (las pro-
piedades L e f t y Top).
Por ultimo, se pueden guardar 10s archivos DFM (Delphi Form Module, Mo-
dulo de Formulario Delphi) en el formato de recurso binario tradicional, en lugar
de hacerlo como texto normal que es el comportamiento predeterminado. Esta
opcion se puede modificar en el caso de un formulario individual, con el menu de
metodo abreviado del Form Designer o establecer un valor predefinido para 10s
formularios nuevos que creemos en la ficha Designer del cuadro de dialogo
Environment Options. En la misma ficha, podemos especificar tambien si 10s
formularios secundarios de un programa se crearan automaticamente a1 arrancar,
una decision que siempre se podra modificar en el caso de cada formulario indivi-
dual (usando la ficha Forms del cuadro de dialogo Project Options).
Disponer de archivos DFM almacenados como texto permite trabajar de mane-
ra mas eficaz con sistemas de control de versiones. Los programadores no se
aprovecharan mucho de esta caracteristica, ya que se podria simplemente abrir el
archivo DFM binario en el editor de Delphi con un comando especifico desde el
menu de metodo abreviado del diseiiador. Por otra parte, 10s sistemas de control
de versiones necesitan guardar la version textual de 10s archivos DFM para ser
capaz de compararlos y extraer las diferencias entre dos versiones del mismo
archivo. En cualquier caso, si se utilizan archivos DFM como texto, Delphi 10s
convertira a un formato de recurso binario antes de incluirlos en el archivo ejecu-
table del programa. Los archivos DFM estan enlazados a su ejecutable en forma-
to binario para reducir el tamaiio del archivo ejecutable (aunque no esten
comprimidos) y para mejorar el rendimiento en tiempo de ejecucion (se pueden
cargar mas rapido).
NOTA: Los archivos de texto DPM resultan m b faciles de tramportar de
una version a otra de Delphi que sus versiones binarias. Aunque una ver-
- de Delphi puede no acevtar una nueva propiedad de un
sion mas antigua
control en un archivo DFM ireado por u& veni6n posterior be Delphi, la 1
version anterior si sera capaz de leer el resto del archivo de texto DFM. Sin
a
,.t,
GuIualgu, A I, L
:.,.. ,A- c~.-.:~,+, A, r\-1-L: -Z.-.Ar.
SI la YGIJIUU 111aaIGUGULG UG U G I ~ L U m a u ~
....
u u ....-.., , , :+
IIUGVU c~yu
A, A,+,.
UG uacua,
Object lnspector
Para visualizar y modificar las propiedades dc 10s componentes de un formula-
rio (u otro disciiador) en tiempo de diseiio, se puede utilizar el Object Inspector.
En comparacion con las primeras versiones de Delphi, el Object lnspector dis-
pone de unas cuantas caracteristicas nuevas. La ultima, presentada en Delphi 7,
es cl uso de una fuente en negrita para resaltar las propiedades que tienen un valor
distinto dcl predefinido. Otro importante cambio (en Delphi 6) es la capacidad del
Object lnspector de desplegar las referencias de 10s componentes emplazados.
Las propiedades que se refieren a otros componentes aparecen en un color dife-
rcntc y pueden desplegarse seleccionado el simbolo + de la izquierda, como ocu-
rre con 10s subcomponentes internos. A continuacion, se pueden modificar las
propiedades de ese otro componente sin tener que seleccionarlo. La siguiente
figura muestra un componente conectado (un menil desplegable) expandido en el
Object lnspector mientras que se trabaja con otro componente (un cuadro de
lista):
Esta caracteristica de ampliacion de la interfaz tambien soporta subcompo-
nentes, tal y como demuestra el nuevo control LabeledEdit.Una caracteristi-
ca relacionada del Object lnspector es que podemos seleccionar el componente
a1 que hace referencia una propiedad. Para ello, hacemos doble clic sobre el valor
de la propiedad con el boton izquierdo del raton mientras mantenemos pulsada la
tecla Control. Por ejemplo, si tenemos un componenteMainMenu en un formu-
lario y estamos echando un vistazo a las propiedades del formulario en el Object
Inspector, podemos seleccionar el componente MainMenu moviendonos a la pro-
piedad MainMenu del formulario y haciendo doble clic sobre el valor de dicha
propiedad mientras mantenemos pulsada la tecla Control. Con esto se selecciona
el menu principal indicado junto con el valor de la propiedad en el Object Ins-
pector. A continuacion aparece una lista de algunos cambios recientes del Object
Inspector:
La lista situada en la parte superior del Object Inspector muestra el tip0 de
objeto y permite escoger un componente. Puede eliminarse para ahorrar
algo de espacio, ya que se puede seleccionar componentes en la Object
Treeview.
Las propiedades que hacen referencia a un objeto son ahora de un color
diferente y pueden ampliarse sin cambiar la seleccion.
Opcionalmente se pueden ver tambien las propiedades de solo lectura en el
Object Inspector. Por supuesto, estan en gris.
El Object lnspector posee un cuadro de dialog0 Properties, que permite
personalizar 10s colores de diversos tipos de propiedades y el comporta-
miento general de esta ventana.
Desde Delphi 5, la lista desplegable de una propiedad puede incluir ele-
mentos graficos. Esta caracteristica la utilizan propiedades como color
y Cursor,y es particularmente util para la propiedad ImageIndex de
10s componentes conectados a una ImageList .
I
B F d
j
1
Charsel
Cob
ITFant)
'DEFAULT-CHARSET
' W cWindowText
Categorias propiedades
Delphi incluye tambien el concept0 de categorias de propiedades, activadas
mediante la opcion Arrange del mcnu local del Object Inspector. Si se activa
csta opcion, las propiedades no se listaran alfabeticamente sino que se organiza-
ran por grupos, con la posibilidad de que cada propiedad aparezca cn diversos
grupos.
Las categorias tienen la ventaja de reducir la complejidad del Object Inspec-
tor. Se puede usar el submenu View presente en cl menu de metodo abreviado
para ocultar propiedades de determinadas categorias. sea cual sea el mod0 en que
aparezcan (es decir, incluso aunque se desee el tradicional orden por nombrc, aun
asi se podran las propiedades de algunas categorias).
Object TreeView
Delphi 5 introdujo una vista en arbol para modulos de datos: en la que se
podian ver las relaciones entre 10s componentes no visuales, como 10s conjuntos
de datos, 10s campos, las acciones, etc. Delphi 6 amplio esta idea a1 proporcionar
una Object TreeView para cada diseiiador, como en el caso de 10s formularios
simples. La Object TreeView se situa por defecto sobre el Object Inspector.
La Object TreeView muestra todos 10s componentes y objetos del formulario
en un arbol en el que se representan sus relaciones.
La relacion mas obvia es la relacion padrelhijo: si colocamos un panel sobre
un formulario, un boton dentro de cste y uno fuera del panel, en el arbol aparece-
ran 10s dos botones, uno bajo el formulario y el otro bajo el panel, tal como
mucstra la figura:
hn New (Newl)
em, Open (Open11
bM,Save (Save1 )
1Folrnl
0 Bullon2
[-I IJ
- ,: Columns
4 o .r L ~ r c o l w m
L: 1 TL~~tCalurnn
2 . TL~stCalumn
4 3 - T L~slCd.mm
T
Framel
I1
IS Smt Smi
(@dad
Framel
All
I
Figura 1.8. El ejemplo Framesl demuestra el uso de marcos El marco (a la
izquierda) y su instancia en un formulario (a la derecha) permanecen en sincronia.
A1 igual que 10s formularios, 10s marcos definen clases, por lo que encajan
dentro del modelo orientado a objetos de la VCL con mayor facilidad que las
plantillas de componentes. Como cabe imaginar a partir de esta introduccion
breve, 10s marcos son una tecnica nueva realmente potente.
Gestionar proyectos
El Project Manager de Delphi (View>Project Manager) funciona con un
grupo de proyectos, que puede englobar a su vez uno o mas proyectos. Por ejem-
plo, un grupo de proyectos puede incluir una DLL y un archivo ejecutable o
varios archivos ejecutables. Todos 10s paquetes abiertos apareceran como pro-
yectos en la vista del Project Manager, incluso aunque no se hayan aiiadido a1
grupo de proyecto.
En la figura 1.9, podemos ver el Project Manager con el grupo de proyecto
del presente capitulo. Como se puede ver, el Project Manager se basa en una
vista en arbol, que muestra la estructura jerarquica del grupo de proyectos, 10s
proyectos y todos 10s formularios y unidades que lo componen. Podemos emplear
la barra de herramientas y 10s menus de metodo abreviado mas complejos del
Project Manager para trabajar con el. El menu de metodo abreviado funciona de
acuerdo con el contexto: sus opciones dependen del elemento seleccionado. Hay
elementos del menu para afiadir un nuevo proyecto o un proyecto esistente a1
grupo de proyecto, para compilar o crear un proyecto especifico o para abrir una
unidad.
Fata P&h - - - - - - -- -
C Wchnos de programa\Borland\Delph17\Prolecls
- ! 3 w e r n ~me D \rn~code\~~\~~agram~erno
d 9 OtagrarnForrn D Lnd7caJe\Ol\D1agramDemo
n
a
ttl
J
ToDoTesl exe
Fiamesl.ere
D \md7code\0l\ToDoTest
D \rnd7caJe\Ol\Fiarnesl
3
,-' Fum D hd7mde\Ol\Framesl
5 Fnm pas D \md7wdeWl\Frametl
a Form1 D \md7codeWl\Fiamesl
13 @ Frame D \rnd7codeWl \Frames1
@ Flame pas D \md7code\Ol \Frames1
a Fianel D hd7mde\O1 \Flames1
De todos 10s proyectos de un grupo, solo hay uno que esta activo y ese es el
proyecto sobre el que trabajamos cuando seleccionamos una orden como
Project>Compile. El menu desplegable Project posee dos ordenes para compi-
lar o crear todos 10s proyectos del grupo. Cuando tengamos que crear diversos
proyectos, podemos establecer un orden relativo usando las ordenes Build
S o o n e r y Build Later.Estas dos ordenes basicamente reorganizan 10s pro-
yectos de la lista.
Entre las caracteristicas avanzadas del Project Manager, se encuentra la
funcion de arrastre de archivos de codigo fuente desde carpetas de Windows o
desde el Windows Explorer a un proyecto de la ventana del Project Manager
para aiiadirlos a un proyecto (tambien se soporta este comportamiento para abrir
archivos en el editor de codigo). Podemos ver facilmente que proyecto esta selec-
cionado y cambiarlo utilizando el cuadro combinado de la parte superior de la
ventana o utilizando la flecha hacia abajo que se encuentra junto a1 boton Run en
la barra de herramientas de Delphi.
Ademas de aiiadir archivos y proyectos de Pascal, se pueden aiiadir archivos
de recurso de Windows a1 Project Manager; estos se compilan junto con el
proyecto. Sencillamente, hay que desplazarse a un proyecto, seleccionar Add en
el menu de metodo abreviado y escoger Resource File (*.rc) como tipo de
archivo. Este archivo de recurso se unira automaticamente a1 proyecto, incluso
aunque no haya una directiva $R correspondiente.
Delphi guarda 10s grupos de proyectos con la extension .BPG (Borland Project
Group). Esta caracteristica procede del C++ Builder y de 10s antiguos compiladores
Borland C++, un historial que resulta claramente visible a1 abrir el codigo fuente
de un grupo de proyectos, que basicamente corresponde a1 de un archivo makefile
de un entorno de desarrollo C/C++. Veamos un ejemplo:
#----------------------------------------------------------
M A K E = $ (ROOT)\bin\make.exe - $ (MAKEFLAGS) -f$**
DCC = $ (ROOT)\bin\dcc32. exe $ * *
BRCC = $ (ROOT)\bin\brcc32. exe $ * *
#----------------------------------------------------------
PROJECTS = Project1 .exe
Opciones de proyecto
El Project Manager no ofrece una forma de definir las opciones para dos
proyectos diferentes a la vez. Sin embargo, se puede recurrir a1 dialog0 Project
Options desde el Project Manager en el caso de cada proyecto. La primera
ficha de Project Options (Forms) muestra la lista de 10s formularios que se
deberian crear automaticamente cuando arranca el programa y 10s formularios
que crea el propio programa. La siguiente ficha (Application) se usa para esta-
blecer el nombre de la aplicacion y el nombre de su archivo de ayuda y para
escoger su icono. Otras posibilidades de Project Options estan relacionadas con
el compilador y el editor de enlaces de Delphi, la informacion sobre la version y el
uso de paquetes en tiempo de ejecucion.
Existen dos formas de configurar las opciones del compilador. Una es utilizar
la ficha Compiler del dialogo Project Options. La otra es definir o eliminar las
opciones individuales del codigo fuente con las ordenes { $x+} o { $x-} , en las
que se reemplazaria la X por la opcion que queramos definir. Esta segunda tecni-
ca resulta mas flexible, puesto que permite modificar una opcion solo para un
archivo de codigo fuente concreto o incluso solarnente para unas cuantas lineas de
codigo. Las opciones del nivel de fuente sobrescriben las opciones del nivel de
compilacion.
Todas las opciones de un proyecto se guardan automaticamente con el, per0 en
un archivo a parte con una extension .DOF. Este es un archivo de texto que se
puede editar facilmente. No se deberia eliminar dicho archivo si se ha modificado
alguna de las opciones predefinidas. Delphi tambien guarda las opciones del
compilador en otro formato, en un archivo CFG, para la compilacion desde la
linea de comandos. Los dos archivos poseen un contenido similar per0 tienen un
formato distinto: el compilador de la linea de comandos dcc no puede usar archi-
vos .DOF, sino que necesita el formato .CFG.
Tambien se pueden guardar las opciones del compilador pulsando Control-0-
0 (pulsar la tecla 0 dos veces mientras se mantiene pulsada la tecla Control).
Esto inserta, en la parte superior de la unidad actual, directivas de compilador
que corresponden a las opciones de proyecto en uso, como en el siguiente listado:
I$A+,B-, C+,D+, E - , F- ,G+,Ht, I t , J t , K - , L t , M - , N t , O+, P t , Q-,R-, S - , T-
,U-,vt, W-,X+,Yt,Zl)
{$MINSTACKSIZE $ 0 0 0 0 4 0 0 0 )
{$MAYSTACKSIZE $OOIOOOOO)
{$IMAGEBASE $ 0 0 4 0 0 0 0 0 )
{SAPPTYPE G U I )
ISWARN SYMBOL-DEPRECATED O N )
{$WARN S Y M B O L - L I B R A R Y ON)
{$WARN SYMBOL-PLATFORM ON)
{$WARN U N I T - L I B R A R Y O N )
{$WARN UNIT-PLATFORM ON)
{$WARN UNIT-DEPRECATED O N )
{$WARN HRESULT-COMPAT ON)
{$WARN HIDING-MEMBER O N )
{$WARN HIDDEN-VIRTUAL ON)
{$WARN GARBAGE O N )
{$WARN BOUNDS-ERROR O N )
{$WARN ZERO-NIL-COMPAT ON)
{$WARN STRING-CONST- TRUNCED O N )
{$WARN FOR-LOOP-VAR-VARPAR ON)
{$WARN TYPED-CONS T-VARPAR O N )
{$WARN ASG- TO- TYPED-CONST O N )
{$WARN CASE-LABEL-RANGE ON)
{$WARN FOR-VARIABLE ON)
{$WARN CONS TRUCTING-ABS TRACT ON) {$WARN COMPARISON-FALSE ON)
{$WARN COMPARISON- TRUE ON)
{$WARN COMPARING- S IGNED- UNSIGNED ON)
$ WARN COMBINING- S I G N E D UNSIGNED ON)
{$WARN UNSUPPORTED-CONS TRUCT ON)
{$WARN FILE-OPEN ON)
{$WARN FILE-OPEN-UNITSRC ON)
{$WARN BAD-GLOBAL-SYWBOL ON)
{$WARN DUPLICATE-CTOR-DTOR ON)
{$WARN INVALID-DIRECTIVE ON)
{$WARN PACKAGE-NO- L I N K O N )
{$WARN PACKAGED- THREADVAR ON)
{$WARN I M P L I C I T - IMPORT ON)
{$WARN HPPEMI T- IGNORED ON)
{$WARN NO-RETVAL ON)
{$WARN USE-BEFORE-DEF ON)
{$WARN FOR-LOOP-VAR-UNDEF ON)
{$WARN UNIT--MISMATCH ON)
{$WARN NO-CFG-FILE-FOUND ON)
{$WARN MESSAGE-DIRECTIVE ON)
{$WARN IMPLICIT-VARIANTS ON)
{$WARN UNICODE- TO-LOCALE ON)
{$WARN LOCALE- TO- UNICODE ON)
{$WARN IMAGEBASE-MULTIPLE ON)
{$WARN SUSPICIOUS-TYPECAST ON)
{$WARN PRIVATE-PROPACCESSOR ON)
{$WARN UNSAFE- T Y P E O F F )
{$WARN UNSAFE-CODE OFF)
ISWARN UNSAFE-CAST O F F )
I
yarn- - - - - - - --.
i
,JJUser message
B lmpl~utuse of Varlants unit
Errol conveltrng Unicode cha~to locale charsel
@ Er~orconverl~qlocals s l i i to
~ Unicode
,4Imagebase e not a rmkiile d 64k
1 M
LI'.Unsafe typecast
- 1
Figura 1.10. La nueva pagina Compiler Messages del cuadro de dialogo Project
Options.
.DFM Delphi Form File (Archivo Desarrc Si. Todos 10s formula-
de formulario de Delphi): rios se almacenan
un archivo binario con la tanto en un archivo
descripcion de las propie- PAS como en un
dades de un formulario (o DFM.
un modulo de datos) y de
10s componentes que con-
tiene.
EXE Executable file (Archivo eje- Compilacion: No. Este archivo que
cutable): la aplicacion Enlace se distribuira incluye
Windows creada. todas las unidades
compiladas, forrnula-
rios y recursos.
OBJ Object file (Archivo objeto) Paso intermedio Podria ser necesario
(cornpilado), tipico del de compilacion, para rnezclar Delphi
rnundo C/C++. generalmente no con codigo cornpilado
se usa en C++ en un tinico
Delphi. proyecto.
RES, .RC Resource file (Archivo de Cuadro de dialo- Si. Delphi crea de
recurso): el archivo binario go Development nuevo el archivo RES
asociado con el proyecto Options. El ITE principal de una apli-
de una aplicacion y que (Integrated cacion en funcion de
normalmente contiene su Translation la inforrnacion de la
icono. Podernos afiadir Environment) ficha Application del
otros archivos de este tip0 crea archivos de cuadro de dialogo
a un proyecto. Cuando recurso con Project Options.
creamos archivos de recur- comentarios
so personalizados pode- especiales.
rnos usar tambien el
forrnato textual .RC.
.UDL Microsoft Data Link (Enlace Desarrollo Usado por ADO para
de datos Microsoft). referirse a un provee-
dor de datos. Similar
a un alias en el entor-
no BDE.
rn
presenta una breve lista de las extensiones que merece la pena conocer. La mayo-
ria de estos archivos estan en formatos propietarios no documentados, por lo que
poco se puede hacer con ellos.
1
TRUCO:En el libro, aparecen normalmente extractos de archivos DFM.
En l a mayoridde estos extractos, aparecen unicamente 10s componentes o
propiedades m k relevantes, por lo general, he elirninado las propiedades de
posicion, 10s valores binarios y otra lineas que ofiecen poca informacion.
Ademas de 10s dos archivos que describen el formulario (PAS y DFM), hay un
tercer archivo que resulta vital para volver a construir la aplicacion. Este es el
archivo de proyecto de Delphi (DPR), otro archivo de codigo f~ienteen Pascal,
que se crea automaticamente y que rara vez es necesario modificar manualmente.
Puede verlo con la orden del menu View>Project Source.
Algunos de 10s demas archivos menos relevantes creados por el IDE usan la
estructura de archivos IN1 de Windows, en la que cada seccion se indica mediante
un nombre que va entre corchetes. Por ejemplo, este es un fragment0 de un archi-
vo de opciones (DOF).
[Compiler]
A= 1
B=O
[Linker]
MinStackSize=16384
MaxStackSize=1048576
ImageBase=4194304
[Parameters]
Runparams=
HostApplication=
El Object Repository
Delphi tiene varias ordenes de menu que se pueden usar para crear un nuevo
formulario, una nueva aplicacion, un nuevo modulo de datos, un nuevo compo-
nente, etc. Dichas ordenes estan situadas en el menu File>New y en otros menus
desplegables. Si seleccionamos sencillamente File>New>Other, Delphi abre el
Object Repository, que se usa para crear nuevos elementos de cualquier tipo:
formularios, aplicaciones, modulos de datos, objetos thread, bibliotecas, compo-
nentes, objetos de automatizacion y muchos mas.
El nuevo cuadro de dialogo (que se ve en la figura 1.12) posee varias fichas,
contiene todos 10s elementos que puede crear, 10s formularios existentes y 10s
proyectos almacenados en el Repository, asistentes Delphi y 10s formularios del
proyecto actual.
Las fichas y las entradas de este cuadro de dialogo con solapas dependen de la
version especifica de Delphi, por lo que no las mencionaremos.
Console
Cwone* Applcat~on
TI
Flame Package Ploiect GI- Resource DLL
Wnald
Service
1 - C
Figura 1.12. La primera pagina del cuadro de dialogo New Items, conocida
generalmente corno Object Repository.
--
I
Wc_hName ' V W - - _ -_ . ..-
rnControls ' ' expected but end of lde found
rnTRad~oButton Symbol was el~mtnatedby l~nker
El lenguaje
de programaclon
Delphi
Como en la mayor parte del resto de 10s lenguajes orientados a objetos (como
Java y C#),en Delphi una variable de tipo clase no proporciona el almacenamien-
to para el objeto, sino solo un punter0 o referencia al objeto en la memoria. Antes
de utilizar el objeto, se debe reservar memoria para 61 mediante la creacion de una
nueva instancia o asignando una instancia ya existente a la variable:
var
Obj 1, Obj2 : TMyClass;
begin
// a s i g n a r un o b j e t o r e c i e n c r e a d o
Objl : = TMyClass.Create;
// a s i g n a r un o b j e t o e x i s t e n t e
Obi2 : = ExistingObject;
Con este codigo, se crean botones en las posiciones en las que se haga clic con
el raton, como muestra la figura 2.1. En el codigo anterior, fijese en concreto en el
uso de la palabra clave S e 1f , tanto como parametro del metodo c r e a t e (para
especificar el dueiio del componente), como valor de la propiedad P a r e n t .
Figura 2.1. El resultado del ejernplo CreateCornps, que crea componentes boton en
tiernpo de ejecucion.
Cuando se escribe un procedimiento como el codigo que acabamos de ver,
podriamos sentirnos tentados a utilizar la variable F o r m l en lugar de S e l f . En
este ejemplo concreto, el cambio no tendria ninguna diferencia practica, per0 si
existen diversas instancias de un formulario, usar F o r m l seria un error. De
hecho, si la variable F o r m l se refiere a1 primer formulario de ese tipo que se ha
creado, a1 pinchar sobre otro formulario del mismo tipo, el nuevo boton siempre
aparecera en el primer formulario. Sus O w n e r y P a r e n t seran el F o r m l y no
el formulario que ha pinchado el usuario. Por lo general, no conviene referirse a
una instancia concreta de una clase cuando se necesita el objeto actual.
Encapsulado
Una clase puede tener cualquier cantidad de datos y cualquier numero de meto-
dos. Sin embargo, para conseguir una buena tecnica orientada a objetos, 10s datos
deberian estar ocultos o encapsulados dentro de la clase que 10s usa. Cuando se
accede a una fecha, por ejemplo, no tiene sentido cambiar solo el valor del dia
directamente. De hecho, si se cambia el valor del dia podria resultar una fecha no
valida, como el 30 de febrero, por ejemplo. Si se usan metodos para acceder a la
representacion interna de un objeto, se limita el riesgo de generar situaciones
erroneas, puesto que 10s metodos pueden verificar si la fecha es valida y negarse
a modificar el nuevo valor si no lo es. El encapsulado es importante porque permi-
te que la persona que escribe las clases modifique la representacion interna en una
version futura.
El concepto de encapsulado se describe normalmente como una "caja negra",
en la que no se conoce el interior: simplemente se sabe como interactuar con ella
o como usarla, sin tener en cuenta su estructura. La parte "modo de empleo",
denominada interfaz de clase, permite que otras partes de un programa tengan
acceso y utilicen 10s objetos de dicha clase. Sin embargo, cuando se emplean 10s
objetos, la mayor parte de su codigo esta oculto. Rara vez se conocen 10s datos
internos que contiene el objeto y normalmente no hay manera de acceder directa-
mente a 10s datos. Por supuesto, se supone que utilizamos 10s metodos para acce-
der a 10s datos, que estan protegidos contra accesos no autorizados. Esta es la
tecnica orientada a objetos del concepto de programacion clasico conocido como
ocultacion de informacion. Sin embargo, en Delphi existe un nivel adicional de
ocultacion, mediante propiedades.
Delphi implementa este encapsulado basado en clases per0 todavia soporta el
encapsulado clasico basado en modulos, que usa la estructura de unidades. Todo
identificador que se declare en la seccion de interfaz de una unidad resulta visible
a otras unidades del programa, siempre que se utilice una sentencia u s e s que se
refiere a la unidad que define el identificador. Por otro lado, 10s identificadores
declarados en la seccion de implernentacion de la unidad seran locales a esa uni-
dad.
Privado, protegido y public0
En el caso de un encapsulado basado en clases, el lenguaje Pascal orientado a
objetos tiene tres especificadores de acccso: p r i v a t e , p r o t e c t e d y p u b l i c .
Un cuarto; p u b l i s h e d , controla la RTTI (informacion de tipo en tiempo de
e.jecucion) y la informacion en tiempo de diseiio, proporcionando la misma dispo-
nibilidad de cara a la programacion que si fuera p u b l i c . A continuacion se
enumeran 10s tres especificadores de acceso clisi'cos:
La directiva private: Denota campos y metodos de clase no accesiblcs
fuera de la unidad (el archivo dc codigo fuente) que declara la clasc.
La directiva protected: Se utiliza para indicar metodos y campos con
visibilidad limitada. Solo la clase actual y sus clases heredadas pueden
acceder a 10s elementos protegidos. Para ser mas precisos, solo la clase.
las subclases y cualquier codigo presente en la misma unidad que la clase
pueden acceder a 10s miembros protegidos.
La directiva public: Denota campos y metodos a 10s que se puede acceder
libremente desdc cualquier otra parte de un programa asi como en la uni-
dad en la que se definen.
Por lo general, 10s campos dc una clase deberian ser privados. Los metodos
son normalmente publicos. Aunque esto no siempre es asi, 10s metodos pueden ser
privados o protegidos si son necesarios internamente solo para realizar parte de
un calculo. Los campos pueden ser protegidos para que se puedan manipular en
subclases, aunque no se considera una buena practica de la orientation a objetos.
Parece que estuvidramos leyendo y escribiendo campos. Sin embargo, las pro-
piedades pueden proyectarse directamente a datos, asi como a metodos de acceso,
para leer y escribir el valor. Cuando las propiedades se proyectan a metodos, 10s
datos a 10s que acceden pueden formar parte del objeto o estar fuera de el y
pueden producir efectos secundarios, como volver a pintar un control tras haber
cambiado sus valores. Tecnicamente, una propiedad es un identificador que esta
proyectado a datos o metodos que usan una clausula r e a d y otra w r i t e . Por
ejemplo, aqui tenemos la definicion de una propiedad M o n t h para una clase de
fecha:
property Month: Integer read FMonth write SetMonth;
Normalmente, 10s datos reales y 10s metodos de acceso son privados (o prote-
gidos) mientras que la propiedad es publica. Esto significa que hay que usar la
propiedad para tener acceso a aquellos metodos o datos, una tecnica que ofrece
tanto la version simplificada como la extendida del encapsulado. Se trata de un
encapsulado ampliado, porque no solo se puede cambiar la representacion de 10s
datos y sus funciones de acceso, sino tambien aiiadir o eliminar funciones de
acceso sin cambiar el codigo de llamada en absoluto. Un usuario solo necesita
volver a compilar el programa usando la propiedad.
p r o c e d u r e T D a t e . S e t Y e a r ( c o n s t Value: I n t e g e r ) ;
begin
f D a t e : = Recodeyear ( f D a t e , Value) ;
end ;
Encapsulado y forrnularios
Una de las ideas clave del encapsulado es reducir el numero de variables globales
cmpleadas por el programa. Se puede acceder a una variable global desde todas
las partes de un programa. Por esa razon, un cambio en la variable global afecta
al programa entero. Por otra parte, cuando se cambia la representacion de un
campo de clase, solo hay que cambiar el codigo de algunos metodos de dicha clase
y nada mas. Por lo tanto, podemos decir que la ocultacion de informacion se
refiere a 10s cambios de encapsulado.
Cuando tengamos un programa con diversos forrnularios, podemos hacer que
algunos datos estkn disponibles para todos 10s formularios, si 10s declaramos
como variable global en la parte de interfaz de la unidad de uno de 10s formula-
rios:
var
Form1: TForml;
nClicks: Integer;
Esto funciona pero tiene dos inconvenientes. En primer lugar, 10s datos no
estan conectados a un caso especifico del formulario, sino a1 programa entero. Si
creamos dos formularios del mismo tipo, compartiran 10s datos. Si queremos que
cada formulario del mismo tip0 tenga su propia copia de 10s datos, la unica
solucion es aiiadirlos a la clase de formulario:
type
T F o r m l = class ( T F o r m )
public
nClicks: Integer;
end;
NOTA:~ i ~ ceg
s equo el a & d ~ p & i ~ d d a dpun f o n d ario, no 3. Mia& -~4'
Esta es una costumbre muy comun en Delphi, pero no es una buena costumbre,
porque no ofrece encapsulado de la estructura de formulario ni de sus componen-
tes. Si hay un codigo similar en una aplicacion y mas tarde se decide modificar la
interfaz de usuario del formulario (y reemplazar S t a t u s B a r por otro control o
activar diversos paneles), habra que adaptar el codigo en muchos sitios. La alter-
nativa es utilizar un metodo o, incluso mejor, una propiedad para ocultar un
control concreto. Esta propiedad puede definirse como:
property StatusText: string read GetText write SetText;
Constructores
Para asignar la memoria a1 objeto, podemos llamar a1 metodo C r e a t e . Este
es un constructor, un metodo especial que podemos aplicar a una clase para
asignar memoria a una instancia de dicha clase. El constructor devuelve la instan-
cia, que puede asignarse a una variable para almacenar el objeto y usarlo mas
tarde. El constructor por defecto TObj e c t .C r e a t e inicializa todos 10s datos
del nuevo caso a cero. Para que 10s datos de dicho caso comiencen con un valor
diferente a cero, hay que escribir un constructor personalizado.
El nuevo constructor se puede denominar c r e a t e o tener otro nombre, y hay
que usar la palabra clave c o n s t r u c t o r delante de el. Fijese en que no es
necesario llamar a TOb j e c t .C r e a t e : es Delphi el que asigna memoria para el
nuevo objeto, no el constructor de clase. Todo lo que hay que hacer es iniciar la
base de clase.
Aunque se puede usar cualquier nombre para el constructor, deberia ajustarse
a1 nombre estandar, c r e a t e . Si se usa otro nombre distinto de c r e a t e , el
constructor C r e a t e de la clase basica TOb j e c t aun estara disponible, per0 un
programador que llame a1 constructor por defecto podria pasar por alto el codigo
de inicializacion ofrecido porque no reconoce el nombre.
A1 definir un constructor c r e a t e con algunos p a r h e t r o s , reemplazamos la
definicion predeterminada por una nueva y hacemos que su uso resulte obligato-
rio. Por ejemplo, despues de haber definido:
type
TDate = class
public
constructor Create (y, m, d: I n t e g e r ) ;
Asignacion de objetos
Podemos preguntarnos que ocurriria si una variable que mantiene un objeto
solo contiene una referencia a1 objeto en memoria y se copia el valor de dicha
variable. Supongamos que escribimos el metodo BtnToda yCli ck del ejemplo
ViewDa te del siguiente modo:
procedure TDateForm.BtnTodayClick(Sender: TObject);
var
NewDay: TDate;
begin
NewDay : = TDate-Create;
TheDay : = NewDay;
LabelDate.Caption : = TheDay-GetText;
end;
Este codigo copia la direccion de memoria del objeto NewDay a la variable
TheDay (corno muestra la figura 2.5); no copia 10s datos de un objeto en el otro.
En esta circunstancia concreta, esta tecnica no es muy adecuada, puesto que cada
vez que se pulsa el boton, se asigna memoria para un nuevo objeto y nunca se
libera la memoria del objeto a la que anteriormente apuntaba la variable TheDay.
NewDay objeto TDate
TheDay
Figura 2.5. Una representacion de la operacion de asignacion de una referencia de
objeto a otro objeto. Esto es distinto de copiar el contenido real de un objeto en otro.
end;
/ / llamar.. .
CaptionPlus (Buttonl)
Esto significa que el objeto se pasa por referencia sin el uso de la palabra clave
var y sin ninguna otra indicacion obvia de la semantica de paso por referencia,
lo que confunde a 10s novatos. Cabria preguntarse lo que sucede si realmente se
quieren cambiar 10s datos de un objeto existente, para que se corresponda con 10s
datos de otro objeto. En este caso, hay que copiar cada campo del objeto, lo cual
es posible solo si son todos publicos, u ofrecer un metodo especifico para copiar
10s datos internos. Algunas clases de la VCL tienen un metodo Assign, que
realiza esta operacion de copia. Para ser mas precisos, la mayoria de las clases de
la VCL que heredan de TPers is tent, per0 no de TComponent, tienen el
metodo Ass ign. Otras clases derivadas de TComponent tienen este metodo
per0 lanzaran una excepcion cuando se llama.
En el ejemplo Da t eCopy, se ha aiiadido un metodo Assign a la clase TDa te
y se le ha llamado desde el boton Today, con el siguiente codigo:
p r o c e d u r e TDate .Assign (Source: TDate) ;
begin
fDate : = Source.fDate;
end ;
p r o c e d u r e TDateForm.BtnTodayClick(Sender: TObject);
var
NewDay: TDate;
begin
NewDay : = T D a t e - C r e a t e ;
TheDay .Assign (NewDay);
LabelDate.Caption : = TheDay.GetText;
NewDay.Free;
end ;
Objetos y memoria
La administracion de memoria en Delphi esta sujeta a tres normas, a1 menos si
se permite que el sistema trabaje en armonia sin violaciones de acceso y sin
consumir memoria innecesaria:
Todo objeto ha de ser creado antes de que pueda usarse
Todo objeto ha de ser destruido tras haberlo utilizado.
Todo objeto ha de ser destruido solo una vez.
El tener que realizar estas operaciones en el codigo o dejar que Delphi controle
la administracion de memoria, dependera del modelo que escojamos entre las
distintas tecnicas que ofrece Delphi.
Delphi soporta tres tipos de administration de memoria para elementos dinamicos:
Cada vez que creamos un objeto explicitamente en el codigo de una aplica-
cion, tambien debemos liberarlo (con la sola excepcion de un puiiado de
objetos del sistema y de objetos que se utilizan a traves de referencias de
interfaz). Si no se hace asi, la memoria utilizada por dicho objeto no se
libera hasta que finaliza el programa.
Cuando creamos un componente, podemos especificar un componente pro-
pietario, pasando el propietario a1 constructor del componente. El compo-
nente propietario (normalmente un formulario) se transforma en el
responsable de destruir todos 10s objetos que posee. Asi, si creamos un
componente y le damos un propietario, no es necesario que nos acordemos
dc destruirlo. Este es el comportamiento estandar de 10s componentes que
se crean en tiempo de diseiio a1 colocarlos sobre un formulario o modulo de
datos. Sin embargo, es imperativo que se escoja un propietario cuya des-
truccion quede garantizada; por ejemplo, 10s formularios suelen pertenecer
a 10s objetos globales A p p l i c a t i o n , que son destruidos por la bibliote-
ca cuando acaba el programa.
Cuando la RTL de Delphi reserva memoria para las cadenas y matrices
dinamicas, libera automaticamente la memoria cuando la referencia resul-
ta inalcanzable. No es necesario liberar una cadena: cuando resulta inacce-
sible, se libera su memoria.
Destruir objetos una sola vez
Otro problema es que si se llama a1 metodo F r e e (o a1 destructor D e s t r o y )
de un objeto dos veces, dara error. Sin embargo, si recordamos cambiar el objeto
a n i l , se puede llamar a F r e e dos veces sin ningun problema.
b -
NOTA: Podriamos preguntarnos por que se puede llamar a Free con total
seguridad si la referencia del objeto es n i l , pero no se pue& llamar a
D e s t r o y . La razon es que F r e e es un mbodo conocido en una posicibn
de memoria dada, rnientras que la funcion virtual Destroy se defrne en
tiempo de ejecucion a1 ver el tip0 de objeto, una operacibn muy peligrosa si
el objeto ya no existe.
Esta definicion indica que la clase T F o r m l hereda todos 10s metodos, cam-
pos, propiedades y eventos de la clase T F o r m . Se puede llamar a cualquier meto-
do public0 de la clase T F o r m para un objeto del tipo T F o r m l . T F o r m , a su vez,
hereda algunos de sus metodos de otra clase, y asi sucesivamente hasta la clase
basica TOb j e c t . Como ejemplo de herencia, podemos cambiar una nueva clase
a partir de T D a t e y modificar su funcion G e t T e x t . Se puede encontrar este
codigo en la unidad Date del ejemplo NewDate:
tYPe
TNewDate = c l a s s (TDate)
pub1i c
f u n c t i o n GetText: string;
end :
Para implementar la nueva version de la funcion GetText, utilizamos la
funcion Format DateTime, que emplea (entre otras caracteristicas) 10s nom-
bres de mes predefinidos disponibles en Windows, estos nombres dependen de la
configuracion regional del usuario y de la configuracion del lenguaje. Muchas de
estas configuraciones las copia Delphi en constantes definidas en la biblioteca,
como LongMonthNames, ShortMonthNames y muchas otras que puede
encontrar bajo el tema "Currencyand datehime formatting variables" (Variables
para formatear la moneda y la fecha/hora) en el archivo de ayuda de Delphi.
Veamos el metodo GetText,en el que 'dddddd' corresponde a1 formato de fecha
largo:
function TNewDate.GetText: string;
begin
GetText : = FormatDateTime ( ' d d d d d d ' , f D a t e ) ;
end :
Cuando tengamos la definicion de la nueva clase, hay que usar este nuevo tipo
de datos en el codigo del formulario del ejemplo NewDate. Simplemente hay que
definir el objeto TheDay de tipo TNewDate y crear un objeto de la nueva clase
mediante en el metodo Formcreate.No es necesario modificar el codigo con
llamadas de metodo, ya que 10s metodos heredados seguiran funcionando del
mismo modo; sin embargo, se modifica su efecto, como muestra la nueva salida
(vease figura 2.6)
Figura 2.6. El resultado del programa NewDate, con el nombre del mes y del dia de
acuerdo con la configuracion regional de Windows.
CCI;I
Campos protegidos y encapsulado
El codigo del mktodo GetText de la clase TNewDate compila solo si esta
escrito en la misma unidad que la clase TDate. De hecho, accede a1 campo
privado f Date de la clase ascendiente. Si queremos colocar una clase descen-
diente en una unidad nueva, debemos declarar el campo fDate como protegido o
aiiadir un metodo de acceso protegido en la clase ascendiente para leer el valor del
campo privado.
Muchos desarrolladores creen que la primera solucion es siempre la mejor, ya
que declarar la mayor parte de 10s campos como protegidos permitira que una
clase resulte mas extensible y hara mas sencillo escribir clases heredadas. Sin
embargo, este enfoque se enfrenta con la idea del encapsulado. En una gran jerar-
quia de clases, modificar la definicion de algunos campos protegidos de las clases
base resulta tan dificil como modificar algunas estructuras globales de datos. Si
diez clases derivadas acceden a estos datos, modificar su definicion significa
modificar potencialmente el codigo de cada una de estas 10 clases.
La flexibilidad, extension y encapsulado normalmente son objetivos conflicti-
vos, por lo que deberiamos favorecer el encapsulado, sin sacrificar la flexibili-
dad. Normalmente eso se puede conseguir usando un metodo virtual. Si se decide
no utilizar el encapsulado para que la codification de las subclases sea mas rapi-
da, el disefio podria no ajustarse a 10s principios de la orientacion a objetos.
cl qcmplu
DBGrid de Row y Col es en &dad un ejemplo de w o opuesto. que
ilustplos riwgm de a c d r a bits que la persona que escribib las clases
prefifio s o exgoner. La fib y colbima de una clase DB-id no significan
lo mismo gw en u& ~ r p w ~ r iQ duna StringGrid &q clpses bhi-
cas). En pjmer h p r , ll5Gxld ao cuenta las w b @as oomo cedas
rcakr ( & t i a d i s eelda? dehatos dc 10s c l ~ ~ d e ~ b r a t i l i q ] , lo
~P~r
que sys indices ,de fils. y aoluraaa.€idrib qua aj$#arse a los,efemento#
biar sin que nos demos cuenta). En segundo lugar, la DBGrid es una vista
virtual de 10s datos. Cuando nos desplazamos hacia arriba en una DBGrid,
10s d a b s pueden moverse bajo ella, bero la fila seleccionada en ese momen-
to podria no cambiar.
Existe una importante excepcion a esta norma en el caso de 10s tipos de clase.
Si se declara una clase, como TAnimaL, y se deriva de ella una nueva clase,
como por cjemplo TDog, se puede asignar un objeto de tipo TDog a una variable
de tipo TAnimal. Esto se debe a que un perro (dog) es un animal. Como regla
general, se puede usar un objeto de una clase descendente cada vez que se espere
un objeto de la clase ascendente. Sin embargo, lo opuesto no resulta legal; no se
puede usar un objeto de una clase antecesora cuando se espera un objeto de una
clase que desciende de la anterior. Para simplificar la esplicacion, veamos este
codigo:
var
MyAnimal : T A n i m a l ;
MyDog: T D o g ;
begin
MyAnimal : = MyDog; // E s t o es correcto
MyDog : = MyAnimal; // ; E s t o es u n error!
lineas de c d i g o que la usan. Por supuesto, existe una condicion: las clases
ascendientes de la jerarquia ban de disekrse con mucho cuidado.
-
Figura 2.7. El resultado del ejemplo PolyAnimals.
type
TMyClass = class
procedure One; virtual;
procedure Two; (metodo estdtico)
end;
TMyDerivedClass = class (TMyClass)
procedure One; override;
procedure Two;
end;
type
TMyClass = class
procedure One;
end;
Manejadores de mensajes
Tambien se puede usar un metodo de enlace posterior para manejar un mensaje
de Windows, aunque la tecnica es algo distinta. Con este proposito, Delphi ofrece
otra directiva, message, para definir 10s metodos de control de 10s mensajes,
que habran de ser procedimientos con un unico parametro var. La directiva
message va seguida del numero del mensaje de Windows que el metodo quiere
controlar.
Metodos abstractos
La palabra clave abstract se usa para declarar metodos que se van a defi-
nir solo en subclases de la clase actual. La directiva abstract define por com-
pleto el metodo, no es una declaracion que se completara mas adelante. Si se
intenta definir el metodo, el compilador protestara. En Delphi se pueden crear
instancias de clases que tengan metodos abstractos. Sin embargo, a1 intentarlo, el
compilador de 32 bits de Delphi emite un mensaje de advertencia "Constrtrcting
instance of <class name> containing abstract methods" (Creando caso de +om-
bre de clase> que contiene metodos abstractos). Si se llama a un metodo abstract0
en tiempo de ejecucion, Delphi creara una escepcion, como muestra el ejemplo
AbstractAnimals (una ampliacion del ejemplo PolyAnimals), que usa la siguiente
clase:
type
TAnimal = c l a s s
public
function Voice: s t r i n g ; v i r t u a l ; abstract;
Podriamos preguntarnos por la razon del uso de 10s metodos abstractos. Esta
razon es el polimorfismo. Si la clase TAnimal tiene el metodo virtual Voice,
toda clase heredada puede volver a definirlo. Si se trata de un metodo abstracto
Voice,cada clase heredada debe volver a definirlo.
En las primeras versiones de Delphi, si un metodo sobrescribia un metodo
abstracto llamado inherited,el resultado era una llamada a1 metodo abstrac-
to. A partir de Delphi 6; el compilador se ha mejorado para detectar la presencia
dcl metodo abstracto y evitar la llamada inherited.Esto significa que se
puede usar con seguridad inherited en todo metodo sobrescrito, a no ser que
se desee inhabilitar esplicitamente la ejecucion de parte del codigo de la clase
basica.
Uso de interfaces
Cuando se define una clase abstracta para representar la clase basica de una
jerarquia, se puede llegar a un punto en el que la clase abstracta sea tan abstracta
que so10 liste una serie de funciones virtuales, sin proporcionar ningtin tip0 de
implernentacion real. Este tip0 de clase puramente abstracta puede definirse tam-
bien mediante una tecnica concreta, una interfaz. Por esta razon, nos referimos a
dichas clases como interfaces.
Tecnicamente, una interfaz no es una clase, aunque puede parecerlo, porque se
considera un elemento totalmente a parte con caracteristicas distintivas:
Los objetos de tipo interfaz dependen de un recuento de referencias y se
destruyen automaticamente cuando no hay mas referencias al objeto. Este
mecanismo es similar a la forma en que Delphi maneja cadenas largas y
administra la memoria casi de forma automatica.
Una clase puede heredar de una clase basica simple, per0 puede implementar
varias interfaces.
A1 igual que todas las clases descienden de T O b j ect, todas las interfaces
descienden de 1Interface y forman una jerarquia totalmente indepen-
diente.
Ahora que hemos definido una implementacion de las interfaces, podemos es-
cribir algo de codigo para usar un objeto de esa clase, mediante una variable de
tipo interfaz:
var
Flyerl: ICanFly;
begin
Flyerl : = TAirplane.Create;
Flyerl.Fly;
end;
En el momento en que se asigna un objeto a una variable de tipo interfaz,
Delphi comprueba automaticamente si el objeto implementa esa interfaz, median-
te el operador as.Se puedc espresar csplicitamente esta operacion de este modo:
Flyerl := T A i r p l a n e - C r e a t e as ICanFly;
--
NOTA:El cornpilador genera diferente cbdigo para el operador as cuamlo
se usa con intefices que cuando se usa con clases. Con clases, introduce
verificaciones en tiempo de ejecuci6n para cornprobar que el objeto es efec-
tivamente "compatible en tipo" con la clase dada. Con las interfaces, com-
prueba en tiempo de compilaci6n que puede extraer la interfaz necesaria del
tipo de clase disponible y asi lo hace. Esta operacih es como un "as en
tiempo de compilation", no algo que exista en tiempo de ejecucibn.
Cuando el programa ejecuta esta funcion, siempre reinicia el cursor, haya una
excepcion (de cualquier tipo) o no. Este codigo no controla la excepcion, simple-
mente hace que el programa sea robusto en caso de que se Cree un una excepcion.
Un bloque t r y puede ir seguido de una sentencia e x c e p t o f i n a l l y , per0 no
por ambas a1 mismo tiempo. La solucion mas comun para controlar tambien la
excepcion consiste en usar dos bloques t r y anidados. En ese caso, hay que
asociar el interno con una sentencia f i n a 11y y el externo con una sentencia
e x c e p t o viceversa, segun lo requiera la situacion. Aqui tiene el esquema del
codigo para el tercer boton del ejemplo T r y F i n a l l y :
Screen.Cursor : = crHourglass;
try try
/ / g r a n a l g o r i tmo . . .
finally
Screen.Cursor : = crDefault;
end;
except
on E: EDivByZero do .. .
end;
Cada vez que haya algun codigo de finalizacion a1 concluir un metodo, hay que
situar dicho codigo en un bloque f i n a l l y . Siempre se deberia, invariablemente
y de forma continuada proteger el codigo con sentencias f i n a l l y , para evitar
problemas de recursos o de goteos de memoria en caso de que se Cree una excep-
cion.
r
- .- - - - - - - - - ---- - - -- - -
Clases de excepciones
En las sentencias de control de escepciones mostradas anteriormente, capta-
mos la excepcion EDivBy Zero, que define el RTL de Delphi. Otras excepcio-
nes como esta se refieren a problemas en tiempo de ejecucion (como una conversion
dinamica erronea), problemas de recursos de Windows (como 10s errores por falta
de memoria), o errores de componentes (como un indexado erroneo). Los progra-
madores pueden definir tambien sus propias excepciones. Se puede crear una
nueva subclase de escepciones predefinidas o de una de sus subclases:
type
EArrayFull = class (Exception) ;
Cuando se aiiade un nuevo elemento a una matriz que ya esta llena (probable-
mente por un error en la Iogica del programa), se puede establecer la excepcion
correspondiente, creando un objeto de esa clase:
if MyArray.Ful1 then
r a i s e EArrayFull .Create ( 'Ma t r i z l l e n a ') ;
'VCLEAbort Exceptions
lndy EIDConnCbsedG~acelul?yEnceplm
' Mtc~osoRDAD Excepl~ons
' V~s~Broke~
lntelnal Except~ons
't CORBA Syslem Exceplans
CORBA User Excepl~onr
Registro de errores
La mayor parte del tiempo, no se sabe que operacion va a crear una excepcion
y no se puede (ni se debe) envolver cada una de las partes del codigo en un bloque
try/except.La tecnica general consiste en dejar que Delphi controle todas las
escepciones y finalmente pasarselas todas a1 usuario, mediante el control del
evento OnException del objeto global Application. Esto se puede hacer
de un mod0 mas sencillo con el componente ApplicationEvents. En el
ejemplo ErrorLog, se ha aiiadido a1 formulario principal una copia del componen-
te Appl icat ionEvent s y un controlador para su evento OnExcept ion:
procedure TForrnLog. LogException (Sender: TObj ect; E: Exception) ;
var
Filename: string;
LogFile : TextFile;
begin
// prepara un a r c h i v o de r e g i s t r o
Filename : = ChangeFileExt (Application.Exename, ' . l o g 1 ) ;
AssignFile (LogFile, Filename) ;
i f FileExists (FileName) then
Append (LogFile) // abre un a r c h i v o e x i s t e n t e
else
Rewrite (LogFile); // c r e a r uno nuevo
tr~
// e s c r i b e e n u n a r c h i v o y m o s t r a r e r r o r
Writeln (LogFile, DateTimeToStr (Now) + ' : ' + E-Message);
i f not CheckBoxSi1ent.Checked then
Application. ShowException (E);
finally
// cierra e l archivo
CloseFile (LogFile);
end:
-- -
- --- - - . - -.
. . .- .-
.- . - .- . -
. -- .- ..- -- - -
-- ~~ - - . -
Referencias de clase
La ultima caracteristica del lenguaje que trataremos en este capitulo son las
referencias de clase, lo cual implica la idea de manipular las propias clases dentro
del codigo. El primer punto que hemos de tener en cuenta es que la referencia de
clase no es un objeto; es sencillamente una referencia a un tipo de clase. Un tipo
de referencia de clase establece el tip0 de una variable de referencia de clase.
Aunque esto suene confuso, con unas cuantas lineas de codigo quedara un poco
mas claro.
.-.
1
- - .-
17/05/2003
..............
-
ll:37:48:Divisiun bv zero
119 - -
......................
I
1 17/05/2003 ll:37:53: raise button pressed I
7/05/2003 11:37:56:Divislon b y zero
7/05/2003 ll:37:58:raise button pressed
7/05/2003 ll:37:59:raise button pressed
........ .
La primera linea del codigo de este metodo es la clave. Crea un nuevo objeto
del tipo de datos de clase almacenados en el campo C l a s s R e f . Esto se consigue
simplemente aplicando el constructor c r e a t e a la referencia de clase. Ahora se
puede establecer el valor de la propiedad P a r e n t , fijar la posicion del nuevo
componente, darle un nombre (que se usa tambien automaticamente como
C a p t i o n o T e x t ) y hacerlo visible.
D
AD
Dephi 5 necesite ntilizar nueva unidad Variants para volver a compi-
esta
lar. Delphi es lo suficientemente listo como para darse cuenta de ello e
incluir automiticamente la unidad Variants en proyectos que usan el tipo
Variant ,emitiendo unicarnente UM advertencia.
I
El tamafio emutable bajo el microscopio
Por ejemplo, otro aiiadido para la compatibilidad entre Linux y Windows esta
relacionado con 10s saltos de linea en 10s archivos de testo. La variable
DefaultTextLineBreakStyle, afecta a1 comportamiento de las rutinas
que leen y escriben en archivos, como la mayoria de las rutinas de flujos de texto.
Los valores posibles para esta variable global son t l b s L F (valor predetermina-
do en Kylix) y t l b s C R L F (valor predeterminado en Delphi). El estilo de salto de
linea tambien se puede configurar archivo por archivo mediante la funcion
S e t T e x t L i n e B r e a k S t y l e . Del mismo modo, la constante global de cadena
s L i n e B r e a k tiene el valor #13#10 en la version Windows del entorno de
desarrollo y el valor # 1 0 en la version para Linux. Otro cambio es que la unidad
System incluye ahora las estructuras T F i leRec y TTex t Rec, que en versiones
anteriores de Delphi estaban definidas dentro de la unidad S y s u t i l s .
/ / s e a s e g u r a q u e e l v a l o r e s t d e n t r e min y m x
value : = EnsureRange (value, min, m a x ) ;
Otro grupo muy util de funciones esta relacionado con las comparaciones. Los
numeros de coma flotante son basicamente inexactos. Un numero de coma flotan-
tc cs una aproximacion de un valor real teorico. Cuando realizamos operaciones
matematicas con numeros de coma flotante, la inesactitud de 10s valores origina-
les se acumula en 10s resultados. Si multiplicamos y dividimos por el mismo
numero puede que no consigamos exactamente el numero original, sino uno muy
proximo a 121. La funcion samevalue permite verificar si dos valores se aproxi-
man lo suficiente como para ser considerados iguales. Se puede especificar el
grado de aproximacion que deberian tener dos numeros o dejar que Delphi calcule
un rango de error razonable para la representacion que estamos utilizando. (Por
csta razon se sobrecarga la funcion.)
Del mismo modo, la funcion Iszero compara un numero con cero, mediante
esta misma "logica borrosa".
La funcion C o m p a r e v a l u e usa la misma norma para 10s numeros de coma
flotante per0 esta disponible tambien para enteros. Devuelve una de las tres cons-
tantes LessThanValue, EqualsValue y GreaterThanValue (que se
corresponden con -1,O y 1). Del mismo modo, la nueva funcion sign devuelve
-1,0 y 1 para indicar un valor negativo, cero o un valor positivo.
La funcion D i v M o d es equivalente a las operaciones de division y resto, de-
volviendo el resultado de la division del entero y del resto al mismo tiempo La
funcion RoundTo nos permite especificar el digito de redondeo (permite, por
ejemplo, redondear hasta el millar mas proximo o hasta dos decimales):
RoundTo (123827, 3 ) ; // e l r e s u l t a d o e s 1 2 4 . 0 0 0
RoundTo (12.3827, -2); // e l r e s u l t a d o e s 1 2 , 3 8
. .
ADVERTENCIA: Fijese en que la funci6n RoundTo usa un nhnero po-
sitivo para indicar la potencia de diez h a s h la que hay que redondear (por
ejemplo, 2 para centenas) o un n6mero negativo para el numero de cifras
decimales. Esto es exactamente lo contrario de la funci6n Round utilizada
por hojas de calculo como Excel.
La unidad DateUtils
La unidad DateUtils es una nueva coleccion de funciones relacionadas con la
fecha y la hora. Engloba nuevas funciones para seleccionar valores de una varia-
ble TDa t e T i m e o contar valores de un intervalo dado como:
// e s c o g e r v a l o r
function DayOf (const AValue : TDateTime) : Word;
function HourOf (const AValue : TDateTime) : Word;
/ / v a l o r en r a n g o
function WeekOf Year (const AValue : TDateTime) : Integer;
function HourOfWeek (const AValue: TDateTime) : Integer;
function SecondOfHour (const AValue: TDateTime) : Integer;
De Pos a PosEx
Delphi 7 aporta su granito de arena a la unidad StrUtils. La nueva funcion
PO sE X resultara muy practica para muchos desarrolladores y merece que hable-
mos de ella. Cuando se buscan multiples apariciones de una cadena dentro de
otra, una solucion clasica de Delphi era utilizar la funcion Pos y repetir la bus-
queda sobre la parte restante de la cadena. Por ejemplo, podria contar el numero
de apariciones de una cadena dentro de otra con un codigo como este:
f u n c t i o n CountSubstr (text, sub: string) : Integer;
var
nPos: Integer;
begin
Result : = 0;
nPos : = Pos (sub, t e x t ) ;
while nPos > 0 do
begin
Inc (Result) ;
text : = Copy (text, nPos + Length ( s u b ), MaxInt) ;
nPos : = Pos (sub, text) ;
end;
end;
La unidad Types
La unidad Types (de tipos) almacena tipos de datos comunes a diversos siste-
mas operativos. En las anteriores versiones de Delphi, la unidad de Windows
definia 10s mismos tipos; ahora se han desplazado a esta unidad comun, compar-
tida por Delphi y Kylix. Los tipos definidos aqui son sencillos y engloban, entre
otros. las estructuras de registro TPoint,T R e c t y TSmallPoint mas sus
tipos de punter0 relacionados.
Convertir datos
Delphi incluye un nuevo motor de conversion, definido en la unidad ConvUtils.
El motor por si mismo no incluye definicion alguna de las unidades de medida
reales; en cambio, posee una serie de funciones principales para 10s usuarios
finales. La funcion clave es la llamada de conversion, la funcion Convert.
Sencillamente, nosotros proporcionamos la cantidad, las unidades en las que se
expresa y las unidades a las que queremos que se conviertan.
Lo siguiente convertiria una temperatura de 3 1 grados centigrados a Fahren-
heit:
Convert (31, tucelsius, tuFahrenheit)
duAngstroms : TConvType;
dulrlicrons: TConvType;
dulrlillimeters: TConvType;
duMeters : TConvType;
duKilometers: TConvType;
duInches: TConvType;
duMiles: TConvType;
duLightYears: TConvType;
duFurlongs: TConvType;
duHands : TConvType ;
duPicas: TConvType;
Eamiks
D~stance Snnple Ted I Inslruciimr hap types
lrorn lM to dlboxes.
enlm am*
lvper Base lype: &&:
LghlYeats Cmlirnetus 1100
Parsecs
Fathom
Furbngs Qerlinalica Type Cmvwted Am&
Hands
Paces
Cham
I 1
// o b t i e n e y v e r i f i c a e l t i p o d e d e s t i n o
i f not DescriptionToConvType (CurrFamily,
EditDestination-Text,
DestType) then
EditDestination.Font.Color : = c l R e d
else
EditDestination.Font.Co1or : = clBlack;
Si todo esto no resulta interesante, hay que tener en cuenta que todos 10s tipos
de conversion proporcionados en el ejemplo son solo una muestra: se puede perso-
nalizar completamente el motor para que proporcione las unidades de medida en
que se este interesado, como se comentara a continuacion.
iConversiones de divisas?
La conversion de divisas no es exactamente lo mismo que la conversion de
unidades de medida, ya que 10s valores de las divisas cambian constantemente. En
teoria, se puede registrar un valor de cambio en el motor de conversion de Delphi.
De vez en cuando, se comprobara el nuevo indice de cambio, se desregistrara la
conversion ya existente y se registrara la nueva. Sin embargo, mantener la tasa de
cambio real implica modificar la conversion tan a menudo que la operacion po-
dria no tener mucho sentido. Ademas, habra que triangular conversiones: hay que
definir una unidad base (probablemente el euro, si vive en Europa) y convertir a/
y desde esta divisa incluso si la conversion se realiza entre dos divisas distintas.
Por ejemplo, antes de la adopcion del euro como divisa de la Union Europea, lo
mejor era utilizar esta divisa como base para las conversiones entre las divisas de
10s estados miembros, por dos motivos. En primer lugar, 10s tipos de cambio eran
fijos. En segundo lugar, la conversion entre las divisas euro se rcalizaban legal-
mente convirtiendo una cantidad a euros y convirtiendo dcspues esa cantidad en
euros a la otra divisa. el comportamiento exacto del motor de conversion de Delphi.
Existe un pequeiio problema: debcria aplicarse un algoritmo de redondco en cada
paso de la conversion.
Considerarcmos este problema mas adelante, tras ofrccer el codigo base para
integrar las divisas euro con cl motor de conversion de Delphi.
var
// U n i d a d e s d e C o n v e r s i o n d e D i v i s a s E u r o p e a s
c b E u r o C u r r e n c y : TConvFamily;
cuEUR: TConvType;
cuDEM: TConvType; // A l e m a n i a
cuESP: TConvType; // E s p a f i a
cuFRF: TConvType; // P r a n c i a
// y e l r e s t o . . .
La seccion de implementation de la unidad define constantes para diversas
tasas de conversion oficiales:
implementation
cons t
DEMPerEuros = 1 , 9 5 5 8 3 ;
ESPPerEuros = 166,386;
FRFPerEuros = 6,55957;
// y e l r e s t o . . .
Finalmente, el codigo de inicializacion de la unidad registra la familia y las
diversas divisas, cada una con su propio tip0 de cambio y un nombre legible:
initialization
/ / T i p o de l a familia de divisas europeas
cbEuroCurrency : = RegisterConversionFamily ( ' D i v i s d s E u r o ) ;
c u E U R : = RegisterConversionType(
cbEuroCurrency, 'EUR', 1) ;
c u D E M : = RegisterConversionType(
cbEuroCurrency, IDEM', 1 / DEMPerEuros) ;
c u E S P : = RegisterConversionType(
cbEuroCurrency, 'ESP', 1 / E S P P e r E u r o s ) ;
c u F R F : = RegisterConversionType(
cbEuroCurrency, ' F R F ', I / FRFPerEuros) ;
Tras registrar esta unidad, se pueden convertir 120 marcos alemanes en liras
italianas de esta manera:
Convert ( 1 2 0 , cuDEM, cuITL)
El programa de ejemplo hace algo mas: ofrece dos cajas de lista con las divisas
disponibles, extraidas como en el ejemplo anterior, y cajas de edicion para el
valor de entrada y el resultado final. La figura 3.4 muestra el formulario.
Figura 3.4. La salida del ejemplo EuroConv, que muestra el uso del motor de
conversion de Delphi con una unidad de medida personalizada.
begin
// comprobacion d e l c a s o e s p e c i a l : s i n c o n v e r s i o n
if AFrom = ATo then
Result : = AValue;
else
begin
/ / conversion a1 euro y redondeo
Result : = ConvertFrom (AFrom, AValue) ;
Result : = EuroRound (Result);
/ / conversion a la divisa y nuevo redondeo
Result : = ConvertTo (Result, ATo) ;
Result : = EuroRound (Result);
end ;
end ;
Por supuesto, podria desearse ampliar el ejemplo para ofrecer conversion a
otras divisas no europeas, tal vez tomando 10s valores automaticamente desde un
sitio Web.
ID \md7code\O2\ClassRel\ClassRel dpr
D \md7cude\02\C1ealeComps\CreateComps
dp~
~:\rnd7code\02\~ale~rdp\~ale~rd~d~
D:W7code\U2\Dalesl\Datesl dpr
D-\md7code\OZ\DBGridCol\DBG11dCol,dpr
D \rnd7coJe\02\Erro1Log\ErrorLog dpf
D \md7code\02\Excepbun1\Excepbornl.dpr
D.\md7wde\O2\F~mPfopU01mProp.dp
D:\md7code\02\1IDrecl1veUID~ecl1ve.dp1
D:Lnd7code\02\lnllDerno\lnllDemo dpr
D hd7code\02\NewDale\NewDale d p ~
D \md7c~de\02\Polu4n1mals\Polu9nmals.&r
La clase TObject
La definicion de la clase T O b j e c t es un elemento clave de la unidad System,
"la madre de todas las clases Delphi". Cada clase del sistema es una subclase de la
clase TObj e c t , directa (si se especifica TOb j e c t como la clase base), impli-
citamente (a1 no indicarse la clase base), o indirectamente (cuando se especifica
otra clase como antecesor). Toda la jerarquia de las clases de un programa en
Pascal orientado a objetos posee una raiz unica. Esta permite usar el tipo de datos
TOb j e c t como substituto del tipo de datos de cualquier tipo de clase del siste-
ma.
Por ejemplo, 10s controladores de eventos de componentes normalmente tienen
un parametro Sender de tipo TObj e c t . Esto significa sencillamente que el
objeto Sender puede pertenecer a cualquier clase, puesto que cada clase se
deriva en ultima instancia de Tob j e c t . El inconveniente mas habitual de esta
tecnica es que para trabajar sobre el objeto, es necesario conocer su tipo de datos.
De hecho, cuando se tiene una variable o un parametro del tipo TObj e c t , se le
pueden aplicar solo 10s metodos y propiedades definidas por la propia clase
TOb j e c t . Si esta variable o parametro se refiere por casualidad a un objeto del
tipo TButton, por ejemplo, no se puede acceder directamente a su propiedad
C a p t i o n . La solucion a este problema recae en el uso de 10s operadores de
conversion segura de tipos siguientes o en 10s operadores de informacion de tip0
en tiempo de ejecucion (RTTI) (10s operadores i s y as).
Existe otra tecnica. Para cualquier objeto, se puede llamar a 10s metodos defi-
nidos en la misma clase TObj e c t . Por ejemplo, el metodo ClassName devuel-
ve una cadena con el nombre de la clase. Debido a que es un metodo de clase, se
puede aplicar tanto a un objeto como a una clase. Supongamos que hemos defini-
do una clase TButton y un objeto B u t t o n 1 de dicha clase. En ese caso, las
siguientes sentencias tendran el mismo efecto:
Text := Button1.ClassName;
Text := TButton.ClassName;
Hay ocasiones en las que es necesario usar el nombre de una clase, per0 tam-
bien puede ser util recuperar una referencia de clase a la propia clase o a su clase
basica. La referencia de clase, de hecho, permite trabajar en la clase en tiempo de
ejecucion, mientras quc cl nombre de clase es simplemente una cadena. Podemos
obtener estas referencias de clase con 10s metodos C l a s sType y C l a s s Parent.
El primer0 devuelve una referencia de clase a la clase del objeto, el segundo a su
clase basica. Cuando tengamos una referencia de clase, podemos aplicarle cual-
quier metodo de clase TOb j e c t , por ejemplo, para llamar a1 metodo ClassName.
Otro metodo que podria resultar util es I n s t a n c e s i z e , que devuelve el
tamaiio en tiempo de ejecucion de un objeto. Aunque se podria pensar que la
funcion global S i z e o f ofrece dicha informacion, esa funcion en realidad de-
vuelve el tamaiio de una referencia al objeto (un punter0 que siempre tiene cuatro
bytes), en lugar del tamaiio del objeto en si.
En el listado 3.1, se puede encontrar la definicion completa de la clase
TOb j e c t , extraida de la unidad System. Ademas de 10s metodos mencionados,
fijese en que I n h e r i t s From proporciona una comprobacion muy similar a la
del operador is, pero que se puede aplicar tambien a clases y referencias de clase
(mientras el primer argument0 dc i s habra dc ser un objeto).
type
TObject = class
constructor Create;
procedure Free;
class function Init Instance (Instance: Pointer) : TObj ect;
procedure CleanupInstance;
function ClassType: TClass;
class function ClassName: ShortString;
class function ClassNameIs(
const Name: string): Boolean;
class function Classparent: TClass;
class function ClassInfo: Pointer;
class function Instancesize: Longint;
class function InheritsFrom(AC1ass: TClass) : Boolean;
class function MethodAddress (const Name : ShortString) :
Pointer;
class function MethodName(Address: Pointer): ShortString;
function FieldAddress (const Name: ShortString) : Pointer;
function GetInterface (const IID: TGU1D;out Obj) : Boolean;
class function GetInterfaceEntry(
const IID: TGUID): PInterfaceEntry;
class function GetInterfaceTable: PInterfaceTable;
function SafeCallException(ExceptObject: TObject;
ExceptAddr: Pointer) : HResult; virtual;
procedure Afterconstruction; virtual;
procedure BeforeDestruction; virtual;
procedure Dispatch(var Message); virtual;
procedure DefaultHandler(var Message); virtual;
class function NewInstance: TObject; virtual;
procedure FreeInstance; virtual;
destructor Destroy; virtual;
end;
Estos metodos de TObj ect estan disponibles para 10s objetos de cada clase,
puesto que TObj ect es el antcccdente comun de cada clase. Veamos como pode-
mos usar estos metodos para acceder a informacion de clase:
procedure TSenderForm.ShowSender(Sender: TObject);
begin
Memo1 .Lines.Add ( 'Nombre de clase:' ' + Sender .ClassName);
Se pueden usar otros metodos para realizar pruebas. Por ejemplo, se puede
verificar si el objeto Sender es de un tipo especifico con el siguiente codigo:
if Sender-ClassType = TButton then .. .
Tambien se puede verificar si el parametro Sender se corresponde a un obje-
to dado, con este test:
if Sender = Button1 then.. .
En lugar de verificar una clase o objeto concreto, sera necesario, por lo gene-
ral, comprobar la compatibilidad de tip0 de un objeto con una clase dada, es
decir, sera necesario verificar si la clase del objeto es una clase determinada o una
clase de sus subclases. Esto permite saber mejor si se puede trabajar sobre el
objeto con 10s metodos definidos para dicha clase. Esta comprobacion se puede
realizar utilizando el metodo InheritsFrom,a1 que tambien se llama cuando
se usa el operador is.Las siguientes pruebas son equivalentes:
if Sender. InheritsFrom (TButton) then ...
if Sender i s TButton then . . .
Se usa una referencia de clase en la parte principal del bucle while, que
comprueba la ausencia de una clase padre (de mod0 que la clase actual es
TOb j ect). Como alternativa, podiamos haber escrito la sentencia while de
una de las siguientes formas:
w h i l e not MyClass.ClassNameIs ('TObject') d o ...
w h i l e MyClass <> TObject do...
Cuando se inicia el programa, se usa la matriz para mostrar todos 10s nombres
de clase en un cuadro de lista. A1 seleccionar un elemento del cuadro de lista se
desencadena la presentacion visual de sus datos y sus clases basicas.
-
i
7
ventana
(subclases de
Controles TWinControl)
(oomponentes visuales)
Controles no
de ventana
(TComponent) (subclases de
TGraphicControl)
Componentes no visuales I
I
(otras subclases de
TComponent)
01 o componente en
el Form Designer, s e puede ver una sugerencia sobre herramientas con su
nombre y tip0 de clase (y alguna information ampliada). Se puede utilizar
tambien una opcion del entorno, show Component Captions, parai
vet el nombre del componente no visual bajo su icono.
La estructura de CLX
Borland se refiere ahora a distintas secciones de la biblioteca CLX empleando
una terminologia para Linux y una estructura de nombrado ligeramente distinta
(y menos clara) en Delphi.
Esta nueva subdivision de la biblioteca multiplataforma representa areas mas
logicas que la estructura de la jerarquia de clases:
BaseCLX: Forma el nucleo principal de la biblioteca de clases: las clases
mas altas (corno T C o m p o n e n t ) y diversas clases de utilidades generales
(corno listas, contenedores, colecciones y streams). En comparacion con
las clases correspondientes de la VCL, BaseCLX ha cambiado poco y
resulta muy facil de transportar entre las plataformas Windows y Linux.
Este capitulo se dedica en gran medida a explorar BaseCLS y las clases
principales comunes de VCL.
VisualCLX: Es la coleccion de componentes visuales, por lo general lla-
mados controles. Esta es la parte de la biblioteca que esta relacionada mas
estrechamente con el sistema operativo: VisualCLX se implementa en la
parte superior de la biblioteca Qt, disponible tanto en Windows como en
Linux. Utilizar VisualCLX permite una capacidad de transporte total de la
parte visual de una aplicacion entre Delphi en Windows y Kylix en Linux.
Sin embargo, la mayoria dc 10s componentes VisualCLX poscen sus co-
rrespondientes controles VCL, por lo que podemos adaptar facilmente el
codigo de una biblioteca a otra.
DataCLX: Engloba todos 10s componentes relacionados con bases de da-
tos de la biblioteca. En realidad, DataCLX es la fachada del nuevo motor
de base de datos dbExpress incluido tanto en Delphi como Kylix. Delphi
incluye tambien la tradicional interfaz BDE, dbGo e InterBase Express
(IBX). Si consideramos todos estos componentes como parte de DataCLX,
solo la interfaz dbExpress e IBX resultan transportables entre Windows y
Linux. DataCLX incluye tambien el componente C l i e n t Data S e t, aho-
ra llamado MyBa s e y otras clases relacionadas.
NetCLX: Incluye 10s componentes relacionados con Internet, desde el marco
de trabajo WebBroker, a 10s componentes del productor HTML, desde
Indy (Internet Direct) a Internet Express, de WebSnap a1 soporte XML.
Esta parte de la biblioteca es, una vez mas, muy facil de transportar entre
Windows y Linux.
La clase TPersistent
La primera clase principal de la biblioteca de Delphi que veremos es su clase
T Pe r s is t e n t , que es bastante atipica: tiene poco codigo y casi no tiene uso
directo, per0 ofrece la base para la idea global de la programacion visual. Se
puede ver la definicion de la clase en el listado 4.1.
Listado 4.1. La definicion de la clase TPersistent, desde la unidad Classes.
ISM+)
TPersistent = class (TObject)
private
procedure AssignError(Source: TPersistent);
protected
procedure AssignTo (Dest: TPersistent) ; virtual;
procedure Defineproperties (Filer: TFiler) ; virtual;
function Getowner: TPersistent; dynamic;
public
destructor Destroy; override;
procedure Assign (Source: TPersistent) ; virtual;
function GetNamePath: string; dynamic;
end;
Como su nombre indica, esta clase controla la permanencia (es decir, el hecho
de guardar el valor de un objeto en un archivo para usarlo mas tarde y volver a
crear el objeto en el mismo estado y con 10s mismos datos). La permanencia es un
elemento clave de la programacion visual. De hecho, en tiempo de diseiio en
Delphi manipulamos objetos reales, que se guardan en archivos DFM y se vuel-
ven a crear en tiempo de ejecucion a1 mismo tiempo que el contenedor especifico
(formulario o modulo de datos) del componente.
- - - - ---- - . .-
NOTA: ~ ~ 1 d o re aplica tarnbien a
10s archivos XF M, el ~ o m r ae o arcolvo que u u ~ ras m t@xwiones CLX.
El formato es idCntico. La diferencia en la extens&n ts 2@Mante porque
Delphi la utiliza para detenninar si el formularib se h a en CLS/Qt o en
. ..
Vf'T
v u r
En U x r l i v tnrln Fnrmn.lnAm m m
r r r u w r r u . uu x r j u n , wuv r w l u r u x u l v v u ~n f~rmulfwripCLXIQt, sin
AUinrlnrtre
- -
.
para evitar 10s problemas con las referencias a objetos que aun no se han
. ..
. pero queaa tan men ocu~toque . ..
resulta mis simple para el p r o g r ~ b r .
El lenguaje Java permite que una herramienta como JBuilder vuelva a com-
.- - .
pilar ma clase formulano y cargarla en m programa en ejecuci6n para
cada cambio. En un sistema cornpilado como Delphi, ese enfoque seria
mucho 11lPls complejo (entiempo de diseiio Delphi utiliza una versibn falsa,
tecnicahmte Jlamada proxy, del formulario, no el formulario real).
Una ventaia del enfoque utilizado por Delphi es que 10s archivos DFM
pueden traducirse a distintos lenguajes sin afectar a1 c M g o fbente; es por
este motivo que Java ofrece la permanencia XML de formularies. Otra
diferen& es c p e Delphi incrusta el grirfico del compomte en el archivo
DFM,en & ~~~
referemias a archivw e?rternoa.'3hxr esto sim-
plifka el desamHq fpprque to& acab farmanda'parte dd,a~lrkvoe j ~ t a -
bIe] pero tambicn puede imficiwque el e j w t a b l e sea mucho mayor,
, <-
La palabra clave published
Junto con las directivas de acceso public, protected y private se
puede usar una cuarta. dcnominada published.Para cualquier campo. propie-
dad o metodo pub 1ished,el compilador genera inforrnacion ampliada RTTI,
de mod0 que el entorno en tiempo de ejecucion de Delphi o un programa pueden
preguntar a una clase sobre su interfaz publicada. Por ejemplo, cada componente
de Delphi tiene una intcrfaz publicada que es usada por el IDE, en particular por
el Object Inspector. Un uso correct0 de 10s elementos publicados cs importante
cuando se escriben componentes. Normalmente, la partc publicada de un compo-
nentc no contiene ni campos ni metodos sino propiedades y eventos.
Cuando Delphi genera un formulario o modulo de datos, coloca las dcfinicio-
nes de sus componentes y metodos (10s controladorcs de eventos) en la primera
parte de su definition, antes de las palabras clave public y private. Estos
campos y metodos de la parte inicial de la clase son publicados. Cuando no se
aiiade ninguna palabra clave especial antes de un elemento de una clase de com-
ponente, la predefinida es published.
Para ser mas precisos, published es la palabra clave predefinida solo si la
clase se compilo con la directiva de compilador $M+ o desciende de una clase
compilada con $M+.Dado que esta directiva se usa en la clase TPersistent,
la mayoria de las clases de la VCL y todas Ias clases de componentes se predefinen
como published. Sin embargo, las clases no componentes en Delphi (corno
TStream y TList) se compilan con $M-y se predefinen como de visibilidad
publica.
Los metodos asignados a cualquier evento en el IDE (y en 10s archivos DFM)
deberian ser publicados y 10s campos correspondientes a nuestros componentes
en el formulario tambien, para que se conecten automaticamente con 10s objetos
descritos en el archivo DFM y sk creen junto con el formulario.
Se puede ver el efecto que se obtiene a1 pulsar el boton Fill List mientras se
usa el valor predefinido C a p t i o n del cuadro de edicion en la figura 4.2. Se
puede probar con cualquier otro nombre de propiedad. Los numeros se converti-
ran en cadenas mediante una conversion de variante. Los ob-ietos (como el valor
de la propiedad Font) apareceran como direcciones de memoria
!?!~ow~Y I ~ a b e lCapl~on
l = &Pmpe~ty
Captron
INO
Ed61 Caption
- eFn Lnl
Figura 4.2. El resultado del ejemplo RunProp, que accede a las propiedades por
nombre en tiempo de ejecucion.
- - - - -
ADVERTENCIA: No conviene usar con frecuencia la unidad Typ In f o
en lugar del polimorfismo y de otras thcnicas de acceso a propiedades. Hay
que usar primer0 el acceso a propiedades de clase basica o la conversion de
tipos segura (as) cuando sea necesario, y reservar el acceso RTTI para
propiedades como ultimisimo recurso. Usat tknicas T y p I n f o ralentiza el
codigo, lo hace d s complejo y mris proclive a1 error humano; de hecho,
o& la verificacibn de tipos en tiernpo de compilacibn.
La clase TComponent
Si la clase T P e r s i s t e n t es realmente mas importante de lo que parece a
primera vista, la clase clave en el corazon de la biblioteca de clases basada en
componentes de Delphi es TComponent, que hereda de T P e r s i s t e n t (y de
T O b je c t ) . La clase TComponent define muchos elementos principales de com-
ponentes, per0 no es tan compleja como se puede pensar, puesto que las clases
basicas y el lenguaje ya ofrecen la mayoria de 10s elementos realmente necesarios.
Posesion
Una de las caracteristicas principales de la clase TComponent es la defini-
cion de posesion. Cuando se crea un componente, se le puede asignar un compo-
nente propietario, que sera responsable de destruirlo. Asi, cada componente puede
tener un propietario y puede ser tambien el propietario de otros componentes.
En realidad, existen varios metodos y propiedades publicos de la clase que se
dedican a controlar las dos partes de la posesion. Veamos una lista, extraida de la
declaracion de clase (en la unidad Classes de la VCL):
type
TComponent = class(TPersistent, IInterface,
IInterfaceComponentReference)
public
constructor Create(A0wner: TComponent); virtual;
procedure DestroyComponents;
function FindComponent(const AName: string): TComponent;
procedure InsertComponent(AComponent: TComponent);
procedure RemoveComponent(AComponent: TComponent);
property Components [Index: Integer] : TComponent read
Getcomponent;
property Componentcount: Integer read Getcomponentcount;
property ComponentIndex: Integer
read GetComponentIndex write SetComponentIndex;
property Owner : TComponent read FOwner;
Cambio de propietario
Hemos visto que casi todos 10s componentes tienen un propietario. Cuando se
crea un componente en tiempo de diseiio (o desde el archivo D F M resultante); su
propietario sera siempre su formulario. Cuando se crea un componente en tiempo
de ejecucion, el propietario se pasa como parametro a1 constructor create.
owner es una propiedad de solo lectura, por lo que no se puede cambiar. El
propietario s e establece en el momento de la creacion y por lo general no deberia
cambiar durante la vida util de un componente. N o se deberia cambiar el propie-
tario de un componente en tiempo de diseiio ni tampoco su nombre libremente,
porque para hacerlo, se puede llamar a 10s metodos Insertcomponent y
RemoveComponent del propietario, que pasan el componente actual como
parametro. Sin embargo, no se pueden aplicar directamente a ningun controlador
de eventos de un formulario, como se pretende aqui:
procedure TForml.ButtonlClick(Sender: TObject);
begin
RemoveComponent (Buttonl);
Form2.InsertComponent (Buttonl);
end;
Este codigo produce una violacion del acceso a la memoria, porque cuando se
llama a R e m o v e C o m p o n e n t , Delphi desconecta el componente del campo del
formulario (Buttonl), definiendolo como nil.La solucion es escribir un pro-
cedimiento como el siguiente:
procedure ChangeOwner (Component, Newowner: TComponent);
begin
Component.Owner.RemoveComponent (Component);
NewOwner.1nsertComponent (Component);
end;
1
Figura 4.3. En el ejemplo Changeowner, al hacer clic sobre el boton Change se
mueve el componente Buttonl a1 segundo formulario.
I
ADVERTENCIA: Si se borran 10s nombres de componente hay que ase- ,
gurarse de que se deja a1 menos un componente con nombre e cada clase 1
- Y el sktema
utilizada en el formulario, de modo aue el'enlazador intelinente
de streaming enlacen el codigo necesario para la clase y lo reconozcan en el
archivo DFM.Si, como ejemplo, se eliminan todos 10s campos de un for-
,..I,,:, ,..-U -
I I I U I ~ I I W~
-,c,,,,
,
G IGLIGIGU
SG
,,
a ,---- ,-a,
w.mlpunGuiGs
ms - L - 1 -..---I,
I Ldual, wauuu GI
, -1
slaicura ,
, -&, : , ,.,
r ; i r ~ g u ~
Eventos
En realidad, 10s componentes de Delphi, se programan usando PME: propieda-
des, metodos y eventos. Aunque a estas alturas, 10s metodos y propiedades debe-
rian estar claros, 10s eventos todavia no se han comentado. La razon es que 10s
eventos no implican una nueva funcion del lenguaje, sin0 que son una tecnica
estandar de programacion. Un evento, de hecho, es tecnicamente una propiedad,
con la unica diferencia de que se refiere a un metodo (un tip0 de puntero a metodo,
para ser precisos) en lugar de a otros tipos de datos.
Eventos en Delphi
Cuando un usuario hace algo con un componente, como hacer clic sobre el, el
componente genera un evento. Otros eventos 10s produce el sistema, en respuesta
a una llamada de metodo o un cambio de una de las propiedades del componente
(o incluso de un componente diferente). Por ejemplo, si ponemos el foco en un
componente, el componente que tenga el foco en ese momento lo pierde y se
desencadena el evento correspondiente. Tecnicamente, la mayoria de 10s eventos
Delphi se desencadenan a1 recibir el mensaje correspondiente del sistema operati-
vo, aunque no existe un mensaje individual para cada evento. Los eventos de
Delphi suelen ser de mayor nivel que 10s mensajes del sistema operativo y Delphi
ofrece una serie de mensajes adicionales entre componentes.
Desde un punto de vista teorico, un evento es el resultado de una peticion
enviada a un componente o control, que puede responder a1 mensaje. Siguiendo
ese enfoque, para controlar el evento clic de un boton, seria necesario crear una
subclase para la clase TButton y aiiadir el nuevo codigo del controlador de
eventos dentro de esa nueva clase.
En la practica, crear una nueva clase para cada componente que queramos
usar es demasiado complicado como para resultar razonable. En Delphi, el con-
trolador de eventos de un componente es normalmente un metodo del formulario
que contiene el componente, no del propio componente. En otras palabras, el
componente confia en su propietario para controlar eventos. Esta ttcnica se deno-
mina delegation y resulta basica para el modelo basado en componentes de Delphi.
De este modo, no hay que modificar la clase T B u t t o n , a no ser que queramos
definir un nuevo tip0 de componente, sino sencillamente personalizar su propieta-
rio para modificar el comportamiento del boton.
Punteros a metodo
Los eventos dependen de una caracteristica especifica del lenguaje Delphi: 10s
punteros a metodo. Un tipo de puntero a metodo es como un tip0 de procedimien-
to, pero uno que se refiere a un metodo. Tecnicamente, un tip0 de puntero a
metodo es un tip0 de procedimiento que tiene un parametro Self implicito. En
otras palabras, una variable de un tip0 de procedimiento almacena la direccion de
una funcion a la que Ilamar, dado que tenga un conjunto de parametros. Una
variable puntero a metodo almacena dos direcciones: la direccion del codigo del
metodo y la direccion de un caso del objeto (datos). La direccion de la instancia
del objeto aparecera como self dentro de la estructura del metodo, cuando se
llame al codigo del metodo utilizando este puntero a metodo.
- -
MyButton = class
OnClick: TNotifyEvent;
end;
var
Form1 : TForml ;
A1 final del codigo anterior, antes de que podamos hacer una conversion de
tipos siguientes con as, es necesario convertir manualmente el puntero que ha
devuelto TList en una referencia TOb j ect. Este tipo de expresion puede cau-
sar una excepcion de conversion de tipos no valida o generar un error de memoria,
si el puntero no es una referencia a un objeto.
Para demostrar que las cosas pueden salir realmente mal, hemos aiiadido un
boton m b , que aiiade un objcto T B u t t o n a la lista mediante una llamada a
L i s t D a t e . A d d ( S e n d e r ) . Si se hace clic sobrc este boton y despues se ac-
tualiza una de las listas, resultara en un error. Por ultimo, hay que recordar que
cuando sc destruye una lista de objetos, hay que destruir primer0 todos 10s objetos
de la lista. El programa L i s t Demo usa para esto el metodo F o r m D e s t r o y del
formulario:
procedure TForml.FormDestroy(Sender: TObject);
var
I: Integer;
begin
for I : = 0 to ListDate.Count - 1 do
TObject (ListDate [I]) .Free;
ListDate.Free;
end;
Colecciones
El segundo grupo, las colecciones, contiene so10 dos clases, T C o l l e c t i o n
y T C o l l e c t i o n I t e m . T C o l l e c t i o n define una lista homogenea de obje-
tos, poseidos por la clase coleccion. Los objetos dc la coleccion han de descender
de la clase T C o l l e c t i o n I t e m . Si se necesita una coleccion que almacene
objetos especificos, hay que crear una subclase de T C o l l e c t i o n y una subclase
correspondiente de T C o l l e c t i o n 1 t e r n . Las colecciones se usan para especifi-
car valores de propiedades de componentes; resulta muy poco frecuente trabajar
con colecciones para almacenar objetos propios.
Clases de contenedores
Las versiones recientes de Delphi incluyen una serie de clases de contenedores,
definida en la unidad C o n t n r s . Las clases contenedores amplian las clases T L i s t
aiiadiendo el concept0 de posesion y definiendo normas de estraccion especificas
(que imitan pilas y colas) o capacidades de ordenacion.
La diferencia basica entre T L i s t y la nueva clase TOb je c t L i s t, por ejem-
plo, es que la ultima se define como una lista de objetos TOb j ec t , no como una
lista de punteros. Sin embargo, es incluso mas importante el hccho de que si la
lista de objetos tiene la propiedad O w n s O b j e c t s definida como True,
automaticamente se elimina un objeto a1 reemplazarlo por otro y se elimina cada
objeto cuando se destruye la propia lista. Veamos una lista de todas las clases de
contenedores:
L a clase TObjectList: Representa una lista de objetos, que en ultimo
termino son poseidos por la propia lista.
La clase heredada TComponentList: Representa una lista de componen-
tes, con total soporte para la notification de la destruccion (una importante
caracteristica de seguridad cuando 10s componentes estan conectados me-
diante sus propiedades, es decir, cuando un componente es el valor de una
propiedad de otro).
L a clase TClassList: Es una lista de referencias de clase. Hereda de T L i s t
y no necesita destruirse de manera especifica, ya que en Delphi no es
necesario destruir las referencias dc clase.
Las clases TStack y TObjectStack: Representan listas de punteros y ob-
jetos, a partir de 10s cuales se pueden extraer unicamente elementos co-
menzando desde el ultimo insertado. Una pila sigue cl orden LIFO (Last
In, F ~ r s Out;
l ultimo en entrar, primero en salir). Los metodos mas comu-
nes de una pila son p u s h para la insercion, Pop para la extraccion y
Peek para obtener una vista previa del primer elemento sin eliminarlo de
la pila. Aun se pueden usar todos 10s metodos de la clase basica, TList .
Las clases TQueue y TObjectQueue: Representan listas de punteros y
objetos, de 10s que siempre se puede eliminar el primer elemento insertado
(FIFO: First In, First Ottt, primero en entrar, primero en salir). Los meto-
dos de estas clases son 10s mismos que 10s de las clases de pila per0 se
comportan de un modo distinto.
A1 pulsar 10s dos botones, se puede ver que cuando se llama a pop para cada
contenedor, devuelve el ultimo elemento. La diferencia es que la clase TQueue
inserta 10s elementos a1 principio y la clase T Sta c k 10s inserta a1 final.
Listas asociativas de verificacion
Desde Delphi 6, el conjunto de las clases contenedores predefinidas incluye
TBucketList y TOb jectBuc ketList.Estas dos listas son asociativas, lo
que significa que tienen una clave y una entrada real. La clave se usa para identi-
ficar 10s elementos y buscarlos. Para aiiadir un elemento, se puede llamar a1
metodo ~ d con d dos parametros: la clave y 10s datos reales. Cuando se usa el
metodo Find,hay que pasar la clave y recuperar 10s datos. Se consigue el mismo
efecto utilizando la matriz Data de forma adecuada, pasando la clave como
parametro.
Estas listas tambien se basan en un sistema de verificacion. La lista crea una
matriz interna de elementos, denominados "cubos", cada uno de 10s cuales tiene
una sublista de 10s elementos reales de la lista. Cuando se aiiade un elemento, su
valor clave se usa para calcular el valor de verificacion (o hash), que determina el
cub0 a1 que se aiiadira el elemento. A1 buscar un elemento, se vuelve a calcular el
valor de verificacion y la lista atrapa inmediatamente la sublista que contiene el
elemento y lo busca en ella. Con esto se consigue que la insercion y las busquedas
Sean muy rapidas, per0 solo si el algoritmo de verificacion distribuye 10s elemen-
tos de manera uniforme entre 10s diversos cubos y si hay suficientes entradas
diferentes en la matriz. De hecho, cuando muchos elementos pueden estar en el
mismo cubo, la busqueda se ralentiza. Por esa razon, cuando se crea
Tob j ectBuc ketlist , se puede especificar el numero de entradas de la lista,
usando el parametro del constructor, eligiendo un valor entre 2 y 256. El valor del
cubo se fija tomando el primer byte del puntero (o numero) pasado como clave y
haciendo una operacion a n d con un numero que corresponda a las entradas.
. - - - - -- - - -
I
..*-S l . - ?~ 3 - 1
marnz, esrameclenao un valor alrerenre para la propleaaa ~ u c ~ e i x o u n t .
~--A.~' 3.4- r - - 1 3 . a -.I
NOTA: Las ~ l a s e de
s contenedores de Delphi utilizan sobrescritura esthti-
ca para realizar las adaptaciones simples de tip0 (sesultados de parhetros
y funciones del tip0 deseado). La sobrescritura estiitica no es lo mismo que
el polimorfismo; alguien que utilice una clase de contenedor mediante una
variable TLis t no Uamara a las funciones espe~iaha&s del contenedor.
La sobrescritura esthtica es una tecnica eficaz y sencilla, pero tiene una
importante restriccion: 10s metodos del descendiente no deberian hacer nada
rnlic nJlli AP rrnn e n n v ~ r c i A nA- t i n n c c p n e i l l a nnrnrre nn hrrv uarantirrc Ae
que se llame a 10s mktodos descendientes. Se podria acceder a la lista y
manipularla utilizando tanto 10s metodos ascendientes como 10s descen-
dientes, por lo que sus operaciones reales han de ser identicas. La 6nica
airerencra es er upo- --*:a:--
J : f ----- :--- -1 A:-
urruzaao 2 - -- a _ - --L&- J - - 2 ----- 3:--*--
en 10s mwwos -..-
aeswnolenres, que permlre ---:A-
type
// b a s a d o e n h e r e n c i a
T D a t e L i s t I = class (TObj e c t L i s t )
protected
p r o c e d u r e S e t O b j e c t ( I n d e x : I n t e g e r ; Item: TDate) ;
f u n c t i o n GetObj e c t ( I n d e x : I n t e g e r ) : TDate;
public
f u n c t i o n Add ( O b j : T D a t e ) : I n t e g e r ;
p r o c e d u r e I n s e r t ( I n d e x : I n t e g e r ; Obj : T D a t e ) ;
p r o p e r t y O b j e c t s [ ~ n d e x : I n t e g e r ] : TDate
r e a d GetObject w r i t e SetObject; d e f a u l t ;
end;
// b a s a d o e n e n v o l t o r i o
TDateListW = class ( T O b j e c t )
private
FList: TObjectList;
f u n c t i o n G e t O b j e c t ( I n d e x : I n t e g e r ) : TDate;
p r o c e d u r e S e t o b j e c t ( I n d e x : I n t e g e r ; Obj : T D a t e ) ;
f u n c t i o n GetCount: I n t e g e r ;
public
constructor Create;
d e s t r u c t o r Destroy; o v e r r i d e ;
f u n c t i o n Add ( O b j : T D a t e ) : I n t e g e r ;
f u n c t i o n Remove ( O b j : T D a t e ) : I n t e g e r ;
f u n c t i o n IndexOf ( o b j : T D a t e ) : I n t e g e r ;
p r o p e r t y Count: I n t e g e r r e a d GetCount;
p r o p e r t y O b j e c t s [ I n d e x : I n t e g e r ] : TDate
r e a d GetObject w r i t e SetObject; d e f a u l t ;
end;
Streaming
Otro ambito principal de la biblioteca de clases de Delphi es el soporte de
streaming, que incluye adrninistracion de archivos, memoria, sockets y otras fuentes
de informacion organizadas de forma secuencial. La idea del streaming consiste
en moverse a traves de 10s datos mientras 10s leemos, de un mod0 muy parecido a
las tradicionales funciones Read y Write utilizadas por el lenguaje Pascal.
La clase TStream
La VCL define la clase abstracta Tst ream y diversas subclases. La clase
padre, TStream,posee solo unas cuantas propiedades y jamas se creara una
instancia de la misma, per0 posee una interesante lista de metodos que, por lo
general, se utilizara cuando se trabaje con clases stream derivadas.
La clase TStream define dos propiedades, size y Position.Todos 10s
objetos stream tienen un tamaiio especifico (que generalmente aumenta si escribi-
mos algo despues del final del stream) y habra que especificar una posicion dentro
del stream en la que se quiere leer o escribir la informacion.
La lectura y escritura de bytes depende de la clase de stream utilizada, per0 en
ambos casos no es necesario saber mucho mas que el tamaiio del stream y la
posicion relativa dentro del stream para leer o escribir datos. De hecho, esta es
una de las ventajas de usar streams. La interfaz basica sigue siendo la misma si
manipulamos un archivo del disco, un campo de objeto binario ancho (BLOB) o
una secuencia larga de bytes en memoria. Ademas de las propiedades size y
Position,la clase Tstream define tambien varios metodos importantes, la ,
mayoria de 10s cuales son virtuales y abstractos. (En otras palabras, la clase
TSt ream no define lo que hacen estos metodos, por lo tanto, las clases derivadas
son responsables de su implementacion.) Algunos de estos metodos solo son im-
portantes en el context0 de lectura y escritura de componentes dentro de un stream
(por ejemplo, Readcomponent y Writecomponent), per0 algunas son uti-
les tambien en otros contextos. En el listado 4.2, se puede encontrar la declara-
cion de la clase TSt ream,extraida de la unidad Classes.
// m u e v e a una p o s i c i d n especifica
function Seek (Offset: Longint; Origin: Word) : Longint;
overload; virtual ;
function Seek (const Offset: Int64; Origin: TSeekOrigin) :
Int64;
overload; virtual ;
// copia el s t r e a m
function CopyFrom(Source: TStream; Count: Int64) : Int64;
// l e e o e s c r i b e un c o m p o n e n t e
function ReadComponent(1nstance: TComponent): TComponent;
function ReadComponentRes(1nstance: TComponent) : TComponent;
procedure WriteComponent(1nstance: TComponent);
procedure WriteComponentRes(const ResName: string; Instance:
TComponent) ;
procedure WriteDescendent(Instance, Ancestor: TComponent);
procedure WriteDescendentRes(
const ResName: string; Instance, Ancestor: TComponent) ;
procedure WriteResourceHeader(const ResName: string; out
FixupInf o: Integer) ;
procedure FixupResourceHeader (FixupInfo: Integer) ;
procedure ReadResHeader;
// p r o p i e d a d e s
property Position: Int64 read GetPosition write SetPosition;
property Size: Int64 read GetSize write SetSize64;
end ;
Una gran ventaja de 10s streams sobre otras tecnicas de acceso a archivos es
que son intercambiables, por lo que podemos trabajar con streams de memoria y
guardarlos despues en un archivo o realizar las operaciones opuestas. Esta podria
ser una forma dc mejorar la velocidad de un programa que haga un uso intensivo
de archivos. Veamos un fragment0 de codigo, una funcion que copia un archivo,
para que hacernos una idea de como se pueden usar 10s streams:
procedure CopyFile (SourceName, TargetName : String) ;
var
Streaml, Stream2: TFileStream;
begin
Streaml : = T F i l e S t r e a m - C r e a t e (SourceName, fmOpenRead) ;
try
Stream2 : = T F i l e S t r e a m - C r e a t e (TargetName, fmOpenWrite or
fmcreate) ;
try
Stream2 .CopyFrom (Streaml, Streaml.Size) ;
finally
Stream2. Free;
end
finally
Streaml. Free;
end
end ;
Otro uso importante de 10s streams es controlar 10s campos BLOB de bases de
datos u otros campos grandes directamente. Se pueden exportar esos datos a un
stream o leerlos desdc uno, llamando simplemente a 10s metodos SaveToStream
y LoadFromStream de la clase TBlobField.
- , ---- - -
Esta vez, el archivo real tambien incluira 10s caracteres de marca adicionales,
para que se pueda leer de nuevo este archivo utilizando unicamente un objeto
T R e a d e r . Por esta razon, el uso de T R e a d e r y TWr i t e r se reduce general-
mente a1 streaming de componentes y rara vez se aplica en la administracion de
archivos general.
Streams y permanencia
En Delphi, 10s streams tienen una hncion bastante importante para la perma-
nencia. Por ese motivo, muchos metodos de T S t r e a m e s t h relacionados con las
acciones de guardar y cargar un componente y sus subcomponentes. Por ejemplo,
se puede almacenar un formulario en un stream a1 escribir:
Cuatro funciones distintas, con 10s mismos parametros y nombres que contie-
nen el nombre Resource en lugar de Binary (como en Obj ect Resource-
ToText), convierten el formato del recurso obtenido por WriteComponentRes.
Un ultimo metodo, TestStreamFormat, indica si un DFM contiene una re-
presentation binaria o textual.
En el programa FormToText, se ha utilizado el metodo O b jectBi-
naryToText para copiar la definicion binaria de un formulario en otro stream
y, a continuacion, se ha mostrado el stream resultante en un campo de texto, como
muestra la figura 4.5. Este es el codigo de 10s dos metodos utilizados:
p r o c e d u r e TformText.btnCurrentClick(Sender: TObject);
var
MemStr: TStream;
begin
MemStr : = TMemoryStream.Create;
try
.
MemStr Writecomponent (Self);
ConvertAndShow (MemStr) ;
finally
MemStr.Free
end;
end;
PadO W Fmm in Ex
TOP- 113
W d h - 545
- -
H c , ~ .374
A c t w ~ o n l l o l blnCtwrenl
Caphon Fmm To Te4
-
C d n = clBlnFace
F d Charel DEFAULT-CWSET
F a d r h -&hdowTexl
-
F a n l H c M - 11
F m Nme M S Sans Sell?
Fwd St* = [I
-
O W C ~ r a l d r d n= T~ue
-
Vabk T w
P m l s P r d n d 96
TeulHmit4 = 13
-
&led m r d u l TMemo
Ldl 0
Top-41
-
Wdlh 537
&n -
Hrn.306
Kl~enl
Scret3rus = r N c l m a l
TabClldcr = 0
Fijese en que si hacemos clic varias veces sobre el boton Current Form Object,
obtendremos mas y mas texto y el texto del campo memo se incluira en el stream.
Despues de unas cuantas lineas, toda la operacion resultara extremadamente len-
ta, hasta que el programa parezca haberse colgado. En este codigo, empezamos a
ver parte de la flexibilidad de utilizar streams (podemos escribir un procedimiento
generic0 y utilizarlo para convertir cualquier stream).
NOTA: Es importante enfatizar que despuds de haber escrito dabs en un
stream, debemos de nuevo volver explicitamente a1 principio (o definir la
propiedad Posit ion como 0) antes de seguir usando el stream, a no ser
que queramos adjuntarle datos, por supuesto.
clase stream con un nuevo nombre para trabajar con un nuevo tipo de
medio, sino solo personalizar un stream existente. En ese caso, todo lo que
hay que hacer es escribir 10s metodos de lectura y escritura apropiados.
Como ejemplo, hemos creado una clase para codificar y decodificar un
stream de archivo generico. Aunque este ejemplo esti limitado por el uso de
.
un mecanismo de Eodificaci6n titia1menti bobo, se integra cokpletamente
7 C . correcramente. *La nueva clase sueam aeclara senci-
con la VLL y runciona I 1
L -
llamente 10s dos mdtodos de lectura y escritura y tiene m a propiedad que
almacena un clave:
type
TEncodedStream = class (TFileStream)
private
FKey: Char;
public
constructor Create (conat FileName: string; Mode: Word) ;
function Read (var Buffer; Count : Longint) : Longint ;
override:
function Write(con6t Buffer; Count: Longint): Longint;
override;
property Key: Char read FKey write FKey;
end ;
Decompress
1
Para conseguir que el codigo de cste programa resulte mas reutilizable, pode-
mos escribir dos funciones para comprimir o descomprimir un stream en otro
stream. Este es el codigo:
procedure C o m p r e s s S t r e a m (aSource, a T a r g e t : T S t r e a m ) ;
var
comprStream: TCompresssionStream;
begin
c o m p r S t r e a m : = TCompressionStream.Create
clFastest, aTarget) ;
t rY
comprStream.CopyFrom(aSource, aSource
comprStream.CompressionRate;
finally
comprStream.Free;
end ;
end;
procedure D e c o m p r e s s S t r e a m (aSource, a T a r g e t : T S t r e a m ) ;
var
decompstream: TDecompressionStream;
n R e a d : Integer;
B u f f e r : array [O. .I0231 of C h a r ;
begin
decompstream : = TDecompressionStream.Create(aSource);
try
/ / a S t r e a m D e s t . C o p y F r o m (decompstream, size) no funciona
/ / c o r r e c t a m e n t e ya q u e s e d e s c o n o c e el tamado a priori,
/ / a s i q u e utilizamos ~ 7 nc o d l g o s i m z l a r "manual"
repeat
n R e a d : = decompstream. Read (Buffer, 102 4 ) ;
a T a r g e t .Write (Buffer, n R e a d ) ;
until nRead = 0;
finally
decompStream.Free;
end ;
end ;
La unidad Classes
Esta unidad es el corazon de las bibliotecas VCL y CLX y aunque ha sufrido
muchos cambios internos desde la ultima version de Delphi, para el usuario medio
las novedades son pocas. (La mayoria de 10s cambios estan relacionados con una
integracion modificada con el IDE y estan dirigidos a 10s escritores de componen-
tes expertos.)
Veamos una lista de lo que se puede encontrar en la unidad classes,una
unidad a la que todo programador deberia dedicar algun tiempo:
I I I I
Standard Addtond Wh32 S~tmDaa Access Data Corirds &€mess I I DdaSnao I BDE I ADO I InloBsrt I Weffiavices I lnteld&
a d,a~FcT~& ~ ~ + ~ i ~ ~ b ~ ~ ~ ~ ~
I 1 1 1
~ l s d a r d A M m d Win32 ~v:tem 1 D a t a A m s 1 D d a Conl~ds dbExrverr I @ & ~ n r n 1 BDE 1 AD0 I lnldaoe 1 WFbSuwcer I l n l e ! n @ ~
Figura 5.1. Una comparacion de las tres primeras fichas de la Component Palette
para una aplicacion CLX o una VCL.
Sentencias uses
Las unicas diferencias entre ambos ejemplos estan relacionadas con las senten-
cias u s e s . El formulario de la aplicacion CLX tiene el siguiente codigo inicial:
u n i t QLibCompForm;
interface
uses
SysUtils, Types, Classes, QGraphics, QControls, QForms,
QDialogs, QStdCtrls;
Ejecucion en Linux
La cuestion real sobre la eleccion de bibliotecas se reduce a la importancia que
tenga Linux o Unicode para nosotros y para 10s usuarios. Es muy importante
destacar que si se crea una aplicacion CLX, se podra compilar de nuevo inalterada
(con el codigo fuente esacto) en Kylis que crea una aplicacion originaria de
Linux, a no ser que se haya hecho algo de programacion con la API de Windows,
en cuyo caso la compilacion condicional resulta esencial.
Como ejemplo, se ha vuelto a compilar el ejemplo QLibComp y se puede ver
en ejecucion en la figura 5 . 2 , donde tambien aparece el IDE de Kylix en accion en
un sistema KDE.
Figura 5.2. Una aplicacion escrita con CLX puede volver a compilarse directamente
bajo Linux con Kylix.
La tabla 5.1 es una comparacion de 10s nombres de las unidades visuales VCL
y CLX, esceptuando la parte de la base de datos y algunas unidades a las que
raramente se hace referencia:
Parent y Controls
La propiedad P a r e n t de un control indica que otro control es responsable de
mostrarlo. Cuando dejamos un componente en un formulario en el Form Designer,
el formulario se transformara tanto en padre como en propietario del nuevo con-
trol.
Pero si dejamos el componente en un Panel, ScrollBox u otro componente
contenedor, este se convertira en su padre, mientras que el formulario seguira
siendo el propietario del control.
Cuando creamos el control en tiempo de ejecucion, sera necesario establecer el
propietario (usando el parametro del constructor C r e a t e ) , per0 habra que esta-
blecer tambien la propiedad P a r e n t o el control no sera visible.
A1 igual que la propiedad Owner, la propiedad P a r e n t posee su inverso. La
matriz C o n t r o l s , de hecho, lista todos 10s controles hijos del actual, enumera-
dos de 0 a C o n t r o l s c o u n t - 1.
Se puede analizar esta propiedad para trabajar con todos 10s controles que
aloje otro control, utilizando en ultimo termino un metodo recursivo que opere
sobre 10s controles hijos de cada subcontrol.
Propiedades relacionadas con el tamaho
y la posicion del control
Algunas de las propiedades introducidas por T C o n t r o l y comunes a todos
10s controles son aquellas relacionadas con el tamaiio y la posicion. La posicion
de un control la fijan sus propiedades L e f t y Top y su tamaiio las propiedades
H e i g h t y W i d t h . Tecnicamente, todos 10s componentes tienen una posicion,
porque cuando abrimos de nuevo un formulario existente en tiempo de diseiio,
queremos que se puedan ver 10s iconos de 10s componentes no visuales en la
posicion exacta en la que 10s situamos. Esta posicion es visible en el archivo de
formulario.
Fuentes
Normalmente se usan dos propiedades para personalizar la interfaz de usuario
de un componente, Co l o r y F o n t . Hay diversas propiedades relacionadas con
el color. La propiedad c o l o r se refiere normalmente a1 color de fondo del com-
ponente. Ademas, existe una propiedad C o l o r para las fuentes y muchos otros
elementos graficos. Muchos componentes tienen tambien las propiedades
P a r e n t c o l o r y P a r e n t F o n t , que indican si el control deberia utilizar la
misma fuente y color que su componente padre, que suele ser el formulario. Se
pueden usar estas propiedades para cambiar la fuente de cada control en un for-
mulario configurando sencillamente la propiedad F o n t de este ultimo.
Cuando se configura una fuente, introduciendo valores para 10s atributos de la
propiedad en el o b j e c t I n s p e c t o r o utilizando el cuadro de dialog0 estandar
de seleccion de fuente, se puede escoger una de las fuentes instaladas en el siste-
ma. El hecho de que Delphi permita usar todas las fuentes instaladas en el sistema
tiene tanto ventajas como inconvenientes. La principal ventaja es que si se tiene
instalado un cierto numero de agradables fuentes, el programa podra utilizarlas.
El inconveniente es que si se distribuye la aplicacion, estas fuentes puede que no
se encuentren disponibles en 10s ordenadores de 10s usuarios.
Si el programa utiliza una fuente que el usuario no tiene, Windows elegira
alguna otra fuente para reemplazarla. Un resultado cuidadosamente formateado
del programa puede verse arruinado por la sustitucion de fuentes. Por esta razon,
probablemente deberiamos confiar solo en las fuentes estandar de Windows (corno
MS Sans Serif, System, Arial, Times New Roman, etc.).
Colores
Existen diversas formas de fijar el valor de un color. El tip0 de esta propiedad
es T C o l o r , que no es un tipo de clase sino simplemente un tipo entero. Para
propiedades de este tipo, se puede escoger un valor de una serie de constantes de
nombre predefinidas o introducir directamente un valor. Las constantes para 10s
colores son entre otras c l B l u e , c l s i l v e r , c l W h i t e , c l G r e e n , c l R e d y
muchas mas (incluidas las aiiadidas con Delphi 6: clMone yGreen, c l SkyBlue,
c l C r e a m y clMedGray). Como una alternativa mejor, se puede utilizar uno de
10s colores usados por el sistema para indicar el estado de algunos elementos.
Este conjunto de colores es diferente en VCL y CLX.
VCL incluye colores predefinidos de Windows como el fondo de una ventana
( c l w i n d o w ) , el color del texto de un menu resaltado ( c l H i g h t l i g h t T e x t ) ,
el titulo activo ( c l A c t i v e c a p t i o n ) y el color de la cara ubicua del boton
( c l B t n F a c e ) . CLX contiene un conjunto diferente e incompatible de colores
del sistema, como c l B a c k g r o u n d , que es el color estandar de un formulario,
c l B a s e , utilizado por 10s cuadros de edicion y otros controles visuales,
c l A c t i v e F o r e g r o u n d , el color de primer plano para 10s controles activos y
c l D i s a b l e d B a s e , el control de fondo para 10s controles de texto desactivados.
Todas las constantes mencionadas aqui estan listadas en 10s archivos de ayuda de
la VCL y CLX bajo el titulo "TColor type" (Tipo TColor).
Otra opcion consiste en especificar un T C o l o r como un numero (un valor
hexadecimal de 4 bytes) en lugar de utilizar un valor predefinido. Si se utiliza esta
tecnica, se deberia saber que 10s tres bytes menores de dicho numero representan
las intensidades RGB de color del azul, verde y rojo respectivamente. Por ejem-
plo, el valor SO 0 FFO 0 0 0 se corresponde a un color azul puro, el valor
$ 0 0 0 0 ~ ~ 0a10verde, el valor SOOOOOOFF a1 rojo, el valor $ 0 0 0 0 0 0 0 0 a1
negro y el valor $0 0 FFFFFF a1 blanco. A1 especificar valores intermedios, se
puede obtener cualquiera de 10s 16 millones de colores posibles.
En lugar de especificar directamente estos valores hexadecimales, deberiamos
utilizar la funcion RGB de Windows, que tiene tres parametros, todos entre el 0 y
el 255. El primer0 indica la cantidad de rojo, el segundo la cantidad de verde y el
ultimo la cantidad de azul. Utilizar la funcion RGB hace que 10s programas Sean
por lo general m b faciles de leer que si usamos una constante hexadecimal sola.
En realidad, RGB es casi una funcion de la API de Windows. Esta definida por las
unidades relacionadas con Windows y no por las unidades de Delphi, per0 no
existe una funcion similar en la API de Windows. En C, existe una macro que
tiene el mismo nombre y efecto. RGB no esta disponible en CLX, por lo que hemos
escrito una version propia del siguiente modo:
function RGB ( r e d , g r e e n , b l u e : B y t e ) : C a r d i n a l ;
begin
R e s u l t : = b l u e + g r e e n * 256 + r e d * 256 * 256;
end:
1 NOTA:
mit&
pKogrr;maddreesmpertos pug& no* q4e en CLX hqy un
utibadi3 can *a hecdrencia, EventHandler, que se cmres-
El control LabeledEdit
Delphi 6 aiiadio un control llamado LabeledEdit, que es un control Edit
con una etiqueta adjunta. La etiqueta aparece como propiedad del control com-
puesto, que hereda de TCustomEdit.
Este componente es muy comodo, porque nos permite reducir el numero de
componentes de nuestros formularios, moverlos mas facilmente y tener una orga-
nizacion mas consistente para las etiquetas de todo un formulario o una aplica-
cion. La propiedad EditLabel esta conectada con el subcomponente, que tiene
las propiedades y eventos normales. Dos propiedades mas, LabelPosit ion y
Labelspacing, nos permiten configurar las posiciones relativas de 10s dos
controles.
El componente MaskEdit
Para personalizar aun mas la entrada de un cuadro de edicion, se puede utilizar
el componente Mas kEdit.Tiene una propiedad EditMask que es una cadena
que indica para cada caracter si deberia ser una mayhcula, minuscula o un nu-
mero y otras condiciones similares.
El editor Input Mask permite introducir una mascara, per0 tambien nos pide
que indiquemos un caracter que reserve el sitio para la entrada y decidir si se
guarda el material presente en la mascara junto con la cadena final. Por ejemplo,
se puede escoger mostrar el prefijo de zona del numero de telefono entre parente-
sis solo como una entrada de sugerencia o guardar 10s parentesis con la cadena
que almacena el numero resultante. Estas dos entradas en el editor'lnput Mask
corresponden a 10s dos ultimos campos de la mascara (separados por puntos y
coma). Se puede ver el editor de la propiedad EditMask a continuacion:
-
Date 06/27/94
Long Tme 09 05 15PM
Shod T~me
ILL.-
kid*... I I T ( I Hdp I
I TRUCO:Si hacemob did. sobre el&th MEW cfBt Inpd Mask ~ditnr. 1
se pueden eswger & c m de entrada predefbidas p&3 difkrentes paises.
Test Html
Test text with bold
4
Figura 5.3. El ejemplo HtmlEdit en tiempo de ejecucion: cuando se aiiade nuevo
texto HTML al campo de memo, se puede previsualizar inmediatamente.
Selection de opciones
Existen dos controles estandar Windows que permiten a1 usuario escoger dife-
rentes opciones, asi como otros dos controles para agrupar conjuntos de opciones.
Los componentes CheckBox y RadioButton
El primer control estandar de seleccion de opciones es la casilla de verificacion
(o check box), que corresponde a una opcion que se puede seleccionar sea cual sea
el estado de otras casillas de verificacion. Configurar la propiedad AllowGrayed
de la casilla de verificacion nos permite mostrar tres estados diferentes (seleccio-
nado, no seleccionado y en gris), que se alternan a medida que el usuario hace clic
sobre la casilla de verificacion.
El segundo tip0 de control es el boton de radio, que corresponde a una selec-
cion exclusiva. Dos botones de radio del mismo formulario o dentro del mismo
contenedor de grupo de radio no se pueden seleccionar a1 mismo tiempo y uno de
ellos deberia estar siempre seleccionado (como programador, se tiene la respon-
sabilidad de escoger uno de 10s botones de radio en tiempo de diseiio).
Los componentes GroupBox
Para alojar varios grupos de botones de radio, se puede usar un control
GroupBox para mantenerlos juntos, tanto funcional como visualmente. Para cons-
truir un cuadro de grupo con botones de radio, sencillamente hay que colocar el
componente GroupBox sobre un formulario y, a continuation, aiiadir 10s boto-
nes de radio a1 cuadro de grupo, como en el siguiente ejemplo:
El componente RadioGroup
Delphi posee un componente similar que se puede utilizar de forma especifica
para botones de radio: el componente RadioGroup. Un RadioGroup es un cua-
dro de grupo con algunos clones de botones de radio en su interior. La diferencia
es que estos botones de radio internos se gestionan automaticamente desde el
control contenedor. Utilizar un grupo de radio es, por lo general, mas sencillo que
utilizar el cuadro de grupo, puesto que 10s diversos elementos forman parte de una
lista, como en un cuadro de lista. Asi es como se puede obtener el texto del
elemento seleccionado:
Text : = RadioGroupl.Items [RadioGroupl.ItemIndex];
El componente ListBox
La seleccion de un elemento en un cuadro de lista usa las propiedades ~t ems
e ItemIndex como en el codigo anterior para el control RadioGroup. Si hay
que acceder con frecuencia a1 texto de 10s elementos del cuadro de lista seleccio-
nado, se puede escribir una funcion envoltorio como esta:
f u n c t i o n SelText (List: TListBox) : string;
var
nItem: Integer;
begin
n I t e m : = List.ItemIndex;
i f n I t e m >= 0 then
Result : = List. Items [nItem]
else
Result := ' ' ;
end;
f o r nItem : = 0 t o ListBoxl.Items.Count - 1 do
i f ListBoxl. Selected [nItem] then
SelItems : = SelItems + ListBoxl.Items [nItem] + ' ' ;
En CLX (no como en la VCL), se puede configurar una ListBox para que
utilice un numero fijo de columnas y filas, utilizando las propiedades columns,
Row, ColumnLayout y RowLayout.De ellas, la ListBox de la VCL tiene
solo la propiedad columns.
El componente ComboBox
Los cuadros de lista acaparan mucho espacio en pantalla y ofrecen unas opcio-
nes fijas (es decir, un usuario puede escoger solo entre 10s elementos de la lista y
no puede introducir ninguna opcion que el programador no haya tenido explicita-
mente en cuenta). Se pueden solucionar ambos problemas utilizando un control
ComboBox,que combina un cuadro de edicion y una lista desplegable. El com-
portamiento de un componente ComboBox cambia mucho dependiendo del valor
de su propiedad sty1e :
El estilo csDropDown: Define un cuadro combinado tipico, que permite
editar directamente y mostrar un cuadro de lista mediante solicitud.
El estilo csDropDownList: Define un cuadro combinado que no permite
editar (pero en el que se pueden pulsar ciertas teclas para seleccionar un
elemento).
El estilo cssimple: Define un cuadro combinado que siempre muestra el
cuadro de lista bajo el.
Fijese en que acceder a1 texto del valor seleccionado de un cuadro combinado
es mas sencillo que hacer la misma operacion en el caso de un cuadro de lista,
pucsto que podemos sencillamente usar la propiedad Text.Un truco util y habi-
tual para 10s cuadros combinados consiste en aiiadir un nuevo elemento a la lista
cuando un usuario introduce texto y pulsa la tech Intro. El siguiente metodo
comprueba primer0 si el usuario ha pulsado esa tecla, analizando el caracter con
el valor numeric0 (ASCII) de 13. A continuacion, verifica que el texto del cuadro
combinado no este vacio y que no esta ya en la lista (si su posicion en la lista es
menor que cero). Veamos el codigo:
procedure TForml.ComboBoxlKeyPress(
Sender: TObject; var Key: C h a r ) ;
begin
/ / s i el usuario pulsa l a tecla Intro
i f Key = Chr ( 1 3 ) then
with C o m b o B o x 3 do
i f (Text <> " I and (1tems.IndexOf (Text) < 0 ) then
1tems.Add (Text);
end :
El componente CheckListBox
Otra ampliacion del control de cuadro de lista la representa el componente
CheckListBox, un cuadro de lista con cada uno de 10s elementos precedidos
por una casilla de verificacion.
Un usuario puede seleccionar un unico elemento de la lista, pero tambien hacer
clic sobre las casillas de verificacion para alternar su estado. Esto hace que
CheckListBox sea un componente realmente adecuado para las selecciones
multiples o para resaltar el estado de una serie de elementos independientes (en
forma de una serie de casillas de verificacion).
10s grupos de colores que queremos ver en la lista (colores estandar, colores
ampliados, colores de sistema. etc.).
Los componentes ListView y TreeView
Si queremos una lista mas sofisticada, se puede utilizar el habitual control
ListView, que hara que la interfaz de usuario de la aplicacion parezca muy mo-
derna. Este componente es ligeramente mas complejo de usar, como ya se vera.
Otras alternativas para listar valores son el control comun TreeView, que mues-
tra elemcntos en una disposicion jerarquica y el control StringGrid, que muestra
divcrsos elementos para cada linea.
Si utilizamos 10s controles comunes en nuestras aplicaciones, 10s usuarios ya
sabran como interactuar con ellos y consideraran la interfaz de usuario del pro-
grama como actualizada. TrecVicw y ListView son dos componentes clave del
Esplorador de Windows y se puede suponer que muchos usuarios estaran familia-
rizados con ellos: incluso mas que con 10s controles tradicionales de Windows.
CLX tambien aiiade un control IconView, que es similar en parte de las caracte-
risticas a la ListView VCL.
El componente ValueListEditor
Las aplicaciones de Delphi utilizan normalmente la estructura nombrelvalor
que ofrecen en principio las listas de cadena. Delphi 6 introdujo una version del
componente StringGrid (tecnicamente una clase descendiente de TCustomDraw-
s t r i n g ) que se ha hecho concordar especificamente con este tip0 de listas de
cadena. El ValueListEditor tiene dos columnas en las que puede mostrar y dejar
que el usuario edite 10s contenidos de una lista de cadena con parejas nombrel
valor: como muestra la figura 5.4. Esta lista de cadena la indica la propiedad
S t r i n g s del control.
La potencia de este control se basa en que se pueden personalizar las opciones
de edicion para cada posicion de la cuadricula (grid)o para cada valor clave,
usando la propiedad solo en tiempo de ejecucion de matriz I t e m P r o p s . Para
cada elemento, se puede indicar:
Si es solo de lectura.
El numero masimo de caracteres de la cadena.
Una mascara de edicion (solicitada en ultimo termino en el evento
OnGetEditMask).
Plain Mema
rone=~
rwo=2
three-3
Figura 5.4. El ejemplo NameValues usa el componente ValueListEditor, que muestra 10s
pares nombrelvalor o clave/valor de una lista de cadena, tambien visible en un simple
campo de memo.
Memol.Lines : = ValueListEditor1.Strings;
ValueListEditorl.ItemProps [0] : = FirstItemProp;
f o r I : = 0 t o ValueListEditor1.Strings.Count - 1 do
ValueListEditorl.1temProps [I] : = SharedItemProp;
end ;
I asi que s61o asignmos el editor a las llneas qua w tienen &a h . I
Otra propiedad, K e y O p t i o n s , permite a1 usuario editar tambiin las claves
(10s nombres), aiiadir nuevas entradas, eliminar las existentes y contar con nom-
bres duplicados en la primera parte de la cadena. Es raro que no se puedan aiiadir
nuevas claves a no ser que se activen tambien las opciones de edicion, lo que
dificulta permitir que el usuario aiiada entradas adicionales mientras que se man-
tienen 10s nombres de las entradas basicas.
Rangos
Por ultimo, existen unos cuantos componentes que podemos usar para selec-
cionar valores dentro de un rango. Los rangos se pueden usar para entradas nu-
mericas y para seleccionar un elemento de una lista.
El componente ScrollBar
El control independiente ScrollBar es el componente original de este grupo,
per0 rara vez se utiliza por si solo. Las barras de desplazamiento (scroll bars) se
asocian normalmente con otros componentes, como cuadros de lista y campos de
memo o se asocian directamente con formularios. En todos estos casos, la barra
de desplazamiento se puede considerar parte de la superficie de otros componen-
tes. Por ejemplo, un formulario con una barra de desplazamiento es en realidad un
formulario que tiene una zona que parece una barra de desplazamiento pintada en
su borde, una caracteristica regida por un estilo especifico de Windows de la
ventana formulario. Con parecer, nos referimos a que no es tecnicamente una
ventana separada del tipo de componente ScrollBar. Estas barras de desplaza-
miento "falsas" se controlan normalmente en Delphi usando propiedades especifi-
cas del formulario y 10s otros componentes que las alojan: V e r t S c r o l l B a r y
HorzScrollBar.
El componente UpDown
Otro control relacionado es el componente U p D o w n , que suele estar conectado
a un cuadro de edicion de forma que el usuario pueda teclear un numero en el o
aumentar y disminuir el numero utilizando dos pequeiios botones de flecha. Para
conectar 10s dos controles, se define la propiedad As s o c i a t e del componente
UpDown. Podemos utilizar el componente U p D o w n como un control indepen-
diente, que muestre el valor actual en una etiqueta, o de cualquier otro modo.
I
ua;Edit con el ~ p ~ o en
w un
n "nico control.
.
NOTA: En CLX no existe el control UpDown, sino un SpinEdit
-
que as&ia.
. . . , a. .. .
El componente PageScroller
El control PageScroller de Win32 es un contenedor que permite desplazar el
control interno. Por ejemplo, si se coloca una barra de herramientas en la barra de
desplazamiento de la pagina y la barra de herramientas es mas grande que el
espacio disponible, el PageScroller mostrara dos pequeiias flechas en el lateral. Si
hacemos clic sobre dichas flechas se desplazara la zona interna. Este componente
se puede usar como una barra de desplazamiento, per0 tambien sustituye en parte
a1 control ScrollBox.
El componente ScrollBox
El control ScrollBox representa una zona de un formulario que se puede des-
plazar independientemente del resto de la superficie. Por esta razon, el ScrollBox
tiene dos barras de desplazamiento utilizadas para mover 10s componentes inser-
tados. Podemos colocar facilmente otros componentes dentro de un ScrollBox,
como en el caso de un panel. De hecho, un ScrollBox es basicamente un panel con
barras de desplazamiento para mover su superficie interna, un elemento de la
interfaz usado en muchas aplicaciones de Windows. Cuando tenemos un formula-
rio con muchos controles y una barra de herramientas o barra de estado, podria-
mos usar un ScrollBox para cubrir la zona central del formulario, dejando sus
barras de desplazamiento y barras de estado fuera de la zona de desplazarniento.
A1 confiar en las barras de desplazamiento del formulario, se podria permitir que
el usuario moviera la barra de herramientas y la barra de estado fuera de vista
(una situation muy extrafia).
Comandos
La categoria final de componentes no es tan clara como en 10s casos anterio-
res, y tiene que ver con 10s comandos. El componente basico de este grupo es el
T B u t t o n (o boton pulsador, en la jerga de Windows). Mas que botones indepen-
dientes, 10s programadores de Delphi utilizan botones (u objetos T T o o l B u t t o n )
dentro de barras de herramientas (en las primeras fases de Delphi, se usaban
botones de atajo dentro de paneles). Ademas de botones y controles similares, la
otra tecnica clave para invocar comandos es el uso de 10s elementos de menu,
parte de 10s menus desplegables enlazados con 10s menus principales de 10s for-
mularios o 10s menus desplegables locales que se activan mediante el boton dere-
cho del raton.
Los comandos relacionados con el menu o la barra de herramientas entran en
distintas categorias dependiendo de su proposito y de la retroalimentacion que
ofrece su interfaz a 10s usuarios:
Comandos: Elementos del menu utilizados para ejecutar una accion.
Definidores d e estado (state-setters): Elementos del menu utilizados para
activar o desactivar una opcion o para cambiar el estado de un elemento
concreto. Los elementos de estado de estas ordenes normalmente tienen
una marca de verificacion a su izquierda para indicar que estan activos (se
puede conseguir automaticamente este comportamiento usando la propie-
dad A u t o c h e c k ) . Los botones generalmente se pintan en un estado pre-
sionado para indicar el mismo estado (el control ToolButton tiene una
propiedad Down).
Elementos de radio: Elementos del menu que posecn una marca circular y
estan agrupados para representar las selecciones alternativas, como 10s
botones de radio. Para obtener 10s elementos de radio del menu, hay que
configurar sencillamente la propiedad RadioItem como True y esta-
blecer la propiedad GroupIndex para 10s elementos alternatives del menu
con el mismo valor. De un mod0 similar, se pueden agrupar botones de la
barra de herramicntas que Sean mutuamente exclusives.
Enlaces d e didogo: Elementos que hacen que aparezca un cuadro de dia-
logo y normalmente estan indicados por tres puntos (...)despues del texto.
Comandos y acciones
Como se vera, las aplicaciones modernas de Delphi tienden a usar el compo-
nente Act ionlist o su extension ActionManager para gestionar comandos del
mcnu o dc la barra de herramientas. En pocas palabras, se define una serie dc
objetos dc accion y se asocia cada uno de ellos con un boton de la barra de
hcrramicntas ylo un elemento del menu. Se puede definir la ejccucion del coman-
do en un unico lugar pero actualizar tanibien la interfaz de usuario conectandola
simplemente con la accion: el control visual relacionado reflejara automaticamente
cl estado del objeto de accion.
Menu Designer
Si simplemente se necesita inostrar un menu sencillo en la aplicacion, se puede
colocar un componentc MainMenu o PopupMenu en un formulario y hacer
doble clic sobre dl para lanzar el Menu Designer, que muestra la figura 5.5. Se
pueden aiiadir nuevos clementos de menu y proporcionarles una propiedad
Caption,usando un guion (-) para separar las etiquetas de 10s elementos del
menu.
Delphi crea un nuevo componente para cada elemento de menu que se aiiada.
Para dar nombre a cada componente, Delphi usa el titulo que introducimos y
adjunta un numero (de tal mod0 que Open se convierta en Openl). Debido a que
Delphi elimina espacios y otros caracteres especiales del titulo cuando crea el
nombre, si no queda nada, Delphi aiiade la IetraNal nombrc. Finalmente adjunta
el numcro, asi que 10s elementos de separation del menu se denominaran N1,N2
y asi sucesivamente. A1 saber lo que suele hacer Delphi de manera predefinida, se
deberia pensar cn editar el nombre en primer lugar. lo que es necesario si se quiere
acabar con un esquema dc nombrado de componentes sensato.
-
wo
k bled ntab order
I
- --
TRUCO: La versi6n CLX de este ejemplo tiene el m i s m &hgo y estA
disponible como programa QInFocus.
Anclajes de control
Para permitir la creacion de una interfaz de usuario agradable y flexible, con
controlcs que se adapten a1 tamaiio real del formulario, Delphi permite determinar
la posicion relativa de un control con la propiedad Anchors. Antes de que se
introdujera esta caracteristica en Delphi 4, todo control situado en un formulario
tenia unas coordenadas relativas a 10s bordes superior e izquierda, a no ser que se
encontrara alineado con el borde inferior o de la derecha. La alineacion es aconse-
jable para algunos controles, per0 no para todos ellos, en particular para 10s
botones.
A1 usar anclajes. podemos hacer que la posicion de un control sea relativa a
cualquiera de 10s lados del formulario. Por ejemplo, para tener un boton anclado
en la esquina inferior derecha del formulario, colocamos el boton en la posicion
deseada y configuramos su propiedad Anchors como [akRight , a kBottom] .
A1 cambiar el tamaiio del formulario, la distancia a1 boton desde 10s laterales a 10s
que se ancla se mantiene fija, el boton permanecera en su esquina.
Por otra parte, si colocamos un componente grande como un Memo o un ListBox
en medio de un formulario, podemos configurar su propiedad Anchors para
incluir 10s cuatro lados. De este modo, el control se comportara como un control
alineado y aumentara o disminuira segun el tamaiio del formulario, per0 habra
cierto margen entre el y 10s lados del formulario.
- .. --
Figura 5.8. Los controles del ejemplo Anchors se mueven y cambian de tamaiio
automaticamente con el formulario. No se necesita ninghn codigo adicional, solo usar
correctamente la propiedad Anchors.
zard
Wh -..-
Dog
cat
11
hr~~np
ug
ee
Rhi Lzf
She,Sheep
Hare
para cada control del formulario, incluso para aquellos no contiguos al divisor.
Teclas aceleradoras
Desde Delphi 5, no se necesita aiiadir el caracter & a la propiedad Caption
de un elemento de menu, que proporciona una tecla aceleradora automatica si se
omite una. El sistema automatico de teclas aceleradoras de Delphi tambien puede
averiguar si hemos insertado teclas aceleradoras que resulten conflictivas y ajus-
tarlas sobre la marcha. Esto no significa que debamos dejar de aiiadir teclas
aceleradoras personalizadas con el caracter &, porque el sistema automatico uti-
liza sencillamente la primera letra disponible y no sigue 10s esthdares predefinidos.
Podriamos encontrar claves mnemotecnicas mejores que las elegidas por el sis-
tema.
Esta caracteristica es controlada por la propiedad Auto Hotkeys,disponi-
ble en el componente menu principal y en cada uno de 10s menus desplegables y
elementos del menu. En el menu principal, esta propiedad tiene de manera
predefinida el valor maAutomat i c , mientras que en 10s menus desplegables y
en 10s elementos del menu es maparent,de manera que el valor fijado para el
componente menu principal lo utilizaran de forma automatica todos 10s
subelementos, a no ser que tengan un valor especifico maAutomat i c o
maManua1.
El motor que se esconde tras este sistema es el metodo Re thin kHotkeys de
la clase TMenuItem y su compaiiero InternalRethinkHotkeys.Existe
tambien un metodo llamado RethinkLine s,que verifica si un menu desplega-
ble posee dos separadores consecutivos o comienza o termina con un separador.
En todos esos casos, se elimina automaticamente el separador.
Una de las razones por las que Delphi incluye esta caracteristica es el soporte
para traducciones. Cuando se necesita traducir el menu de una aplicacion, resulta
comodo no tener que trabajar con teclas aceleradoras o a1 menos no tener que
preocuparse de 10s posibles problemas entre dos elementos de un mismo menu. A1
tener un sistema que pueda resolver automaticamente problemas similares, conta-
mos en definitiva con una gran ventaja. Otro motivo era el propio IDE de Delphi.
Con todos 10s paquetes que se pueden cargar de forma dinamica, que instalan
elementos del menu en el menu principal del IDE o en 10s menus contextuales, y
con diferentes paquetes cargados en distintas versiones del producto, resulta casi
imposible conseguir teclas aceleradores no conflictivas en cada menu. Por esa
razon, este mecanismo no es un asistente que realiza un analisis estatico de 10s
menus en tiempo de diseiio, sino que se creo para resolver el problema real de la
administracion de 10s menus creados de forma dinamica en tiempo de ejecucion.
ADVERTENCIA: Esta caracteristica es realmente 6til, pero al estar acti-
vada por defecto, puede estropear el c6digo existente. Un problema puede
ser que si se usa el titulo en el cbdigo, 10s caracteres & adicionales pueden
romperlo. Aun asi, el cambio es bastante simple: todo lo que es necesario
hacer es establecer la propiedad AutoHot keys del componente menu
principal como maManual.
Sugerencias flotantes
Otro elemento habitual en las barras de herramienta es la sugerencia de la
barra, tambien llamada sugerencia flotante, un texto que describe brevemente el
boton que se encuentra en ese momento bajo el cursor. Este testo suele mostrarse
en un cuadro amarillo junto al cursor del raton que haya permanecido parado
durante un boton durante una cantidad de tiempo dada.
Para aiiadir sugerencias a la barra de herramientas de una aplicacion, sencilla-
mente hay que definir su propiedad ShowHints como T r u e y escribir texto
para la propiedad Hint de cada boton. Podria desearse habilitar las sugerencias
para todos 10s componentes de un formulario o para todos 10s botones de una
barra de herramientas o panel.
Si queremos tener mayor control sobre como aparecen las sugerencias, pode-
mos usar algunas de las propiedades y eventos del objeto Application. Este
objeto global tiene, entre otras; las siguientes propiedades:
El resultado final es que cada linea del cuadro de lista parece tener una suge-
rcncia especifica, como muestra la figura 5.10. La posicion de la sugerencia se
calcula de tal manera que cubra el testo del elemento actual, extendiendose mas
alla del borde del cuadro de lista.
Figura 5.10. El control ListBox del ejemplo CustHint rnuestra una sugerencia
diferente, dependiendo del elemento de la lista sobre el que se encuentre el raton.
Los tres controladores para este evento de 10s elementos del menu desplegable
Shape son todos ellos distintos, aunque usan un codigo similar:
p r o c e d u r e TForml.EllipselDrawItem(Sender: TObject; ACanvas:
TCanvas ;
ARect : TRect; Selected: Boolean) ;
begin
// f i j a e l c o l o r d e l f o n d o y l o p i n t a
i f Selected then
ACanvas.Brush.Color : = clHighlight
else
ACanvas.Brush.Color : = clMenu;
ACanvas .FillRect (ARect);
// d i b u j a l a e l i p s e
ACanvas.Brush.Co1or : = clwhite;
InflateRect (ARect, -5, -5) ;
ACanvas.Ellipse (ARect.Left, ARect.Top, ARect.Right,
ARect .Bottom) ;
end;
-- .. .
NOTA: Para acomodar el cada vez mayor nhero de egtados en el estiko de
interfaz de usuario de Windows 2000 , belphi inahye el evento
OnAdvancedDraw I tern para 10s menu:
Para compilar la version CLX de este codigo, hemos aiiadido la funcion RGB
descrita anteriormente. El codigo usado para dibujar 10s elementos no es especial-
mente complejo. Sencillamente obtenemos el color asociado con el elemento, lo
establecemos como el color de la fuente y despues dibujamos el testo:
p r o c e d u r e TODListForm.ListBoxlDrawItem(Control: Twincontrol;
Index: Integer;
Rect : TRect; State: TOwnerDrawState) ;
begin
w i t h Control as TListbox d o
begin
// elimina
Canvas. FillRect (Rect);
// dibuja el elemento
Canvas.Font.Color : = TColor ( I t e m s - O b j e c t s [Index]);
Canvas.TextOut(Rect.Left, R e c t - T o p , Listboxl.Items[Index]);
end ;
end :
El sistema ya establece el color de fondo adecuado, de mod0 que el elemento
seleccionado aparezca de forma adecuada, aunque no aiiadamos codigo adicional.
Aun mas, el programa permite aiiadir nuevos elementos a1 hacer doble clic sobre
el cuadro de lista:
procedure TODListForm.ListBoxlDblClick(Sender: TObject);
begin
i f ColorDialogl.Execute t h e n
Addcolors ([ColorDialogl.Color]);
end;
Si se intenta usar esta capacidad, se vera que algunos de 10s colores aiiadidos
se transforman en nombres de color (una de las constantes de color de Delphi),
mientras que otros se convierten en numeros hexadecimales.
Ft
definidos en la propiedad C o l u m n s :
W bbb
-
ADVERTENCIA: El conti01 Listview no tieno ep CLX fas vjstqs de icom
pequdoa y g r a d e s . En Qt,este tip0 de apariencia esddisponi.ble graciaa a
otro a m p a e f i t e . el IcanView. -
El programa posee un menu que podemos emplear para escoger una de las
distintas vistas que soporta el control ListView y para aiiadir casillas de verifica-
cion a 10s elementos, como en un control CheckListBox. Se pueden ver las distin-
tas combinaciones de estos estilos en la figura 5.12.
Otra caracteristica importante, que es habitual en la vista detallada o de infor-
me del control, consiste en dejar que un usuario clasifique 10s elementos de una de
las columnas. En la VCL, esta tecnica requiere tres operaciones. La primera es
cstablecer la propiedad SortType de ListView como st Both o st Data. De
este modo, la ListView realizara la clasificacion no basandose en 10s titulos, sin0
llamando a1 evento Oncompare de cada uno de 10s dos elementos que ha de
clasificar.
I-
Borland
Develo..
Delohi Delohi Delahi
ClienIfS ... Develo ... Informant
Masterinq
Delohi
The
Delphi ... I
in Java fib ~ i mr ~ l p
Borland Develooers Conference ...
.:-Delohi ClienVServer
00 Pelohi Develooer's Handbook
n ~ $ + D e l o hInformant
i
nQ Mastering D e b h i
n & T h e D e b h i Maaanne
q -Thinkina in Java
OW marco@marcocantu.com
OQwww.borland.com
O.dwww.marcocanlu.com
Aunque este ejemplo no incluye todas las caracteristicas, muestra parte del
potencial del control ListView. Tambien hemos activado la caracteristica de "se-
guimiento activo", que permite que la vista en lista resalte y subraye el elemento
que se encuentra bajo el raton. Las propiedades mas relevantes de ListView pode-
mos verlas en su descripcion textual:
object ListViewl: TListView
Align = alClient
Columns = <
item
Caption = 'Referencia '
Width = 2 3 0
end
item
Caption = 'Autor'
Width = 1 8 0
end
item
Caption = 'Pais '
Width = 8 0
end>
Font.Height = - 1 3
Font .Name = 'MS Sans Serif'
Font.Style = [fsBold]
FullDrag = True
Hideselection = False
HotTrack = True
HotTrackStyles = [htHandPoint, htUnderlineHot]
SortType = stBoth
Viewstyle = vsList
OnColumnClick = ListViewlColumnClick
Oncompare = ListViewlCompare
OnMouseDown = ListViewlMouseDown
end
Para crear la version CLX de este ejemplo, QRefList, hub0 que emplear solo
una de las listas de imagenes y desactivar 10s menus de imagenes pequeiias e
imagenes grandes, dado que ListView esta limitado a 10s estilos de vista detallada
y en lista.
Los iconos grandes y pequeiios estan disponibles en un control diferente, deno-
minado Iconview. Como ya se comento, el soporte de clasificacion ya se encuen-
tra ahi, lo que podria haber ahorrado la mayoria del codigo de este ejemplo.
Un arbol de datos
Ahora que hemos visto un ejemplo basado en ListView, podemos examinar el
control TreeView (arbol de datos). El TreeView posee una interfaz de usuario que
es flexible y potente (con soporte para editar y arrastrar elementos). Tambien es
estandar, porque es la interfaz de usuario del explorador de Windows. Existen
propiedades y diversos modos de personalizar el mapa de bits de cada linea o de
cada tipo de linea.
Para definir la estructura de nodos del TreeView en tiempo de diseiio, podemos
emplear el editor de propiedades TreeView Items:
de
(3 eded
a rkd
. .
Sin cmbargo, cn este caso, hcmos decidido cargarlo en 10s datos del TreeView
a1 arrancar, de un mod0 similar a1 del ultimo ejemplo.
La propicdad Items del componente TrccView time muchas funciones mieni-
bro que podcmos emplear para modificar la jerarquia de cadenas. Por ejcmplo,
podemos crcar un arb01 de dos niveles con las siguientes lineas:
var
Node: TTreeNode;
begin
Node : = TreeViewl. Items .Add (nil, 'First level');
TreeViewl. Items .Addchild (Node, I Second level1) ;
Utilizando cstos dos metodos (Add y Addchild), podemos crear una com-
pleja estructura en tiempo de ejecucion. Para cargar la informacion, podemos
emplear dc nuevo una StringList en tiempo de ejecucion, cargar un archivo de
testo con la informacion y analizar la estructura gramaticalmente.
Sin embargo. dado que el control Treeview posee un mdtodo LoadFromFile,
10s ejemplos DragTree y QDragTree utilizan el siguiente codigo, mucho mas
sencillo:
procedure TForml.FormCreate(Sender: TObject);
begin
TreeViewl.LoadFromFile (ExtractFilePath
(Application.ExeName) +
'TreeText. txt ' ) ;
end;
3 US Hsadquarle~s
Q Board d Dnectors
-
Mon~caRoss
Sales
Fat E j s t
Steve Fubens
Palls
Tck1o
Sirmoilre
Terty Merks
Q Sidney
John Calgary
Matk R m n
!3 J o h Rcitsr
Ian Green
El AdmLi~sl~ation
Figura 5.13. El ejernplo DragTree despues de cargar 10s datos y expandir las ramas.
En lugar dc cspandir 10s elementos de 10s nodos uno a uno. tambien se puedc
usar cl mcnu File>Expand All de este programa. quc llama a1 metodo
FullExpand del control TreeView o e.jecuta el codigo cquivalente (en este caso
especifico dc un arb01 con un elemento raiz):
TreeViewl. Items [0] .Expand ( T r u e );
Ademas de cargar 10s datos, el programa 10s guarda a1 finalizar y asi hacc que
10s cambios Sean permanentes. Tambien hay unos cuantos elementos de menu
para personalizar la fucnte del control TreeView y modificar otros sencillos
paramctros. La caracteristica especifica implementada en cste ejemplo es el so-
porte para arrastrar elementos y subarboles enteros. Hemos definido la propiedad
DragMode del componente como dmAutomatic y escrito 10s controladores de
eventos para 10s eventos OnDragOver y OnDragDrop.
En el primer0 de 10s dos controladores, el programa se asegura que el usuario
no esta intentando arrastrar un elemento sobre un elemento hijo (que seria despla-
zado junto con el elemento y originaria una repeticion infinita):
p r o c e d u r e TForml.TreeViewlDragOver(Sender, Source: TObject;
X, Y: Integer; State: TDragState; var Accept: Boolean);
var
TargetNode, SourceNode: TTreeNode;
begin
TargetNode : = TreeViewl GetNodeAt (X, Y ) ;.
/ / a c e p t a a r r a s t r e d e s d e e l mismo
i f (Source = Sender) a n d (TargetNode <> nil) t h e n
begin
Accept : = True;
/ / determina origen y destino
SourceNode : = TreeViewl-Selected;
// busca la cadena padre destino
while (TargetNode.Parent <> nil) and (TargetNode <>
SourceNode) d o
TargetNode : = TargetNode.Parent;
/ / si se encuentra el origen
if TargetNode = SourceNode then
/ / no permite el arrastre a un hijo
Accept : = False;
end
else
Accept : = False;
end;
El efecto conseguido por este codigo es que (a escepcion del caso concreto que
se necesita evitar) un usuario puede arrastrar un elemento del TreeView a otro. Es
sencillo escribir el codigo necesario para desplazar 10s elementos, porque el con-
trol TreeView ofrece soporte para dicha operation, mediante el metodo MoveTo
de la clase TTreeNode.
procedure TForml.TreeViewlDragDrop(Sender, Source: TObject; X,
Y: Integer) ;
var
TargetNode, SourceNode: TTreeNode;
begin
TargetNode : = TreeViewl .GetNodeAt ( X , Y) ;
if TargetNode <> nil then
begin
SourceNode : = TreeViewl.Selected;
SourceNode.MoveTo (TargetNode, naAddChildFirst);
TargetNode .Expand (False);
TreeViewl.Selected : = TargetNode;
end;
end;
- - - - - - . - - - -- - - --- -- - --
NOTA: Entre las demos incluidas con Delphi, hay una muy interesante que
muestra un control TreeView de dibujo personalizado. El ejemplo se en-
cuentra en el subdirectorio CustomDraw.
interface
uses
SysUtils, Classes,
{SIFDEF LINUX]
Qt, Libc, QGraphics, QControls, QForms, QDialogs,
QStdCtrls, QComCtrls, QMenus, QTypes, QGrids;
{SENDIF]
{ S I F D E F MSWINDOWS]
Windows, Graphics, Controls, Forms, Dialogs,
StdCtrls, ComCtrls, Menus, Grids;
{SENDIF)
{ $ IFDEF MSWINDOWS]
{SR *.dfm]
{SENDIF]
El programa establece esta referencia de clase antes de crear nodos para cada
tipo, por ejemplo, con un codigo como el siguiente:
var
MyNode : TMyNode ;
begin
CurrentNodeClass : = TMyNode;
MyNode : = TreeViewl. Items .Addchild (nil, 'item' + IntToStr
(nValue)) as TMyNode;
MyNode.ExtraCode : = nValue;
Pagecontrols y Tabsheets
Como es habitual, cn lugar de repetir la lista de propiedades y metodos dcl
sistcma dc ayuda del componente PageControl. hemos creado un ejenlplo quc
bosqueja sus capacidadcs y permite modificar su comportamiento en ticmpo de
ejccucion. El cjcmplo, denominado Pagcs, tienc un PageControl con tres paginas.
La estructura del PageControl y de otros componentes clave se muestra en el
listado 6.1.
Fijese en que las solapas estan conectadas a mapas de bits mediante un control
ImageList y en que algunos controles usan la propiedad A n c h o r s para mantener
una distancia fija con 10s bordes derecho o inferior del formulario. Aunque el
formulario soporte un reajuste del tamaiio (esto hubiera resultado mucho mas
complicado de realizar con tantos controles), las posiciones pueden variar cuando
las solapas aparecen en varias lineas (simplemente aumenta la longitud de 10s
titulos) o en el lateral izquierdo del formulario.
Cada objeto T a b S h e e t tiene su propio C a p t i o n , que aparecera en la sola-
pa de la hoja. En tiempo de diseiio, podemos usar el menu local para crear fichas
nuevas y para movernos por ellas. Podemos ver el menu local del componente
P a g e C o n t r o 1 en la figura 6.1, junto con la primera ficha. Esta ficha contiene
un cuadro de lista y un pequeiio titulo y comparte dos botones con las otras fichas.
Si colocamos un componente en una ficha, esta disponible solo en dicha ficha.
Para tener el mismo componente (en este caso, dos botones de mapas de bits) en
cada ficha, sin duplicarlo, sencillamente hay que colocar el componente en el
formulario, fuera del Pagecontrol (o antes de alinearlo con la zona de cliente) y,
a continuacion, moverlo hacia delante de las fichas, mediante la orden B r i n g
To F r o n t del menu local del formulario. Los dos botones que hemos colocado
en cada ficha se pueden usar para mover las fichas adelante y atras, y ofrecen una
alternativa a1 uso de las solapas. Veamos el codigo asociado a uno de ellos:
procedure TForml.BitBtnNextClick(Sender: TObject);
begin
PaqeControll.SelectNextPaqe (True);
end;
I Paged ) @ Tebr Sue I
.
A Tabs T& I
Ckk on lha Mbcx
a, to change papc
New_P q e
Mxt Pagl
Control
Add tu R_epo.;itory.. .
1
frcw as Text
TextDFM
- .-
Figura 6.1. La prlmera h o p de Pagecontrol del ejemplo Pages con su menu local.
- I
El otro boton llama a1 mismo procedimiento y pasa False como su parametro
para selcccionar la ficha anterior. Fi-jese cn que no es neccsario verificar si esta-
mos cn la primcra o en la ultima ficha, porque el metodo SelectNext Page
considera quc la ultima ficha es la quc csta antes de la primcra y nos llevara
directamcnte cntre estas dos fichas.
Ahora podemos centrarnos de nucvo en la primera ficha. Posce un cuadro de
lista, que cn tiempo de ejecucion contiene 10s nombres de las solapas. Si un usua-
rio hace clic sobre un elemento del cuadro dc lista, la pagina actual cambia. Este
cs el terccr mctodo del que disponemos para cambiar de ficha (despues de las
solapas y de 10s botones Next y Previous). El cuadro de lista se rcllena con cl
metodo Formcreate, asociado con el evento Oncreate dcl formulario, y
copia cl titulo de cada ficha (la propicdad Page contiene una lista de objetos
Tabsheet):
for I : = 0 to Pagecontroll. Pagecount - 1 do
ListBoxl.Items.Add (PageContro1l.Pages.Caption);
Cuando hacemos clic sobre un elemento de la lista, podemos seleccionar la
pagina correspondiente:
procedure TForml.ListBoxlClick(Sender: TObject);
begin
Pagecontroll-Activepage : = Pagecontroll-Pages
[ListBoxl.ItemIndex];
end;
Figura 6.2. La segunda pagina del ejemplo puede ser usada para ajustar el tamaiio y
posicion de las pestaiias, que aqui se muestran a la izquierda de la pagina.
Con este codigo, podemos cambiar cl ancho y la altura de cada solapa (recuer-
de que 0 significa que el tamaiio se calcula automaticamente a partir del espacio
que ocupa cada cadena). Podemos escoger tener diversas lineas de solapas o dos
pequeiias flechas para recorrer la zona de la solapa. y podemos moverlas al late-
ral izquierdo de la ventana. El control tambien permite situar las solapas en la
parte inferior o a la derecha, pero no nuestro programa, porque de ese mod0 la
colocacion de 10s otros controles resultaria bastante compleja. Tambien podemos
ocultar la ultima solapa del Pagecontrol, que corresponde a1 componente
T a b s h e e t 3 . Si ocultamos una de las solapas definiendo su propiedad
T a b V i s i b l e como F a l s e , no podemos alcanzar dicha solapa haciendo clic
sobre 10s botones Next ni Previous, que se basan en el metodo S e l e c t N e x t Page.
En lugar de eso, habria que usar la funcion F i n d N e x t P a g e , que seleccionara
esa pagina incluso aunque la pestaiia no sea visible. La Nueva version del contro-
lador de eventos OnCl i c k del boton Next muestra una llamada a1 metodo
FindNext Page:
procedure TForml.BitBtnNextClick(Sender: TObject);
begin
PageControl1.ActivePage : = Pagecontroll-FindNextPage (
PageControll.ActivePage, True, False);
end;
Finalmente, el ultimo boton, Add Page, nos permite aiiadir una nueva hoja de
solapa a1 control de ficha, aunque el programa no aiiada ningun componente a la
misma. El objeto hoja de solapa (en blanco) se crea usando el control ficha como
su propietario, per0 no funcionara a no ser que tambien se configure la propiedad
P a g e c o n t r o l . Sin embargo, antes de hacer esto, deberiamos hacer que la nue-
va solapa fuera visible. Veamos el codigo:
procedure TForml.BitBtnAddClick(Sender: TObject);
var
strcaption: string;
NewTabSheet: TTabSheet;
begin
strcaption : = ' N e w t a b ' ;
if InputQuery ( ' N e w t a b ' , ' T a b C a p t i o n ' , strcaption) then
begin
/ / s e a d a d e una n u e v a f i c h a e n b l a n c o a 1 c o n t r o l
NewTabSheet : = TTabSheet-Create (PageControll);
NewTabSheet.Visible : = True;
NewTabSheet.Caption : = strcaption;
NewTabSheet-Pagecontrol : = PageControll;
PageControl1.ActivePage : = NewTabSheet;
// s e a d a d e a ambas l i s t a s
Memol. Lines .Add (strcaption);
ListBoxl .Items .Add (strcaption);
end;
end;
Dcspues de mostrar las nuevas solapas, tenemos que actualizar la imagen para
que se corresponda con la primera solapa. Para esto, el programa llama a1 metodo
concctado con el evento OnChange de Tabcontrol, que carga el archivo
correspondiente a la solapa actual en el componente imagen:
procedure TFormBmpViewer.TabControllChange(Sender: TObject);
begin
Imagel.Picture.LoadFromFi1e (TabControll.Tabs
[TabControll.TabIndex]);
end;
Este programa pega el mapa de bits del portapapeles cada vez que cambiamos
de solapa. El programa almacena solo una imagen cada vez y no tiene forma de
almacenar el mapa de bits Clipboard. Sin embargo, si el contenido del portapapeles
cambia y el formato de mapa de bits ya no esta disponible, la solapa Clipboard se
borra automaticamente (como se puede ver en el listado anterior). Si no quedan
mas solapas, el componente Image esta oculto.
Una imagen tambien puede eliminarse utilizando una de las dos ordenes del
menu: Cut o Delete . Cut elimina la solapa despues de hacer una copia del
mapa de bits en el portapapeles. En la practica, el metodo Cut lClick no hace
nada a parte de llamar a 10s metodos CopylClick y DeletelClick. El
metodo CopylClick se encarga de copiar la imagen actual a1 portapapeles,
Delete1C 1ic k sencillamente elimina la solapa actual. Veamos su codigo:
procedure TFormBmpViewer.CopylClick(Sender: TObject);
begin
Clipboard.Assign (1magel.Picture.Graphic);
end;
El programa tiene tambien soporte para imprimir el mapa de bits actual, tras
haber mostrado un formulario de vista previa de la ficha, en el que el usuario
puede seleccionar la escala apropiada. Esta parte adicional del programa no se
comenta en detalle, per0 el codigo esta ahi para quien desee examinarlo.
Tras activar el boton Back comiin. el programa cambia la ficha activa y, por
ultimo, dcsplaza la park grafica a la nucva ficha. Como este codigo ha de repetir-
se para cada boton, lo hemos colocado en un metodo despues de aiiadir unas
cuantas caractcristicas adicionales. Este es cl codigo actual:
p r o c e d u r e TForml.btnNextlClick(Sender: TObject);
begin
i f Check1nprise.Checked then
MoveTo (TabSheet2)
else
M o v e T o ( T a b S h e e t 3 );
end;
Con este codigo, el usuario puede volver varias fichas a t r b hasta que la lista
quede vacia, punto en el que el boton Back se desactiva. La complicacion a la que
hemos de enfrentarnos es que mientras nos movemos desde una ficha concreta,
sabemos que ficha es su ficha "posterior" y "anterior", per0 no sabemos de que
ficha se procede, porque hay diversas rutas para llegar a una ficha. Solo podemos
retroceder de forma segura, si guardamos la pista de 10s movimientos con una
lista.
El resto del codigo del programa, que simplemente muestra algunas direccio-
nes de sitios Web, es muy sencillo. Lo bueno es que podemos reutilizar la estruc-
tura de desplazamiento de este ejemplo en nuestros programas y modificar solo la
parte grafica y el contenido de las paginas. En realidad, como la mayoria de las
etiquetas de 10s programas muestran direcciones HTTP, un usuario puede hacer
clic sobre dichas etiquetas para abrir el explorador predefinido para que muestre
esa pagina. Para ello, se extrae la direccion HTTP de la etiqueta y se llama a la
funcion ShellExecute.
procedure TForml.LabelLinkClick(Sender: TObject);
var
Caption, StrUrl: string;
begin
Caption : = (Sender as TLabel) .Caption;
StrUrl : = C o p y (Caption, Pos ('http://', Caption), 1000) ;
ShellExecute (Handle, 'open ', PChar (StrUrl) , ' ', ' ', sw-Show) ;
end;
Este metodo esta enganchado a1 evento oncl i c k de varias etiquetas del for-
mulario, que se han transformado en enlaces a1 configurar su cursor como una
mano. Esta es una de las etiquetas:
o b j e c t Label2 : TLabel
Cursor = crHandPoint
Caption = 'Main site: http://www. borland. corn'
OnClick = LabelLinkClick
end
El control ToolBar
Para crear una barra de herramientas, Delphi incluye un componente ToolBar
especifico, que encapsula el control comun de Win32 correspondiente o el widget
Qt correspondiente en VisualCLX. Dicho componente proporciona una barra de
herramientas, con sus propios botones y tiene muchas capacidades avanzadas.
Para usarlo, lo colocamos en un formulario y, a continuacion, usamos el editor de
componentes (el menu de metodo abreviado activado con un clic del boton dere-
cho del raton) para crear unos cuantos botones y separadores.
L a b a r r a de herramientas esta compuesta por objetos de l a clase
TToo lBut ton.Dichos objetos tienen una propiedad bbica, Style,que deter-
mina su comportamiento:
El estilo tbsButton: Indica un boton pulsador estandar.
El estilo tbscheck: Indica un boton con el comportamiento de una casilla
de verificacion, o de un boton de radio si el boton esta agrupado con otros
en su bloque (determinado por la presencia de separadores).
El estilo tbsDropDown: Indica un boton desplegable, una especie de cua-
dro combinado. La parte desplegable se puede implementar facilmente en
Delphi conectando un control PopupMenu a la propiedad Dropdown-
Menu del control.
Los estilos tbsseparator y tbsDivider: Indican separadores con lineas
verticales diferentes o sin ellas (dependiendo de la propiedad Flat de la
barra de herramientas).
Para crear una barra de herramientas grafica, podemos aiiadir un componente
ImageLis t a1 formulario, cargar algunos mapas de bits en el y a continuacion,
conectar la ImageList con la propiedad Images de la barra de herramienta. Por
defecto, las imagenes se asignaran a 10s botones en el orden en el que aparecen,
per0 podemos cambiar facilmente este comportamiento fijando la propiedad
ImageIndex de cada boton de la barra de herramientas. Podemos preparar
listas de imagenes adicionales para condiciones especiales de 10s botones y asig-
narlas a las propiedades DisabledImages y HotImages de la barra de
herramientas. El primer grupo se usa para 10s botones desactivados, el segundo
para el boton actual que esta bajo el raton.
tr -
El ejemplo RichBar
Como ejemplo del uso de una barra de herramientas, hemos creado la aplica-
cion RichBar, que tiene un componente RichEdi t con el que se puede trabajar
utilizando la barra de herramientas. El programa tiene botones para cargar y
guardar archivos, para las operaciones de copiar y pegar y para cambiar algunos
de 10s atributos de la fuente en uso.
Pretendemos centrarnos aqui en caracteristicas especificas de la ToolBar utili-
zadas por el ejemplo y visibles en la figura 6.5. Esta barra de herramientas tiene
botones, separadores e incluso un menu desplegable y dos cuadros combinados.
This document explains how do you create a simple editor based on the RichEdit control using Delphi
I
6 The program has a toolbar andmplements a number of features, mcluding a complete scheme for
opening and saving the text Ues, drscussed m this document. In fact, we want to be able to ask the
user to save any modified Ue before opening a new one, to avoid losmg any changes. Sounds k e a
professional apphcaboh doesn't it?
1 l ~ i l eOperations
The most complex part of this program is implemenhng the commands of the File pull-down menu-
New. Ope& Save, and Save As. In tach case. we need to track Mether the current Ue has changed,
C I A ,,l..:C:rLr. nr- AA..ld .,. *.I., ...,- .,..,
a- &- CI, -,-L i,- ,
,-,
- , 6,.
,i'
Figura 6.5. La barra de herramientas del ejemplo RichBar.
Los distintos botones implementan caracteristicas, una de las cuales consiste
en un esquema completo para abrir y guardar archivos de texto (se pide a1 usuario
que guarde cualquier archivo modificado antes de abrir uno nuevo, para no perder
ningun cambio). La parte del programa encargada de la administracion de archi-
vos es bastante compleja, per0 vale la pena explorarla, dado que muchas aplica-
ciones basadas en archivos utilizan un codigo similar.
Ademas de las operaciones de archivo, el programa soporta las operaciones de
copiar y pegar, y la administracion de fuentes. Para las operaciones de copiar y
pegar no es necesario una interaccion real con el portapapeles, dado que el com-
ponente puede controlarlas con ordenes sencillas como:
Cuando seleccionamos una nueva fuente del cuadro combinado, ocurre lo con-
trario. El texto del elemento en uso del cuadro combinado se asigna como nombre
de la fuente para cualquier texto seleccionado en el control RichEdit:
En el ejemplo RichBar, hay una barra de estado con tres paneles: para suge-
rencias sobre ordenes: el estado de la tecla BloqMayiis y la posicion de edicion
activa. El componente StatusBar del ejemplo tiene en realidad cuatro paneles (es
necesario definir el cuarto para delimitar la zona del tercer panel). El ultimo panel
siempre es lo suficientemente grande como para cubrir la superficie que queda en
la barra de estado.
l h s document explams how do you create a sunple ecttor based on the F x h a t control usmg Delph
6 The program has a toolbar and unplements a number of feahues, mcludmg a complete scheme for
opening and savmg the text files, hscussed m h s document In fact, we want to be able to ask the
user to save any modified file before opening a new one, to avold losmg any changes Sounds Wte a
profess~onalapphcahon doesn't ~ t ?
1 I~ile
Operations
The most complex p a t o f h s program 1s rnplemenhng the commands of the Fie pull-down menu-
*----. .-
New, Open Save, and Save As In each case, we need to track whether the current file has changed, ,
.LA CIA -1..
rCIA- Ihs
.c.* L A -
- s c k t m to the dpboard
.Z," -L-..IA .L*
7
*-..* .LA CIA
7-
h-- *L- A-a-*-.
s
Figura 6.6. La barra de estado del ejernplo RichBar muestra una descripcion mas
detallada que la sugerencia contextual
,. . .- . -
I
< . >
Por ultimo, el programa usa el tercer panel para mostrar la posicion actual de
cursor (medida en lineas y caracteres por linea) cada vez que cambia la seleccion.
Debido a que 10s valores C a r e t P O S se basan en cero (es decir, la esquina supe-
rior derecha es la linea 0, caracter O), hemos decidido aiiadir uno a cada valor
para que resulten mas razonables para un usuario que desconozca este detalle:
procedure TFormRichNote.RichEditSelectionChange(Sender: TObject);
begin
...
// a c t u a l i z a l a p o s i c i o n e n l a b a r r a d e e s t a d o
Status~ar. Panels [sbpposition] .Text : = Format ( ' % d / % d l ,
[RichEdit .CaretPos .Y + 1, RichEdit .CaretPos.X + 11 ) ;
end;
Temas y estilos
En el pasado, un sistema operativo basado en una interfaz grafica determinaba
todos 10s elementos de la interfaz de usuario para 10s programas que se ejecuta-
ban sobre el.
~ltimamente,Linux ha comenzado a permitir que 10s usuarios personalicen la
apariencia tanto de la ventana principal de las aplicaciones como de 10s controles
de la interfaz de usuario como 10s botones. La misma idea (que suele referirse
como slnn, pie1 o tema) ha aparecido en numerosos programas con un impact0 tan
positivo que incluso Microsoft ha comenzado a integrar este concept0 (a1 princi-
pio en programas y despues en todo el sistema operativo).
Estilos CLX
Como ya se ha comentado. en Linux (para ser mas precisos en X Window) el
usuario generalmente puede escoger el estilo de la interfaz de usuario de 10s
controles. Este enfoque esta completamente soportado por Qt y por el sistema
KDE que se basa en el. Qt ofrece unos cuantos estilos basicos, como la apariencia
de Windows, el estilo Motif y otros. Un usuario tambien puede instalar nuevos
estilos en el sistema y ponerlos a disposicion de las aplicaciones.
- -
- --- -- - .- - - - - - --
El efecto es que a1 hacer doble clic sobre el cuadro de lista, se puede cambiar
el estilo actual de la aplicacion y comprobar inmediatamente su efecto en panta-
lla, como muestra la figura 6.7.
Temas de Windows XP
Con la aparicion de Windows XP, Microsoft ha creado una nueva version,
independiente, de la biblioteca de controles habituales. La antigua biblioteca si-
gue estando disponible por cuestiones de compatibilidad, de manera que un pro-
grama que se ejecute sobre XP puede escoger cual de las dos bibliotecas usar. La
principal difercncia de la nueva biblioteca es que no tiene un motor de representa-
cion fijo, sino que confia en cl motor de temas de XP y delcga la interfaz de
usuario de 10s controles sobre cl tema actual.
Figura 6.7. El prograrna StylesDernos, una aplicacion para Windows que tiene en
este momento un poco habitual aspect0 Motif.
[F]
Figura 6.8. El ejemplo Pages usa el tema de Windows XP actual, ya que incluye un
archivo de manifiesto.
El Componente ActionList
La arquitcctura de eventos de Delphi es mug abierta: se puede escribir un
scncillo controlador de eventos y conectarlo a 10s eventos O n C l i c k de un boton
de la barra dc hcrramientas y a un menil. Se puede incluso conectar el mismo
controlador de eventos a diferentes botones o elementos de menu, dado que el
controlador puede utilizar el parametro S e n d e r para referirse a1 objeto que
lanzo el evento. Es algo mas dificil sincronizar el estado de 10s botones de la barra
de herramientas y 10s elementos de menu. Si tenemos un elemento de menu y un
boton de la barra de herramientas y ambos accionan la misma operacion, cada vez
que se activa dicha operacion, hay que aiiadir la marca de comprobacion a1 ele-
mento de menu y cambiar el estado del boton para que aparezca como pulsado.
Para superar este problema, Delphi incluye una estructura de gestion de even-
tos basada en acciones. Una accion (u orden) indica tanto la operacion que se
realiza cuando se pulsa un elemento de menu o boton que determina el estado de
todos 10s elementos conectados a dicha accion. La conexion de la accion con la
interfaz de usuario de 10s controles enlazados resulta muy importante y es el
ambito en el que podemos entender las autenticas ventajas de esta estructura.
En esta estructura de manipulacion de eventos participan diversos agentes. La
accion principal la realizan 10s objetos de la accion. Un objeto de accion tiem un
nombre, como cualquier otro componente y otras propiedades que se aplicaran a
10s controles enlazados (llamados tambien clientes de la accion). Entre dichas
propiedades estan C a p t i o n , la representacion grafica ( I m a g e I n d e x ) , el esta-
do ( C h e c k e d , E n a b l e y V i s i b l e ) y la information para el usuario ( H i n t y
H e l p c o n t e x t ) . Tambien estan S h o r t c u t y una lista de S e c o n d a r y S h o r t -
C u t s , la propiedad A u t o c h e c k para acciones de dos estados, el soporte de
ayuda y una propiedad C a t e g o r y utilizada para organizar las acciones en gru-
pos logicos.
La clase basica para todos 10s objetos de accion es TBas i c A c t i o n , que
introduce el comportamiento abstract0 fundamental de una accion, sin ningun
enlace especifico ni correccion (ni siquiera a elementos de menu ni controles). La
clase derivada TC o n t a i n e dAc t i o n introduce propiedades y metodos que per-
miten que las acciones aparezcan en una lista de acciones o administrador de
acciones. La clase derivada TCus t omAc t i o n introduce soporte para las pro-
piedades y metodos de 10s elementos de menu y controles que estan enlazados a
10s objetos de accion. Por ultimo, esta la clase derivada lista para ser usada,
TAction.
Cada objeto de accion esta conectado a uno o mas objetos clientes a traves de
un objeto A c t i o n L i n k. Como indica su propiedad A c t i o n , posiblemente va-
rios controles de diferentes tipos pueden compartir el mismo objeto de accion.
Tecnicamente, 10s objetos A c t i o nL i n k mantienen una conexion bidireccional
entre el objeto cliente y la accion. El objeto ~ ci otn ~ i n k es necesario porque la
conexion funciona en ambas direcciones. Una operacion realizada sobre el objeto
(como un clic) se reenvia a1 objeto de accion y origina una llamada a su evento
O n E x e c u t e ; y una actualizacion del estado del objeto de accion se refleja en 10s
controles clientes conectados. En otras palabras, uno o mas controles cliente pue-
den crear un ActionLink, que se registra con el objeto de accion.
No se deberian definir las propiedades de 10s controles de cliente que se conec-
ten a una accion, ya que esta accion sobrescribe 10s valores de propiedad de 10s
control& de cliente. Por esa razon, normalmente se deberian escribir primer0 las
acciones y despues crear 10s elementos de menu y 10s botones que se quieran
conectar con ellas. Fijese en que cuando una accion no tiene un controlador
O n E x e c u t e , el control de cliente se desactiva automaticamente (o aparece en
gris), a menos que se haya definido la propiedad D i s a b l e I f N o H a n d l e r como
False.
Normalmente, 10s controles de cliente que se conectan a acciones son elemen-
tos de menu y diversos tipos de botones (botones pulsador, casillas de verifica-
cion, botones de radio, botones de velocidad, botones de la barra de herramientas
y similares), per0 tambien se pueden crear nuevos componentes que encajen en
esta estructura. Incluso se pueden definir nuevas acciones y nuevos objetos de
accion de enlace. Ademas de un control de cliente, algunas acciones pueden tener
tambien un componente destino. Algunas acciones predefinidas se conectan con
un componente destino especifico. Otras acciones buscan automaticamente un
componente destino en el formulario que soporte la accion especificada, empe-
zando por el control activo.
Por ultimo, 10s objetos de accion se encuentran dentro de un componente
A c t i o n L i s t o A c t i o n M a n a g e r , la unica clase de la estructura basica que
aparece en la Component Palette. La lista de acciones recibe las acciones
ejecutadas que no controlan 10s objetos de accion especificos y activa
O n E x e c u t e A c t i o n . Si la lista de acciones no controla la accion, Delphi hace
una llamada a1 evento O n E x e c u t e A c t i o n del o b j e t o A p p 1 i c a t i o n . El com-
ponente ActionList tiene un editor especial que se puede utilizar para crear diver-
sas acciones, como se muestra la figura 6.9.
Figura 6.9. El editor del cornponente ActionList, con una lista de acciones predefinidas
que se pueden w a r .
-
, - -
NOTA: Las teclas de mktodo abreviado e s t h almacenadas en 10s archivos
DFM usando numeros de teclas virtuales, entre 10s que hay valores para las
teclas Control y Alt. En este y otros listados a lo largo del libro, se han
reemplazado 10s numeros por 10s valores literales, que se insertan entre 10s
simbolos < y >.
-
1 Calmofies: Actions:
BQ
Las tres acciones predeterminadas del menu Edit no tienen controladores aso-
ciados, per0 estos objetos especiales tienen un codigo interno para realizar la
accion relacionada con el control de edicion o de memo activo. Estas acciones se
activan y desactivan tambien a si mismas, dependiendo del contenido del
portapapeles y de la existencia de texto seleccionado en el control de edicion
activo. La mayoria de las otras acciones tienen un codigo personalizado, menos
en el caso del objeto NoAction. A1 no tener codigo, el elemento de menu y el
boton asociado a esta orden estan desactivados, aunque la propiedad Enabled
de esta accion esta definida como True.
Hemos afiadido a1 ejemplo y a1 menu Test otra accion que activa el elemento
de menu conectado a1 objeto NoAct ion:
procedure TForml.ActionEnableExecute(Sender: TObject);
begin
NoAction.Disable1fNoHandler : = False;
NoAction.Enabled : = True;
ActionEnable.Enabled : = False;
end ;
Por i~ltimo,hemos aiiadido una accion especial que comprueba el objeto remi-
tente del controlador de eventos de la accion y obtiene otra informacion sobre el
sistema. Ademis de mostrar la clase y nombre del objeto, hemos aiiadido un
codigo que accede a1 objeto de la lista de acciones, bisicamente para mostrar
como acceder a esta informacion:
procedure TForml.ActionSenderExecute(Sender: TObject);
begin
Memol .Lines .Add ( ' C l a s e r e m i t e n t e : ' + Sender .ClassName);
Memol.Lines.Add ( ' N o m b r e d e l r e m i t e n t e : ' + (Sender as
TComponent) .Name) ;
Memol. Lines .Add ( ' C a t e g o r i a : ' + (Sender as TAction) .Category) ;
Memol.Lines.Add ('Action l i s t n a m e : ' + (Sender as
TAction) .ActionList.Name);
end;
Se puede ver el resultado de este codigo en la figura 6.1 1, junto con la interfaz
dc usuario del ejemplo. Observe que el S e n d e r no es el elemento de menu selec-
cionado, aunqbe el controlador esta conectado a el. El objeto S e n d e r que activa
el evcnto es la accion que intercepta la operacion de usuario.
Figura 6.11. El ejemplo Actions, con una descripcion detallada del Sender del evento
OnExecute de un objeto de accion.
Por ultimo. hay que tcner presente que tambien se pueden escribir controladorcs
para evcntos del propio objeto ActionList. que jueguen el papel de controladores
globales para todas las acciones de la lista y para el objeto global Appl i c a t i o n ;
que se dispara para todas las acciones de la aplicacion. Antes de invocar a1 evento
O n E x e c u t e de la accion, Delphi activa el evento O n E x e c u t e de la A c t i o n -
L i s t y el evento OnAct i o n E v e n t del objeto global A p p l i c a t ion.Estos
eventos se fiaran en la accion, ejecutando eventualmente algo de codigo compar-
tido, y despues detendran la ejecucion (mediante el parametro H a n d l e d ) o deja-
ran que se propague hasta cl siguiente nivel.
Si no se asigna ningun controlador de eventos para responder a la accion, ni en
la lista de acciones, ni la aplicacion, ni en el ambito accion, la aplicacion trata de
identificar un objetivo a1 quc se pueda aplicar dicha accion.
- - - - - - - - -- .-- . - -
NOTA: Cuando se ejecuta una accion, esta busca un control como destino
de la accion, fijhdose en el control activo, el formulario activo y en otros
controles del formulario. Por ejemplo, las acciones de edicion se refieren a1
control activo en cada momento (si hereda de T C u s tomEdi t ) y 10s con-
troles de conjuntos de datos buscan el conjunto de datos conectado con la
fuente de datos del control data-aware que tiene el foco de entrada. Otras
acciones seguirin distintos enfoques para encontrar un componente desti-
no, pero la idea general es compartida por la mayoria de las acciones
esthdar.
p r o c e d u r e TFormRichNote.acSaveUpdate(Sender: TObject);
begin
acSave.Enabled : = Modified;
end;
p r o c e d u r e ~ ~ o r m ~ i c h ~ o t e . a c C u t U p d a t e ( S e n d eTObject);
r:
begin
acCut.Enabled : = RichEdit.SelLength > 0;
acCopy.Enabled : = acCut.Enabled;
end :
Se puede colocar una banda en cada linea o todas ellas en la misma. Cada
una utilizara una parte de la superficie disponible y aumentara de tamaiio
automaticamente cuando el usuario pinche sobre su titulo. Resulta mas
facil utilizar este componente que explicarlo. Se puede probar con el ejem-
plo CoolDemo:
ControlBar
La barra de control (ControlBar) es un contenedor de controles y se crea sim-
plemente colocando otros controles dentro de ella, como si lo hicieramos en un
panel (en ella no hay lista de B a n d s ) . Cada control colocado en la barra consigue
su propia zona de arrastre o agarradera (un pequeiio panel con dos lineas vertica-
les, a la izquierda del control), incluso un boton solitario:
Por ello. generalmente, se deberia evitar colocar botones especificos dentro del
ControlBar, en su lugar se deberian colocar contenedores en 10s que se incluyan
botones. En lugar de un panel, como norma general, se deberia usar un control
ToolBar para cada seccion del ControlBar.
El ejemplo MdEdit2 es otra version de la prueba creada en este capitulo. Basi-
camente, se han agrupado 10s botones en tres barras de herramientas (en lugar de en
una) y dejado 10s dos cuadros combinados como controles independientes. Todos
estos componentes estan dentro del componente C o n t r o l B a r , para que el usua-
rio 10s pueda organizar en tiempo de ejecucion, como muestra la figura 6.12.
El siguiente fragment0 de listado DFM del ejemplo MdEdit2 muestra la forma
en que se incluyen varias barras de herramientas y controles en un componente
ControlBar:
object ControlBarl: TControlBar
A l i g n = alTop
AutoSize = True
ShowHint = True
PopupMenu = BarMenu
object ToolBarFile: TToolBar
Flat = True
Images = Images
Wrapable = False
object ToolButtonl: TToolButton
Action = acNew
end
/ / mds botones. . .
end
object ToolBarEdit: TToolBar . . .
object ToolBarFont: TToolBar . . .
object ToolBarMenu: TToolBar
AutoSize = True
Flat = True
Menu = MainMenu
end
object ComboFont : TComboBox
Hint = ' F a m i l y fonts'
Style = csDropDownList
OnClick = ComboFontClick
end
object ColorBoxl: TColorBox.. .
end
Para conseguir el efecto estandar, hay que desactivar 10s bordes de 10s contro-
les de la barra de herramientas y definir su estilo como plano. Ajustar el tamaiio
de 10s controles del mismo modo, para poder obtener una o dos filas de elementos
con la misma altura, no es tan facil como parece. Algunos controles tienen ajuste
de tamaiio automatico o diversas restricciones. Concretamente, para que el cua-
dro combinado tenga la misma altura que la barra de herramientas, hay que ajus-
tar el tip0 y tamaiio de su fuente. Reajustar el tamaiio del control no tiene ningun
efecto.
La barra de control tiene tambien un menu de metodo abreviado que permite
mostrar u ocultar cada uno de 10s controles que contiene. En lugar de escribir un
codigo especifico para este ejemplo, hemos implementado una solucion mas gene-
rica (y reutilizable). El menu de metodo abreviado, llamado BarMenu, esta vacio
en tiempo de diseiio y se llena cuando arranca el programa:
procedure TFormRichNote.FormCreate(Sender: TObject);
var
I: Integer;
mItem: TMenuItem;
begin
...
// l l e n a e l m e n u d e l a b a r r a d e c o n t r o l
for I : = 0 to ControlBar .Controlcount - 1 do
begin
mItem : = TMenuItem-Create (Self);
mItem.Caption : = ControlBar.Controls [I].Name;
mItem.Tag : = Integer (ControlBar.Controls [I]) ;
mItem-OnClick : = BarMenuClick;
BarMenu.Items.Add (mItem);
end;
Cuando se saca una de las barras de herramientas del contenedor, Delphi crea
automaticamente un formulario flotante. Podriamos sentirnos tentados a recupe-
rarla cerrando el formulario flotante. Pero, eso no funciona, porque el formulario
flotante se elimina junto con la barra de herramientas que contiene. Sin embargo,
sc puede utilizar el menu de metodo abreviado de la barra de control superior,
unido tambitn a la otra barra de control, para mostrar esta barra de herramientas
oculta.
El formulario flotante creado por Delphi para albergar controles no anclados
tienc un titulo muy pequeiio, llamado "titulo de barra de herramientas", que por
defect0 no tiene ningun testo. Por ello, hemos aiiadido algo de codigo a1 evento
OnEndDock de cada control anclable para fijar el titulo del formulario recien
creado en que se ancla el control.
Para evitar una estructura de datos personalizada para esta informacion, he-
mos usado el texto de la propiedad Hint de estos controles (que basicamente no
se utiliza) para proporcionar un titulo aceptable:
procedure TFormRichNote.EndDock(Sender, Target: TObject; X, Y:
Integer) ;
begin
i f Target i s TCustomForm then
TCustomForm(Target) .Caption : = GetShortHint ( (Sender as
TCont rol) .Hint) ;
end;
Se puede ver el resultado del ejemplo MdEdit2 en la figura 6.13. Otra posible
ampliacion de este ejemplo podria ser aiiadir zonas de anclaje en ambos laterales
del formulario. El unico esfuerzo adicional necesario seria una rutina para orien-
tar las barras de herramientas en vertical en lugar de en horizontal. Hacer esto
requiere conmutar las propiedades L e f t y Top de cada boton despues de inhabi-
litar el dimensionamiento automatico.
Control de las operaciones de anclaje
Delphi ofrece muchos eventos y metodos para controlar las operaciones de
anclaje, entre ellas un administrador de anclaje. El ejemplo DockTest es una
prueba para operaciones de anclaje, y se muestra en la figura 6.14.
Figura 6.13. El ejemplo MdEdit2 permite anclar las barras de herramientas (pero no el
menu) en la parte superior o inferior del formulario, o hacerlas flotar.
Figura 6.14. El ejemplo DockTest con tres controles anclados en el formulario principal.
// v u e l v e a c a r g a r l a c o n f i g u r a c i o n
DockFileName : = ExtractFilePath (Application-Exename) +
'dock-dck';
if FileExists (DockFileName) then
begin
FileStr := TFileStream. Create (DockFileName, fmOpenRead) ;
try
Panell.DockManager.LoadFromStream (FileStr);
finally
FileStr.Free;
end:
end ;
Panel1.DockManager.ResetBounds (True);
end;
Este codigo funciona bien mientras que todos 10s controles estan anclados
inicialmente. Cuando se guarda el programa, si algun control permanece flotante,
no se vera cuando se vuelvan a cargar 10s parametros. Sin embargo, debido a1
codigo de inicializacion insertado con anterioridad, el control aparecera de todos
modos anclado a1 panel, y aparecera cuando se arrastren otros controles. No es
necesario decir que se trata de una situacion complicada. Por este motivo, des-
pues de cargar 10s parametros, hemos aiiadido este codigo:
for i : = Panell.DockClientCount - 1 downto 0 do
begin
aCtrl : = Panell.DockClientes[i];
Panell.DockManager.GetControlBounds(aCtr1, aRect);
if (aRect.Bottom - aRect .Top <= 0 ) then
begin
aCtrl-ManualFloat (aCtrl-ClientRect);
Panell.DockManager.RemoveControl(aCtrl);
end ;
end;
Anclaje a un PageControl
Otra caracteristica importante de 10s controles de ficha es su soporte especifi-
co para anclaje. Al anclar un nuevo control sobre un PageControl, automaticamente
se aiiade una nueva ficha que lo alberga, como se puede ver en el entorno Delphi.
Para realizar esto, simplemente hay que designar el PageControl como anclaje
anfitrion y activar el anclaje para 10s controles clientes. Esto funciona mejor si
tenemos formularios secundarios que queremos albergar. Ademas, para mover el
PageControl cornpleto a una ventana flotante y despues anclarlo otra vez, sera
necesario un panel de anclaje en el formulario principal.
Esto es exactamente lo que hemos hecho en el ejemplo Dockpage, que tiene
un formulario principal con 10s siguientes valores:
object Forml: TForml
Caption = ' Docking p a g e s '
object Panell: TPanel
Align = alLeft
DockSite = True
OnMouseDown = PanellMouseDown
object Pagecontroll: TPageControl
Activepage = TabSheetl
Align = alClient
DockSite = True
DragKind = dkDock
object TabSheetl: TTabSheet
Caption = ' List'
object ListBoxl: TListBox
Align = alClient
end
end
end
end
object Splitterl: TSplitter
Cursor = crHSplit
end
object Memol: TMemo
Align = alClient
end
end
eat
Figura 6.15. El formulario principal del ejemplo DockPage despues de que se haya
anclado un formulario al control de ficha de la izquierda.
La arquitectura de ActionManager
Hemos visto que las acciones y el componente A c t ionManager pueden
representar un papel principal en el desarrollo de las aplicaciones Delphi, ya que
permiten separar mejor la interfaz de usuario del codigo real de la aplicacion. Asi,
la interfaz de usuario puede cambiar ahora sin que eso tenga un gran impact0 en
el codigo. El inconveniente de esta tecnica es que el programador tiene mas traba-
jo. Para crear un nuevo elemento de menu, hay que aiiadir primer0 la accion
correspondiente, moverse a1 menu, aiiadir el elemento de menu y conectarlo con la
accion.
Para resolver este asunto y para ofrecer a 10s desarrolladores y usuarios fina-
les algunas caracteristicas avanzadas, Delphi 6 introdujo un nuevo tip0 de estruc-
tura, basada en el componente ActionManager, que amplia sobremanera la funcion
de las acciones. De hecho, el ActionManager no solo posee una coleccion de
acciones, sino tambien una coleccion de barras de herramientas y menus asocia-
dos a ellas. El desarrollo de estas barras de herramientas y menus es completa-
mente visual: se arrastran las acciones desde un editor de componente especial del
ActionManager hacia las barras de herramientas para acceder a 10s botones nece-
sarios. Ademas, se puede permitir a1 usuario final de 10s programas realizar la
misma operacion y reagrupar sus propias barras de herramientas y menus, empe-
zando por las acciones que se le ofrezcan.
En otras palabras, utilizar esta arquitectura permite construir aplicaciones con
una interfaz de usuario moderna y personalizable por el propio usuario. El menu
puede mostrar solo 10s elementos usados mas recientemente (como muchos pro-
gramas de Microsoft), permitir animaciones, y muchos detalles mas. Esta estruc-
tura se centra en el componente ActionManager, per0 incluye tambien otros
componentes que se encuentran al final de la ficha Additional de la paleta:
El componente ActionManager: Es un sustituto de ActionList (pero pue-
de utilizar tambien uno o mas ActionList existentes).
El control ActionMainMenuBar: Es una barra de herramientas usada
para mostrar el menu de una aplicacion basada en las acciones de un com-
ponente ActionManager.
El control ActionToolBar es una barra de herramientas utilizada para
albergar botones basados en las acciones de un componente ActionManager.
El componente CustomizeDlg: Contiene el cuadro de dialog0 que se pue-
de utilizar para permitir a 10s usuarios personalizar la interfaz de una
aplicacion basada en el componente A c t ionManager.
El componente PopupActionBarEx: Es un componente adicional que de-
beria usarse para permitir que 10s menus desplegables sigan la misma
interfaz de usuario que 10s menus principales. Este componente no se in-
cluye con Delphi 7, sino que se encuentra disponible como una descarga
separada.
Figura 6.16. Las tres paginas del cuadro de dialogo del editor de ActionManager.
rn
Las tres paginas del editor son asi:
La primera ficha proporciona una lista visual de contenedores de acciones
(barras de herramientas o menus). Para aiiadir nuevas barras de herra-
mientas, se hace clic en el boton New. Para aiiadir nuevos menus, hay que
aiiadir el componente correspondiente a1 formulario, despues abrir la co-
leccion A c t i o n B a r s del ActionManager, seleccionar una barra de ac-
ciones o aiiadir una nueva y engancharle el menu usando la propiedad
A c t i o n B a r . Estos son 10s mismos pasos que podriamos seguir para co-
nectar una nueva barra de herramientas a esta estructura en tiempo de
ejecucion.
La segunda ficha del editor de ActionManager es muy similar a la del
editor de ActionList, que ofrece una forma estandar de aiiadir acciones
nuevas o personalizadas, organizarlas en categorias y modificar su orden.
Sin embargo, la caracteristica importante de esta ficha consiste en que se
puede arrastrar una categoria o una simple accion desde la misma y soltar-
la en un control de una barra de acciones. Si se arrastra una categoria a un
menu, se consigue un menu desplegable con todos 10s elementos de la
categoria. Si se arrastra a una barra de herramientas, cada una de las
acciones de la categoria genera un boton en la barra de herramientas. Si se
arrastra una orden sencilla a una barra de herramientas, se obtiene el co-
rrespondiente boton, si se arrastra al menu, se obtiene una orden directa de
menu, algo que como norma general se deberia evitar.
La ultima pagina del editor ActionManager permite (a1 programador y
opcionalmente a un usuario final) activar el visor de 10s elementos de menu
usados recientemente y modificar algunas de las propiedades visuales de la
barra de herramientas.
El programa AcManTest es un ejemplo que usa algunas de las acciones
estandar y un control RichEdit para explicar el uso de esta estructura (no se ha
escrito nada de codigo personalizado para hacer que las acciones funcionen me-
jor, porque el objetivo era solo el administrador de acciones). Se puede experi-
mentar con el en tiempo de diseiio o ejecutarlo, hacer clic en el boton Customize
y ver lo que el usuario final puede hacer para personalizar la aplicacion. (Vease la
figura 6.17.) En realidad, en el programa se puede evitar que el usuario realice
algunas operaciones sobre acciones. Cualquier elemento especifico de la interfaz
de usuario (un objeto T ~ c t i o n ~ l i e ntiene
t ) una propiedad C h a n g e A l l o w e d
que se puede usar para desactivar la modificacion, el desplazamiento y la elimina-
cion de operaciones. Cualquier contenedor de clientes de accion (las barras visua-
les) tiene una propiedad para inhabilitar su ocultacion (A1 l owH i d i ng , fijada
por defect0 a T r u e ) . Cada coleccion de It e m s de una barra de accion tiene una
opcion C u s t o m i z a b l e que se puede inhabilitar para desactivar todos 10s cam-
bios de usuario en toda la barra.
Figura 6.17. Mediante el componente CustomizeDlg se puede permitir que un usuario
personalice las barras de herramientas y el menu de una aplicacibn arrastrando
elementos desde el cuadro de dialog0 o moviendolos en las barras de accion.
Para que 10s valores de usuario sean permanentes, hemos conectado un archivo
(llamado settings) a la propiedad Fi leName del componente A c t ionManager.
Cuando se asigna esta propiedad, hay que introducir el nombre del archivo que se
quiere usar; a1 iniciar el programa, el ActionManager creara el archivo. La perma-
nencia se consigue mediante streaming de cada ActionClientItem conectado con el
administrador de acciones. Como estos elementos de cliente de accion estan basa-
dos en la configuracion de usuario y mantienen la informacion de estado, un
simple archivo recoge tanto 10s cambios que el usuario ha realizado en la interfaz
como 10s datos de uso.
Dado que Delphi almacena valores de usuario e informacion de estado en un
archivo que nosotros ofrecemos, se puede hacer que la aplicacion soporte varios
usuarios en un solo ordenador. Simplemente, hay que usar un archivo de configu-
raciones para cada uno de ellos (dentro de la carpeta Mi s document 0s) y
conectarlo con el administrador de accion cuando el programa arranque (utilizan-
do el usuario actual del ordenador o despues de algun nombre de usuario que se
solicite). Otra posibilidad es almacenar estas configuraciones en la red, de forma
que si un usuario esta en otro ordenador, su configuracion personal viaje con el.
En el programa hemos decidido guardar 10s valores en un archivo dentro de la
misma carpeta que el programa, asignado la ruta relativa (el nombre de archivo)
a la propiedad FileName del ActionManager. El componente rellenara el nom-
bre de archivo completo con la carpeta del programa, encontrando sin problemas
el archivo que cargar. Sin embargo, el archivo incluye entre sus datos su propio
nombre, con una ruta absoluta. Por eso, cuando llegue el momento de guardar el
archivo, la operacion puede referirse a la ruta antigua. Esto impediria que se
copiara este programa con sus configuraciones a una carpeta distinta (por ejem-
plo, esto es un problema para la prueba AcManTest). Se puede devolver su valor
a la propiedad FileName despues de cargar el archivo. Como otra alternativa
mejor, podria establecerse el nombre de archivo en tiempo de ejecucion, en el
evento oncreate del formulario. En este caso tambien habria que obligar a que
el archivo se recargase, ya que se asigna despues de que ya se hayan creado e
inicializado 10s componentes ActionManager y 10s distintos ActionBar. Sin
embargo, podria desearse forzar el nombre de usuario despues de la carga:
procedure TForml. Formcrate (Sender:TObject) ;
begin
ActionManagerl.Fi1eName : = ExtractFilePath
(Application.ExeName) + ' s e t t i n g s ' ;
ActionManagerl.LoadFromFi1e(ActionManagerl.FileName);
// devolvemos el nombre a 1 fichero d e configuracion despues
d e cargarlo (ruta relativa)
ActionManagerl.FileName : = ExtractFilepath
(Application.ExeName) + 'settings';
end ;
procedure TForml.VirtualListActionlGetItem(Sender:
TCustornListAction;
const Index: Integer; var Value: String;
var ImageIndex: Integer; var Data: Pointer) ;
begin
Value : = 'Item' + IntToStr (Index);
end;
Si se han leido 10s capitulos anteriores, ahora deberia poder utilizar 10s com-
ponentes visuales de Delphi para crear la interfaz de usuario de una aplicacion.
Es hora de fijarse en otro elemento central del desarrollo en Delphi: 10s formula-
rios. Se han venido usando desde el principio del libro, per0 nunca se ha descrito
en detalle lo que se puede hacer con un formulario, que propiedades puede usar o
que metodos de la clase TForm resultan de interes particular.
Este capitulo analiza algunas de las propiedades y estilos de formularios y las
tecnicas de dimcnsionamiento y position, a1 igual que su escalado y desplaza-
miento. Tambien hablaremos de las aplicaciones con varios formularios, el uso de
10s cuadros de dialog0 (personalizados y predefinidos), marcos y herencia visual
de formularios. Por ultimo, dedicaremos algo de tiempo a1 sistema de entrada en
un formulario, tanto mediante el teclado como mediante el raton.
Este capitulo trata 10s siguientes temas:
Estilos de formularios, de bordes e iconos de bordes.
Entrada de raton y teclado.
Dibujo direct0 sobre el formulario y efectos especiales.
Posicion, escala y desplazamiento de formularios.
Creacion y cierre de formularios.
Cuadros de dialogo y formularios modales y no modales.
Creacion dinamica de formularios secundarios
Cuadros de dialogo predefinidos.
Construccion de una pantalla de inicio.
La clase TForm
La clase T Form,incluida en la unidad Forms de la VCL, define 10s formula-
rios en Delphi. Ahora, existe tambien una segunda definicion de 10s formularios
en la biblioteca VisualCLX. Aunque a lo largo del presente capitulo, nos referire-
mos principalmente a la clase de la VCL, intentaremos resaltar tambien las dife-
rencias con la version multiplataforma que proporciona la biblioteca CLX.
La clase TForm forma parte de la jerarquia de controles de ventana, que
comienza con la clase TWinControl (o TWidgetControl). En realidad,
TForm hereda de la "casi completa" TCustomForm,que a su vez hereda de
TScrollingWinControl (o TScrollingWidget). A1 tener todas las
funciones de sus clases basicas, 10s formularios tienen una gran serie de metodos,
propiedades y eventos. Por este motivo, no vamos a intentar mostrar una lista de
todos ellos. En su lugar, a lo largo del capitulo, explicaremos una serie de tecni-
cas interesantes relacionadas con 10s formularios. Remarcaremos las pocas dife-
rencias existentes entre 10s formularios VCL y 10s formularios CLX. Para la
mayoria de 10s ejemplos existe una version CLX, para que se pueda comenzar a
experimentar a1 instante con formularios y cuadros de dialogo en la CLX, a1 igual
que con la VCL. La inicial de estas versiones CLX de cada ejemplo sera la Q.
uses
Forms,
DynaMemo in ' DynaMerno .pas ' ;
var
str: string;
begin
str : = 1 1 .
Randomize;
while Length (str) < 2000 do
str : = str + Char (32 + Random (74));
ShowStringForm (str);
Application.Run;
end.
A1 ejecutar el programa DynaForm, se obtiene un formulario de extraAa apa-
riencia cubierto con caracteres aleatorios (como muestra la figura 7.1).
DFM del archivo ejecutable real de Delphi, per0 tambien se puede hacer lo
mismo con cualquier archivo ejecutable compilado con Delphi del que no
tengamos el c6digo fuente. Si es importante guardarse un conjunto especifi-
co de componentes que se utilicen (quizas en un formulario especifico),
junto con 10s valores predefinidos para sus propiedades, escribir el codigo
adicional puede merecer la pena.
Figura 7.2. Forrnularios de ejemplo con diversos estilos de borde, creados por el
ejernplo Borders.
En tiempo de diseiio, el formulario siempre se muestra con el valor predetermi-
nado para la propiedad BorderSylte, bs Si zeab le. Este valor se corresponde
con un estilo de Windows conocido como "marco fino". Cuando una ventana
principal tiene un marco fino a su alrededor, un usuario puede ajustar su tamaiio
arrastrando su borde. Este estado se manifiesta mediante unos cursores de
redimensionamiento especiales (con la forma de una flecha de dos puntas) que
aparecen cuando el usuario mueve el raton sobre el borde de esta ventana.
Una segunda opcion bastante importante de esta propiedad es bsDialog. Si
la seleccionamos, el formulario utiliza como borde el tipico marco de cuadro de
dialogo (un marco grueso que no permite que se reajuste su tamaiio).
Ademas de este elemento grafico, observe que si seleccionamos este valor
bsDialog, el formulario se transforma en un cuadro de dialogo. Esto implica
una serie de cambios: por e.jemplo, 10s elementos de su menu de sistema son
distintos y el formulario ignora algunos de 10s elementos de la propiedad
Border Icons.
lag &nu -fbs Iform border style, estilo del h d e del formuhrio). Asi
tdremos 'Pbssingle, fbsDialog, etc.
Este codigo usa en realidad un truco: convierte el numero del elemento selec-
cionado en la enumeracion T FormBorderSt yle. Esta tecnica funciona por-
que hemos colocado 10s botones de radio en el mismo orden que 10s valores de
esta enumeracion.
El metodo BtnNew FormClic k copia a continuation el testo del boton de
radio en el titulo del formulario secundario. Este programa remite a1 T Form2, el
formulario secundario definido en una unidad secundaria del programa, guardado
como SECOND.PAS. Por esa razon, para compilar el ejemplo, habra que aiiadir
las siguientes lineas a la seccion de implernentacion de la unidad del formulario
principal:
uses
Second;
TRUCO: Siempre que bay6 quq fiferirse a otra unidRd de un pi@mha,
hay que .colocq 1a correspamdiente sentencia udes en lrr3ec&n
irnp;lemsn$ation y no en la secci6n inter face, 3 se; podble, E s t ~
rrcelerie#pptoceso de compilaci6n. origins un cbdigo miis limpio (porque
laa pnfdadses'que se incluyen esdn sep&adas de lasque incluie ~ e l ~ hyi )
evita emks de @qilacion ~irculaPdeunidades. Para hacer referencia ti
otr& archivos del Ynismo proyecto, tambitn se puede usar la opcion de
m d FileNse UNt.
Figura 7.3. El ejemplo Blcons. Al seleccionar el icono de ayuda dt borde y hacer clic
sobre el boton. aparece la ayuda.
rn
ADVERTENCIA: Si se analiza la versi6n QBIcons, creada con CLX, se
puede comprobar que un fallo de la biblioteca impide modificar 10s iconos
de 10s bordes en tiempo de ejecuci6n. Las distintas configuraciones en tiem-
po de diseiio h n c i o n a r b completamente, asi que sera necesario modificar
el programa antes de ejecutarlo si se quiere ver algun tipo de efecto. Este
programa no hace nada en tiempo de ejecucion.
Este es el unico mod0 de usar 10s peculiares estilos de ventana que no cstan
directamente disponibles mediante las propiedades del formulario. Para ver una
lista de 10s estilos y estilos ampliados de ventana, se pueden estudiar en la ayuda
de la API temas como "CreateWindowMy "CreateWindowEs". Se vera que la API
de Win32 tiene estilos para estas funciones, incluidos aquellos relacionados con
las ventanas de herramientas.
Para mostrar la utilization de este metodo, hemos creado el ejemplo NoTitle,
que pcrmite crear un programa con un titulo personalizado. Primero tenemos que
eliminar el titulo estandar. per0 mantener el marco que permite ajustar el tamaiio,
definiendo 10s estilos correspondientes:
p r o c e d u r e TForml.CreateParams ( v a r Params: T C r e a t e P a r a m s ) ;
begin
i n h e r i t e d C r e a t e P a r a m s ( P a r a m s );
P a r a m s . S t y l e : = (Params.Style or ws-Popup) a n d n o t ws-Caption;
end;
Figura 7.4. El ejemplo NoTitle no posee un titulo real sin0 uno falso creado con una
etiqueta.
Para que el titulo falso funcione, debemos decirle a1 sistema que una operacion
dc raton en esta zona se corresponde con una operacion de raton sobre el titulo.
Para ello, sc puede interceptar el mensaje de Windows wm N C H i t T e s t , que
normalmente se le envia a Windows para establecer el lugar en el que esti en ese
momcnto cl raton. Cuando la accion de pulsado se realiza en la zona del cliente y
cn la etiqueta, podemos simular que el raton esta sobre el titulo definiendo el
rcsultado adecuado:
procedure TForml.HitTest ( v a r Msg: TWrnNCHitTest);
// mensaje w m _ N c H i t T e s t
begin
inherited;
i f (Msg.Result = htclient) and
(Msg.YPos < Labell.Height + Top + GetSystemMetrics
(sm-cyFrame) ) then
Msg.Result : = htcaption;
end;
. . . . . . . Aid,
..
. .
..
..
..
.... . . . . . . . . . . . . . . . . .
: , I '- ,"On= . . . .l~dill
......................
. .
. .
..I
.. ::::I
. , . . Edit2
.
. . . . . . . .
. . . . . . . . . . .
. .
.. .... . . . . . . . . . . .
, .... . . . . . . . . . . . . . . . .
, . . , Edit3 . . . . . .
.... . . . . . . . . . . . .
I . . . . . . . . .: : : : : . : : : : . . .
I . . . . .
.. . . . .
.. I . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Por defect0 el programa no hace nada especial, a escepcion de que 10s diversos
botones de radio que se usan para activar la vista previa de la tecla:
procedure TForml.RadioPreviewClick(Sender: TObject);
begin
KeyPreview : = RadioPreview.ItemIndex <> 0;
end;
Ahora empezaremos a recibir 10s eventos OnKeyPress y podremos realizar
una de las tres acciones solicitadas por 10s tres botones especiales del grupo de
radio. La accion depende del valor de la propiedad ItemIndex del componente
grupo de radio. Esta es la razon por la que el controlador de eventos se basa en
una sentencia case:
p r o c e d u r e TForml.FormKeyPress(Sender: TObject; var Key: Char);
begin
c a s e RadioPreview.Item1ndex of
En el primer caso, si el valor del parametro Key es #13, valor que correspon-
de a la tecla Intro, desactivamos la operacion (definiendo Key como cero) y, a
continuacion, imitamos la activacion de la tecla Tab. Existen muchas formas de
hacer esto, per0 en este caso hemos escogido una bastante particular. Hemos
enviado el mensaje CM -DialogKey al formulario, pasando el codigo para la
tecla Tab (VK-TAB):
1: // I n t r o = T a b
i f Key = #13 t h e n
begin
Key : = #O;
Perform (CM-DialogKey, VK-TAB, 0 );
end;
oars sop&.a;-
el uso 6el teclado' usar if ratbn es& '
bien, per0 suele ser m b lento. Si se es habil con el teclado, no se querra
utilizar el raton para arrastrar una palabra de un texto; se utilizarb las
teclas de rn~toddabreviado para copiar y pegar el texto sin separar las
manos del teclado.
-Par estas razones, siempre deberia establecer un orden de tabulacion co-
rrecto para 10s componentes de un formulario. Hay que recordar aiiadir
teclas para 10s botones y para acceder a 10s elementos de menu mediante el
--
.^^l^l^&:I:-.... .^^I^- -1- -A*^*-
LGGI~UU, U I - w ~ a rL C G I ~ S UG IIIGLVUU
-L-.^-.:^l^ ---- --^:^-^-
~ U I G V I ~ U U para U~GIUIIGS
J^ ---- L -.
uc IIIG~U y w s a s
asi.
contiene una colec ion de herramientas de dibujo (como un lhpiz, una bro-
cha y una fuente) Y posee algunos metodos de dibujo, que usan las herra-
,-,..,I,, I A, f
:L
..
:, A, - ,,,
w u a l c a . El L I ~ U uo ~uulgu
,
a
,
,,
:
, b:,, A, -LA:-- A:,-+, ,:-,Ih
~ l u c u ~ ia
r s uc u v u j u UIIGGLU uc csw GJGIU~IU uu GS
correcto, porque la imagen en pantalla no es pennanente: a1 mover otra
ventana sobre la actual se eliminara su efecto.
Se puede usar esta caracteristica del programa para entender mejor como fun-
ciona el raton. Se puede hacer esta prueba: se ejecuta el programa (esta version
simple o la version completa) y se ajusta el tamafio de las ventanas del escritorio
para que el formulario del programa MouseOne o QMouseOne quede detras de
otra ventana e inactivo per0 con el titulo visible. Si ahora se mueve el raton sobre
el formulario, se podra ver que las coordenadas cambian. Este comportamiento
significa que se envia el evento OnMouseMove a la aplicacion incluso aunque su
ventana no se encuentre activa, y demuestra lo ya comentado: 10s mensajes de
raton siempre se dirigen a la ventana que se encuentra bajo el cursor del raton. La
unica excepcion a esto es la operacion dc captura de raton que enseguida comen-
taremos .
Adcmas de mostrar la posicion en cl titulo de la ventana, el ejemplo Mouseonel
QMouseOne puede realizar un seguimiento de 10s movimientos del raton, pintan-
do pequeiios pixeles en el formulario si el usuario mantiene pulsada la tecla Mayus.
(De nuevo estc codigo de dibujo directo produce un resultado no permanente.)
procedure TMouseForm.FormMouseMove(Sender: TObject; Shift:
TShiftState;
X, Y: Integer);
begin
// rnuestra l a p o s i c i o n d e l r a t o n e n e l t i t u l o
Caption : = Format ( ' M o u s e i n x = % d , y = % d l , EX, Y]) ;
if ssShift in Shift then
// m r c a p u n t o s e n a m a r i l l o
Canvas.Pixels [ X I Y] : = clYellow;
end ;
Figura 7.6. El ejernplo MouseOne usa una linea de puntos para dibujar, durante la
operacion de arrastre, un rectangulo.
Pintar sobre formularios
~ Q u Ces lo que hace necesario controlar el evento onpaint para producir un
resultado correcto, y por que no se puede dibujar directamente sobre la superficie
del formulario? Depende del comportamiento predefinido de Windows. A1 pintar
sobre una ventana, Windows no almacena la imagen resultante. Cuando se cubre
la ventana, normalmente se pierde su contenido.
La razon de este comportamiento es sencilla: el almacenamiento en memoria.
Windows asume que resulta "mas barato" a largo plazo dibujar de nuevo la ven-
tana mediante codigo que dedicar la memoria de sistema a conservar el estado de
una ventana. Se trata de un compromiso clasico entre la memoria y 10s ciclos de
CPU. Un mapa de bits a color de una imagen a 600x800 en 256 colores necesita
unos 480 KB. A1 aumentar la calidad del color o el numero de pixeles, se pueden
alcanzar facilmente 10s 4 MB de memoria para una resolucion de l28Ox 1024 con
16 millones de colores.
En caso de que se quiera tener una salida coherente en las aplicaciones, se
pueden usar dos tecnicas. La solucion general consiste en almacenar suficientes
datos sobre la salida para poder reproducirla cuando el sistema realiza una solici-
tud para pintar. Una tkcnica alternativa consiste en guardar la salida del formula-
rio en un mapa de bits mientras se crea, colocando un componente Image sobre
el formulario y dibujando sobre el lienzo de este componente imagen.
La primera tecnica, pintar, es la tecnica comun para controlar la salida en la
mayoria de 10s sistemas de ventanas, a parte de 10s programas orientados a grafi-
cos especificos que almacenan la imagen del formulario completa en un mapa de
bits. La tecnica utilizada para implementar el pintado tiene un nombre muy des-
criptivo: almacenar y pintar. Cuando el usuario pulsa un boton del raton o realiza
alguna otra operacion, hay que almacenar la posicion y otros elementos. A conti-
nuacion, en el metodo de pintado, se usa esta informacion para pintar la imagen
correspondiente.
La idea de esta tecnica es permitir que la aplicacion pinte de nuevo su superfi-
cie completa en cualquier situacion posible. Si ofrecemos un metodo para dibujar
de nuevo el contenido del formulario y se llama a1 metodo automaticamente cuan-
do se ha ocultado una parte del formulario y es necesario pintarla de nuevo,
podremos volver a crear la salida de forma adecuada.
Como esta tecnica se realiza en dos etapas, debemos ejecutar esas dos opera-
ciones seguidas, solicitando a1 sistema que pinte de nuevo la ventana (sin esperar
a que el sistema lo pida). Se pueden usar diversos metodos para originar un nuevo
pintado: Invalidat e l Update, Repaint y Refresh. Los dos primeros
se corresponden a las funciones de la API de Windows, mientras que el ultimo lo
ha introducido Delphi:
El mttodo Invalidate: Informa a Windows de que hay que volver a pintar
la superficie total del formulario. Lo mas importante es que Invalidate
no desencadena una operacion de pintado de forma inmediata. Windows
almacena simplemente la solicitud y respondera solo despues de que se
haya ejecutado por completo el procedimiento actual (a no ser que se llame
a Application. ProcessMessages oUpdate) y e n cuanto no haya
otros eventos pendientes en el sistema. Windows retrasa deliberadamente
la operacion de pintado porque es una de las operaciones para las que mas
tiempo se necesita. A veces, con dicho retraso, solo es posible pintar el
formulario despues de que se hayan producido diversos cambios, lo cual
evita que haya muchas llamadas consecutivas a1 metodo (lento) de pintar.
El mCtodo Update: Solicita a Windows que actualice el contenido del
formulario, pintandolo de nuevo inmediatamente. Sin embargo, hay que
recordar que esta operacion se realizara solo si existe una zona no valida.
Esto ocurre si se acaba de llamar a1 metodo Invalidate o como resul-
tad0 de una operacion realizada por el usuario. Si no existe una zona no
valida, una llamada a Update no tiene ningun efecto. Por esa razon, es
frecuente ver una llamada a Update inmediatamente despues de una Ila-
mada a Invalidate.Como hacen exactamente 10s dos mktodos de Delphi:
Repaint y Refresh.
El mCtodo Repaint: Llama a Invalidate y, a continuacion, a Updat e.
Como consecuencia, activa el evento onpaint inmediatamente. Existe
una version ligeramente diferente de este metodo denominada Refresh.
El hecho de que existan dos metodos para la misma operacion se remonta a
10s dias de Delphi 1, cuando ambos eran sutilmente distintos entre si.
Cuando hay que pedir a1 formulario que se vuelva a pintar, normalmente debe-
riamos llamar a Invalidate,siguiendo el enfoque estandar de Windows. Esto
es importante sobre todo cuando hay que solicitar dicha operacion con bastante
frecuencia, porque si Windows emplea demasiado tiempo en actualizar la panta-
lla, las solicitudes de pintado se pueden acumular en una sencilla accion de pinta-
do. El mensaje wm Paint de Windows es un mensaje de baja prioridad; si hay
una solicitud pend&e, per0 hay mas mensajes esperando, 10s otros mensajes se
controlan antes de que el sistema lleve a cab0 la accion de pintado.
Por otra parte, si se llama varias veces a1 metodo Repaint, habra que volver
a pintar la pantalla cada vez antes de que Windows pueda procesar otros mensa-
jes y debido a que para las operaciones de pintado se realizan calculos de forma
exhaustiva, la capacidad de respuesta de la aplicacion sera menor. Sin embargo.
si queremos que la aplicacion pinte de nuevo una superficie lo antes posible hay
que llamar a Repaint .
Fijese en que hay que llamar a1 metodo show a1 final para que el comporta-
miento del formulario sea el adecuado. Tambien se puede conseguir un efecto de
animation similar al modificar la propiedad AlphaBlendValue en un bucle.
La API AnimateWindow se puede usar tambien para controlar el mod0 en que
se presenta el formulario: empezando desde el centro (con el indicador A w -
C E N T E R ) o desde uno de sus lados (AW H O R P O S I T I V E , AW -H O R -
NEGATIVE, AW V E R POSITIV, o AW VER NEGATIVE).
Esta misma fuhci6nie puede aplicar tambitin-a 10s controles de ventana para
darles un aspect0 transparente en lugar de su habitual apariencia directa. No
queda muy claro el gasto de ciclos de CPU que causan estas animaciones, pero si
se aplican correctamente y en el programa apropiado, pueden mejorar la interfaz
de usuario.
Desplazar un formulario
Cuando se crea una aplicacion simple, un solo formulario podria albergar
todos 10s componentes necesarios. Sin embargo, a medida que crece la aplicacion,
tal vez haya que reducir el espacio para 10s componentes y juntarlos mas, aumen-
tar el tamaiio del formulario o aiiadir formularios nuevos. Si se juntan mas 10s
componentes, se podria aiiadir la capacidad de modificar su tamaiio en tiempo de
ejecucion, posiblemente dividiendo el formulario en dos zonas diferentes. Si deci-
dimos aumentar el tamaiio del formulario, podriamos usar las barras de desplaza-
miento para permitir que el usuario se mueva por un formulario que sera mas
grande que la pantalla (o a1 menos mas grande que su zona visible en la pantalla).
Aiiadir una barra de desplazamiento a un formulario es sencillo. De hecho, no
hay que hacer nada. Si se colocan varios componentes dentro de un gran formula-
rio y se reduce su tamaiio, automaticamente se aiiadira una barra de desplaza-
miento a1 formulario, siempre que no se haya cambiado el valor de la propiedad
AutoScroll predefinida como True.
Junto con A u t o s c r o 1 1 , 10s formularios tienen dos propiedades,
HorzScrollBar y VertScrollBar, que se pueden usar para definir diver-
sas propiedades de 10s dos objetos T FormScro 11Bar asociados con el formu-
lario.
La propiedad Visible indica si esta presente la barra de desplazamiento, la
propiedad Posit ion determina el estado inicial del control de desplazamiento y
la propiedad Increment determina el efecto que se obtiene a1 hacer clic sobre
una de las flechas situadas en 10s extremos de la barra de desplazamiento. Sin
embargo, la propiedad mas importante es Range.
La propiedad Range de una barra de desplazamiento establece el tamaiio
virtual del formulario, no el rango real de valores de la barra de desplazamiento.
Supongamos que se necesita un formulario que aloje diversos componentes y que
por tanto necesite ser de 1000 pixeles de ancho. Podemos usar este valor para
definir el "rango virtual" del formulario, cambiando el Range de la barra de
desplazamiento horizontal.
La propiedad Position de la barra de desplazamiento variara entre 0 y
1000 menos el tamaiio actual de la zona de cliente. Por ejemplo, si la zona de
cliente de un formulario tiene 300 pixeles de ancho, podemos desplazarnos 700
pixeles para ver el extremo mas alejado del formulario (el pixel milesimo).
En el formulario del ejemplo se han colocado dos cuadros de lista sin ninguna
funcion y se podria haber obtenido el mismo rango de barra de desplazamiento
colocando el cuadro de lista fijo a la derecha de tal manera para que su posicion
(Left) mas su tamaiio (Width) fuese igual a 1000.
La parte interesante del ejemplo es la presencia de una ventana de cuadro de
herramientas que muestra el estado del formulario y de su barra de desplazamien-
to horizontal. Este segundo formulario tiene cuatro etiquetas, dos con texto fijo y
dos con la salida. Ademas de eso, el formulario secundario (Ilamado st at us)
tiene un estilo de borde bsToolWindow y es una ventana que siempre estara
por encima. Tambien deberiamos definir su propiedad V i s ible como True,
para que su ventana se muestre automaticamente a1 arrancar:
object Status: TStatus
BorderIcons = [biSystemMenu]
Borderstyle = bsToolWindow
Formstyle = fsStayOnTop
Visible = True
object Labell: T L a b e l ...
...
El unico objetivo de este programa es actualizar 10s valores del cuadro de
herramientas cada vez que se modifica el tamaiio del formulario o que este se
desplaza (como muestra la figura 7.8). La primera parte es muy sencilla. Se
puede controlar el evento O n R e s i z e del formulario y copiar simplemente un par
de valores en ambas etiquetas. Estas etiquetas forman parte de otro formulario,
por lo que sera necesario aiiadirles como prefijo el nombre de la instancia del
formulario, S t a t us :
procedure TForml. FormResize (Sender: TObject) ;
begin
Status.Label3.Caption : = IntToStr(C1ientWidth);
Status.Label4.Caption : = IntToStr(HorzScrollBar.Position);
end;
r1 I -
b
Desplazamiento automatic0
La propiedad R a n g e de la barra de desplazamiento puede parecer extraiia
hasta que se comienza a usar continuamente. Entonces, se empieza a pensar en las
ventajas de la tecnica del "rango virtual". En primer lugar, la barra de desplaza-
miento se elimina automaticamente del formulario cuando la zona de cliente del
formulario es lo suficientemente amplia como para acomodar el tamaiio virtual y
cuando se reduce el tamaiio del formulario, la barra de herramientas vuelve a
aparecer.
Esta caracteristica resulta sobre todo interesante cuando la propiedad
A u t o s c r o l l del formulario se establece como T r u e . En este caso, las posicio-
nes extremas de 10s controles situados mas a la derecha y mas abajo se copian
automaticamente en las propiedades Range de las dos barras de desplazamiento
del formulario. El desplazamiento automatic0 funciona bien en Delphi. En el
ejemplo anterior, el tamaiio virtual del formulario se estableceria en el borde
derecho del ultimo cuadro de lista. Esto se definia con 10s siguientes atributos:
object ListBox6: TListBox
Left = 832
Width = 145
end
Por lo tanto, el tamaiio virtual horizontal del formulario seria de 9 7 7 (la suma
de 10s dos valores anteriores). Este numero se copia automaticamente en el campo
Range de la propiedad Horz S c r o l l B a r del formulario, a menos que se cam-
bie manualmente para conseguir un formulario mas grande (corno en el ejemplo
Scroll 1, en el que se usaban un valor de 1 0 0 0 para que hubiera algo de espacio
entre el ultimo cuadro de lista y el borde del formulario). Podemos ver dicho valor
en el Object Inspector o realizar la siguiente prueba: ejecutar el programa,
establecer el tamaiio deseado para el formulario y mover el control de desplaza-
miento hasta el extremo derecho. Cuando aiiadimos el tamaiio del formulario y la
posicion del control, siempre obtendremos 1 0 0 0, la coordenada virtual del pixel
situado mas a la derecha, sea cual sea su tamaiio.
// d i b u j a una l i n e d a m a r i l l a
Canvas.Pen.Width : = 30;
Canvas.Pen.Color : = clYellow;
Canvas .MoveTo (30-XI, 30-Y1) ;
Canvas .LineTo (1970-XI, 1970-Y1) ;
// y a s i s u c e s i v a m e n t e ...
2000 Pixeles
2000
'ixeles
500 Pixeles I
Figura 7.9. Las lineas a dibujar sobre la superficie virtual del formulario
// d i b u j a una l i n e a a m a r i l l a
Canvas.Pen.Width : = 30;
Canvas.Pen.Color : = clYellow;
Canvas .MoveTo (30, 3 0 ) ;
C a n v a s .LineTo ( 1 9 7 0 , 1970) ;
// y a s i s u c e s i v a m e n t e ...
Esta es la version del programa que encontrara en el codigo fuente del libro. Se
puede probar el programa y comentar la llamada a SetWindowOrgEx para ver
lo que sucede si no se usan las coordenadas virtuales. Se puede ver que el resulta-
do del programa no es correct0 (no se desplazara, y siempre permanecera la
misma imagen en la misma posicion, sin importar las operaciones de desplaza-
miento). Observe tambien que en la version QtICLX del programa, denominada
QScroll2, no se usan las coordenadas virtuales sino que simplemente se restan las
posiciones de desplazamiento a cada coordenada codificada manualmente.
Escalado de formularios
Cuando sc crea un formulario con multiples componentes, se puede escoger un
borde de tamaiio fijo o permitir que el usuario ajuste el tamaiio del formulario y se
aiiadan automaticamente barras de desplazamiento para poder acceder a 10s com-
poncntes que se encuentren fuera de la parte visible del formulario, como ya se ha
visto. Tambien podria suceder esto porque un usuario de la aplicacion utilizara
un controlador de pantalla con un numero de pixeles mucho menor que en el
desarrollo. En lugar de reducir el tamaiio del formulario y desplazar el contenido,
podria quercrse reducir el tamaiio de cada uno de 10s componentes al unisono.
Esto ocurre automaticamente si el usuario tiene una fuente de sistema con una
tasa dc piselcs por pulgada distinta que la usada para el desarrollo. Para enfren-
tarse a estos problemas, Delphi disponc de unas apreciables caracteristicas de
escalado, per0 no son completamente intuitivas.
El metodo S c a l e B y de formulario permite ajustar la cscala del formulario y
de cada uno dc sus componentes. Las propiedadcs P i x e l s P e r I n c h y S c a l e d
permiten que Delphi modifique el tamaiio de una aplicacion de forma automatica,
cuando esta se ejecuta con un tamaiio de fuente de sistema diferente, normalmente
debido a una resolution de pantalla distinta. En ambos casos, para que el formu-
lario establezca la escala de su ventana, hay que asegurarse de definir tambien la
propiedad A u t o s c r o l l como F a l s e . De otro modo, el contenido del formula-
rio se ajustara a la escala, pero el borde de dicho formulario no.
r --Man
- -
-
fam l~olrn~
Figura 7.10. La ficha Forms del cuadro de dialogo Project Options de Delphi.
Como se puede ver, cada evento tiene una funcion especifica ademas de la
inicializacion del formulario, a escepcion del evento Oncreate,a1 que se llama
solo una vez de manera garantizada cuando se crea el formulario.
Sin embargo, existe un enfoque alternativo para aiiadir codigo de inicializacion
a un formulario: sobrescribir el constructor. Esto se hacc del siguiente modo:
constructor TForml.Create(A0wner: TComponent);
begin
inherited Create (AOwner);
// c o d i g o d e i n i c i a c i o n a d i c i o n a l
end;
Cerrar un formulario
Cuando cerramos un formulario utilizando el metodo c l o s e o mediante el
tipico mdtodo (Ah-F4, el mcnu de sistcma o el boton Close), llamamos a1 evento
O n C l o s e Q u e r y . En este caso, se puede pedir a1 usuario que confirme la ac-
cion, sobrc todo si hay datos sin guardar en el formulario. Veamos la sencilla
cstructura dcl codigo que podemos escribir:
procedure TForml.FormCloseQuery(Sender: TObject; var CanClose:
Boolean) ;
begin
if MessageDlg ( ' A r e y o u s u r e y o u w a n t t o e x i t ? ' ,
mtconfirmation, [&Yes, &No] , 0 ) = idNo then
CanClose : = False;
end:
Cada vez que hacemos clic sobre el boton, se crea una nueva copia del formu-
lario. Hay que darse cuenta de que no utilizamos la variable global F o r m 3 por-
que no tiene mucho sentido asignar a esta variable un nuevo valor cada vez que se
crea un nuevo objeto de formulario. Sin embargo, lo importante es no referirse a1
objeto global F o r m 3 en el codigo del formulario o en otras partes de la aplica-
cion. La variable F o r m 3 sera invariablemente un punter0 a n i l . Lo mas reco-
mendable es que en un caso como este se elimine de la unidad para evitar cualquier
confusion posible.
-- -.. -- - . -
No hacer esto conllevara un gran consumo de memoria, ya que todos 10s for-
mularios que se creen (tanto las ventanas como 10s objetos Delphi) se mantendran
en memoria y se ocultaran.
Con este codigo, se crea el formulario la primera vez que se necesita y despues
se guarda en memoria, visible en pantalla u oculto. Para evitar consumir memoria
y recursos del sistema de forma innecesaria, debemos destruir el formulario se-
cundario cuando se cierre. Para ello podemos escribir un controlador del evento
OnClose:
procedure TForm2.FormClose(Sender: TObject; var Action:
TCloseAction) ;
begin
Action : = caFree;
/ / i m p o r t a n t e : d e f i n i r p u n t e r 0 como n i l
Form2 : = n i l ;
end ;
I
1,A
,, a
, ,
.
a " ,
A
*,",, A,"+,.:, ,I C,-..l..,, ,
,
+a
, ,
,A,
,:
,a+
,..
, A,
~a I ~ L U I GJ
I ~ U uu
G ~ J U G I I I V J U G J C I U I ~GI 1 ~ 1 1 1 1 ~ 1 4 CULCGJ
1 1 ~ UG ~ U CGIIIIIUGII
G UG
// l o r n u e s t r a
if FormItem.ShowModa1 = mrOK then
begin
// l e e 1 0 s v a l o r e s n u e v o s
ListViewl.Se1ected.Caption : =
FormItem.EditReference.Text;
ListViewl.Selected.ImageIndex : =
FormItem.ComboType.ItemIndex;
ListViewl.Selected.Sub1tems [O] : =
Form1tem.EditAuthor.Text;
ListViewl.Selected.SubItems [I] : =
Form1tem.EditCountry.Text;
end ;
end ;
end;
Se puede ver el efecto de este codigo en la figura 7.11. Observe que el codigo
utilizado para leer el valor de un elemento nuevo o modificado es similar. En
general, hay que evitar este tip0 de codigo duplicado y colocar las sentencias de
codigo compartidas en un metodo aiiadido a1 cuadro de dialogo. En este caso; el
metodo podria recibir como parametro un objeto TList I t e m y copiar en el 10s
valores adecuados.
Marca Canlu
Eapaiia
Figura 7.11. El cuadro de dialogo del ejernplo RefList2 utilizado en modo edicion.
- . .- - - ... . - . - .. - - .
.Bob
C njy Name
Jane
Jelf Name
John
J iha
Ma~k Name
Martha
Mn,,
Name
Name
I Sample label
Figura 7.12. Los tres forrnularios del ejernplo DlgApply en tiempo de ejecucion (un
formulario principal y dos cuadros de dialogo).
Cuando el usuario hace clic sobre el boton Apply, el programa copia el estilo
de la etiqueta de muestra en cada una de las etiquetas del formulario, en lugar de
tener en cuenta 10s valores de las casillas de verificacion:
procedure TStyleDial.ApplyBitBtnClick(Sender: TObject);
begin
Forml.Labell.Font.Style : = LabelSarnple.Font.Sty1e;
Forrnl.Label2.Font.Style : = LabelSample.Font.Style;
Esta segunda version del codigo es realmente mas lenta, porque tiene que
realizar mas operaciones, per0 la diferencia no se podra percibir porque sigue
siendo muy rapido.
Por supuesto, este enfoque tambien es mas flesible: si se aiiade una nueva
etiqueta, solo se necesita cambiar el limite superior para el bucle f o r , siempre
que todas las etiquetas tengan numeros consecutivos.
Hay que darse cuenta de que cuando el usuario hace clic sobre el boton Apply,
no se cierra el cuadro de dialogo (solo el boton Close tiene este efecto). Hay que
considerar tambih que este cuadro de dialogo no necesita codigo de inicializacion
porque el formulario no se destruye, y sus componentes mantienen su estado cada
vez que se muestra el cuadro de dialogo. Sin embargo, en la version CLX del
programa, QDlgApply, el dialogo es modal, incluso aunque se llame con el meto-
do Show.
A
Elector - - I r Eismph - I
--- --
NOTA: Qt ofiece un conjunto similar de cuadros de d i h g o predeibiidos,
pero el conjunto de opciones suele ser mis limitado. La versih Q C o d g
del ejemplo pemite experimentar con estas configuracianaa,El programa
CLX tiene menos elementos de mmu, ya que algunas de opeiones no se
encuentran disponibles. Adern&, hay otros cambioa miaim08 en el M g o
fuente.
- ---- ~ - ~- -~ -
Figura 7.14. El formulario principal del ejemplo Splash, con la pantalla inicial (se
trata de la version Splash2).
Existen tres versiones de este programa (ademas de las tres versiones corres-
pondientes para CLX). A1 ejecutar SplashO, el problema es que para la operacion
inicial, que se realiza en el metodo FormCreate, se emplea mucho tiempo.
Cuando arrancamos el programa, tarda varios segundos en mostrar el formulario
principal. Si el ordenador es muy rapido o muy lento, podemos cambiar el limite
superior del bucle for del metodo FormCreate para que sea mas rapido o mas
lento. Este programa tiene un cuadro de dialogo sencillo con un componente de
imagen, un titulo y un boton de mapa de bits, todo colocado en un panel que ocupa
toda la superficie del cuadro "Acerca de". Este formulario aparece cuando selec-
cionamos la opcion del menu Help>About. Pero lo que queremos en realidad es
mostrar el cuadro "Acerca den mientras arranca el programa. Podemos ver este
efecto a1 ejecutar Splashl y Splash2, que muestran una pantalla inicial mediante
dos tecnicas distintas.
En primer lugar, hemos afiadido un metodo a la clase TAboutBox. Este
metodo, llamado Make S p l a s h, cambia algunas propiedades del formulario para
que la pantalla inicial encaje como formulario de pantalla inicial. Basicamente
elimina el borde y el titulo, oculta el boton OK, hace que el borde del panel sea
grueso (para sustituir el borde del formulario) y, a continuacion, muestra el for-
mulario y lo pinta inmediatamente:
procedure TAboutBox.MakeSplash;
begin
Borderstyle : = bsNone;
BitBtnl-Visible : = False;
Panell.BorderWidth : = 3;
Show;
Update;
end;
begin
Application.Initialize;
// c r e a y m u e s t r a e l f o r m u l a r i o i n i c i a l
SplashAbout := TAboutBox .Create (Application);
try
SplashAbout.MakeSp1ash;
// c o d i g o e s t d n d a r ...
Application.CreateForm(TForml, Forml);
// e l i m i n a e l f o r m u l a r i o i n i c i a l
SplashAbout.Close;
finally
SplashAbout.Free;
end;
Application.Run;
end.
Esta tecnica solo tiene sentido solo si se tarda en crear el formulario principal
de la aplicacion, para ejecutar su codigo de arranque (como en este caso) o para
abrir tablas de bases de datos. Fijese en que la pantalla inicial es el primer formu-
lario que se crea, per0 como el programa no usa el metodo CreateForm del
objeto Application, este no se transforma en el formulario principal de la
aplicacion. En ese caso: cerrar la pantalla inicial finalizaria el programa.
Un enfoque alternativo es mantener el formulario inicial en pantalla algo mas
de tiempo y utilizar un temporizador para librarse de el. Esta tecnica se utiliza en
el ejemplo Splash2. Este ejemplo tambien utiliza un enfoque distinto para crear el
formulario inicial: en lugar de crear el formulario inicial en el codigo fuente del
proyecto, lo crea a1 comienzo del metodo FormCreate del formulario principal.
procedure TForml. FormCreate ( S e n d e r : T O b j e c t ) ;
var
I: Integer;
SplashAbout: TAboutBox;
begin
// c r e a y m u e s t r a e l f o r m u l a r i o i n i c i a l
SplashAbout : = TAboutBox.Create ( A p p l i c a t i o n ) ;
SplashAbout.MakeSp1ash;
// c o d i g o l e n t o ( o m i t i d o ) . . .
// e l i m i n a e l f o r m u l a r i o i n i c i a l , d e s p u e s d e u n t i e m p o
SplashAbout.Timerl.Enab1ed : = True;
end ;
Hay algo mas que solucionar. El formulario principal aparecera mas tarde y
delante del formulario inicial, a menos que lo convirtamos en un formulario fijo
por encima de la pantalla. Por esa razon, hemos aiiadido una linea a1 metodo
Makesplash del cuadro "Acerca de" en el ejemplo Splash2:
Parte II
Arquitecturas
orientadas
a objetos
en Delphi
La arauitectura de
las aplicaciones
Delphi
El objeto Application
Se ha mencionado el objeto global Application en multiples ocasiones,
per0 dado que este capitulo se centra en la estructura de las aplicaciones Delphi,
pasemos a describir en detalle este objeto global y su clase correspondiente.
App 1icat ion es un objeto global de la clase TApp 1icat ion,definido en la
unidad Forms y creado en la unidad Controls. La clase TApplication es un
componente, per0 no se puede utilizar en tiempo de diseiio. Algunas de sus pro-
piedades pueden definirse directamente en la ficha Application del cuadro de
dialog0 Project Options, otras deben asignarse en el codigo.
Para controlar estos eventos, en cambio, Delphi incluye un componente muy
comodo, App 1icat ionEvent s. Ademas de permitir asignar controladores en
tiempo de diseiio, la ventaja de este componente es que permite usar multiples
controladores. Si situamos una instancia del componente App 1icationEvents
en dos formularios diferentes, cada uno de ellos podra controlar el mismo evento
y se ejecutaran ambos controladores. En otras palabras, multiples componentes
App 1icat io nEve nt s pueden encadenar sus controladores.
Algunos eventos que afectan a toda la aplicacion, como OnAct ivat e,
OnDeactivate, OnMinimize y OnRestore, permiten realizar un segui-
miento del estado de la misma. En el caso de otros eventos, 10s controles que 10s
reciben 10s reenvian a la aplicacion, como en el caso de OnActionExecute,
OnActionUpdate,OnHelp,OnHint,OnShortCut y OnShowHint.Por
ultimo, existe un controlador global de excepciones, 0nEx ception,el evento
O n ~ d l eutilizado para ejecutar procesos en segundo plano y el evento
OnMessage, que se activa siempre que se envia directamente un mensaje a
cualquier ventana o control de ventana de la aplicacion.
Aunque su clase hereda directamente de T C o m p o n e n t , el objeto
Application tiene asociada una ventana. La ventana de la aplicacion esta
oculta per0 aparece en la barra de tareas. Por este motivo Delphi llama Form1 a
la ventana y Pro j e ct 1 a1 icono correspondiente en la barra de tareas.
La ventana relacionada con el objeto Application ( la ventana de la apli-
cacion) sirve para mantener todas las ventanas de una aplicacion juntas. El hecho
de que todos 10s formularios de alto nivel de un programa tengan invisible esta
ventana propietaria resulta fundamental, por ejemplo, cuando se activa la aplica-
cion. De hecho. cuando las ventanas de un programa estan detras de las de otros
programas. a1 hacer clic sobre una ventana de la aplicacion todas las ventanas de
la aplicacion apareceran en primer termino. Es decir, la ventana invisible se utili-
za para conectar 10s diferentes formularios de la aplicacion. En realidad, la venta-
na de la aplicacion no esta oculta; porque eso afectaria a su comportamiento, solo
tiene una altura y anchura nulas y, por lo tanto, no se ve.
-- --, -
TRUCO: En Windows, las operaciones de minimizar y maximizar e s t h
asociadas Dor defect0 con sonidos del sistema y con un efecto visual anima-
producen el sonido y muestran el
Cuando se crea una nueva aplicacion en blanco, Delphi genera un codigo para
el archivo de proyecto. que incluye lo siguiente:
begin
Application.Initialize;
Application.CreateForm(TForml, Forml);
Application.Run;
end.
TRUCO: Para evitar discrepancias entre 10s dos titulos, se puede cambiar
el titulo de la aplicacion en tiempo de disefio. En caso de que cambie en
tiempo de ejecucion, se puede copiar el titulo del formulario a1 de la aplica-
cion con el siguiente codigo: Application.Title := Forml.
Caption.
p r o c e d u r e TForml.FormDeactivate(Sender: TObject);
begin
LabelForm.Caption : = 'Form2 N o Activo';
LabelForm.Color : = clBtnFace;
end;
Figura 8.1. El ejemplo ActivApp muestra si la aplicacion esta activa y cual de sus
formularios esta activo.
Hay que tener en cuenta que debe desactivarse la creacion del formulario se-
cundario mediante la pagina Forms del cuadro de dialog0 Project Options.
Una de las partes clave del programa es el controlador del evento oncreate del
formulario, que rellena la lista por primera vez y, a continuacion. conecta un
controlador a1 evento OnAc tiveFormchange:
procedure TMainForm.FormCreate(Sender: TObject);
begin
FillFormsList (Self);
/ / define el contador de formularios secundarios a 0
nForms : = 0;
/ / define un controlador de eventos para el objeto en
pantalla
Screen.0nActiveFormChange : = FillFormsList;
end;
OnActiveFormChange.
Aclive Farn : S e c d 3
MI
Farns: 4
TSecondForm . Second 3
TSecondForm - Second 2
TSecondForrn .Second 1
TMa~nForrn- Sc~eenInfo
I
Figura 8.2. El resultado del ejemplo Screen con algunos formularios secundarios.
Cada uno de 10s formularios secundarios tiene un boton Close que podemos
pulsar para eliminarlos. El programa controla el evento onclose,definiendo el
parametro Action como caFree,de mod0 que en realidad el formulario se
destruye cuando lo cerramos. Este codigo cierra el formulario, per0 no actualiza
la lista de ventanas como es debido. El sistema desplaza primer0 el foco a otra
ventana, activando el evento que actualiza la lista y destruye el antiguo formula-
rio solo despues de dicha operacion.
Una priinera aprosimacion puede plantear que para actualizar la lista de ven-
tanas adecuadamente puede introducirse un retraso, enviando un mensaje de
Windows definido por el usuario. Pero debido a que el mensaje enviado es encola-
do y no es tratado inmediatamente, aunque el envio se realice a1 final de la esis-
tencia del formulario secundario, el formulario principal lo recibira cuando el
otro formulario sea destruido. El truco esta en poder enviar el mensaje con el
controlador del evento OnDestroy del formulario secundario. Para ello, es ne-
cesario referirse al objeto MainForm,aiiadiendo una sentencia uses en la parte
de implernentacion de esta unidad. Hemos enviado un mensaje wm User,contro-
lado por un mCtodo message especifico del formulario como pode-
mos ver a continuacion:
public
procedure Childclosed (var Message: TMessage) ; message
-User;
Con este codigo, el cuadro de lista siempre muestra todos 10s formularios de la
aplicacion. ,
Existe otra alternativa, una solucion mas orientada a Delphi. El truco esta en
considcrar que cada vez que un componente es destruido, avisa a su propietario
acerca del evento llamando a1 metodo Notification definido en la clase
TComponent.Dado que 10s forn~ulariossecundarios son propiedad del formu-
lario principal, como se ha mostrado en el codigo del metodo NewButtonClick,
puede sobrecargarse este metodo y simplificarse el codigo (vease el directorio
Screen2 de 10s ejemplos adjunto para ver el codigo de esta version):
procedure TMainForm.Notification (AComponent: TComponent;
Operation: Toperation) ;
begin
i n h e r i t e d Not if ication (AComponent, Operation) ;
i f (Operation = opRemove) and Showing and (AComponent i s
TForm) then
FillFormList ;
end ;
De eventos a hilos
Para comprender como funcionan las aplicaciones de Windows internamente,
dedicaremos unos momentos a explicar como soporta este entorno la multitarea.
Es necesario comprender tambien, el papel de 10s temporizadores (y el componen-
te T i m e r ) y 10s calculos en segundo plano (o en espera), asi como el metodo
P r o c e s s M e s s a g e s del objeto global A p p l i c a t i o n .
Resumiendo, debemos sumergirnos mas en la estructura orientada a eventos de
Windows y su soporte a la multitarea. Dado que este libro esta dedicado a la
programacion con Delphi no entraremos en detalles sobre este tema, per0 dare-
mos una vision global para aquellos lectores que tengan poca experiencia en
programacion con la API de Windows.
Multihilo en Delphi
Cuando es necesario realizar operaciones en segundo plano, o cualquier proce-
so no estrictamente relacionado con la interfaz de usuario, se puede utilizar la
aproximacion mas correcta desde el punto de vista tecnico: crear un hilo de ejecu-
cion separado dentro del propio proceso. La programacion multihilo puede pare-
cer un tema complejo, pero, realmente, no es tan complicado, aunque tenga que
ser considerado cuidadosamente. Es conveniente conocer a1 menos 10s fundamen-
tos de la programacion multihilo porque, en el mundo de 10s sockets y la progra-
macion para Internet, hay pocas cosas que se puedan hacer sin hilos.
La biblioteca RTL de Delphi contiene una clase TThread que permite crear y
controlar hilos. La clase TThread no se utiliza nunca directamente dado que es
una clase abstracta (una clase con un metodo abstracto virtual). Para usar hilos,
sc hereda de T T h r e a d y se utilizan las caracteristicas de esta clase base.
La clase T T h r e a d tiene un constructor con un unico parametro
( C r e a t e s u s p e n d e d ) que permite elegir entre arrancar el hilo inmediatamente
o dejarlo en espera hasta mas tarde. Cuando el objeto hilo arranca automaticamente,
o cuando se reanuda su ejecucion, mantiene el metodo E x e c u t e en funciona-
miento hasta el final. La clase proporciona una interfaz protegida que incluye dos
mctodos basicos para 10s hilos:
procedure Execute:virtual; abstract;
p r o c e d u r e Synchronize(Method: TThreadMethod);
tYPe
TPrimeAdder = c l a s s (TThread)
private
FMax, FTotal, FPosition: Integer;
protected
procedure Execute; override;
procedure ShowTotal;
procedure UpdateProgress;
public
property Max: Integer read FMax write FMax;
end ;
procedure TPrimeAdder.ShowTota1;
begin
ShowMessage ( ' Thread: ' + IntToStr ( FTotal) ) ;
end :
procedure TPrimeAdder-Updateprogress;
begin
Forml.ProgressBar1.Position : = £Position;
end :
Uso de un mutex
Un mutex, u objeto de exclusion mutua, es una tecnica totalmente distinta. Es
una tecnica comun de Win32, utilizada normalmente para sincronizar hilos. Aqui,
utilizamos un mutex para sincronizar dos aplicaciones diferentes o, para ser mas
precisos, dos instancias de la misma aplicacion.
Cuando una aplicacion ha creado un mutex con un nombre determinado, puede
probar si el objeto ya lo posee otra aplicacion, llamando a la funcion de la API de
Windows Wait ForSingleOb jec t.De no ser asi, la aplicacion que llama a
esta funcion se transforma en la aplicacion propietaria. Pero si el mutex ya tiene
un propietario, la aplicacion espera hasta que se consume el tiempo (el segundo
parametro de la funcion). Entonces devuelve un codigo de error.
Para implementar esta tecnica, podemos usar el siguiente codigo fuente del
proyecto:
var
hMutex: THandle;
begin
hMutex : = CreateMutex (nil, False, 'OneCopyMutex ' ) ;
if WaitForSingleObject (hMutex, 0 ) <> wait-Timeout then
begin
Application.Initialize;
Application.CreateForm(TForm1, Forml);
Application.Run;
end;
end.
Si ejecutamos dos veces este ejemplo, veremos que se crea una nueva copia
temporal de la aplicacion (aparece el icono en la barra de tareas) y despues se
destruye cuando se ha consumido el tiempo Esta tecnica es realmente mas robusta
que la anterior, per0 tiene un pequeiio problema que esta en activar la instancia
existente de la aplicacion y encontrar su formulario, para lo que podemos emplear
una tecnica mejor.
Esta funcion, a la que se llama para cada ventana no hijo del sistema, verifica
el nombre de cada clase de ventana, buscando el nombre de la clase TForml.
Cuando encuentra una ventana con esta cadena en su nombre de clase, usa
GetModule Fi lename para extraer el nombre del archivo ejecutable de la
aplicacion que pertenece a1 formulario correspondiente. Si el nombre del modulo
corresponde a1 del programa en uso (que se extrajo anteriormente con un codigo
similar), podemos estar casi seguros de que encontraremos una instancia anterior
del mismo programa. Veamos el mod0 en que podemos llamar a la funcion enu-
merada:
var
FoundWnd: THandle;
ModuleName: string;
begin
i f Wait ForSingleObj ect (hMutex, 0) <> wait-Timeout then
- ..
else
begin
/ / o b t i e n e e l n o m b r e d e l m o d u l o en u s o
SetLength (ModuleName, 200) ;
GetModuleFileName (HInstance, PChar (ModuleName), Length
(ModuleName)) ;
ModuleName : = PChar (ModuleName); // a j u s t a l a l o n g i t u d
// b u s c a una v e n t a n a d e una i n s t a n c i a p r e v i a
EnumWindows (@EnumWndProc, 0) ;
NOTA: Microsofi se aleja cada vez mas del modelo MDI utilizado en 10s
dias de Windows 3. Incluso las versiones recientes de Office tienden a
utilizar ventanas principales especificas para cada documento, la tCcnica
clasica SDI (Interfaz de Documento ~ n i c o ) En
. cualquier caso, MDI no
esta muerto y, a veces, puede ser una estructura util.
Tambien podemos abrir ventanas hijo, minimizar o maximizar cada una de las
mismas, cerrarlas y usar el menu desplegable Window para movernos de unas a
otras. Supongamos ahora que queremos cerrar alguna de estas ventanas hijo, para
despejar el area cliente del programa. Si hacemos clic en las cajas Close de
alguna de estas ventanas, esta se minimiza, en lugar de ocultarse como ocurria
hasta ahora. Los formularios cerrados en Delphi siguen existiendo aunque no
Sean visibles.
En el caso de las ventanas hijo, ocultarlas no funcionara porque el menu MDI
Window y la lista de ventanas continuara mostrando las ventanas hijo existentes,
aunque esten ocultas. Por esta razon, Delphi minimiza las ventanas MDI hijo
cuando intentamos cerrarlas. Para resolverlo, debemos borrarlas cuando son ce-
rradas dando el valor ca Free a1 p a r h e t r o de referencia Act ion del evento
OnClose.
o lnleresanle
lgo Inlelesante
lgo Intelesante
lgo lntelesanle
lyo ~nteleranle
h o ~nlererante
Figura 8.3. El prograrna MdiDemo hace uso de una serie de acciones Delphi
predefinidas conectadas a un menu y una barra de herrarnientas.
Figura 8.4. El resultado del ejemplo MdiMulti, con una ventana hijo que rnuestra
10s circulos.
Si preparamos un menu principal para el formulario hijo, sustituira a1 menu
principal de la ventana marco cuando se active el formulario hijo. De hecho, una
ventana MDI hijo no puede tener un menu propio. Pero el hecho de que una
ventana hijo no pueda tener menus no deberia preocuparnos porque este es el
comportamiento estandar de las aplicaciones MDI. Podemos usar la barra de
menu de la ventana marco para mostrar 10s menus de la ventana hijo. Una tecnica
mejor es mezclar la barra de menu de la ventana marco y la del formulario hijo.
Por ejemplo, en este programa, el menu del formulario hijo puede colocarse entre
el marco de 10s menus desplegables File y Window de la ventana. Para ello se
usan 10s siguientes valores GroupIndex:
Menu desplegable File, formulario principal: 1.
Menu desplegable Circle, formulario hijo: 2.
Menu desplegable Window, formulario principal: 3 .
A1 usar estas definiciones para 10s indices de grupo del menu, la barra de menu
de la ventana marco tendra dos o tres menus desplegables. A1 arrancar, la barra
de menu tiene dos menus. Desde el momento en que creamos una ventana hijo,
existen tres menus y cuando se cierra la ultima ventana hijo, el menu desplegable
Circle desaparece.
El segundo tip0 de formulario hijo muestra una imagen en movimiento. El
cuadrado, un componente Shape,se mueve por la zona de cliente del formulario
a intervalos fijos de tiempo, utilizando un componente Timer y rebota a1 chocar
contra 10s extremos del formulario, cambiando su direccion. Este proceso de cam-
bio utiliza un complejo algoritmo que no examinaremos aqui, ya que el objetivo
principal del ejemplo es mostrar como se comporta la combinacion de menus
cuando tenemos un marco MDI con formularios hijo de diferentes tipos. (Queda
en manos del lector examinar el codigo fuente para ver como funciona).
El formulario principal
Tenemos que integrar ahora 10s dos formularios hijo en una aplicacion MDI.
El menu desplegable File tiene dos elementos de menu New aparte, que se usan
para crear una ventana hijo de cualquier tipo. El codigo usa un solo contador de
ventanas hijo. Como alternativa, podriamos usar dos tipos diferentes de contado-
res para 10s dos tipos de ventanas hijo. El menu Window usa las acciones MDI
predefinidas. Desde el momento en que un formulario de este tip0 aparece en
pantalla, su barra de menu se mezcla automaticamente con la barra de menu
principal. Cuando seleccionamos un formulario hijo de uno de 10s dos tipos, la
barra de menu cambia de acuerdo con ello. Cuando se hayan cerrado todas las
ventanas hijo, se configura de nuevo la barra de menu original del formulario
principal. A1 usar indices de menu adecuados, permitimos que Delphi lo haga
todo de forma automatica, como muestra la figura 8.5.
Figura 8.5. La barra de menu de la aplicacion MdiMulti cambia de forrna autornatica
para reflejar la ventana hijo seleccionada, corno puede verse cornparando la barra de
menu con la de la figura 8.4.
Se han aiiadido unos pocos elementos a1 menu del formulario principal para
cerrar todas las ventanas hijo y mostrar algunas estadisticas sobre ellas. El meto-
do relacionado con el comando C o u n t analiza la propiedad MDIChildren
para contar el numero de ventanas hijo de cada tipo (usando el operador RTTI
is):
for I : = 0 t o MDIChildCount - 1 do
if MDIChildren is TBounceChildForm then
Inc (NBounce);
else
Inc (NCircle) ;
A mcnos que tengamos una razon para cambiar el comportamiento que tiene
por defecto esta ventana de sistema, podemos guardar cl procedimiento original y
llamarlo para obtener el procesamiento por defecto. Los dos procedimientos (vie-
jo y nuevo) a 10s que se refieren 10s dos punteros de funcion se guardan en dos
campos locales del formulario:
private
OldWinProc, NewWinProc: Pointer;
procedure NewWinProcedure (var Msg: TMessage) ;
NOTA: Delphi incluye otra funcion, 10s marcos, que imita la herencia de
formularios visuales. En arnbos casos, se puede trabajar en tiempo de dise-
iio en las dos versiones de un formulario/marco. Sin embargo, en la heren-
cia de formularios visuales, definimos dos clases distintas (padre y derivada),
mientras que con 10s marcos, trabajamos en una clase y en una instancia.
Los marcos se trataran mas adelante en este capitulo.
a a Acb&
Form2
Mukltler Vfl F m Dialogs Rolccts
r 6 !nhrit r
Figura 8.6. El cuadro de dialogo New Items permite crear un formulario heredado
Figura 8.7. Los dos formularios del ejemplo VFI en tiempo d e ejecucion
Cada uno de 10s botones del primer formulario tiene un controlador OnClic k .
El primer boton muestra el formulario heredado llamando a su metodo Show,el
segundo y tercer boton llaman a1 procedimiento Beep y el ultimo boton muestra
un mensaje sencillo.
En el formulario heredado, primer0 deberiamos eliminar el boton Show, por-
que el formulario secundario ya esta visible. Sin embargo; no podemos borrar un
componente de un formulario heredado. Podemos dejar el componente, per0 defi-
nir su propiedad Visible como False (el boton seguira estando ahi per0 no
sera visible). Los otros tres botones estaran visibles per0 con distintos
controladores. Esto es sencillo de conseguir. Si seleccionamos el evento OnClic k
de un boton en el formulario heredado (haciendo doble clic en el), obtendremos un
metodo ligeramente diferente a1 predefinido; porque incluye la palabra clave
inherited.Esta palabra clave representa m a llamada a1 controlador de even-
tos correspondiente. Esta palabra clave siempre la aiiade Delphi, incluso si el
controlador no se define en la clase padre o si el componente no esta presente en la
clase. Es sencillo ejecutar el codigo del formulario base y realizar algunas opera-
ciones mas:
procedure TForm2.Button2Click(Sender: TObject);
begin
inherited;
ShowMessage ( 'Hi' ) ;
end;
Formularios polimorficos
Si quercmos afiadir un controlador de eventos a1 formulario y despues conver-
tirlo en formulario heredado, no hay forma de referirse a 10s dos metodos que
usan una variable comun de la clase basica; porque 10s controladores de eventos
usan por defect0 enlace estatico.
Veamos un ejemplo: queremos crear un formulario visor de mapas de bits y un
visor de texto en el mismo programa. Los dos formularios tienen elementos simi-
larcs, una barra de herramientas similar, un menii similar, un componente
OpenDialog y componentes diferentes para ver 10s datos. Por lo tanto, decidimos
crear un formulario de clase basica que contenga 10s elementos comunes y herede
10s dos formularios de el. En la figura 8.8, podemos ver 10s tres formularios en
tiempo de diseiio.
Figura 8.8. El forrnulario d e clase basica y 10s dos forrnularios heredados del
ejernplo PoliForrn.
La otra clase heredada tiene un codigo similar, que carga el texto en el compo-
nente memo. El proyecto tiene un formulario mas, un formulario principal con
dos botones, utilizados para cargar de nuevo 10s archivos en cada uno de 10s
formularios de visor. El formulario principal es el unico formulario creado por el
proyecto a1 iniciar. El formulario de visor generic0 nunca se crea: solo es una
clase basica generica, que contiene codigo comun y componentes de dos subclases.
Los formularios de las dos subclases se crean en el controlador de eventos
oncre at e del formulario principal:
p r o c e d u r e TMainForm.FormCreate(Sender: T O b j e c t ) ;
var
I: Integer;
begin
FormList [ l ] : = TTextViewerForm.Create (Application);
FormList [2] : = T1mageViewerForm.Create (Application);
for I := 1 to 2 do
FormList [I] .Show;
end ;
//ReloadButtonZClick
for I := 1 to 2 do
FormList [I] .LoadFile;
type
TImageViewerForm = class (TViewerForm)
procedure ButtonLoadClick (Sender: TOb ject) ; override;
public
procedure LoadFile; override;
end;
......
&d(~I-l Du I , .: .; .: .; .; .:
meted , ... ... ... ... ... ...
I
Some !ex(
Figura 8.9. Un rnarco y dos instancias del rnisrno en tiempo de disetio, en el ejemplo
Frarnes2.
Marcos y fichas
Cuando tenemos un cuadro de dialogo con muchas fichas llenas de controlesj
el codigo subyacente a1 formulario se vuelve muy complejo, porque todos 10s
controlcs y metodos se declaran en un ilnico formulario. Ademas, a1 crear todos
estos componentes (e iniciarlos) podriamos originar un retraso en la aparicion del
cuadro de dialogo. En realidad, 10s marcos no reducen el tiempo de construccion
e inicializacion dc 10s formularios cargados de forina equivalente. A1 contrario, es
mas complicado cargar 10s marcos para el sistema de slrenrning que cargar com-
ponentes simples. Sin embargo, a1 utilizar marcos se puedan cargar solo las fi-
chas visibles de un cuadro de dialogo, reduciendo el tiempo de carga inicial, que
es el que percibe cl usuario.
Los marcos pueden resolver estos dos temas. En primer lugar, se puede dividir
facilmente el codigo de un formulario unico complejo en un marco por ficha. El
formulario albergara sencillamente todos 10s marcos en un PageControl. Esto
ayuda realmente a tener unidades mas sencillas y mas centradas, y hace que
resulte mas sencillo reutilizar una ficha concreta en un cuadro de dialogo diferen-
te o una aplicacion. Reutilizar una unica ficha de un PageControl sin usar un
marco o un formulario incrustado es muy complicado (para conocer una tecnica
alternativa vease el apartado "Formularies en fichas").
Para ilustrar esto, hemos creado el e.jemplo FramePag,que tiene algunos mar-
cos colocados en el interior de las tres fichas de un Pagecontrol, como muestra la
figura 8.10. Todos 10s marcos estan alineados con la zona del cliente, utilizando
toda la superficie de la hoja de solapa (la ficha) en la que se encuentran. En reali-
dad, dos de las fichas tienen el mismo marco, per0 dos de las instancias del inarco
tienen algunas diferencias en tiempo de diseiio. El marco, llamado Frame3 en el
ejemplo, tiene un cuadro de lista que contiene un archivo de texto al arrancar y tiene
botones para modificar 10s elementos de la lista y guardarlos en un archivo. El
nombre de archivo se coloca en una etiqueta, para poder seleccionar con facilidad
un archivo en tiempo de diseiio cambiando el titulo de la etiqueta.
Figura 8.10. Cada ficha del ejemplo FramePag contiene un marco, separando el codigo
de este formulario complejo en trozos mas rnanejables.
Formularios en fichas
A pesar de que podemos utilizar marcos para definir las fichas de un
PageControl en tiempo de diseiio, tambien podemos usar otros formula-
rios en tiempo de ejecucion. Esta tecnica nos da la flexibilidad de tener las
fichas definidas en unidades y ficheros DFM separados y, al mismo tiempo,
permite utilizar esos formularios como ventanas independientes.
Cuando tenemos un formulario principal con un control de fichas y uno o
mas formularios secundarios para mostrar en el, todo lo que tenemos que
hacer es escribir este codigo para crear 10s formularios secundarios y si-
tuarlos en las fichas:
var
Form: TForm;
Sheet: TTabSheet;
begin
// c r e a r una h o j a d e t a b u l a c i o n e n e l c o n t r o l f i c h a
Sheet : = TTabSheet.Create(PageContr011);
Sheet.PageContro1 : = PageControll;
// c r e a r e l f o r m u l a r i o y s i t u a r l o e n l a h o j a d e t a b u l a c i o n
Form := TForm2 .Create (Application);
Form.BorderStyle := bsNone;
Form.Align : = alclient;
Form. Parent := Sheet;
Form. Visible := True;
// a c t i v a r l o y p o n e r l e t i t u l o
PageContro1l.ActivePage : = Sheet;
Sheet.Caption : = Form.Caption;
end;
p r o c e d u r e TForml.ShowFrarne(FrameNarne: string);
var
Frame: TFrame;
FrameClass: TFrameClass;
begin
Frame : = Findcomponent (FrameName + '1 ' ) a s TFrame;
i f n o t Assigned (Frame) t h e n
begin
FrameClass : = TFrameClass ( Findclass ( 'T' + FrameName) ) ;
Frame : = FrameClass .Create (Self);
Frame.Parent : = Tab;
Frame.Visible : = True;
Frame.Name : = FrameName + '1';
end;
Frame.BringToFront;
end ;
p r o c e d u r e TSaveStatusForm.DoDestroy;
var
Ini: TIniFile;
begin
Ini : = T I n i F i l e - C r e a t e (ExtractFileName (Application.ExeName));
Ini .WriteInteger (Caption, ' I z q u l e r d a ', Left) ;
Ini .WriteInteger (Caption, ' A r r i b a ', Top) ;
Ini .WriteInteger (Caption, ' A n c h u r a ', Width) ;
1ni.WriteInteger (Caption, ' A l t u r a ', Height) ;
Ini. Free;
inherited;
end ;
Estc cs un ejemplo muy sencillo per0 podemos definir una clase compleja en su
interior. Para utilizar esta como clase base para 10s formularios que construya-
mos, debemos dejar que Delphi c:ee 10s formularios como siempre (sin herencia)
y, despues, actualizaremos la declaracion a algo parecido a este:
type
TFormBitmap = class (TSaveStatusForm)
Imagel: TImage;
OpenPictureDialogl: TOpenPictureDialog;
Aunque es tan sencilla como parece, esta tecnica es muy potente, porque lo
unico que tenemos que hacer es cambiar la definicibn de 10s formularios de nues-
tra aplicacion para referirnos a esta clase base. Incluso si este paso nos resulta
muy tedioso porque, quiza, queramos en algun momento cambiar esta clase en
nuestro programa, podemos usar un truco extra: las clases de interposicion
- - - - -- - - -- - -
c s p c c ~ ~ ~UIIc a
valor
~ p~cu~lmu
para
u uiuuar SI lir c o ~ ~ c s p o u u r c rcn-
~u
trada no existe en un archivo INI.
Debemos tener en cuenta que Delphi utiliza 10s ficheros IN1 muy a
menudo, per0 con nombres diferentes. Por ejemplo, 10s archivos de es-
critorio (.dsk) y de opciones (.dof) e s t h estructurados como archivos
INI.
Las clases TRegistry y TRegIniFile: El Registro es una base de datos
jerarquica de inforrnacion sobre el ordenador, la configuraci6n de soft-
ware y las preferencias del usuario. Windows tiene un conjunto de fun-
ciones API para interactuar con el Registro. Basicamente abrimos una
clave (o carpeta) y, a continuacion, trabajamos con subclaves (o
subcarpetas) y con valores (o elementos), pero debemos ser conscientes
de la estructura y 10s datos del Registro.
Delphi ofrece basicamente dos tkcnicas a1 uso del Registro: la clase
T R e g i s t r y un encapsulado del Registro API, mientras que Ia clase
T R e g I n i F i l e la interfaz de la clase T I n i F i l e per0 guardando 10s
datos en el Registro. Esta clase es la opcion natural para conseguir el
intercambio entre la informacibn basada en IN1 y las veniones basadas
en Registro de un mismo programa. Cuando creamos un objeto
TReg I n i F i l e , nuestros datos termina.n en la infonaacb5n de usuario
actual, por lo que nomlmente usamos un ~wstructorcmm:
IniFile := TRegIniFile.Create
( 'Software\MyCompany \MyProgramp)
;
NOTA: Esta tecnica es mucho mas antigua que CLXNCL. Por ejemplo,
las unidades de servicio y panel de control definen su propio objetaI
TApplication. que no tiene nada que ver con el TApplication usa-
.. . . . -.-- . - . . .. . -
do por las apllcaciones Vlsuales V L L y dekmldo en la unidad k-oms.
Existe una tecnica que hemos visto mencionada con el nombre de "clases de
interposicion", que sugeria que se sustituyesen 10s nombres de clase estandar de
Delphi por versiones propias, que tuviesen el mismo nombre de clase. Asi, pode-
mos usar el diseiiador Delphi que se refiere a componentes estandar de Delphi en
tiempo de diseiio, per0 usando nuestras propias clases en tiempo de ejecucion.
La idea es sencilla. En la unidad SaveStatusForm, podriamos definir la nueva
clase de formulario asi:
type
TForm = class ( F o r m s . TForm)
protected
procedure D o c r e a t e ; override;
procedure D o D e s t r o y ; override;
end;
Uso de interfaces
Otra tecnica, que es ligeramente mas compleja per0 mas potente que la defini-
cion de una clase de formulario comun consiste en tener formularios que
implementes interfaces especificas. Asi, podemos tener formularios que
implementen una o mas interfaces, consulten cada formulario para saber que
interfaz implementa y llamen a 10s metodos soportados.
Como ejemplo, hemos definido una interfaz simple para cargar y almacenar:
type
IFormOperations = i n t e r f a c e
[ ' {DAC,FDB76-0703-4A40-A951-1OD1 40B4AZAO) '1
p r o c e d u r e Load;
p r o c e d u r e Save;
end;
El codigo de ejemplo incluye 10s metodos Load y Save, que utilizan 10s
cuadros de dialog0 estandar para cargar o guardar la imagen (en el ejemplo, el
formulario hereda tambien de la clase TSaveStatus Form). Cuando una apli-
cacion tiene uno o mas formularios que implementan interfaces, podemos aplicar
un metodo de interfaz concreto a todos 10s formularios que lo soportan, con codi-
go como este (extraido del formulario principal del ejemplo FormIntf):
p r o c e d u r e TFormMain.btnLoadClick (Sender: T O b j e c t ) ;
var
i: Integer;
iFormOp: IFormOperations;
begin
f o r i : = 0 t o Screen.FormCount - 1 d o
i f Supports (Screen.Forms [i],' IFormOperations, iFormOp) t h e n
iFormOp-Load;
end ;
Tengamos en cuenta que en una aplicacion empresarial podemos sincronizar
todos 10s formularios con 10s datos de una empresa especifica o un evento empre-
sarial especifico. Ademas, a diferencia de la herencia, podemos tener varios for-
mularios que implementen cada uno varias interfaces, con combinaciones
ilimitadas. Esta es la razon por lo que utilizar una arquitectura como esta puede
mejorar mucho una compleja aplicacion Delphi y hacerla mucho mas flexible y
mas sencilla de adaptar para la implernentacion de cambios.
Listado 8.1. Una unidad para cornprobar agujeros de rnernoria, del ejemplo ObjsLeft.
unit SimpleMemTest;
interface
implementation
uses
Windows ;
var
msg: string;
initialization
finalization
if AllocMemCount > 0 then
begin
Str (AllocMemCount, msg) ;
Msg : = msg + ' b l o q u e s r e s t a n t e s e n l a p i l a ' ;
MessageBox ( 0 , PChar (msg), ' a g u j e r o d e memoria ' , MB-OK) ;
end ;
end.
r
~-
1
. - - - .-
TRUCO: Lon nombres de archivide 10s oaouetes s61o de diseiio de Delobi
comienzan con las letras DCL (por ejemplo, DCLSTDGO BPI,). Los nom- .
? -
r
-
bres de archivo de 10s paquetes solo de ejecuci6n comienzan con las letras
VCL (por ejemplo, VCL60 .BPL). Podemos, si queremos, utilizar la mis-
/ . .
ma teenlea en nuestros paquetes.
&kUePapa hid A
~dk c+.nm 1ud~0n1~0mbo
pas
J
El nombre del tip0 ascendente: la clase de componente de la que se quiere
heredar. En este caso podemos usar TComboBox.
El nombre de la clase del nuevo componente que vamos a crear. Podemos
usar TMdFontCombo.
La ficha de la Component Palette en la que queremos que aparezca el
componente, que puede ser una ficha nueva o una que ya exista. Podemos
crear una nueva ficha, llamada Md.
El nombre del archivo de la unidad en la que queremos que Delphi coloque
el codigo fuente del nuevo componente. Podemos escribir MdFont Box.
La ruta de busqueda actual (que deberia aparecer de forma automatica).
Hacemos clic sobre el boton OK y el asistente para componentes generara el
archivo fuente mostrado en el listado 9.1 con la estructura de nuestro componen-
te. El boton Install se puede usar para instalar el componente en un paquete de
forma inmediata. Veamos el codigo primer0 y despues trataremos la instalacion.
Listado 9.1. Codigo del TMdFontCombo, producido por el asistente para componentes.
u n i t MdFontBox;
interface
uses
windows, Messages, SysUtils, Classes, Graphics, Controls,
Forms, Dialogs, StdCtrls;
type
TMdFontCombo = c l a s s (TComboBox)
private
{ Private declarations 1
protected
{ Protected declarations 1
public
{ Public declarations 1
published
{ Published declarations ]
end;
p r o c e d u r e Register;
implementation
p r o c e d u r e Register;
begin
Registercomponents ( ' M d ' , [TMdFontCombo] ) ;
end;
end.
Uno de 10s elementos clave de este listado es la definicion de clase, que co-
mienza indicando la clase padre. La otra unica parte importante es el procedi-
miento R e g i s t e r . Como podemos ver, el asistente para componentes hace muy
poco trabajo.
r
r
r~ornvresue clase u~sr~rl~os.
ror esa razo11,la mayorla ue los uesarrollauores
de componentes en Delphi han escogido aiiadir un prefijo de dos o tres
letras a 10s nombres de nuestros componentes. En este libro, hemos escogi-
d o Md para identificar 10s componentes escritos en este. La ventaja de esta
tecnica esta en que podemos instalar un componente TMd F o n t C o m b o ,
aunque ya hayamos instalado un componente denominado T F o n t Combo.
Observe que 10s nombres de unidad han de ser unicos para todos 10s com-
ponentes instalados en el sistema, por lo que hemos aplicado el mismo
prefijo a los nombres de unidad.
Esto es todo lo que hay que hacer para crear un componente. Este codigo, por
supuesto, no incluye demasiado codigo. Ahora, solo hay que copiar todas las
fuentes del sistema en la propiedad I t e m s del cuadro combinado a1 arrancar.
Para ello, podemos intentar sobrescribir el metodo c r e a t e en la declaracion de
clase, afiadiendo la sentencia I t e m s : = S c r e e n . F o n t s . Sin embargo, esta
no es la tecnica adecuada. El problema esta en que no podemos acceder a la
propiedad I t e m s del cuadro combinado, antes de que el manejador de ventana
del componente este disponible. El componente no puede tener un manejador de
ventana hasta que se defina su propiedad P a r e n t y esa propiedad no se define en
el constructor, sin0 mas adelante.
Por esa razon, en lugar de asignar las nuevas cadenas en el constructor Create.
debemos realizar esta operacion en el procedimiento C r e a t e W n d , a1 que se
llama para crear el control ventana despues de que se construya el componente, se
defina su propiedad P a r e n t y su manejador de ventana este disponible. De
nuevo, ejecutamos el comportamiento predefinido y, a continuacion, podemos
escribir nuestro codigo personalizado. Podiamos habernos saltado el constructor
C r e a t e escribiendo todo el codigo en C r e a t e W n d , per0 hemos usado ambos
metodos iniciales para mostrar las diferencias entre ellos. Veamos la declaracion
de la clase del componente:
type
TMdFontCombo = class (TComboBox)
private
FChangeFormFont: Boolean;
procedure SetChangeFormFont(c0nst Value: Boolean);
public
constructor Create (AOwner: TComponent); override;
procedure CreateWnd; override;
procedure Change; override;
published
property Style default csDropDownList;
property Items stored False ;
property ChangeFormFont: Boolean
read FChangeFormFont write SetChangeFormFont default True;
end;
procedure TMdFontCombo.CreateWnd;
begin
inherited CreateWnd;
Items .Assign (Screen.Fonts) ;
/ / obtiene la fuente predefinida del forrnulario propietario
if FChangeFormFont and Assigned (Owner) and (Owner is TForm)
then
ItemIndex := Items. IndexOf ( (Owner as TForm) .Font .Name) ;
end:
Creacion de un paquete
Ahora, tenemos que instalar el componente en el entorno, usando un paquctc.
Para este ejemplo; podemos crcar un nucvo paquctc o utilizar uno cxistcntc, como
el paquete predefinido del usuario.
En cada caso, hay que selcccionar la orden dcl menu Component>lnstall
Component. El cuadro dc dialog0 rcsultantc ticnc una ficha para instalar cl
componentc en un paquetc cxistentc y una ficha para crcar un nucvo paquctc. En
este ultimo caso? simplemente tecleainos un nombre dc archivo y una dcscripcion
dcl paquctc. A1 haccr clic sobrc cl boton OK sc abrc el Package Editor (veasc
la figura 9. l ) , que tiene dos partcs:
La lista Contains: lndica 10s componentes incluidos en el paquete (0, para
scr mas exactos, las unidades que definen esos componentes).
La lista Requires: Indica 10s paquctcs necesarios para dicho paqucte.
Norinalinentc, nucstro paquete necesitara 10s paquetes rtl y vcl (el paquetc
de la bibliotcca cn ticmpo dc cjccucion y el paquete principal VCL), pero
podria neccsitar tambicn cl paqucte vcldb (que contiene la mayoria de las
clases relacionadas con bases dc datos) si 10s componentes del nuevo pa-
quctc rcalizan alguna operacion relacionada con bases de datos.
22 2 s T
I
Compk Add Remove Opl~ons
I ..
NOTA: Los nombres de Daauetes desde Debhi 6 va no son es~ecificosde I
la version, aunque 10s paquetes compilados tengan todavia el numero de la
version en el nombre del archivo. Para conocer mas detalles acerca de como
- - . ..
I se logra. esro recnlcamenre
1 * . *
poaemos1.
acuair 3. 1 *
mas aaelanre,_ J 1 I
a la seccron que (
I trata 10s cambios en 10s nombres de proyecto y biblioteca. I
Si afiadimos el componente a1 nuevo paquete que acabamos de definir y, a
continuacion, sencillamente compilamos el paquete y lo instalamos (usando 10s
dos botones correspondientes de la barra de herramientas del Package Editor),
veremos aparecer inmediatamente el nuevo componente en la ficha Md de la
Component Palette. El procedimiento Register del archivo de la unidad del
componente inform6 a Delphi sobre donde instalar el nuevo componente. Por
defecto, el mapa de bits utilizado sera el mismo que el de la clase padre, porque no
hemos ofrecido un mapa de bits personalizado (haremos esto en ejemplos poste-
riores). Fijese en que si movemos el raton sobre el nuevo componente, Delphi
mostrara en forma de sugerencia el nombre de la clase sin la letra inicial T.
~ Q u hay
e detras de un paquete?
El Package Editor produce basicamente el codigo fuente del proyecto de
paquete: un tipo especial de DLL creada en Delphi. El proyecto de paquete se
guarda en un archivo con la extension DPK (de Delphi PacKage), mostrado si
pulsamos la tecla F12 en el editor de paquetes. Un proyecto de paquete normal es
como el siguiente:
package M d P a c k ;
...
{ $ D E S C R I P T I O N 'Mastering D e l p h i Package I }
{ $ IMPLICITBUILD ON)
requires
vcl ;
contains
MdFontBox in 'MdFontBox.pa s ';
end.
Como se puede ver, Delphi usa palabras clave del lenguaje especificas para
paquetes: la primera es la palabra clave package (similar a la palabra clave
library que se tratara mas adelante), que introduce un nuevo proyecto de
paquete.
A continuacion, hay una lista con todas las opciones del compilador, algunas
de las cuales han sido omitidas. Normalmente, las opciones de un proyecto Delphi
se guardan en un archivo a parte. Por el contrario, 10s paquetes incluyen todas las
opciones del compilador directamente en su codigo fuente. Entre las opciones de
compilador, esta la directiva de cornpilacion DESCRIPTION,utilizada para que
la descripcion del paquete este disponible en el entorno Delphi. De hecho, despues
de haber instalado un nuevo paquete, su descripcion aparecera en la ficha
Packages del cuadro de dialog0 Project Options, una ficha que se puede
activar tambidn seleccionando el elemento del menu Component>lnstall
Packages. Este cuadro de dialogo aparece en la figura 9.2.
..
Desciition I Campier I Cornplerbtessages 1 Luiker
DrectarmlConddimls Version Info Packages
, Design packages
I. _ .. ... .- - -- . ....-. _ -
. _ .-.
: J Contains
3
Md4ctiveBtn pas D:\md7code\OS\Mdpack
3
Md4rrow.dcr D:\md7code\OS\Mdpack
k
J Md4rrow.pas D:\md7code\OS\Mdpack
MdClock.dc1 D:\md7code\OS\Mdpack
a
MdClock.pas D:\rnd7code\OS\Mdpack
E 9
MdClockFfarns D:\rnd7code\OS\Mdpack
MdCollect.pas D:\md7code\OS\Mdpack
MdfontCombo.pas D:\md7code\OS\Mdpack
9
MQntfTest.pas D:\md7code\OS\M&ack
MdLifl4ct pas D.\rnd7de\OS\Md~zck
K 3
MdLiflDial D:\rnd7code\OS\Mdpack
9 MdListDddcr D:\rnd7code\OS\Mdpack
b D:\md7codeU!S\Mdpack
MdNumEd pas D:\md7code\OS\Mdpack
MdPersonalData... D:\md?code\OS\Mdpack
MdSounB dcr D:\rnd7code\OS\Mdpack
MdSounB.pas D:\rnd7code\OS\Mdpack
3 0 Requires
~N.dcp
vcl.dcp
Figura 9.3. La seccion Contains del Package Editor rnuestra tanto las unidades
incluidas en el paquete como 10s ficheros de recursos de componente.
Creacion de componentes compuestos
Los componentes no existen de un mod0 aislado. Los programadores usan a
menudo 10s componentes en conjuncion con otros. codificando la relacion en uno
o mas controladores de eventos. Una tecnica alternativa es crear componentes
compuestos, lo que puede encapsular esta relacion y facilitar su manejo. Hay dos
tipos diferentes de componentes compuestos:
Componentes Internos: Son creados y gestionados por el componentc
principal, que puede mostrar algunas de sus propiedades y eventos.
Componentes Externos: Se conectan usando propiedadcs. Automatizan
la interaccion entre componentes separados, que pueden estar en el mismo
formulario o diseiiador o en uno diferente.
En ambos casos, el desarrollo sigue algunas reglas estandar. Una tercera alter-
nativa, menos esplorada, implica el desarrollo de contenedores de componentes,
que pueden interactuar con 10s controles hijo. Este es un tema mas avanzado por
lo que no se tratara aqui.
Componentes internos
El componente en el que nos centraremos ahora es un reloj digital. Este ejem-
plo tiene algunas caracteristicas muy interesantes. Primero, tiene un componente
dentro de otro componente (un Temporizador). Segundo, muestra la tecnica de
datos en vivo: tendremos la posibilidad de ver un comportamiento dinamico (la
actualizacion del reloj) incluso en tiempo de diseiio, como ocurre, por ejemplo,
con componentes relacionados con datos.
Dado que el rcloj digital ofrecera una salida con un cierto texto, hemos consi-
derado el heredar de la clase TLabel.Sin embargo, esto permitiria que el usua-
rio cambiase el titulo de la etiqueta (es decir, el testo del reloj). Para evitar este
problema, sencillamente hemos utilizado el componente TCustomLabel como
clase padre.
Un objeto TCustomLabel tiene las mimas capacidades que un objeto
TLabel,per0 pocas propiedades publicadas. En otras palabras, una clase que
hereda de TCUS tomLabel puede decidir que propiedades deberian estar dispo-
nibles y cuales deberian permanecer ocultas.
-- _I
Publicacion de subcomponentes
Ya desdc Delphi 6. podemos exponer simplemcnte el componente completo (cl
tcmporizador) en una sola propiedad, quc ampliara norn~allnenteel Object Ins-
pector y pcrmitira a1 usuario definir cada una dc sus subpropiedades e incluso
controlar sus evcntos.
Vcamos la dcclaracion de tipo completa del componente T M d C l o c k , con cl
subconiponentc dcclarado en 10s datos privados y espucsto como una propiedad
publicada (en la ultima linea):
type
TMdClock = c l a s s (TCustomLabel)
private
FTimer : TTimer ;
protected
p r o c e d u r e Updateclock (Sender: TOblect);
public
constructor Create (AOwner: TComponent); override;
published
property Align;
property Alignment;
property Color;
property Font;
property Parentcolor;
property ParentFont;
property ParentShowHint;
property PopupMenu;
property ShowHint ;
property Transparent;
property Visible;
property Timer: TTimer read FTimer;
end;
I
Pfoyr~ks Events 1- - -
I
.. -
1 ~elc 120 A
Name Mdnockl
ParelllCobr True
published
property FirstName: s t r i n g r e a d F F i r s t N a m e w r i t e SetFirstName;
property LastName: s t r i n g r e a d FLastName w r i t e SetLastName;
property Age: Integer r e a d FAge w r i t e SetAge;
property Description: string r e a d GetDescription;
property OutLabel: TLabel r e a d FLabel w r i t e SetLabel;
end ;
p r o c e d u r e TMdPersonalData.UpdateLabe1;
begin
i f Assigned (FLabel) t h e n
FLabel.Caption : = Description;
end;
Este metodo U p d a t e L a b e l es ejecutado cada vez que una de las otras pro-
piedades cambia (como puede verse en tiempo de diseiio en la figura 9 . 5 ) , como
podemos ver aqui:
p r o c e d u r e TMdPersona1Data.SetFirstName (const Value: string);
begin
i f FFirstName <> Value t h e n
begin
FFirstName : = Value;
UpdateLabel;
end ;
end;
if FLabel <> Value then
begin
FLabel : = Value;
if FLabel < > n i l then
begin
UpdateLabel;
FLabel-FreeNotification ( S e l f ) ;
end;
end ;
end ;
tYPe
TMdIntfTest = class (TComponent)
private
FViewer: IViewer;
FText: string;
procedure SetViewer (const Value: IViewer);
procedure SetText (const Value: string);
protected
procedure Notification (AComponent: TComponent;
Operation: TOperation) ; override;
pub1i shed
property Viewer: IViewer read FViewer write SetViewer;
property Text: string read FText write SetText;
end;
{ TMdIntfTest }
'I -
Creaci6n de componentes compuestos con marcos
En lugar de crear un componente compuesto utilizando esta tecnica, po-
r l r i n m n c h a h p r i i c a r l n iin m a r r n I .nc m a r m c h d~ mmnn-
a r ~ rn l ~rll ~ c a r r n l l n
-
Figura 9.7. El resultado del cornponente Arrow con un lapiz grueso y una trarna
especial.
En este caso, hemos usado el tipo TNo t i fy E v e n t , que solo tiene un parametro
S e n d e r y Delphi lo utiliza con muchos eventos, comoOnClick y O n D b l C l i c k .
Usando este campo hemos definido una propiedad publicada muy simple, con
acceso direct0 al campo:
property OnArrowDblClick: TNotifyEvent
read FArrowDblClick write FArrowDblClick;
TRUCO: El uso de Self como ~a&nit& dkla inmcacibn dcl mttodo del :
Una vez definida la zona, podemos probar si el punto en cl que se hizo doble
clic csta dentro de la zona, utilizando la llamada Pt InRegion de la API. Pode-
mos utilizar el codigo fuente completo de este procedimiento del siguiente listado:
procedure TMdArrow.WMLButtonDb1Clk (
var M s g : TWMLButtonDblClk) ; // mensa je wm-LBut tonDblClk;
var
HRegion: HRgn;
begin
// r e a l i z a e l c o n t r o l p r e d e f i n i d o
inherited;
// c a l c u l a l a zona d e l a p u n t a d e f l e c h a
HRegion : = CreatePolygonRgn (fArrowPoints, 3, WINDING);
try / / v e r i f i c a s i e l c l i c s e r e a l i z o e n l a z o n a
i f PtInRegion (HRegion, Msg.XPos, Msg.YPos) then
ArrowDblClick;
finally
DeleteOb ject (HRegion);
end ;
end :
.
procedure TMdNumEdit SetValue (Value: Integer) ;
begin
Text : = IntToStr (Value);
end;
El metodo mas importante es el metodo redefinido Keypress, que filtra to-
dos 10s caracteres no numericos y crea un evento especifico en caso de error:
p r o c e d u r e TMdNumEdit .WmChar ( v a r Msg : TWmChar) ;
begin
i f n o t (Key i n [ ' O r . . ' 9 ' ] ) a n d n o t (Key = # 6 ) t h e n
begin
K e y : = #O; / / s i m u l a r q u e n o s e h a p u l s a d o n a d a
begin
i f Assigned ( F I n p u t E r r o r ) t h e n
FInputError ( S e l f );
end
else
inherited;
end;
procedure TMdThousandEdit.Change;
var
CursorPos, // p o s i c i o n o r i g i n a l d e l c u r s o r
LengthDiff: Integer; // n u m e r o d e n u e v o s s e p a r a d o r e s (+ o -)
begin
if Assigned (Parent) then
begin
CursorPos : = SelStart;
LengthDif f : = Length (Text);
Text : = FormatFloat ( I # , # # # ' ,
StringToFloatSkipping (Text)) ;
LengthDif f : = Length (Text) - LengthDif f ;
// mover e l c u r s o r a l a p o s i c i o n apropiada
SelStart : = CursorPos + LengthDiff;
end;
inherited;
end;
El boton Sound
Nuestro proximo componente, TMdSoundBut ton,emite un sonido cuando
pulsamos el boton y otro cuando lo soltamos. El usuario especifica cada sonido
modificando dos propiedades String que denominen 10s archivos WAV corres-
pondientes a 10s respectivos sonidos. Una vez mas, es necesario interceptar algu-
nos de 10s mensajes del sistema (wm LButtonDown y wm -LButtonUp), o
sobrescribir el controlador de segundonivel apropiado.
Veamos el codigo de la clase TMdSoundBut t o n , con 10s dos metodos prote-
gidos y las dos propiedades de cadena que identifican 10s archivos de sonido,
proyectados sobre campos privados porque no necesitamos hacer nada especial
cuando el usuario cambia esas propiedades:
type
TMdSoundButton = class (TButton)
private
FSoundUp, FSoundDown: string;
protected
procedure MouseDown(Button: TMouseButton;
Shift: TShiftState; X, Y: Integer) ; override;
procedure MouseUp (Button: TMouseButton;
Shift: TShiftState; X, Y: Integer) ; override;
published
property SoundUp: string read FSoundUp write FSoundUp;
property SoundDown : string read FSoundDown write FSoundDown;
end:
type
TMdActiveButton = class (TButton)
protected
procedure MouseEnter (var Msg: TMessage); message
cm-mouseEnter;
procedure MouseLeave (var Msg: TMessage); message
end;
El codigo que escribimos para estos dos metodos puede hacer lo que nosotros
queramos. Por ejemplo, hemos decidido que alternara el estilo negrita de la fuente
del propio boton. Podemos ver el efecto que se obtiene al mover el raton sobre uno
de esos componentes en la figura 9.9.
procedure TMdActiveButton.MouseEnter (var Msg: TMessage);
begin
Font.Style : = Font.Style + [fsBold];
end;
Podemos aiiadir otros efectos, como agrandar el tip0 de letra, hacer el boton el
seleccionado por defect0 o cambiar el tamaiio del boton. Los meJores efectos
normalmente implican colores, pero debemos heredar de la clase T B i t B t n para
poder manipularlos (10s controles T B u t t on tienen un color predefinido).
Mensajes de componentes
Un componente de Delphi pasa mensajes de un componente a otros componen-
tes, para indicar cualquier cambio en su estado que podria afectar a dichos com-
ponentes. La mayoria de estos mensajes comienzan como mensajes Windows,
per0 algunos son mas complejos, traducciones de alto nivel y no simples
reproyecciones. Ademas, 10s componentes envian sus propios mensajes y reen-
vian aquellos recibidos de Windows. Por ejemplo, cambiar un valor de propiedad
o alguna otra caracteristica del componente puede requerir el informar a uno o
mas componentes sobre dicho cambio.
Podemos agrupar 10s mensajes en categorias:
Los mensajes de activacion y foco de entrada se envian a1 componente que
se activa o desactiva y que recibe o pierde el foco de entrada:
c m A c t i v a t e : Corresponde a1 evento OnActivate de formularios y
de la aplicaci6n.
c m-D e a c t i v a t e : Corresponde a OnDeactivate.
c m-E n t e r : Corresponde a OnEnter.
c m-E x i t : Corresponde a OnExit.
c m F o c u s C h a n g e d : Se envia siempre que cambia el foco entre 10s
componentes del mismo formulario (mas adelante, veremos un ejemplo
con este mensaje).
c m-~ oFO tc u s : Declarado per0 no se usa.
c m-L o s t F O C U S : Declarado per0 no se usa.
Los mensajes enviados a 10s componentes hijo cuando cambia una propie-
dad:
c m-BiDiModeChanged: c m-I c o n c h a n g e d
c m-B o r d e r C h a n g e d : c m-S h o w H i n t C h a n g e d
c m-C o l o r C h a n g e d : c m-S h o w i n g c h a n g e d
c m-C t l 3 D C h a n g e d : c m-S y s F o n t C h a n g e d
c m-C u r s o r C h a n g e d : c m-T a b s t o p c h a n g e d
c m-E n a b l e d C h a n g e d : cm-T e x t c h a n g e d
c m-F o n t C h a n g e d : c m-V i s i b l e C h a n g e d
Si se siguen estos mensajes, eso puede ayudarnos a mantener la pista de 10s
cambios de una propiedad. Podemos necesitar responder a estos mensajes
en un nuevo componente, per0 no es probable.
Los mensajes relacionados con las propiedades ParentXxx: c m -
P a r e n t F o n t C h a n g e d , c m P a r e n t C o l o r C h a n g e d , cm -
P a r e n t C t l 3 D C h a n g e d , c m ~ a r e n t B i D i M o d e C h a n g e dy
c m-P a r e n t ~ h o w ~ i n t ~ h a n g e d similares
Son a 10s mensajes del grupo
anterior.
Las notificaciones sobre 10s cambios en el sistema Windows: cm-
SysColorChange, cm-WinIniChange, cm-Timechange y
cm-Fontchange.Controlar estos mensajes resulta util en componentes
especiales que necesitan mantener un seguimiento de 10s colores o fuentes
del sistema.
Los mensajes del raton: cm-Drag se envia varias veces durante las opera-
ciones de arrastre. cm MouseEnter y cm MouseLeave se envian a1
control cuando el cursor entra o sale de su<uperficie, per0 10s envia el
objeto Ap p 1 i c a t i o n como mensajes de poca prioridad. c m
Mouse Whee 1 corresponde a las operaciones basadas en la rueda del ra:
ton.
Mensajes de la aplicacion:
cm AppKeyDown: Se envia a1 objeto Application para dejarlo que
decida si una tecla corresponde a un menu de metodo abreviado.
cm-AppSysComrnand:Corresponde a1 mensaje wm-SysCommand.
cm DialogHandle:Se envia en una DLL para recuperar el valor
de la propiedad DialogHandle (utilizada por algunos cuadros de dido-
go no creados en Delphi).
cm Invo keHelp: Lo envia el codigo en una DLL para llamar a1
m&do InvokeHelp .
cm WindowHook: S e e n v i a e n u n a D L L p a r a l l a m a r a l o s m e t o d o s
~ o i k ~ a i n ~ i n dy oUnhookMainWindow
w .
Es poco probable que necesitemos usar estos mensajes. Existe tambien un
mensaje cm-Hintshowpause, que nunca se maneja en VCL.
Mensajes internos de Delphi:
cm CancelMode:Termina operaciones especiales, como mostrar la
list; desplegable de un cuadro combinado.
cm Controlchange: Se envia a cada control antes de aiiadir o
elihinar un control hijo (controlado por controles comunes).
cm ControlLis tChange:Se envia a cada control antes de aiiadir
o eiminar un control hijo (controlado por el componente DBCtrlGrid).
cm DesignH itTest : Determina si una operacion de raton deberia
ir alcomponente o a1 diseiiador de formularios.
cm Hintshow:Se envia a un controljusto antes de mostrar su suge-
rencia (solo si la propiedad ShowHint est6 definida como rue).
cm Hit T est : Se envia a un control cuando un control padre intenta
loc&zar a un control hijo en una position de rat6n dada (si la hay).