Professional Documents
Culture Documents
Usuarios
FIG. 1.
El manejo distribuido facilita muchos aspectos técnicos, mas involucra otros que
deben resolverse de la mejor forma, sin afectar la funcionalidad del sistema, por ejemplo,
para el manejo del tiempo distribuido, los sistemas operativos manejan técnicas como la
sincronización periódica de los relojes de cada máquina, o la introducción de componentes
de imprecisión para compensar las desigualdades.
BDC
PDC
WINS
DHCP
Segmento de
RED
FIG. 2.
Comunicaciones:
Este aspecto es muy importante, ya que dependiendo del enfoque puede ser tan
amplio como se desee, ahora bien nuestro enfoque será dirigido hacia las capas de software
que permiten comunicación entre aplicaciones.
Gracias a los Objetos nace lo que se conoce como ORB (Object Request Broker),
muy bien conocida como la capa de administración de los objetos, permite administrar el
acceso a los objetos, así como brindar el mecanismo de transferencia de información entre
el ente externo y el objeto. La tecnología de objetos distribuidos es extremadamente apta
para la creación de sistemas flexibles de C/S, porque los datos y lógica se encapsulan
dentro de los objetos, permitiéndoles así ubicarse en cualquier punto dentro de un sistema
distribuido. Los objetos distribuidos poseen el inherente potencial para permitir que
componentes granulares de software sean conectados y usados (plug-and-play), interoperen
entre redes, corran en diferentes plataformas, coexistan con aplicaciones de herencia a
través de envolturas de objetos, merodeen en redes y se administren solos, lo mismo que los
recursos que controlan. Esta tecnología cuenta con la capacidad para revolucionar la
computación de C/S porque facilita a programadores, usuarios y administradores de
sistemas el desarrollo, uso y administración de software.
Existe una diferencia notable entre un objeto clásico, de C++ por ejemplo, y un
objeto distribuido, el objeto clásico es un glóbulo inteligente que encapsula código y datos,
proporcionan magníficas facilidades de reutilización de código por medio de herencia y
encapsulado, sin embargo viven en un solo programa. Por el contrario, un objeto distribuido
es un glóbulo inteligente capaz de vivir en cualquier punto de una red, estos se empaquetan
como piezas de código independiente a las que puedan acceder clientes remotos mediante
invocaciones de métodos. El lenguajes y compilador usado para el desarrollo objetos
distribuidos es totalmente transparente para el cliente.
FIG. 3.
Objetos
FIG. 4.
Especificaciones de COM:
1. Servidores de automatización
2. Aplicaciones de servicio WEB
3. Servidores de Objetos COM (in proccess)
4. Servidores de Controles ActiveX
5. Servidores de Objetos MTS
1. In Process
2. Out Process
Los Objetos COM fuera de proceso, son ejecutables que pueden crear objetos COM
para que los utilicen otras aplicaciones, estos no se ejecutan dentro del mismo proceso del
cliente, sino que son ejecutables que operan dentro del contexto de sus propios procesos.
¿Qué usaremos para manejar esta plataforma?. Tomando en cuenta los lineamientos
de la empresa en cuanto a la estandarización de lenguajes de desarrollo, así como el
resultado de las evaluaciones de herramientas de desarrollo, nos apegaremos para esto a C+
+, con la versión C++ Builder de Borland INC.
Para iniciar esta aventura es necesario que se realice una revisión del lenguaje, esto
con la finalidad de nivelar las diferencias que puedan existir en el grupo. Si el nivel de C++
es bueno, esta parte se puede obviar.
Introducción a C++:
# include <librería.h>
.
.
.
<definición de estructuras / variables globales>
void mi_funcion(void)
{
.
.
.
.
.
.
void main(void)
{
.
.
.
mi_funcion();
}
Tipos de datos:
La sintaxis es la siguiente:
Estructuras:
Ej.:
Struct ficha
{
string nombre;
int edad;
} Fich;
Aquí se declara la estructura ficha la cual posee dos campos, nombre del tipo string
y edad del tipo integer. Seguido a la definición de la estructura se define la variable fich que
hace referencia a la estructura creada.
Fich.nombre = ”juan”;
Fich.edad = 24 ;
Int j = Fich.edad ;
Estructuras de Lazo :
La sentencia FOR :
Sintáxis :
La sentencia WHILE :
Sintáxis :
While(num<=10)
{
// Bloque de instrucciones
}
Variante con DO :
Sintáxis :
do
{
// Bloque de instrucciones
}while(expresión)
La sentencia break :
Ej. :
int i ;
for (i=1 ;i<100 ; i++)
{
cout << i << ’’ ‘’ ;
if (i==10) break //salir del bucle
}
Cadenas :
Es el uso más común del array de una dimensión de C++. El lenguaje no tiene un
tipo definido para las cadenas, en su lugar C++ define las cadenas utilizando un arreglo de
caracteres de una dimensión.
Ej. :
char str[10] ;
int i ;
cout << ‘’introducir cadena :’’ ;
cin >> str ;
for (i=0 ; i<80 ; i++) cout << str[i] << ‘’ ‘’;
Para el tratamiento de cadenas existe unidades stándar de C++ llamadas ‘’string’’,
‘’ctype’’, las cuales poseen una gran cantidad de funciones, algunas de estas son :
Intrucciones condicionales :
La sentencia if :
sintáxis :
if(expresión) then
{
//sentencias
}
else
{
//sentencias
}
La sentencia switch :
sintáxis :
switch(variable)
{
case (valor) : { sentencias ; break }
.
.
.
else { sentencias }
}
Del ‘’if’’ no se tiene mucho que decir, pues es la forma tradicional de como los
lenguajes manejan esta instrucción, mas el ‘’switch’’ es más variada, ya que permite
selección múltiple, nótese que el break permite que no se ejecuten los sub siguientes case,
así sólo un case se ejecutará. por otro lado el else permite la opción de ejecutar un código
en caso de no se cumpla ninguno de los case anteriores.
Punteros :
Los punteros son parte de la fuerza que le da a C++ el poder que manifiesta en las
aplicaciones. Básicamente un puntero es una variable que almacena la dirección de
memoria de otro objeto.
tipo *nombre-variable ;
Ej. : int * p ;
Uso de punteros : Veamos algunos ejemplos para ilustrar el uso de este tipo de variable :
main()
{
int *p, q ;
p = &q ; //obtener la dirección de q ;
*p = 199 ; //asignar a q un valor usando un puntero ;
cout << ‘’el valor de q es :’’<<q ;
}
recuerde que un apuntador necesita apuntar a algo antes de asignarle un valor, esto es un
error típico, por ejemplo :
{
int *p ;
*p = 10 ; //error p no apunta a nada ;
}
cuando un apuntador no es inicializado apunta a null, esto también puede hacerse por
código :
Ej. :
p = NULL;
este convenio de puntero nulo, es muy utilizado en C++ como na condición de terminación
de bucles y sentencias if.
El puntero void :
Este puntero puede apuntar a cualquier tipo de objeto, recuerde que la asignación de
un puntero a otro se puede realizar sólo si son compatibles los tipos, no obstante, un
puntero void puede ser asignado a cualquier tipo de puntero sin el uso de una conversión
forzada.
Ej. :
int *p ;
void *v ;
v=p;
Aritmética de punteros :
Sólo hay cuatro operadores aritméticos que se pueden aplicar a las variables
puntero : (+, ++, -, y --).
Ej. :
Punteros y arreglos :
Cuando se usa un arreglo sin índice, está generando un puntero al inicio del arreglo. Así por
ejemplo :
Indexación de punteros :
Así como se puede acceder a un arreglo por medio de un puntero, se puede hacer lo
contrario, indexar un puntero como si se tratase de un arreglo.
Ej. :
char str[] ;
char *p ;
int i ;
p = str ;
for( i=0 ; p[i] ; i++ ) // itera mientras no se encuentre nulo.
cout << p[i] ;
char *p ;
p = ‘’constante de cadena’’ ;
cout << p ;
Aquí p es declarado como un puntero a caracter, esto significa que puede apuntar a un
arreglo de caracteres.
Arreglos de punteros :
Ej. :
int *p[20] ;
luego, es posible :
p[8] = &q ;
Indirección múltiple :
Ej. :
Funciones en C++:
void MiFuncion(void)
{
//Sentencias;
}
Pase de parámetros por valor: Son en un sólo sentido, es decir, la función no retorna
actualizaciones en los parámetros.
Ej.:
int suma(int i, int j)
{
return(i+j);
}
Pase de parámetros por referencia: La función referencia las variables, lo que permite
retornar valores a través de los parámetros.
Ej:.
int l=10 ;
int m=20 ;
int n ;
suma(l, m, &n) ; //se pasa la referencia de la variable.
cout << n; //se imprime la suma.
Otro forma :
Se define:
void suma(int i, int j, int &k) /se define la referencia del parámetro
{
k=i+j;
}
se referencia:
int l=10 ;
int m=20 ;
int n ;
suma(l, m, n) ;
cout << n; //se imprime la suma.
Punteros a funciones :
Es una característica poderosa de C++, aunque una función no es una variable, siempre
tiene una localización física en memoria que puede ser asignada a un puntero, la dirección
que es asignada al puntero es el punto de entrada a la función. El puntero puede ser usado
en lugar de la función.
Ej.:
#include <iostream.h>
int suma(int a, int b);
void main(void)
{
int (*p) (int a, int b);
int resultado;
p=suma;
resultado = (*p) (10, 20);
cout << resultado;
}
Archivos en C++
Archivos de Texto:
Ej:
#include <fstream.h>
ofstream es;
main()
{
es.open(“Archivo”,ios::app);
es<<”dato1\n”;
es<<”dato2\n”;
es<<”dato3\n”;
es.close();
return 0;
}
Ej:
#include <fstream.h>
#include <iostream.h>
ofstream es;
main()
{
char cad[200];
ifstream entrada(“archivo”);
if(¡entrada)
{
cout<<”no se puede abrir el archivo”;
}
entrada >>cad
cout<<cad
entrada.close;
return 0;
}
EJ:
Apertura de un archivo
Modos:
“r” : Abrir un archivo para leer
“w” : Crear un archivo para escribir
“a” : Añadir a un archivo
“rb” : Abrir un archivo binario para leer
“wb” : Crear un archivo binario para escribir
“ab” : Añadir a un archivo binario
“r+” : Abrir un archivo para lectura / escritura
“r+b” : Abrir un archivo binario para lectura / escritura
“w+b”: Crear un archivo binario para lectura / escritura
“a+b” : Añadir a un archivo binario o crearlo para lectura / escritura
“rt” : Abrir un archivo de texto para lectura
“wt” : Crear un archivo de texto para escritura
“at” : Añadir a un archivo de texto
“r+t” : Abrir un archivo de texto para lectura / escritura
“w+t” : Crear un archivo de texto para lectura / escritura
“a+t” : Añadir a un archivo de texto o crearlo para lectura / escritura
FILE *fp;
if ((fp=fopen(“prueba”,”w”))==NULL)
{
puts(“no puedo abrir el archivo\n”);
exit(1);
}
do
{
car=getchar();
if (EOF==putc(car, fp)
{
printf(“Error de Archivo”);
break;
}
}
Ej: Lectura de un carácter
#include <stdio.h>
#include <stdlib.h>
main(void)
{
FILE *fp;
float f=12.23;
if((fp=fopen(“prueba”,”wb”))==NULL)
{
printf(“no puedo abrir el archivo”);
exit(1);
}
if(fwrite(&f, sizeof(float), 1, fp)!=1)
{
printf(“error de archivo”);
}
fclose(fp);
return 0;
}
Ej: fseek()
.
.
.
FILE *fp;
char car;
if((fp=fopen(“prueba”, “rb”))==NULL)
{
printf(“no puedo abrir el archivo”);
exit(1);
}
fseek(fp,234,0);
car = getc(fp); // se lee el carácter de la posición 235
De esta forma le hemos dado un breve paseo al lenguaje, de forma de conocer los
aspectos más usuales del mismo. Se recomienda al participante buscar bibliografía
avanzada sobre el lenguaje ya que los aspectos de profundidad del lenguaje son importantes
y muy útiles a la hora de desarrollar. En este taller no se pretende formar expertos
programadores en C++, ya que esto sólo se puede lograr con la práctica y la investigación.
Algo de historia: Para la década de los noventa windows dió un vuelco completo a
su producto Windows for work groups, el cual fue creado para trabajar en grupos de
usuarios conectados a un mismo segmento de red, windows 95 fue el bombazo de
Microsoft, Windows NT 4.0 se encontraba dando la talla a nivel de redes corporativas, el
mercado violentamente se nutrió de estos sistemas y hoy en día se considera que un
grandísimo número de usuarios en el mundo se manejan bajo estas plataforma. Como
lenguaje de desarrollo rápido, Microdoft introce Visual Basic, y posteriormente la versión
Visual de C++ denominada Microsoft Visual C++, las cuales tienen un éxito indiscutible,
sin embargo muchos programadores de otros lenguajes se encontraban escépticos ante este
nuevo mecanismo de programación, donde la exigencia al desarrollador era muchísimo
menor que la que exigían los lenguajes tradicionales, sin embargo para el desarrollo de
software intermedio siempre se recurrió a C++, aunque era menos visual que su homólogo
VB, era muchísimo más poderoso, esto por una razón, era C++. Borland dándose cuenta de
la gran demanda de desarrolladores hacia los entornos visuales lanza al mercado la versión
de Delphi 1.0 la cual de frente compite con VB, para conseguir posicionar a Delphi, el cual
posee como lenguaje nativo Pascal, en los primeros lugares, nutre al mismo de un gran
poder basándose en el lenguaje de programación y en la técnica de POO(Programación
Orientada a Objetos), esto colocó a Delphi en una posición muy buena en el mercado. La
estrategia de Borland siempre se basó en la de ofrecer lenguajes de desarrollo completos,
con los cuales se podía realizar cualquier tipo de desarrollo aparte de dotar el IDE de un
toque visual muy parecido al de Microsoft VB, con lo cual le daba un atractivo muy
especial. Hoy en día Delphi está lanzando su versión 6.0 y su versión para Linux (Kylix),
indudablemente herramientas muy atractivas para los desarrolladores de última generación.
Sin embargo, Pascal no es un lenguaje tan portable como C++, y sin pensarlo 2 veces
Borland lanza al mercado su producto visual C++ Builder, manejándose bajo un entorno
visual basando el código en el lenguaje C++. Actualmente conocemos la versión 5.0, sin
embargo Inprise anuncia para este año el destape de la versión 6.0.
Forma que
soportará la
programación
visual
Componentes pre-
construidos
Inspector de
Ventana de código Objetos
FIG 5.
Los Iconos de acceso directo manejan las mismas opciones del menú, El inspector
de objetos maneja las propiedades y eventos de cada objeto seleccionado, los componentes
están organizados por funcionalidad y en tag’s, cada tag o pestaña puede poseer varios
componentes. La forma es el panel que soportará los componentes y permitirá la
visualización de los mismo en tiempo de ejecución.
Pestaña de selección de
eventos del objeto
Definición de la propiedad
FIG. 6.
Objeto
seleccionado
FIG. 7.
Es a través del inspector de objetos que se definen las propiedades y los eventos del
objeto seleccionado, este elemento realiza las llamadas a la zona de código donde se
escribirá o se escribió la acción solicitada, para bien escribir el código a ejecutar, modificar
o simplemente revisar el mismo.
Menús y Componentes:
Menús de
Pestañas deopciones
componentes
Iconos de acceso
rápido
Selecciona el
Ambiente del
entorno a utilizar
FIG. 8.
Comencemos ahora a realizar una aplicación de consola. Para ello usaremos la siguiente
secuencia para el IDE:
FIG. 9.
2. Seleccionar
Consola
Aplicación
del diálogo
New Items
FIG. 10.
FIG. 11.
Árbol de
variables y
funciones
Zona para escritura del código.
FIG. 12.
#include <stdio.h>
#include <conio.h>
int main(void)
{
char c;
printf("Hola Mundo..!");
c=getch();
return 0;
}
FIG. 13.
Este ejemplo muestra el texto “Hola Mundo y espera que se pulse una tecla para
terminar la aplicación.
#include <stdio.h>
#include <conio.h>
void main(void)
{
char *c;
char car='A';
c=&car;
printf("resultado:%c",*c);
car=getch();
}
El ejemplo 2 hace uso de un apuntador para transferir y mostrar el contenido, la
salida es como sigue:
FIG. 14.
#include <stdio.h>
#include <conio.h>
void main(void)
{
int i;
for (i=1;i<10;i++)
{
printf("salida Nro.%i\n",i);
}
char c;
printf("\n");//salto de línea
printf("pulse una tecla para salir..!");
c=getch();
}
FIG. 16.
La pestaña New nos muestra:
Aplicaciones (.EXE)
Batch File
Archivos C
Componentes (Para incrustarlos en la paleta)
Console Wizard (Aplicaciones de consola)
Aplicaciones para el control panel
Módulos para el control panel
Archivos .cpp
Módulo de datos (para agrupar componentes de conectividad a las bd)
DLL Wizard (para hacer DLL´s)
FORM Crea una nueva forma
Header file (Archivos de cabecera)
Library (Archivos .h)
MFC Wizard (Aplicaciones con la MFC de Microsoft)
Package (Paquetes de software instalables)
Project Group (Grupos de proyectos)
Report (Reportes)
Resource DLL Wizard (Recursos de DLL)
Service (Servicios)
Service Application (Aplicaciones de servicio para el OS
Text (Archivos de texto)
Thread Object (Hilos)
Unit (Unidades tipo pascal)
Web Server Application (CGI, WCGI, DLL’s de WEB)
FIG. 17.
En la figura 17 se muestra la pestaña Active X, en la cual se manejan:
FIG. 18.
Las subsiguientes figuras 19 – 23, muestran el resto de tipos de desarrollo que se pueden
realizar con C++ Builder:
FIG. 19.
FIG. 20.
FIG. 21
FIG. 22.
FIG. 23.
En primera instancia crearemos la base de datos con el wizard de C++ Data Base Desktop
1. Cierre todo a través de la opción de menú <File> <Close all> Fig. 24.
FIG. 24.
En este momento usted tiene una nueva forma disponible para incluirle
componentes y desarrollar su aplicación, tal como se muestra en la figura 26.
FIG. 26
FIG. 28.
4.Utilice la siguiente secuencia de opción de menú para entrar al diálogo donde escogerá el
manejador de BD deseado. <File> <New><Table> Fig. 29.
FIG. 30.
FIG. 31.
FIG. 32.
FIG. 34.
6. Salve la tabla en el directorio indicado para el ejemplo. Para esto utilice el botón Save ó
Save As y a través del Browser que aparecerá ubique el directorio.
Ahora bien, para conectarnos a la base de datos necesitamos crear un alias que
permita a través de uno de los mecanismos estándar establecer la conexión a la BD. Para
esto utilizaremos la herramienta que provee C++ Builder que entre otras cosas nos permite
generar el alias de conexión a la BD, esta herramienta es “SQLExplorer”.
6. Pulse en el menú la secuencia de opciones <Database> <Explorer> como se muestra
en la figura 35.
FIG. 35.
7. En el menú Object del Explorer pulse la opción <New>, para crear un nuevo alias.
Aparecerá el diálogo de la Fig. 37.
FIG. 37.
8. Del combo box seleccione la opción “STANDARD” la cual se presenta por defecto,
y pulse OK, esto iniciará la creación del alias.
FIG. 38.
FIG. 39.
FIG. 40.
11. Utilice el botón que se ubica a la esquina derecha que posee tres puntos y pulse en
él para escribir la ruta física donde se encuentra la base de datos, o escríbala. FIG.
41.
FIG. 41.
12. Ubíquese sobre el alias y pulse botón derecho del ratón luego del POPUP menú
emergente pulse sobre Apply para aplicar los cambios, luego salve aceptando. Ver
FIG 42.
FIG. 42.
13. El siguiente diálogo aparece para aceptar los cambios, pulse Ok.
FIG.43.
Hasta ahora hemos creado la base de datos y el alias de conexión. Ahora viene lo
bueno, crearemos una aplicación visual que interactúe con la BD creada.
Bien, cierre el Data Base Explorer y ubique el ratón en la pestaña Data Access de la
paleta de componentes, tal como se ilustra en la FIG. 44.
FIG. 44.
Para seleccionar un componente, pulse sobre el mismo con el botón derecho del
ratón, y para ubicarlo en la forma, ubique el ratón en la posición deseada y pulse botón
derecho nuevamente, así el componente seleccionado es asignado a la forma.
FIG. 46.
FIG. 47.
FIG. 48.
4. Ejecute el programa pulsando con el ratón el botón RUN, tal como lo indica la FIG.
49, o ejecute la opción RUN desde el menú <RUN>. Después de esto note que ya
puede incluir, modificar y eliminar registros en la base de datos de prueba.
Este ejemplo demuestra lo fácil y RAPIDO que se realizan las aplicaciones con C++
Builder. Este ejemplo es una aplicación de 2 capas, modelo tradicional de desarrollo para
aplicaciones con acceso a las BD.
Una de las cosas más interesantes que presenta este entorno es el tener todo en uno,
es decir, no se requiere recurrir a otras herramientas para desarrollar una aplicación
completa.
EJEMPLO 5 Acceso a puertos: (Assembler en línea)
Este ejemplo demuestra lo fácil que es el acceso al hardware con C++ Builder.
Implementaremos una aplicación que escriba un byte por el puerto paralelo y luego lea el
byte escrito, esto se puede hacer ya que el puerto paralelo presenta la característica de
poseer un Latch de salida lo que obliga a mantener en sus pines la última palabra enviada.
FIG. 49.
Haga doble click sobre el botón Escribir para tipear el código que se ejecutará al
pulsar el botón, el código a escribir se presenta a continuación:
byte b;
b = byte(StrToInt(Edit1->Text));//se obtiene el contenido del editor y se convierte
asm
{
mov dx,378h;
mov al,b;
out dx,ax;
}
Para el segundo botón se realizará la misma labor, con el siguiente código:
byte b;
asm
{
mov dx,378h;
in ax,dx;
mov b,al;
}
Edit2->Text=IntToStr(b);
El primer bloque de código define la dirección del puerto paralelo, (378h), luego
mueve a la parte baja del registro acumulador del CPU (al) el valor capturado desde el
editor, que a su vez fue convertido a byte, finalmente ejecuta un “out” en la dirección de
puerto cargada en dx (378h) con el valor guardado en el registro acumulador del CPU, esto
escribe en el puerto paralelo el byte escrito en el edit1. Recuerde que el valor a colocar en
el Edit1 debe estar entre 0 y 255, ni mayor ni menor, ya que lo se escribirá por el puerto
será un byte.
1. Cierre todas las aplicaciones desde el menú <File> opción <close all>
2. Cree una nueva aplicación desde el menú <File> opción <New Application>. Una
nueva forma aparecerá en blanco.
3. Ahora pulse desde el menú <File> la opción <New>, aparecerá un diálogo (New
Items), con varias pestañas, seleccione la pestaña “Multitier” y pulse sobre el Icono
“Remote Data Module”, tal como se muestra en la FIG. 51.
pestaña
Multitier
Icono Remote
Data Module
FIG. 51.
4. Aparecerá un diálogo (New Remote Data Module), que solicita información
referente al objeto que se creará, el CoClass Name, que es el nombre que se le dará
al objeto COM, el Threading Model, que es el modelo de hilo a seleccionar, en este
caso lo dejaremos aparment, ya que este modo serializa las conexiones hacia la BD.
En el editor de descripción escribiremos la descripción de nuestro objeto. La FIG.
52. nos muestra como se verá esta pantalla una vez llenada sus opciones.
FIG. 52.
Define el
alias de la
BD
Define el
Data Set
Activa las para el
propiedades proveedor
autorefresh,
Propagación de
data, permitir
comandos de
texto (Query’s),
y resolver al
cliente de datos
7. Desde el menú “View” pulse la opción “Type Library” para llamar al Type Library
Editor, el cual nos permitirá incluir métodos al objeto con sus parámetros OLE.
Incluir nuevo Exporta IDL
método a la interface Refrescar, genera el código
seleccionada y actualiza las
modificaciones
Registra el Type
Library
Interface
Objet
o
FIG. 57.
Código
generado para
el método
FIG. 58.
FIG. 59.
FIG. 60.
17. Desde el menú de opciones del RegEdit ejecutar <Edición> <Buscar>, luego
colocar el nombre del objeto, “DataMod” y pulse Ok para comenzar la búsqueda.
18. copie el número Guid en un archivo de texto con el nombre guids.txt. Ver FIG. 61.
FIG. 61.
En este momento la aplicación del servidor está culminada, solo resta hacer la
aplicación cliente para probar el servidor.
ClientDataSet
DComConnection
DBGrid
DBNavigator
FIG. 62.
FIG. 67.
FIG. 66.
FIG. 68.
10. Ahora Añadiremos un botón con el caption: “Query”, adicional para conectar al
cliente con el provider1 del servidor.
11. El código del botón será como sigue:
DCOMConnection1->Connected=true;
ClientDataSet1->Close();
ClientDataSet1->CommandText="select * from persona";
ClientDataSet1->Open();
12. Ejecutemos la aplicación y hagamos click en cada botón, el botón buscar retorna el
mensaje en el edit1, y el botón Query se trae la data de la base de datos y la muestra en el
DBGrid, tal como se visualiza en la FIG. 69.
FIG. 69.
Nótese que si nos movemos a través del DBNavigator funciona correctamente, sin
embargo puede existir problemas a la hora de insertar registros a través del DBNavigator ya
que los componentes de conexión están en el servidor y las modificaciones se deberán
realizar sólo a través de los mecanismo que el servidor provee.
C++ y la WEB:
C++ Builder provee a través de su entorno de desarrollo mecanismo para el manejo
de aplicaciones bajo el WEB. El mecanismo utilizado para esto es prácticamente un
estándar adoptado por los diferentes lenguajes que manejan código bajo la WEB. Este tipo
de aplicaciones requiere un servidor de aplicaciones WEB que sirva a los clientes los
servicios solicitados a través del Navegador.
FIG. 70.
5. Escoja la Opción ISAPI/NSAPI Dinamic Link Library, tal como se muestra en la
FIG. 71.
FIG. 71.
FIG. 73.
FIG. 74.
FIG. 75.
Observe que el Action Editor incluyó el nuevo Action en su ListBox. Ver Figura 76.
FIG. 76.
11. En el inspector de objetos haga click sobre la pestaña “Events”, esto hará aparecer
el evento OnAction tal como se muestra en la figura 77.
FIG. 77.
12. Haga doble click sobre el ComboBox de la propiedad para escribir el código del
Action. Esto hará emerger la ventana de código como se muestra en la figura 78.
FIG. 78.
El método Request será utilizado para recibir los parámetros desde el cliente y el
método Response será utilizado para enviar la respuesta al cliente. Como este método se
desarrolla sólo para decirle al cliente que la dll está funcionando Ok, la respuesta que
enviará este método será: “La dll está Ok..!”.
FIG. 78.
FIG. 80.
Esto se puede hacer manualmente o pulsando sobre el Icono que está a la derecha el
cual posee 3 puntos, y buscar a través del explorador de Windows que aparecerá al pulsar el
botón, el directorio de publicación WEB para la dll. De esta forma C++ Builder generará la
dll en el directorio asignado cada vez que se ejecute la opción de reconstrucción “Build”
Ahora escribiremos el código para los demás Actions que se crearán en este
ejemplo.
{
try
{
Query1->Close();
Query1->SQL->Clear();
Query1->SQL->Add("select * from person");
Query1->Open();
Query1->First();
PageProducer1->HTMLDoc->Add("<HTML>");
while (!Query1->Eof())
{
PageProducer1->HTMLDoc->Add(Query1->Fields->Fields[0]->AsString+
" "+Query1->Fields->Fields[1]->AsString+" "+
Query1->Fields->Fields[2]->AsString);
PageProducer1->HTMLDoc->Add("<BR>");
Query1->Next();
}
PageProducer1->HTMLDoc->Add("</HTML>");
Response->Content=PageProducer1->Content();
PageProducer1->HTMLDoc->Clear();
}
__except(0)
{
Response->Content="Error en la conexión a la BD..!";
}
}
FIG. 83.
Response->Content = DataSetTableProducer1->Content();
{
try
{
Query1->Close();
Query1->SQL->Clear();
Query1->SQL->Add("select * from person");
Query1->Open();
Response->Content=QueryTableProducer1->Content();
}
__except(0)
{
Response->Content="Error con conexión a la BD..!";
}
}
FIG. 86.
FIG. 87.
13. Ajuste las propiedades del componente QueryTableProducer1 al gusto, tal como lo
hizo para el componente DataSetTableProducer1.
13. Ejecute el Internet Explorer o el Nestcape para probar los Actions creados.
14. Escriba las siguientes llamadas en el Browser para probar cada Action:
http://sirio/scripts/Midll1.dll/ok
http://sirio/scripts/Midll1.dll/buscar
http://sirio/scripts/Midll1.dll/tabla
http://sirio/scripts/Midll1.dll/query
FIG. 88.
FIG. 89.
FIG. 90.
FIG. 91.
Ahora realizaremos una página HTML con un Form, luego, utilizando el método Post
llamaremos a una Action de la DLL de WEB.
<HTML>
Pulsa el botón Ok para obtener respuesta de la DLL..!
<Form Method="post" action="HTTP:\\sirio\scripts\MiDll1.dll\ok" >
<input type= "submit" value="Ok" name="B1">
</Form>
</HTML>
FIG. 92.
Como puede notar, al pulsar el botón se invoca al método Action “OK” y se ejecuta
el código escrito en el Action. Llamemos al método Buscar, para ello realice el siguiente
cambio a la página:
<HTML>
Pulsa el botón Ok para obtener respuesta de la DLL..!
<Form Method="post" action="HTTP:\\sirio\scripts\MiDll1.dll\buscar" >
<input type= "submit" value="Ok" name="B1">
</Form>
</HTML>
Modificación
AnsiString s;
s=Trim(Request->ContentFields->Values["E1"]);
try
{
Query1->Close();
Query1->SQL->Clear();
Query1->SQL->Add("select * from person where ced="+s);
Query1->Open();
Response->Content=Query1->Fields->Fields[1]->AsString+" "+
Query1->Fields->Fields[2]->AsString;
}
__except(0)
{
Response->Content="Error con conexión a la BD..!";
}
FIG. 95.
Tipee una cédula conocida, la cual se encuentre en la base de datos, en este caso se tipea 1.
Ver figura 96.
FIG. 96.
Al pulsar Ok se obtiene:
FIG. 97.
...el browser muestra el nombre y el apellido de la cédula introducida, para este caso,
“juan garcia”.
Los Active Forms son formas activas en la WEB, esto se refiere a tener una forma
tal como se maneja en los lenguajes visuales, con todas las potencialidades de la misma,
pero trabajando dentro del browser.
Para lograr esto, se utiliza la tecnología de ActiveX, creada por Microsoft, esto se
refiere a crear un control activeX (.ocx ), el cual se cargará en el browser como una
aplicación. Esto sólo es posible utilizarlo con el internet explorer ya que esta tecnología
sólo es soportada por los navegadores de Microsoft.
Note que los ActiveX son libres de interactuar con el hardware del equipo, ya que
son aplicaciones independientes, lo que no garantiza la seguridad de que el programa (.ocx)
no pueda realizar estragos en la máquina local, esta ha sido la crítica más fuerte que ha
recibido Microsoft para con esta tecnología, sin embargo las potencialidades de la misma
no dejan de ser atractivas.
Pestaña de
Icono de
ActiveX
Active Form
FIG. 98.
4. Sobre el diálogo Active Forms Wizard tipee en el editor “New ActiveX Name”, el
nombre del componente, para este caso MiActiveX1. Figura 99.
FIG. 99.
5. pulse Ok
6. Sobre la forma coloque los siguientes componentes:
Table1
DataSource1
DBGrid1
DBNavigator1
FIG. 100.
Esto es:
Table1.DatabaseName = persona
Table1.TableName = person
Table1.Active = true;
DataSource1.DataSet = Table1
DBGrid1.DataSource = DataSource1
DBNavigator.DataSource = DataSource1
8. Salve la aplicación con los nombres por defecto, añadiendo al final su número
asignado por el facilitador para distinguir su ocx de los de otro participante.
9. Pulse a través del menú project la opción “Web Deployment Option”. Ver figura
101.
FIG. 101.
10. En el diálogo Web Deployment Option llenar las opciones como sigue:
Seleccione sólo los 2 primeros Checks, tal como se muestra en la figura 102.
FIG. 102.
11. pulse Ok
12. Desde el menú project pulse la opción “Web Deploy”. Esto generará el componente,
así como la página web.
13. Registre el componente ActiveX desde el menú Run pulsando la opción Register
ActiveX Server.
14. Ejecute la página Web creada haciendo doble click sobre el archivo.
15. Disfrute de la versatilidad de un componente ActiveX. Figura 103
FIG. 103.
Veamos ahora un tipo de objeto que trabaja con la filosofía ActiveX, esto es un
objeto COM que permite componentes visuales.
FIG. 104.
FIG. 105.
6. Coloque un Label, un Memo y un Botón en la forma, tal como se muestra en la
figura 106.
FIG. 106.
7. A través del Type Library añada un método al objeto llamado “Mensaje”. FIG. 107.
8. Añada un parámetro de salida del tipo *Variant, tal como se muestra en la figura
108.
9. Pulse el botón de refrescar en el Type Library para generar la plantilla de código
para el método.
FIG. 107.
Botón
Refrescar
FIG. 108.
10. Para el método escriba el siguiente Código mostrado en la FIG. 109. para el botón
limpiar escribir: Memo1->Clear();
FIG. 109.
FIG. 110.
13. Cierre todo con la opción Close All del menú <file>
14. Genere una nueva aplicación para el cliente
15. Incluya en la forma, un editor, un botón, un label y un DComConnection, tal como
se muestra en la figura 111.
FIG. 111..
16. Conecte el DCOM al COM creado utilizando el GUID y el nombre del equipo. Ver
figura 112.
FIG. 112.
{
TVariant v;
DCOMConnection1->Connected=true;
IDispatch* disp = (IDispatch*)(DCOMConnection1->AppServer);
IMiAutoObjxDisp TempInterface( (IMiAutoObjx*)disp);
TempInterface.mensaje(&v);
Edit1->Text=v;
}
18. Ejecute la aplicación cliente y haga click 3 veces en el botón solicitud de mensaje.
Nótese que al pulsar el botón se realiza la llamada al objeto COM, el cual muestra la
pantalla al momento de servir al cliente. El mensaje que recibe el cliente también es
enviado al memo del servidor, lo que muestra la versatilidad de este objeto de poder
manejar componentes visuales.
servido
clienter
FIG. 113.
FIG. 114.
5. En el botón Word escriba el siguiente código:
{
OleVariant Template = EmptyParam;
OleVariant NewTemplate = False;
OleVariant ItemIndex = 1;
try
{
try
{
WordApplication1->Connect();
}
catch (Exception &exception)
{
MessageDlg("El Word no debe estar instalado..!", mtError, TMsgDlgButtons() << mbYes, 0);
Abort;
}
WordApplication1->Visible = True;
WordApplication1->Caption = StringToOleStr("Office automation");
//Creando un nuevo document0
WordApplication1->Documents->Add(Template, NewTemplate);
//Assign WordDocument component
WordDocument1->ConnectTo(WordApplication1->Documents->Item(ItemIndex));
//colocando el Spell checking en false, ya que esto toma mucho tiempo si está deshabilitado
WordApplication1->Options->CheckSpellingAsYouType = False;
WordApplication1->Options->CheckGrammarAsYouType = False;
//Insertando data
WordDocument1->Range(EmptyParam, EmptyParam)->InsertAfter(StringToOleStr("Texto de prueba...línea1\n" ));
WordDocument1->Range(EmptyParam, EmptyParam)->InsertAfter(StringToOleStr("Texto de prueba...línea2\n" ));
}
catch (Exception &exception)
{
Application->ShowException(&exception);
WordApplication1->Disconnect();
}
}
Esta es otra de las bondades que podemos aprovechar de este entorno. Veamos
como realizar una aplicación de interacción con Excel.
Procedimiento:
Variant vExcelApp,vExcelWorkbook,vExcelWorksheet;
vExcelApp = CreateOleObject("Excel.Application");
vExcelApp.OlePropertySet("Visible",true);
vExcelApp.OlePropertyGet("Workbooks").OleProcedure("Add");
vExcelWorkbook=vExcelApp.OlePropertyGet("Workbooks").OlePropertyGet("Item",1);
vExcelWorksheet=vExcelWorkbook.OlePropertyGet("ActiveSheet");
vExcelWorksheet.OlePropertySet("Range", "C1", "C1", "=A1+B1");
vExcelWorksheet.OlePropertySet("Range", "A1", "A1", 1);
vExcelWorksheet.OlePropertySet("Range", "B1", "B1", 1);
ExcelApplication1->Quit();
ExcelWorksheet1->Disconnect();
ExcelWorkbook1->Disconnect();
ExcelApplication1->Disconnect();
Fig. 117.
Observe que se necesita crear instanciamientos de los objetos utilizados, para ello
utilizamos las variables “vExcelApp ,vExcelWorkbook, vExcelWorksheet”, declaradas como
Variant.
En este ejemplo veremos como se pueden crear gráficos interactivos en C++. Para
esto utilizaremos la componente “Tchar” ubicada en la paleta “Aditional”.
Pasos:
Chart1->View3DOptions->Orthogonal=false;
Chart1->Series[0]->Add(100,"Valor1",clBlue);
Chart1->Series[0]->Add(50,"Valor2",clBlue);
Chart1->Series[0]->Add(150,"Valor3",clBlue);
Timer1->Enabled=true;
a++;
b=a;
if (a>360)
{
a=0;
b=0;
}
Chart1->View3DOptions->Rotation=a;
Chart1->View3DOptions->Elevation=a;
10. Pruebe la aplicación
FIG. 118 (Editor TChar)
FIG. 119.
FIG. 120
Chart1->Series[0]->Add(100,"Valor1",clBlue);
Chart1->Series[0]->Add(50,"Valor2",clBlue);
Chart1->Series[0]->Add(150,"Valor3",clBlue);
Esto genera un gráfico de 3 barras 3d. Note que al variar los valores para las
propiedades de rotación y elevación, se consigue el efecto de animación. Toda la
animación se hace posible si la propiedad de “Orthogonal” está en “false”. Esto s1e
maneja mediante el código del evento del Timer.
Este ejemplo nos mostrará la forma como C++ Builder maneja los reportes. Para
esto se manejará el componente de Reportología proveído por C++ “QuickRep”.
Pasos:
QuickRep1->Preview();
Note que el reporte emerge cuando se llama la función Preview(), lo que nos indica
que la forma con el Reporte sin ejecutar no tiene porque mostrarse, esta puede ocultarse
ya que se puede asignar la llamada del reporte a un evento en cualquier otra forma.
Vale la pena mencionar que Delphi utiliza el mismo componente para generar los
reportes en su entorno, además para cada versión de C++ existe una versión del Quick
Report, las cuales guardan diferencias, esto es, no se puede ir de versiones superiores a
inferiores. Borland también creó el Report Smith, el cual promociona como el paquete
más completo para generación de reportes, este paquete no viene con la versión del
lenguaje, por lo que no se considera en este taller.
ANEXOS
Build Reports
using
QuickReport 3
for Borland Delphi
TQuickAbstractRep is a descendant of the TCustomQuickRep base class that does not use
TDataset - use it to build your own report systems.
TQRLoopBand prints the number of times set in its PrintCount property - great for
creating blank forms.
TQRListWizard will create an instant report based on the fields of a table.
• Expert technical support via email.
• Full source code. Use the source, Luke! The user can easily modify the code to localise
the language, adopt it to local interface standards, add new features and so on.
• More demos with more depth, including examples of how to make use of all the Pro
edition features, and advanced techniques such as writing custom functions for the
expression evaluator.
You can upgrade to QuickReport Professional by ordering from our web site, that of our
distributor QBS Software Ltd at http://www.qbss.com or from your local Delphi add-on
reseller.
A first report
The best way to get the hang of the QuickReport library is to see it in action. So this section
explains how to set up a very basic report. With the Delphi IDE running, follow these steps:
1 Choose File | New Application.
2 Drop a TTable component onto the main form.
3 Use the Object Inspector to set its DatabaseName property to ‘DBDemos’,
TableName to ‘CUSTOMER.DB’ and Active to True.
4 Drop a TQuickRep component on the main form. Its size and position don’t matter.
5 Set its DataSet property to ‘Table1’. This is a key step. The report object to iterates
through all the records in it DataSet, in this case Table1, whenever it is printed or
previewed.
6 If necessary, expand the Bands property in the Object Inspector by clicking on the +
symbol to its left. Set the HasDetail item to True. You will see the detail band appear
inside the report; changing the property actually creates the DetailBand1 object.
7 Drop a TQRDBText component onto the newly created detail band.
8 Set its DataSet to ‘Table1’ and DataField to ‘Company’.
At this point your form should look something like Figure 1 below.
Figure 1 – Setting up a basic report
To check that you have set up the properties correctly, preview the report by right-clicking
somewhere on the TQuickRep component and selecting the Preview item from the popup
menu. If you did everything right you should now see a preview window containing your
report, as shown in Figure 2.
Figure 2 – The preview window
If all has gone well, you now have a report that works at design time. Of course all may not
have gone well. If you are now mournfully gazing at an entirely blank report, please check
that you have completed all the steps – a likely explanation is that you forgot to set
TTable1’s Active property to True. Similarly, if you are looking at a report with only one line
– ‘Kauai Dive Shoppe’ – the problem is probably that you failed to connect QuickRep1’s
Dataset property to TTable1.
One other problem which may bite you is that the buttons on the toolbar above the report
preview area fail to appear. This is nobody’s fault: you have become a victim of what the
manufacturer of your PC’s operating system is pleased to call, in its technical documents,
‘DLL Hell’. Specifically, your machine’s copy of the common control library
(comctrl32.dll) is before 4.72, and needs updating.
You can download a later version of comctrl32.dll from the Microsoft website at
http://www.microsoft.com. But since this is one of those files that invariably turns up in
new versions of Internet Explorer and Windows Service Packs, you may well find it on one
of those CDs that they give away with PC magazines, and save a download. (In fact, it is
unlikely that this bug will bite you the developer. We describe it here so that you will
recognise the problem if one of your users is caught by it.)
Now lets make the report work as part of a compiled program. You need to write code to
call TQuickRep.Preview:
1 Drop a button to your form and set its Caption property to ‘Preview’
2 Double click on the button to add an OnClick event. Add a line of code, so that it
looks like this:
procedure TForm1.Button1Click(Sender: TObject);
begin
QuickRep1.Preview;
end;
Now run your application and click the Preview button. As before, you should see the
preview window appear. If you want to try printing the report directly to the default printer,
simply change the call to the Preview method to a call to Print, ie
procedure TForm1.Button1Click(Sender: TObject);
begin
QuickRep1.Print;
end;
At this point I must admit to taking a slightly dirty shortcut. Our test application a
TQuickRep component on its main form and, as you can see, this looks pretty odd. In real
applications you never display a form containing a TQuickRep component. Instead you use
them from other forms.
So what we should really do to finish off, if this little example were going to be a real
application, is:
3 Create another form – it will be called Form2
4 Make the new form into the main form of the project by setting Project | Options |
Main form to Form2
5 Drop a button on Form2
6 Write code like this in the button’s event handler
procedure TForm2.Button1Click(Sender: TObject);
begin
Form1.QuickRep1.Preview;
end;
7 Compile the project. The compiler will complain that Unit1 is not in Unit2’s Uses
list, and offer to fix the code. Accept the offer.
The application should now compile and run, and looks prettier and more ‘realistic’. The
end user doesn’t get to see any bewildering TQuickRep components.
But doing this aesthetic polishing doesn’t get us any further with QuickReport. So I am
going to leave out the need to have a second form from all the examples from this point
onwards, and trust you will remember when making real applications.
The components
The QuickReport components are all contained in the QReport tab of the Delphi component
palette. Here is a whistle stop tour of what they are and what they do to help you get your
bearings.
Figure 3 - TQuickRep and band components
TQuickRep. This is the most important component of them all, a container for all the other
printing components. It represents the paper on which your report will be printed. Its Page
property lets you set up the dimensions of the paper you are going to print on, while the
Dataset property specifies a source of data that the report will iterate through.
Note that, instead of dropping a TQuickRep component onto an ordinary form, you can
instead add a TQuickReport module to your project:
8 Choose File | New… to display the New Items dialog box.
9 Choose the New tab
10 Select the Report item (middle of the bottom row)
Band components
These are also container components, representing horizontal strips across report. Bands
can be associated with a physical position on a page – for example the top – and also reflect
the master/detail relationships in the database that is being displayed. For example, in the
same way that there might be many sales records for a given customer record, so a band
containing data about an individual sale might appear many times for each occurrence of a
band containing customer data.
TQRSubDetail. This is the detail band in a master/detail relationship. You can also make it
the master of another detail band, and so create multiple levels of subdetails.
TQRStringsBand. This band provides one mechanism to report on data without using a
TDataSet. It encapsulates a TStrings container; instead of retrieving a sequence of records
from a database, it retrieves a sequence of strings from its container.
TQRBand. A generic band type, which can act in different roles according to it BandType
property. Usually there is no need to drag a TQRBand onto a report. Instead use the Bands
property of TQuickRep, which creates TQRBand objects and sets their band type in one go.
TQRChildBand. Use TQRChildBand objects when you need to extend an existing band. For
example, suppose you have placed some TQRMemo components in a band, and wish to add,
say, a TQRLabel, which should always appear below. Since TQRMemo objects can expand
themselves according to their contents, it is not sufficient to arrange the label within the
band. Instead add in a TQRChildBand object, and put your label on that. The easiest way to
add a child band, by the way, is to double-click the HasChild property of its parent in the
Object Inspector.
TQRGroup. A band that prints whenever an expression changes, usually a database field.
This band is used to group like records together. For example, given a database table
containing US addresses, one could sort them by a State Code field and add a group band
with it Expression property set to the State Code field. When the report is printed, the
contents of the group band will be printed between records belonging to a given state.
Printable components
Figure 4 - Printable components
The QuickReport printable components are mostly equivalents of standard controls that you
use on forms. These are the controls, which actually get printed on the paper. Position them
within bands to define the layout of your report.
TQRLabel. Place some static text on the page.
TQRDBText. Equivalent of a TDBText control – use it to display the contents of a linked
database field. Unlike ordinary data-aware controls, but in common with all QuickReport
controls, TQRDBText uses a DataSet property to specify its source of data. Normal data-
aware controls use a DataSource property, which requires you to supply an extra
TDataSource component to ‘wire’ controls to a dataset. QuickReport controls have no such
requirement.
TQRExpr. Use this to display an ‘expression’. Typically you use one of these when you
need to massage the appearance of your data before printing it out. The best way to think of
these is as ad hoc calculated fields, used only in the report. For example, you might use it to
concatenate the parts of a customer name, held in a customer table as string fields called
“Title”, “Forename” and “Surname”. To do this simply set the Expression property of
TQRExpr to
Title + " " + Forename + " " + Surname
In real life, you would probably use a more complex expression to cope with blank fields
elegantly, but you get the idea.
TQRSysData. A control to display ‘system data’, by which we mean things like the current
page number within the report, and the current date and/or time.
TQRMemo. Very much like its standard control cousin the TMemo; use this to display
multiple lines of text. As you would expect, the text to be printed is held in a TStrings type
property called Lines.
TQRExprMemo. A composite of TQRExpr and TQRMemo. You can use this to include
{braced} expressions in multi-line blocks. This makes it an absolute natural for doing
addresses, especially since it includes a boolean property RemoveBlankLines. For example:
Company : {CompanyName}
Address : {Address1}
{Address2}
Contact : {Contact + ' ' + Phone number}
TQRRichText. Place some rich text (ie multi-line text with RTF formatting) on the page. One
use of this component is to print the contents of a TRichEdit control – simply assign it to
TQRRichText’s ParentRichEdit property.
TQRDBRichText. As you’d expect, this is a data-aware version of TQRRichText. Use it to
print formatted memos stored in BLOB fields.
TQRShape. A cousin of the little-used TShape control from Delphi’s ‘Additional’ palette.
Actually the QuickReport version is very useful for placing ‘furniture’ into report layouts
such as dividing lines above totals and grouping rectangles.
TQRImage. Display a picture or logo on a report using this control. Supports the same vector
and bitmap image formats as TImage, and can be loaded at design time using the Picture
property.
TQRDBImage. A data-aware image control for displaying images stored in BLOB fields in
the database.
Previews and composite reports
Figure 5 - Filters and miscellaneous components
TQRCompositeReport. Sometimes you need to group together separate reports into a single
print run. For example, maybe you need to print out all the new customers obtained in the
last week, together with a summary of all orders in the last week and also a list of stock that
needs reordering. As far as your customer is concerned these things belong together and
should be printed together. But from the database point of view you will want to use three
separate TQuickRep components to do the job.
The way to handle this situation is to use a TQRCompositeReport component. Drop one on
the form where you want to kick off the printing. First you need to define a handler for its
OnAddReports event, which calls the TQRCompositeReport.Add method to add all the
TQuickRep components you need to print. Suppose the reports you want to print are held on
forms called RepNewCust, RepOrderSummary and RepStockReorder, and in each case the
TQuickRep component on the form is called ‘Report’ (see the section ‘TQuickRep in detail’
below for why you might do this). Then your OnAddReports event handler should look like
this
procedure TForm1.QRCompositeReport1AddReports(
Sender: TObject);
begin
QRCompositeReport1.Reports.Add(RepNewCust.Report);
QRCompositeReport1.Reports.Add(RepOrderSummary.Report);
QRCompositeReport1.Reports.Add(RepStockReorder.Report);
end;
(If you don’t mind using the with statement in your code, you can tidy up this fragment
considerably by wrapping it up in
with QRCompositeReport1.Reports do
begin
...
end;
and knocking out the ugly repetitive QRCompositeReport1.Reports from the middle three
lines.)
Now you can call QRCompositeReport1.Print to print out all three reports in a single batch,
and QRCompositeReport1.Preview to preview them together. There are also TQRCompositeReport
component properties that let you set up paper sizes and set an overall title for the composite report
– basically everything you need to handle the output from the multiple reports in one place.
TQRPreview. To preview a report before it is printed for real, all you need do is call
TQuickRep.Preview and a standard preview window will appear. There are times, however,
when you want to have more control over the exact appearance of the preview.
The TQRPreview control lets you do this. Drop it on one of your own forms and, after you
have added a line of code to the TQuickRep.OnPreview event, the control will act as a frame
for the previewed report. If you are more ambitious, or you want to change the preview of a
composite report, you can register your own preview form as the default. See the section
‘Error: Reference source not found’ later on for details.
Filters
Sometimes, instead of printing or displaying it directly, you need to export data from your
database to another format. QuickReport comes complete with three filter components that
let you do this quickly and easily for their respective formats. Simply drop the export filter
onto the same form as the report, and the file format appears in the drop-down list of the
Save To file dialog in the preview. Registration is automatic, and you don’t need to code a
thing! (Don’t worry - you can export a report programmatically too if you wish – see
below.)
Note that not all printable components are exported by filters. Specifically, only the
contents of the following text-producing components appear in exported data: TQRLabel,
TQRDBText, TQRExpr, TQRMemo, TQRSysdata and TQRExprMemo.
TQRTextFilter. ‘Text’ format: exports the contents of the report in plain ASCII, using spaces
to separate fields.
TQRCSVFilter. CSV format: exports the report as ‘Comma Separated Variables’. As well as
using a comma to separate fields, this filter places “double quotes” around them, which
allows you to have commas within the fields themselves. This format is easily imported
into spreadsheets such as Microsoft Excel. By the way, the component has a Separator
property that specifies the character used to separate the fields. By default this value is set
to ‘,’ comma, but it can be changed to match your requirements.
TQRHTMLFilter. HTML format: exports the report to a HyperText Markup Language file,
as used in web browsers, some emailers, help systems and many other places.
It is also possible to call filters explicitly from code. This fragment uses the HTML filter.
quickrep1.ExportToFilter(
TQRHTMLDocumentFilter.Create('c:\report.txt'));
To use the Text or CSV filters in this way, use the same ExportToFilter call but instantiate a
TQRAsciiExportFilter or TQRCommaSeparatedFilter object as appropriate.
Chart
TQRChart is a version of TChart adapted to work with QuickReport. This allows you to add
complex charts to your reports – the combination is very powerful indeed. TQRChart is used
in the same way as the ordinary TChart control – double click it to bring up its extensive
property editor. For details of how to accomplish tasks such as setting up series and
adjusting the look of a chart, please see the TeeChart documentation.
Version incompatibility
Because of dependency issues beyond our control, certain versions of TeeChart are
incompatible with newer versions of QuickReport, and the TQRChart control and the whole
of TeeChart can be unloaded when you upgrade QuickReport. For example, at time of
writing the Delphi 3 version of QuickReport 3 (or higher) will not work with TeeChart,
because the version of TeeChart that is shipped with Delphi 3 is coded to depend on the
QuickReport 2 package. A workaround is to download the (free) TeeChart 4 evaluation
version from the TeeMach site at http://www.teemach.com/.
This issue extends to the Decision Cube components found in Client/Sever and Enterprise
Editions of Delphi – these depend on TeeChart, and get unloaded when it does. At present,
there is no way to use the Delphi 3 Decision Cube with both QuickReport 3 and TeeChart.
We do apologise for this unsatisfactory situation. Since it is caused by design decisions
made by other parties in code we cannot access, so that we are not able to fix matters
autonomously. If you run into trouble when upgrading QuickReport, please check our
website http://www.qusoft.no/ and TeeMach’s for the latest information.
Creating reports
The first step when creating a QuickReport is to create a form to store your TQuickRep
component. We refer to this form as a ‘report form’ since it’s just a container for a report
component and is not shown to the end user at runtime. It’s a good idea to adopt a naming
convention for such reports so that they are easily identifiable in the project manager and in
directory listings. For example, you could prefix all report form names with ‘rep’ or ‘rp’ to
make them stand out. You might want to use a similar scheme with form and data module
unit names.
TQuickRep in detail
The next step is to drop a TQuickRep component onto the form. Another useful convention
you may like to adopt: by naming all TQuickRep components ‘Report’, you can reference
them as repCustomerListing.Report, repSalesListing.Report and so on.
Units and Zoom properties
When dropping the TQuickRep component on a form you will se a grid to act as a guide for
positioning components. The grid is shown in the current QuickReport units. Select the
currently active unit by changing the TQuickRep.Units property in the property inspector.
The grid will be updated when you change this property.
Figure 6 - Adjusting the Units property to alter grid spacing
With Units set to ‘MM’, the grid displays at 10mm intervals; if it is set to ‘Inches’ then the
grid displays at 1" intervals. Using the grid you can produce very accurate report layouts,
positioning and sizing QuickReport components to 0.01" or 0.01mm.
Usually your screen is too small to display an entire TQuickRep component, since it sizes
itself based on the actual paper size selected. To get a clearer picture of the whole report,
change the Zoom property to 50% or less. Changing the zoom causes the TQuickRep
component and all the printable controls it contains to be redrawn at once to the requested
scale. This feature can also be used to enlarge important details for accurate positioning and
sizing.
Paper size and margins
You can set up page layout accurately by expanding the Page property of the TQuickRep
component. Double click on the + sign to the left of ‘Page’ in the Object Inspector to
expand the sub properties. You will now see all the settings controlling the page layout.
Figure 7 - Page sub properties
The values given are in the currently selected Units, in this case inches. The margin settings
can be seen as blue dotted lines on the TQuickRep component. All bands are sized to fit
inside the margins.
The sub properties are described in Table 1 below:
Table 1 - Sub properties of Page
Not all printer drivers support setting custom paper sizes through the Custom setting of
Papersize. In these cases you must select ‘Custom paper size’ in the printer driver’s own
dialog (accessed from the Windows Control Panel) and define the paper’s dimensions
there. Set this custom paper size to be the default paper size for that printer and finally set
the TQuickRep.Page.PaperSize property to Default. Your custom size will now be picked
up at runtime.
Alternatively, and perhaps more robustly, use the next largest standard paper size, and set
the margins to keep the printing within the custom area.
Selecting a font
As you would expect, you can set the default font for your report in the TQuickRep.Font
property. Double click on the property to get the standard Delphi font dialog.
The fonts listed are the Windows system fonts, True Type fonts and any PostScript fonts (if
Adobe TypeManager is installed). You can use any combination of fonts in your reports but
we advise the use of TrueType or PostScript fonts if you intend to allow the user to preview
the report. The system fonts do not scale very well in preview.
Some dot matrix printers print much faster if you select a font already build into the printer
hardware, called a ‘printer font’. Such fonts are not listed by the font dialog, but can be set
programmatically:
repCustomerListing.Report.Font.Name := 'CG TIMES';
The readability of your report depends very much on your font selection. You should
consider this carefully when selecting fonts. Using many different fonts, colours and styles
in a report can easily make it look cluttered and difficult to read.
Title and Description
The TQuickRep component has Title and Description string properties to identify and
describe the report. These are provided for your convenience, so you can make report
selection a data-driven procedure. For example, you might have generate a menu that lists
all your reports by title and shows the description when the user selects a report. An
example of this can be seen in the QuickReport example project.
The Title property can be printed on the report itself using a TQRSysData component.
Functions
The Functions property of a TQuickRep allows you to set up constants and functions that can
be used by QuickReport expressions contained in any TQRExpr, TQRExprMemo and
TQRGroup components that you drop on the report. Double-click the property the ‘…’
button in the object inspector to bring up the special property editor:
Figure 8 - Functions property editor
Use this dialog, and the expression builder that underlies it, to define constants that you
expect to require in multiple expressions. For example, you can see that in Figure 8 above I
have defined, with a regrettable lack of originality, the constant PI as 3.14159265358979.
The other functions you see, PAGENUMBER, COLUMNNUMBER and REPORTTITLE, are
automatically predefined.
Working with bands
QuickReport is a banded report generator. If you are unfamiliar with banded report
generators you can think of them as small paper templates, which are arranged horizontally
on a page and filled with data. Different templates are copied into different parts of the
page/report. The printable components, TQRLabel, TQRDBText and so on, are designed to be
placed on these bands. Placing these components directly on the report is not supported.
The easiest way to add bands is via the TQuickRep.Bands property in the Property Inspector.
Click the ‘+’ sign to the left of the word ‘Bands’ to expand the list of common bands:
Figure 9 - Bands sub properties
The Object Inspector shows if a given band type exists in the report or not, and you can add
or delete a band simply by changing the relevant property. Bands created this way get
names that describe their function: DetailBand1, PageHeaderBand1 and so on. The BandType
property of each band is also set automatically.
You can also add bands by selecting the TQRBand component on the component palette and
dropping it on the report. Note that if you do it this way you must take care to set the
BandType property to the desired band type, and you should also give the band a descriptive
name. The Bands property of the container TQuickRep will update itself to reflect bands
added to the report this way.
Here are the simple band types you can add to a report:
While it is possible to add a band manually and set its BandType to rbSubDetail or rbGroupHeader,
this is not recommended. These band types are intended for use only with TQRSubDetail and
TQRGroup components. Using them elsewhere may cause unexpected and undesirable effects
when the report is printed.
As you add new bands to a report, you will notice that they automatically position
themselves in the actual printing order. You will see that the Page Header band is on top,
followed by the Title band, column header band and so on, as shown in Figure 10 below.
Figure 10 - Simple band types
Each band has its band type printed in small letters in its lower left corner. This allows you
to identify the bands while designing the report. This text is not printed on the final report.
Bands appear on the TQuickRep component in the order in which they are printed. It is
helpful to understand why the bands line up the way they do. Generally bands will print in
at the frequency shown in Figure 10, although things become more complicated when you
start to add sub details and group bands.
Sizing the bands
Bands derive their horizontal size from the containing TQuickRep object. Their Size.Width
properties should be considered read only; values written to them are ignored. For a single
column report, the width of all bands is set to the page width minus the left and right
margins. In multi-column reports, the width of certain band types (Column Header, Detail,
Sub Detail, Group Header and Group Footer) is adjusted to reflect the width available for a
single column.
However you can adjust the vertical size of the bands. Select a band and resize it with the
mouse in the usual way or, if you want more accurate control, setting an exact value in the
Size.Height property.
Turning bands on and off
You might sometimes want to disable printing of a certain band. This can be done, either at
design time or at run time, by setting the TQRBand.Enabled property to False.
During report generation you can also temporarily disable printing of a band by creating an
event handler for the band’s BeforePrint event. This event handler takes a boolean parameter
PrintBand that can be set to False to disable band printing – but just for that single instance.
This feature can be used to perform simple filtering:
procedure TrepCusList.RepDetailBeforePrint
(Sender: TQRCustomBand;
var PrintBand: Boolean);
begin
PrintBand := CustTableTotalSales > 3000000;
end;
Note: When PrintBand is set to False for a detail band, the values for that record are not
included in any aggregate TQRExr function, for example the SUM function. This is a
behaviour change between QuickReport 2 and QuickReport 3.
If you turn off a Page Footer band, it will have the effect of leaving a blank space at the
bottom of each page – the Detail Bands will not expand to fill the space. To optimise
performance, QuickReport doesn’t check the length of the page footers all the time. So after
you change the Enabled property of your Page Footer, call the report object’s
ResetPageFooterSize method to force QuickReport to update its page footer information.
Groups
Groups allow you to generate extra bands between groups of records. For example, if you
were listing an address book, you might wish to group all the contacts whose name began
with the same capital letter, and to print that letter in large type above each group – in fact
this is what we do in the example.
To create a group:
11 Create a simple report as described in ‘A first report’ above.
12 Set the IndexName property of the TTable component to ‘ByCompany’.
13 Drop a TQRGroup component onto an existing TQuickRep object, where it appears
as a new band. This band will be the group header. Every time the group ‘breaks’, this
band will be printed.
14 Set the Expression property to
COPY(Table1.Company, 1, 1)
COPY(Table1.Company, 1, 1)
In addition you can also add a group footer band. Although we don’t really need one here,
we’ll make one for practice.
16 Select the TQRBand component on the palette and drop it on the report. Rename it
to FooterBand1.
17 Click on the group header band once more. Set the TQRGroup.FooterBand
property to FooterBand1.
18 Drop a TQRLabel onto the footer band. Set its Caption property to ‘FOOTER’.
If all has gone to plan, you should be looking at something like Figure 11:
Figure 11 - Creating a group
Now preview the report, either by running the program, or simply right-clicking on the
report object and choosing Preview:
Figure 12 - Preview of a grouped report
As expected, the resulting report shows a list of all the companies grouped in alphabetical
order, with each group headed by a line showing the current letter.
Master/detail reports
You will very often wish to create a master/detail report – that is one where you are
extracting data from two datasets connected by a master/detail relationship. QuickReport
allows you to include one or more such relationships in a report using TQRSubDetail
components.
An obvious example of a master/detail report is to list out all the orders associated with
each customer in a database. Here is a quick example:
19 Start with the group report as created in the previous section.
20 Drop a TDataSource component on the form, and make its DataSet property
‘Table1’.
21 Drop a new TTable component on the form. Set its DatabaseName property to
‘DBDemos’, TableName to ‘ORDERS.DB’, IndexName to ‘CustNo’, MasterSource to
‘DataSource1’, MasterFields to ‘CustNo’ and Active to True. The two TTable
components are now set up in a master/detail relationship.
22 Drop a TQRSubDetail component onto the existing TQuickRep object, where it
appears as a new band. Notice that its Master property is automatically set to QuickRep1.
The master/detail relationship between the two TTable objects is mirrored between the
report object and its sub detail band.
23 Set the TQRSubDetail component DataSet property to ‘Table2’. The TQRSubDetail
component iterates all through its DataSet for each change in the Dataset of its Master.
24 Drop three TQRDBText components on the sub detail band. Set their DataSet
properties to ‘Table2’, and set the DataField properties to ‘OrderNo’, ‘SaleDate’ and
‘ItemsTotal’ respectively.
If you have done all that correctly, you should now be looking at something like Figure 13
below.
Figure 13 - Creating a report with sub detail
Now preview the report. The result will look like Figure 14, with each customer’s orders
listed below the customer name. Note that the format of the date and currency fields
depends on Windows’ international settings. Mine are set to British, your mileage will
obviously vary.
Figure 14 - Preview of a master/detail report
More about printable components
By now, you will have a feel for QuickReport’s printable components – use them like
ordinary controls to define you layout. However, there are a few QuickReport-specific
things to learn about them.
Text components
QuickReport’s printable text components – TQRLabel, TQRDBText, TQRExpr, TQRSysData,
TQRMemo, TQRExprMemo, TQRRichText and TQRDBRichText – share some common
properties, which they inherit from a parent, class:
Table 3 - Text component properties
Property Purpose
AlignToBand By default components will print/align at the position
set in the designer. But sometimes it is more practical
to align components to the vertical edges of the band
that it is placed on. When AlignToBand is True, a
text components will align itself relative to its parent
band instead of its own text rectangle.
AutoSize Set this property to True and a component sizes itself
horizontally to fit whatever text is put into it.
AutoStretch If AutoStretch and WordWrap are both True, a
component can expand vertically to accommodate its
text. When a component expands in this way it also
expands its parent band, provided that band’s
CanExpand property is set to True. A band can
expand over multiple pages if necessary.
Note that if a component expands it will not move
other components on the same band down. If you
have components whose desired position should
depend on the length of a stretching text, you should
place these in a child band.
Note also that this property cannot be used for
components on any band that prints at the bottom of
the page. This is typically the page footer, but also
applies to any band that has had its AlignToBottom
property set to True.
Frame All text components can display a visible frame
around them. This property controls the appearance
of the frame, which sides it is drawn on and so on.
Size All printable components share the Size property. If
AutoSize is False you can use this property to set the
exact size of the component. This property also
contains the position of the component relative to its
parent band.
WordWrap If WordWrap is set to True text can span multiple
lines.
Formatting fields
TQRDBText components use any formatting options defined for the field to which they are
connected. Sometimes, however, you need to customise the display of a particular value;
this can be achieved using TQRDBText’s Mask property. This takes the same values as
Delphi’s own FormatFloat function (for numeric fields) and FormatDateTime function (for
date and time fields) – in fact, QuickReport itself calls these functions to do the work. To
give you an example, suppose you wished to print out a numeric value to two decimal
places, with the thousands separated by a comma, and negative values shown (in
parentheses) in the style favoured by accountants. Then you could use a Mask like this
#,##0.00;(#,##0.00)
which would cause the values
1234 and -1234.5
to be printed as
1,234.00 and (1,234.50)
respectively.
Check out the Delphi help for FormatFloat and FormatDateTime for details and more
examples.
To specifically set a formatting of a field use the Mask property. The mask works
differently for different field types.
Using expressions
QuickReport includes an advanced expression evaluator, used by the TQRExpr,
TQRExprMemo and TQRGroup components. Expressions can be used to combine and
manipulate database fields, and perform advance formatting. Their syntax is rather like that
of Object Pascal: the expressions can be of boolean, float, integer or string type. Note that
date and time fields are converted into strings, and BLOB and memo fields are not
supported in expressions.
The evaluator supports the usual set of operators:
Table 4 - Operators supported by the expression evaluator
Operators Function
+ Addition, string concatenation
- * / Subtraction, multiplication, division
() Parentheses
And Or Not Logical operators
= < > Comparison operators
<= >= <>
Function Description
AVERAGE(EXPR) Aggregate function. Averages the EXPR
COPY(STR,S,L) Returns a sub string of STR starting at
character S length L
COUNT Aggregate function. Returns the number of
iterations of the Master band.
DATE Return current date as a string
DIV(X, Y) Integer division of X by Y
FALSE Logical value False
FORMATNUMERIC(F, N) Format numeric N using string mask F. The
mask takes the values as Delphi’s
FormatFloat function.
FRAC(NUM) Returns the fractional part of a NUM
IF(EXPR, R1, R2) Returns R1 or R2 depending on the boolean
EXPR
INT(NUM) Returns the integer part of NUM
LOWER(STR) Returns STR in lowercase
MAX(EXPR) Aggregate function. Returns the highest
value of EXPR
MIN(EXPR) Aggregate function. Returns the lowest
value of EXPR
PRETTY(STR) Returns STR in ‘pretty’ case, ie first latter
in uppercase, the remainder lowercase
SQRT(NUM) Returns the square root of NUM
STR(NUM) Converts NUM to a string
SUM(EXPR) Aggregate function. Returns the sum of
EXPR
TIME Return current time as a string
TRUE Logical value True
TYPEOF(EXPR) Returns the data type of EXPR as a string,
eg ‘BOOLEAN’
UPPER(STR) Returns STR in uppercase
If your expression includes aggregate functions like SUM or COUNT you must link the
Master property to the component, TQuickRep or TQRSubDetail that will be used to update
the expression. For a simple report this is your TQuickRep component, but in a complicated
report with many datasets you must take care to link to the correct TQRSubDetail. The
expression is recalculated each time the record pointer of the linked master is advanced.
The ResetAfterPrint property is also useful when working with aggregation functions, and
allows you to create, for example, group totals as well as running totals.
The expression builder
To make it easier to create expressions for your reports, QuickReport includes a special
property editor, which appears when you click the ‘…’ button. This is shown in Figure 15.
Figure 15 - Expression builder dialog
The expression builder lets you design your expression by selecting functions and field
names from lists – so it makes it a lot easier to avoid typos in identifier names. It also
brings up special dialogs to prompt for function arguments.
Figure 16 - Setting function arguments in the expression builder
TQRCustomPreviewInterface = class(TQRPreviewInterface)
public
function Show(AQRPrinter : TQRPrinter)
: TWinControl; override;
function ShowModal(AQRPrinter : TQRPrinter)
: TWinControl; override;
end;
Notice that this is an interface1 class – it serves only to define a couple of functions, and has
no data of its own. These two functions are implemented to construct and display your
custom preview in non-modal and modal forms.
1
We have not used Delphi’s interface keyword to define these classes because this is not currently common practice, and many
Delphi programmers are unfamiliar with the syntax. However, the concepts are very similar.
Lets suppose that the preview form is going to be called TCustPreview. Then the
implementation of the TQRCustomPreviewInterface methods might look like this:
function TQRCustomPreviewInterface.Show(
AQRPrinter: TQRPrinter): TWinControl;
var
frm : TCustPreview;
begin
frm := TCustPreview.Create(Application, AQRPrinter);
frm.Show;
Result := frm;
end;
function TQRCustomPreviewInterface.ShowModal(
AQRPrinter: TQRPrinter): TWinControl;
var
frm : TCustPreview;
begin
frm := TCustPreview.Create(Application, AQRPrinter);
frm.ShowModal;
Result := frm;
end;
Now we are done with the glue code, and can build the actual previewer form. Mine is
minimal; just a single TQRPreview control stuck onto a form:
Figure 17 - Simple preview form
When you do real previews in your applications, you will probably want to add buttons to
call TQRPreview’s Zoom method and other facilities.
To support the previewing mechanism, I had to write a little more code. Here is the
declaration of TCustPreview. Notice I have added a new constructor, which expects to
receive the TQRPrinter argument passed in by the Show and ShowModal methods of the
interface class. Delphi generates a warning message that the new constructor hides the
original. In this case it is deliberate, so I have wrapped the class in
{$WARNINGS ON} ... {$WARNINGS OFF}
compiler directives to make it shut up.
{$WARNINGS OFF}
TCustPreview = class(TForm)
QRPreview1: TQRPreview;
procedure CustPreviewClose(Sender: TObject;
var Action: TCloseAction);
private
{ Private declarations }
fQRPrinter : TQRPrinter;
public
{ Public declarations }
constructor Create(AOwner : TComponent;
AQRPrinter : TQRPrinter); virtual;
end;
{$WARNINGS ON}
Finally, here is the implementation of the class. Notice in particular the cleanup code held
in the form’s OnClose event. If you don’t call ClosePreview here, you will get a nasty
memory leak. (QuickReport 2 users should note that this is a new requirement. You must
modify your existing preview forms when porting them to QuickReport 3 and later.)
constructor TCustPreview.Create(AOwner: TComponent;
AQRPrinter: TQRPrinter);
begin
inherited Create(AOwner);
fQRPrinter := AQRPrinter;
QRPreview1.QRPrinter := AQRPrinter;
end;