Boletín Pascal #25
Los ejemplos completos de código fuente de este número están disponibles para descargar.
![]() |
![]() |
Boletín Pascal #25 - 18-AGO-2001 INDICE 1. UNAS PALABRAS DEL EDITOR 2. IBADMIN 3.2 - COMPLETE INTERBASE SQL TOOL - ¿Qué es IBAdmin? - Ediciones - Características - Precios - Descargas - Más información 3. DELPHI ADENTRO (I) - Una necesaria introducción teórica - Un segundo para la historia - Paradigmas y abstracción - Unas palabras finales - Referencias bibliográficas - Glosario 4. DESCARGAS DEL SITIO DE BORLAND 5. INCRUSTANDO ARCHIVOS COMO RECURSOS EN UN EJECUTABLE DELPHI 6. TRUCOS - Moviendo filas y columnas de un StringGrid por código - Impidiendo que el usuario seleccione texto en un control Memo - Desactivando el menú contextual - Ordenando un TListView por la columna cliqueada por el usuario - Haciendo que un formulario se mantenga "siempre visible" - Actualizando una tabla con datos de otra tabla con Local SQL - Buscando texto en cualquier parte de un campo - Pintando filas en un DBGrid de Delphi 4 7. DELPHI EN LA RED ________________________________________________________________________ 1. UNAS PALABRAS DEL EDITOR Me complace anunciar que el Foro para Programadores en Delphi de Latium Software cuenta ya con más de 250 miembros y un promedio de unos doce mensajes diarios. Lo invito a que vea los últimos mensajes: http://espanol.groups.yahoo.com/group/delphi-intermedio/messages Para unirse al foro, puede hacerlo desde la web: http://espanol.groups.yahoo.com/group/delphi-intermedio/join O por email: delphi-intermedio-subscribe@gruposyahoo.com Puede configurar su suscripción al foro para convertir o no los mensajes a formato HTML, o para no recibir los mensajes en su email (podrá ver los mensajes en la web). En esta edición del boletín me complace presentar IBAdmin, una de las mejores herramientas de administración de Interbase, como así también la primera parte del artículo "Delphi Adentro" de MSc. Víctor Lorenzo Prado. Saludos, Ernesto De Spirito eds2004 @ latiumsoftware.com ________________________________________________________________________ JfControls Lib. Multilenguaje. Multiapariencia. Skins. Privilegios. Más de 40 componentes integrados y personalizables. Múltiples problemas de programación resueltos. Administración centralizada de recursos. Para Delphi 3-7 y C++ Builder 3-6. http://www.jfactivesoft.com/spindex.htm ________________________________________________________________________ 2. IBADMIN 3.2 - COMPLETE INTERBASE SQL TOOL ¿Qué es IBAdmin? ================ IBAdmin es una herramienta de administración y desarrollo de bases de datos para manejar servidores y bases de datos Interbase. IBAdmin provee muchas capacidades para ayudarle en el diseño y gestión de su base de datos. Puede emplear el "Database Designer" (diseñador de base de datos) para diseñar visualmente la estructura de la base de datos, o el "Grant Manager" (administrador de permisos) para administrar los usuarios, o el "SQL Debugger" (depurador SQL) que puede usarse para depurar procedimientos almacenados y triggers. Se puede realizar una edición confortable de código con Code-Insight y Code Completion. Otras características incluyen el "Databases Comparer" (comparador de bases de datos), donde uno puede fácilmente actualizar las bases de datos ya distribuidas, y el "Dependencies Explorer" (explorador de dependencias) que puede mostrarle todas las dependencias entre los objetos de la base de datos. Ediciones ========= IBAdmin 3 puede adquirirse como tres productos distintos: * IBAdmin Professional - versión completa con todas las características * IBAdmin Standard - éste es igual que el profesional excepto que no incluye el diseñador de base de datos (útil cuando uno no necesita esta herramienta CASE incorporada). * IBAdmin Lite - incluye solamente la capacidad de gestión de bases de datos básica, el administrador de permisos y el asistente de migración de Interbase 5.x a 6.0 (útil para la distribución a los clientes y una modesta cantidad de escritura de código de base de datos). Características =============== * Editores de metadatos fáciles y confortables. * Data View (vista de datos) con ordenamiento de datos y capacidad de exportación. * Descripción de todos los objetos de bases de datos (excepto generadores) * Cualquier objeto puede ser duplicado. * Usando el SQL Editor (editor SQL) usted puede: - ejecutar cualquier sentencia SQL o DDL - obtener estadísticas completas de ejecución - ejecutar consultas parametrizadas - usar el Visual Query Builder (constructor visual de consultas) - ejecutar consultas en "background" - guardar sus consultas en la lista de "Favoritos" - guardar y cargar la historia de sesiones de consulta - exportar los resultados de las consultas a archivos Excel * Extracción de metadatos de la base de datos: - extracción sólo de los objetos seleccionados - extracción de tablas como INSERT (...) INTO XXX VALUES(...) - impresión de metadatos * Búsqueda de metadatos [S] * Depurador de procedimientos almacenados con puntos de interrupción [S] * Diseñador de bases de datos (Database Designer) [P] - herramienta CASE para bases de datos Interbase * Agrupamiento de objetos [S] * Explorador de dependencias [S] * Ejecución de scripts SQL * Comparador de bases de datos * Administrador de permisos * Generador de datos de prueba [S] * Asistente de migración de Interbase 5.x a 6.0 * Plantillas de código SQL * Instalador de listas de palabras clave * Soporte para Interbase 6.0 que incluye: - Copia de respaldo y restauración - Administración de usuarios - Apagado y reiniciado de bases de datos - Bitácora del servidor - Propiedades de base de datos extendidas (dialecto, intervalo de barrido, señal de sólo lectura, escrituras forzadas) Leyenda: sin marca - incluida en todas las versiones. [S] - incluida en las versiones estándar y profesional. [P] - incluida en las versiones profesional únicamente. Precios ======= Hasta tres licencias, el costo por licencia se muestra abajo: - IBAdmin 3.2 Professional............$ 229 (USD) - IBAdmin 3.2 Standard................$ 159 (USD) - IBAdmin 3.2 Lite....................$ 79 (USD) Si adquiere cuatro o más licencias, los costos por licencia son: - IBAdmin 3.2 Professional............$ 199 (USD) - IBAdmin 3.2 Standard................$ 129 (USD) - IBAdmin 3.2 Lite....................$ 69 (USD) En el caso de IBAdmin 3.2 Lite, por ocho licencias o más, el costo de cada una es de $ 59 (USD). Hay muchas formas de pago a través de ShareIt! Para más información sobre precios, descuentos, y cómo ordenar, visite: http://www.sqlly.com/ordering.htm Descargas ========= Hay una versión shareware totalmente funcional que puede descargar para evaluar el software por 30 días: http://www.sqlly.com/files/ibadmin3.zip Una herramienta adicional para IBAdmin 3 está disponible. El IBAdmin Model Viewer se puede utilizar para visualizar e imprimir los modelos de base de datos creados con el diseñador de base de datos. El IBAdmin Model Viewer se puede descargar de la siguiente dirección: http://www.sqlly.com/files/dbm_viewer.zip Más información =============== Puede ver unas capturas de pantalla de IBAdmin en: http://www.sqlly.com/ibadmin_screenshots.htm Para más información, puede visitar el sitio web de SQLLY Development, los creadores de IBAdmin: http://www.sqlly.com/ibadmin2.htm --------------------- Partes de este artículo fueron tomadas del sitio web de SQLLY Development y son Copyright (c) 2001 SQLLY Develpoment Inc. - Todos los derechos reservados. Han sido reproducidas y modificadas aquí con permiso del autor. ________________________________________________________________________ 3. DELPHI ADENTRO (I) Por Víctor Lorenzo Prado <vlorz@yahoo.es> Una necesaria introducción teórica ================================== En la teoría de la programación encontramos que nuestros maestros ponen un gran empeño en que sigamos el conjunto de reglas y paradigmas que, como varita mágica, nos convertirán en "Verdaderos Programadores". Pero, "lamentablemente", a veces nos vemos obligados a apartarnos un poco de sus enseñanzas para poder explotar al máximo a nuestro compilador y al código que genera. En esta primera parte se darán algunos conceptos básicos sobre la nece- sidad de realizar abstracciones para obtener un software de calidad. Y en las siguientes partes se tratará lo opuesto, cómo el no abstraerse por completo ayuda a comprender mejor los por qué y para qué de las cosas y los puntos débiles y fuertes del compilador utilizado. Un segundo para la historia =========================== Hace ya algunos años la programación de ordenadores era considerada Un Arte. Recuerdo títulos de libros como "The Art of Computer Programming", "Programming as an Art", y otros. Y en cierto modo lo era. Piense un poco en el panorama de la época, los ordenadores más comunes eran las PC-XT y compatibles, un "pobre", "diminuto" e "indefenso" ordenador si pensamos en las limitadas posibilidades de su procesador central Intel 8088 (16 bits internos y 8 bits externos, CLK de 4.77MHz), con una memoria RAM de hasta 640 Kbytes (aunque podía ser expandida por encima de 1 MByte utilizando tarjetas de expansión de memoria), unido esto a un "primitivo" sistema operativo: el MS-DOS. Con tal tipo de ordenador cada byte de datos, cada byte de código, cuenta, y por lo tanto el progra- mador no puede abstraerse del tipo de ordenador que utiliza cuando genera su código de programa. Actualmente el panorama es ya bien distinto, y tanto que las potencia- lidades de los ordenadores personales actuales van ya varios años por delante (en cuanto a tecnología, según criterios de especialistas) de las potencialidades medias requeridas por el software que se produce. Si antes el arte de la programación radicaba en cómo hacer programas lo más pequeños y veloces posible para explotar al máximo un ordenador tan limitado en potencialidades, actualmente el arte de la programación está en cómo lograr un nivel de abstracción tal, que permita modelar de la manera más fiel y confiable posible el comportamiento de los objetos y entes del mundo real. Ya no constituyen problemas mayores la disponibi- lidad de memoria RAM o disco duro. Veamos a continuación algunos conceptos que nos serán de utilidad como programadores. Paradigmas y abstracción ======================== Primero demos un vistazo a algunos de los paradigmas que son de mayor importancia en la teoría de la programación. Seguramente coincidiremos en que todo desarrollador de software desea producir siempre un código que sea: - PORTABLE: ¿A quién le interesa comprar un software que no pueda utilizar en su PC? - EXTENDIBLE y MANTENIBLE: ¿A quién le interesa comprar un software obsoleto, que no sea constantemente actualizado y renovado con la incorporación de nuevas tecnologías? - REUTILIZABLE: ¿A qué programador le interesa comprar, o producir, una biblioteca de componentes que no le sea útil para nada? - CONFIABLE: ¿A alguien le gusta que le mientan, o que se le destruya todo un día de trabajo en segundos? Si se analiza lo planteado en [1] sobre el diseño orientado a objetos y en [2] sobre el diseño del software para los sistemas de adquisición de datos (además de lo planteado por otros autores), podrá verse la extra- ordinaria importancia que posee la ABSTRACCIÓN sobre la calidad del software obtenido, calidad que se mide, entre otros muchos aspectos, por los planteados anteriormente. Veamos el siguiente ejemplo: Ejemplo 1. Supongamos un software que constituye un juego instructivo, donde se generan imágenes y sonidos. Aquí hay un pequeño/gran problema, el acceso al hardware de visualización (controladora de vídeo) y de generación de sonidos (tarjeta de audio). Centrémonos en el acceso a la tarjeta de audio y veamos varios niveles de abstracción: 1. No considerar ninguna abstracción y realizar el acceso directo al hardware, "simples" operaciones de lectura y escritura en los puertos de la tarjeta. Pero... ¿Poseen todas las tarjetas de audio la misma INTERFAZ? La respuesta es NO. Y esto nos genera un tremendo dolor de cabeza. No realizar abstracciones con respecto a la implementación física del hardware obliga al software a "conocer" todas las variantes de hardware con que deberá trabajar, y por ende, todos los formatos que deberán tomar los datos que estos aceptarán o entregarán. ¿Puede Ud. nombrar paquetes de software que estén en este caso? Seguro que sí, los hay que han sido muy populares tanto para el entretenimiento como para aplicaciones profesionales. *** AL DESARROLLARSE UN SOFTWARE SIN CONSIDERAR ABSTRACCIONES, SE LE ESTÁ CONDENANDO A UNA MUY LIMITADA POSIBILIDAD DE ADAPTACIÓN A NUEVAS Y DISTINTAS CONDICIONES DE TRABAJO *** 2. Incluir como parte del sistema operativo (extensiones), o del software, un conjunto de APIs que permitan obtener la funcionalidad necesaria del hardware con relativa independencia sobre la configu- ración que este toma. Ah..., ya esto suena mejor. Ahora bastaría, "únicamente", con realizar un conjunto de llamadas a funciones de las API utilizando unos formatos bien determinados de datos. ¿Cómo actualizar el software? Actualizando las APIs. ¿Cómo hacer que las APIs sean seleccionables en tiempo de corrida? Utilizando APIs en forma de DLL con enlace explícito. Pero... ¿Nuevamente un "pero"? Sí. Cuando se utilizan las funciones de las API el programador es totalmente RESPONSABLE de la INTEGRIDAD DE LOS DATOS. Si una función, encargada de realizar alguna labor de inicialización, da como resultado un bloque de datos que será utilizado por otra función (y que en este caso puede ser precisamente la que hace lo que nos interesa que se haga), no es otro sino el programador el responsable de mantener a salvo esos datos. Y no son pocos los casos donde esos bloques de datos no son más que datos intermedios, sin otra utilidad que esa. Y a esto se suma un problema adicional: una API, a fin de cuentas, es simplemente un conjunto de funciones agrupadas en una DLL, y como toda función, necesita que sus parámetros sean dados con una sintaxis específica. No realizar abstracciones con respecto a las estructuras de los datos, empleando sintaxis con fuertes especificaciones de tipo para los parámetros de entrada, limita el proceso de desarrollo de una API. Si se requiere que una versión superior mantenga compatibi- lidad con las anteriores, la sintaxis del pase de los parámetros debe permanecer igual. Un error en el diseño inicial de la interfaz de la API, en aras de la compatibilidad, podrá ser arrastrado de por vida. ¿Puede Ud. nombrar ejemplos? Claro que también puede, ¿Recuerda todas esas funciones API de Windows que terminan en Ex()? Seguro que para casi todas encontrará versiones anteriores sin las terminaciones Ex(). *** DESARROLLAR EL SOFTWARE SIN CONSIDERAR ABSTRACCIONES SOBRE LOS TIPOS DE DATOS Y LAS INTERFACES DE LAS FUNCIONES QUE LOS PROCESARÁN LIMITARÁ ENORMEMENTE SU PROCESO DE DESARROLLO *** 3. Realizar un doble nivel de abstracción, de datos y de hardware, utilizando herramientas de programación que permitan encapsular, en una misma entidad, los datos y los métodos de procesamiento. Nada más parecido a la Programación Orientada a Objetos (POO), ¿Verdad? Ah... Lo soñado..., el gran paradigma de la programación, el no tener que saber en detalles cómo se hacen las cosas a bajo nivel, sino únicamente quién debe hacerlas y, por supuesto, cómo decirle que las haga. ¿Ha tratado alguien de desarrollar una aplicación Delphi sin utilizar la VCL? ¿Por cuanto disminuye su productividad? *** MEDIANTE LA CREACIÓN DE UN CONJUNTO DE CLASES QUE ENCAPSULEN TODA LA FUNCIONALIDAD NECESARIA SE PODRÁ CREAR UN SOFTWARE ROBUSTO, CONFIABLE, FÁCILMENTE EXTENDIBLE Y SOBRE TODO MANTENIBLE *** Unas palabras finales ===================== La teoría es extremadamente útil cuando se quiere hacer las cosas de forma profesional y elegante. Pero por sí sola no lo es todo. Existen casos donde "abstraerse de las abstracciones" puede ser de gran utilidad para poder obtener el máximo de las prestaciones del compilador. De algunos de estos casos se tratará en las próximas secciones; algunos para entender mejor el funcionamiento de Delphi y otros, simples trucos para lograr mejores prestaciones en el código producido. Referencias bibliográficas ========================== [1] - Grady Booch; Object Oriented Design; 1991, pp. 39-41, 49-53. [2] - Lorenzo Prado, Victor P.; "La Arquitectura del Software: Importancia en el Diseño de los Sistemas de Adquisición de Datos"; Revista Ingeniería Electrónica, Automática y Computación. ISPJAE(CUBA), Volumen XXII, No. 2, 2001, pp. 54-59. Glosario ======== API - Del inglés Applications Programmer Interface, Interfaz para el programador de aplicaciones. Conjunto de bibliotecas de funciones (en forma de DLL) que brindan el soporte para el desarrollo de aplicaciones sin la necesidad de conocer exactamente las especifi- caciones técnicas del hardware ni las interioridades del sistema operativo. CLK - Del inglés CLOCK, señal de reloj (temporización) del procesador. Determina la frecuencia a la cual son ejecutadas las instruc- ciones, mientras más alta sea esta frecuencia, mayor será la velocidad de ejecución del microprocesador. Cuando se dice "este procesador trabaja a TANTOS MHz", no necesariamente todo el ordenador estará utilizando esa frecuencia de reloj, generalmente es solo el núcleo central de procesamiento quien trabaja a frecuencias elevadas (debido a limitaciones físicas debidas a demoras y a la radiación de ondas electromagnéticas) y el resto del ordenador a frecuencias más bajas ya estandarizadas. DLL - Del inglés Dynamic Link Library, Biblioteca de Enlace Dinámico. Biblioteca de funciones que son enlazadas (explícita o implícita- mente) con la aplicación en tiempo de corrida. KByte - Kilo Byte. 1 KByte = 1024 Bytes. MByte - Mega Byte. 1 MByte = 1024 KBytes = 1048576 Bytes. RAM - Del inglés Random Access Memory, Memoria de Acceso Aleatorio. Memoria a la cual el procesador puede tener acceso tanto en escritura como lectura. Es donde son almacenados los datos y programas al momento de ser ejecutados. ------------------ Copyright (c) 2001 Víctor Lorenzo Prado ________________________________________________________________________ 4. DESCARGAS DEL SITIO DE BORLAND La edición Open Edition de Kylix está lista para descargar gratis del sitio de Borland: * Kylix Open Edition http://www.borland.com/kylix/openedition/ Como ya hemos mencionado antes, la licencia de esta edición solamente permite la creación de aplicaciones distribuibles únicamente bajo los términos de la GNU GPL. El que sería de algún modo el equivalente de Kylix Open Edition en el entorno Windows es Delphi 6 Personal. Su licencia impide el uso comercial (ni directo ni indirecto), y también está disponible para descargar gratis: * Delphi 6 Personal http://www.borland.com/delphi/personal/index.html También están disponibles para descarga las versiones de evaluación por 30 días de Delphi 6 Enterprise y de Kylix Server Developer Edition: * Delphi 6 Enterprise - Trial Edition http://www.borland.com/delphi/trial6/index.html * Kylix Server Developer Edition - Trial Edition http://www.borland.com/kylix/trial/ En el tercer paso del proceso de descarga no se olvide de hacer clic en el enlace "Send me a serial number and activation key via email" para recibir por email el número de serie y la clave de activación que se requiere para poder instalar el producto. ________________________________________________________________________ 5. INCRUSTANDO ARCHIVOS COMO RECURSOS EN UN EJECUTABLE DELPHI Es posible incrustar cualquier clase de archivo en un ejecutable usando archivos de recursos (*.RES). Ciertos tipos de recursos son reconocidos por la API y pueden utilizarse directamente. Otros se toman simplemente como datos binarios y y queda en uno cómo utilizarlos. En este artículo verá ejemplos de ambas clases. Para crear el archivo de recursos comenzamos con el archivo fuente (*.RC), por ejemplo llamado RESOURCES.RC, que es un simple archivo de texto que contiene las entradas de recursos (nombre, clase y archivo): sample_bmp BITMAP sample.bmp sample_ico ICON sample.ico sample_cur CURSOR sample.cur sample_ani ANICURSOR sample.ani sample_jpg JPEG sample.jpg sample_wav WAVE sample.wav sample_txt TEXT sample.txt Los nombres de los recursos (sample_bmp, sample_ico, etc.) son arbi- trarios. La clase de recurso puede ser o bien una reconocida por las APIs (BITMAP, ICON, CURSOR) o en su defecto una arbitraria (JPEG, WAVE, TEXT). Los nombres de archivo especifican los archivos que serán incluidos en el archivo .RES (y más adelante en el EXE). Ahora tenemos que compilar el archivo .RC para producir el archivo .RES. Para esa tarea podemos utilizar el compilador de recursos de Borland (brcc32.exe) que usted podrá encontrar probablemente en la carpeta BIN de Delphi. Se trata de una simple utilidad de consola (en línea de órdenes) que espera el nombre del archivo fuente como parámetro: C:\DELPHI\P0025>brcc32 resources Borland Resource Compiler Version 5.40 Copyright (c) 1990, 1999 Inprise Corporation. All rights reserved. C:\DELPHI\P0025>_ Para intruir al enlazador a que incluya el archivo de recursos en el ejecutable usamos la directiva de archivos de recurso ($R o $RESOURCE) en nuestro código fuente Pascal: {$R resources.res} La carga de los recursos en nuestra aplicación es fácil para los recursos "reconocidos" como BITMAP, ICON y CURSOR puesto que la API de Windows provee funciones (LoadBitmap, LoadIcon y LoadCursor respecti- vamente) para obtener los manejadores (handles) de estos elementos, que por ejemplo podemos asignar a la propiedad Handle del objeto correspon- diente: Image1.Picture.Bitmap.Handle := LoadBitmap(hInstance, 'sample_bmp'); Icon.Handle := LoadIcon(hInstance, 'sample_ico'); Screen.Cursors[1] := LoadCursor(hInstance, 'sample_cur'); Para más alternativas respecto de cómo cargar recursos de imágenes, vea la API LoadImage. Otros recursos son un poco más difíciles de manejar. Comencemos con las imágenes JPEG. La siguiente función usa TResourceStream para cargar el recurso como una corriente (stream) desde la que cargaremos el objeto TJPEGImage que la función devuelve: function GetResourceAsJpeg(const resname: string): TJPEGImage; var Stream: TResourceStream; begin Stream := TResourceStream.Create(hInstance, ResName, 'JPEG'); try Result := TJPEGImage.Create; Result.LoadFromStream(Stream); finally Stream.Free; end; end; Ejemplo: var Jpg: TJPEGImage; begin // ... Jpg := GetResourceAsJpeg('sample_jpg'); Image2.Picture.Bitmap.Assign(Jpg); Jpg.Free; // ... end; Para archivos WAV necesitamos un puntero al recurso cargado en memoria y para un archivo de texto necesitamos cargar el recurso como una cadena. Podemos hacerlo usando TResourceStream, pero veamos un ejemplo usando la API: function GetResourceAsPointer(ResName: pchar; ResType: pchar; out Size: longword): pointer; var InfoBlock: HRSRC; GlobalMemoryBlock: HGLOBAL; begin InfoBlock := FindResource(hInstance, resname, restype); if InfoBlock = 0 then raise Exception.Create(SysErrorMessage(GetLastError)); size := SizeofResource(hInstance, InfoBlock); if size = 0 then raise Exception.Create(SysErrorMessage(GetLastError)); GlobalMemoryBlock := LoadResource(hInstance, InfoBlock); if GlobalMemoryBlock = 0 then raise Exception.Create(SysErrorMessage(GetLastError)); Result := LockResource(GlobalMemoryBlock); if Result = nil then raise Exception.Create(SysErrorMessage(GetLastError)); end; function GetResourceAsString(ResName: pchar; ResType: pchar): string; var ResData: PChar; ResSize: Longword; begin ResData := GetResourceAsPointer(resname, restype, ResSize); SetString(Result, ResData, ResSize); end; Llamadas de ejemplo: var sample_wav: pointer; procedure TForm1.FormCreate(Sender: TObject); var size: longword; begin ... sample_wav := GetResourceAsPointer('sample_wav', 'wave', size); Memo1.Lines.Text := GetResourceAsString('sample_txt', 'text'); end; Una vez que tenemos cargado del recurso de ondas (wave) podemos ejecutarlo cuantas veces queramos con la API sndPlaySound declarada en la unidad MMSystem: procedure TForm1.Button1Click(Sender: TObject); begin sndPlaySound(sample_wav, SND_MEMORY or SND_NODEFAULT or SND_ASYNC); end; Hay algunos recursos (como fuentes y cursores animados) que no se pueden usar desde memoria sino que necesariamente debemos guardar dichos recursos en un archivo temporario y cargarlos desde allí. La siguiente función guarda un recurso en un archivo: procedure SaveResourceAsFile(const ResName: string; ResType: pchar; const FileName: string); begin with TResourceStream.Create(hInstance, ResName, ResType) do try SaveToFile(FileName); finally Free; end; end; La siguiente función usa la anterior para guardar un recurso en un archivo temporario: function SaveResourceAsTempFile(const ResName: string; ResType: pchar): string; begin Result := CreateTempFile; SaveResourceAsFile(ResName, ResType, Result); end; El tratamiento de la función CreateTempFile cae fuera del propósito de este artículo y su implementación puede verse en el ejemplo adjunto a este boletín. La siguiente función hace uso de SaveResourceAsTempFile para guardar un recurso de cursor animado en un archivo temporario, luego carga el cursor desde el archivo con LoadImage y finalmente elimina el archivo temporal. La función devuelve el manejador (handle) devuelto por LoadImage: function GetResourceAsAniCursor(const ResName: string): HCursor; var CursorFile: string; begin CursorFile := SaveResourceAsTempFile(ResName, 'ANICURSOR'); Result := LoadImage(0, PChar(CursorFile), IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE or LR_LOADFROMFILE); DeleteFile(CursorFile); if Result = 0 then raise Exception.Create(SysErrorMessage(GetLastError)); end; Llamada de ejemplo: Screen.Cursors[1] := GetResourceAsAniCursor('sample_ani'); Form1.Cursor := 1; Bien, esto es todo. Espero que lo encuentre de utilidad. Puede encontrar más información acerca de los archivos de recursos en la MSDN Library: http://msdn.microsoft.com/library/en-us/winui/hh/winui/rc_6cs3.asp ________________________________________________________________________ 6. TRUCOS Moviendo filas y columnas de un StringGrid por código ===================================================== El usuario puede mover filas y columnas de un StringGrid con el ratón. ¿Puede eso además hacerse por código? En la ayuda de TCustomGrid puede ver los métodos MoveColumn y MoveRow, pero éstos están ocultos en TStringGrid. Podemos hacerlos nuevamente accesibles derivando TStringGrid y declarando esos métodos como públicos: type TStringGridX = class(TStringGrid) public procedure MoveColumn(FromIndex, ToIndex: Longint); procedure MoveRow(FromIndex, ToIndex: Longint); end; La implementación de esos métodos simplemente consiste en invocar los métodos correspondientes del ancestro: procedure TStringGridX.MoveColumn(FromIndex, ToIndex: Integer); begin inherited; end; procedure TStringGridX.MoveRow(FromIndex, ToIndex: Integer); begin inherited; end; No tiene que registrar este componente en la Paleta de Componentes. Use un TStringGrid o cualquier descendiente de TCustomGrid, y cuando desee llamar estos métodos, simplemente convierta el objeto a la nueva clase. Por ejemplo: procedure TForm1.Button1Click(Sender: TObject); begin TStringGridX(StringGrid1).MoveColumn(1, 3); end; Impidiendo que el usuario seleccione texto en un control Memo ============================================================= La forma más sencilla sería establecer la propiedad Enabled del control Memo (o Edit) en False para que el control no pueda recibir eventos, pero esto tiene la contra que primero el usuario no puede hacer scroll y segundo que el texto inhabilitado se ve mal. Para impedir que el usuario pueda escribir en el memo, asignamos True a su propiedad ReadOnly. Para que el usuario no pueda seleccionar texto con el ratón, generamos el manejador del evento MouseMove del control y escribimos el siguiente código: procedure TForm1.Memo1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); begin if ssLeft in Shift then Memo1.SelLength := 0; end; Para impedir que el usuario pueda hacer una selección usando el teclado, generamos los manejadores de los eventos KeyDown y KeyUp asignando las propiedades OnKeyDown y OnKeyUp al mismo procedimiento: procedure TForm1.Memo1KeyDownUp(Sender: TObject; var Key: Word; Shift: TShiftState); begin if (ssShift in Shift) and (Key in [VK_LEFT, VK_RIGHT, VK_UP, VK_DOWN, VK_PRIOR, VK_NEXT, VK_HOME, VK_END]) then Key := 0; end; Desactivando el menú contextual =============================== Al hacer clic con el botón derecho del ratón sobre un componente Edit (u otros componentes que permiten la edición de textos como por ejemplo MaskEdit, Memo, DbEdit, etc.), de manera predeterminada se despliega el menú contextual del sistema con opciones para deshacer, copiar, pegar, etc. Si por alguna razón no deseamos que aparezca este menú, una forma posible de lograrlo consiste simplemente en colocar un componente PopupMenu en el formulario y asignarlo a la propiedad PopupMenu de los componentes cuyo menú contextual queremos desactivar. Ordenando un TListView por la columna cliqueada por el usuario ============================================================== Pretendemos el siguiente comportamiento deseado para un ListView: * Cuando el usuario haga clic sobre la cabecera de una columna, el ListView debería ser ordenado por esa columna * El orden inicial debería ser ascendente. Si el usuario hace clic de nuevo en la misma columna, el orden debe invertirse, pero si el usuario hace clic en otra columna, el orden de la nueva columna debería ser el mismo que el de la última columna que se ordenó. Para la implementación necesitamos dos variables para almacenar la última columna cliqueada por el usuario y el orden actual: var LastSortedColumn: integer; Ascending: boolean; Podemos inicializarlas cuando se crea el formulario: procedure TForm1.FormCreate(Sender: TObject); begin LastSortedColumn := -1; Ascending := True; end; En el evento ColumnClick del ListView determinamos el orden y realizamos el ordenamiento: procedure TForm1.ListView1ColumnClick(Sender: TObject; Column: TListColumn); begin if Column.Index = LastSortedColumn then Ascending := not Ascending else LastSortedColumn := Column.Index; TListView(Sender).CustomSort(@SortByColumn, Column.Index); end; SortByColumn es una función que debería ser previamente declarada y es la función usada por CustomSort para comparar dos elementos. El valor pasado en el parámetro Data de CustomSort será pasado como el parámetro Data a SortByColumn y podemos usarlo para saber la columna a ordenar: function SortByColumn(Item1, Item2: TListItem; Data: integer): integer; stdcall; begin if Data = 0 then Result := AnsiCompareText(Item1.Caption, Item2.Caption) else Result := AnsiCompareText(Item1.SubItems[Data-1], Item2.SubItems[Data-1]); if not Ascending then Result := -Result; end; Haciendo que un formulario se mantenga "siempre visible" ======================================================== Para que un formulario se mantenga siempre visible por encima de otros formularios, tanto pertenecientes a la misma aplicación como a otras aplicaciones, podemos establecer el valor de la propiedad FormStyle en fsStayOnTop. Si luego deseáramos volver el formulario a la normalidad, podemos establecer FormStyle en fsNormal. Sin embargo, modificar el calor de la propiedad FormStyle en tiempo de ejecución no se recomienda. En vez de ello, podemos usar la función SetWindowPos de la API de Windows pasándole HWND_TOPMOST como segundo parámetro para activar el efecto: procedure TForm1.Button1Click(Sender: TObject); begin SetWindowPos(Handle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE + SWP_NOSIZE); end; Para desactivar este efecto, volvemos a llamar a SetWindowPos, esta vez pasándole WND_NOTOPMOST como segundo parámetro: procedure TForm1.Button1Click(Sender: TObject); begin SetWindowPos(Handle, WND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE + SWP_NOSIZE); end; Actualizando una tabla con datos de otra tabla con Local SQL ============================================================ +-------------+ +-------------+ | Orders.db | | Customer.db | +-------------+ +-------------+ | CustNo | <<---------> | CustNo | | ShipToAddr1 | | Addr1 | | ShipToAddr2 | | Addr2 | +-------------+ +-------------+ Asumiendo que deseáramos actualizar los campos ShipToAddr1 y ShipToAddr2 de la tabla Orders.db con los valores de los campos Addr1 y Addr2 respectivamente de la tabla Customer.db, para aquellos registros de Orders que tienen ambos campos en blanco, y combinando las tablas por el campo CustNo presente en ambas tablas, tal vez estaríamos tentados en escribir: UPDATE Orders INNER JOIN Customer ON Customer.CustNo = Orders.CustNo SET ShipToAddr1 = Addr1, ShipToAddr2 = Addr2 WHERE ShipToAddr1 = "" AND ShipToAddr2 = "" Sin embargo, en Local SQL (el SQL que usa la BDE), la sentencia UPDATE no soporta los Join, y entonces tenemos que usar subconsultas para lograr el resultado esperado: UPDATE Orders SET ShipToAddr1 = (SELECT Addr1 FROM Customer WHERE Customer.CustNo = Orders.CustNo), ShipToAddr2 = (SELECT Addr2 FROM customer WHERE Customer.CustNo = Orders.CustNo) WHERE ShipToAddr1 = "" AND ShipToAddr2 = "" En el tema "UPDATE statement" de la Local SQL Guide puede encontrar un ejemplo de una relación 1 a muchos que usa agrupamiento en las subcon- sultas. Buscando texto en cualquier parte de un campo ============================================= La siguiente función busca texto en cualquier parte de un campo de cualquier dataset (puede ser por ejemplo el de un componente Table, Query, TADOTable, TADOQuery, TIBTable, TIBQuery, etc.) type TLocateStrOption = (loCaseSensitive, loContinue); TLocateStrOptions = set of TLocateStrOption; function LocateStr(Dataset: TDataset; Field: TField; Str: String; LocateOptions: TLocateStrOptions): boolean; // Busca un texto en un campo de un dataset. La búsqueda puede ser // sensible a mayúsculas minúsculas (opción loCaseSensitive) y puede // puede comenzar desde el principio o a partir de la posición actual // (opción loContinue). // // Devuelve True si encontró la cadena (el dataset queda posicionado // en el registro) y False en caso contrario (el dataset queda en EOF) var ControlsDisabled: boolean; begin ControlsDisabled := Dataset.ControlsDisabled; if not ControlsDisabled then Dataset.DisableControls; try if loContinue in LocateOptions then begin if not Dataset.Eof then Dataset.Next; end else Dataset.First; // Busca desde el principio if not (loCaseSensitive in LocateOptions) then Str := UpperCase(Str); while not Dataset.Eof do begin if loCaseSensitive in LocateOptions then begin if Pos(Str, Field.AsString) <> 0 then break; end else begin if Pos(Str, UpperCase(Field.AsString)) <> 0 then break; end; Dataset.Next; end; Result := Dataset.Eof; finally if not ControlsDisabled then Dataset.EnableControls; end; end; Pintando filas en un DBGrid de Delphi 4 ======================================= En una edición pasada se presentó un ejemplo de cómo pintar las filas de un DBGrid con diversos colores, pero aquel código estaba pensado para Delphi 5. El siguiente código utiliza el evento OnDrawDataCell y el método DefaultDrawDataCell, y con suerte funcionará en Delphi 4: procedure TForm1.DBGrid1DrawDataCell(Sender: TObject; const Rect: TRect; Field: TField; State: TGridDrawState); begin with TDBGrid(Sender) do begin if SelectedRows.IndexOf(DataSource.Dataset.Bookmark) >= 0 then Canvas.Brush.Color := clPurple else if gdSelected in State then Canvas.Brush.Color := clHighlight else if (DataSource.Dataset.RecNo and 1) <> 0 then Canvas.Brush.Color := $00DDEEFF else Canvas.Brush.Color := $00DDFFFF; DefaultDrawDataCell(Rect, Field, State); end; end; ________________________________________________________________________ 7. DELPHI EN LA RED Artículos ========= * Delphi Database Programming Course - Por Zarko Gajic Free online database programming course for beginner Delphi developers focused on ADO techniques. Two new chapters have been added in the last month (Chapter 12 "Master detail relationships" and Chapter 13 "New...Access Database from Delphi"). http://delphi.about.com/compute/delphi/library/weekly/aa010101a.htm * Building a stand-alone Web service with Indy - By Dave Nottage This article shows how to build a Web service using Indy and Delphi 6. http://community.borland.com/article/0,1410,27513,00.html * An introduction to programming strategy games - Por Armando de la Torre This article describes the creation of a game known as Pente or Go, a simple strategy game which has similar rules to tic-tac-toe. http://community.borland.com/article/0,1410,27512,00.html * WebSnap is now in session - Por Nick Hodges This article discusses how to maintain session information in a WebSnap application. http://community.borland.com/article/0,1410,27521,00.html * Loggin' in ain't hard to do - Por Nick Hodges This article illustrates how easy it is to add log-in capability to your WebSnap applications http://community.borland.com/article/0,1410,27485,00.html * Managing sessions with Delphi 6 Web services (updated) - Por Daniel Polistchuck Building an e-business application? Then you'll need techniques like these to manage session states and authentication. http://community.borland.com/article/0,1410,27575,00.html * Groping in the dark - Por John Flaxman A first look into the Open Tools API to implement an extension to the Alt-[ key mapping http://community.borland.com/article/0,1410,26389,00.html * Simulating the Windows API's FindWindow function with Kylix - By Matthias Thoma This article descripes a very handy FindWindow function. http://community.borland.com/article/0,1410,27395,00.html * Maximizing Performance of Delphi/C++Builder/InterBase Applictions - By Robert Schieck This paper will present some tips to help you with getting better performance from your Delphi/C++Builder/InterBase system. http://community.borland.com/article/0,1410,27534,00.html * Migrating Your Delphi 5 Projects to Kylix - Por Bob Swart This paper provides an overview and detailed description of migrating applications from Borland Delphi 5 for Windows to Kylix for Linux http://community.borland.com/article/0,1410,27534,00.html * Community TV: Delphi 6 WebSnap with Jim Tierney John K interviews Jim Tierney, architect of WebSnap, for the details on this new Delphi 6 feature. Now with text transcript and MP3 audio. http://community.borland.com/article/0,1410,27303,00.html * Sip From The Firehose: July 9, 2001 - Everybody's Talking About Web Services... - Por David Intersimone In this installment of my column, we'll take a look at the hot topic of Web Services. Publishing your web application interfaces over the Internet is the next great evolution in the developent of distributed objects and applications. http://community.borland.com/article/0,1410,27501,00.html * Aspect ratio lab report - Por Earl F. Glynn This lab report demonstrates how to display a rectangular TBitmap in a rectangular TImage of any size while preserving the original aspect ratio. http://homepages.borland.com/efg2lab/ImageProcessing/AspectRatio.htm * Cursor overlay lab report - Por Earl F. Glynn This lab report demonstrates how to convert a cursor to a bitmap and then overlay the cursor's bitmap on another bitmap. http://homepages.borland.com/efg2lab/Graphics/CursorOverlay.htm * Windows Shell Extensions – Info Tip - Por Larry J. Rutledge Creating the InfoTip Shell Extension, which lets us, control the information that appears in Explorer when the mouse hovers over a file. Creating a Delphi Infotip that will display the FileName, the project type (Program or Library), the Project Name (from the source file), and the size of the file in bytes. http://delphi.about.com/library/bluc/text/uc071701a.htm * A more powerful Delphi Form - Por Zarko Gajic Messing with the creation process of a form object, or how to change the default style of a window when it gets created to suit your particular needs. Transparent forms, no caption forms, realy StayOnTop forms, ... http://delphi.about.com/library/weekly/aa073101a.htm * Component writing, part 1 - By Peter Morris This first part demonstrates some of the best approaches to building components, and at the same time provides tips on deciding on the best base class to inherit from, using virtual declarations, the process of overriding, and so on. http://www.howtodothings.com/showarticle.asp?article=310 * Real-time 2D particle systems (with gravitation!) -Por Curtis W. Socha This tutorial is going to discuss a library of Delphi routines that will help you create your own particle systems. The term system is defined to mean "A group of interacting, interrelated, or interdependent elements forming a complex whole". You can use the particle to represent a cannonball being shot out of a cannon in a trajectory game. http://delphi.about.com/library/bluc/text/uc071501a.htm Otros enlaces ============= * ADO alternativos (freeware y código abierto) Para quienes tienen Delphi 4, o quienes tienen Delphi 5 Profesional sin el ADO Express, pueden incorporar acceso a bases de datos usando ADO en sus aplicaciones gracias a los siguientes componentes gratuitos y de código abierto: http://www.deer-soft.com/ http://www.alohaoi.com/Software/Products/aoado/default.htm http://www.agric.za/freeway/ADOds.htm * The Delphi Inspiration Componentes Delphi Freeware: rjExContainers Library (contenedores: vector, lista, árbol y hash), rjHtmlParser (analizador de archivos Html), rjMime (codifica y decodifica), rjPCRE (expresiones regulares de Perl en Delphi), rjPasDoc (genera ayuda Html Help a partir de fuentes en Pascal). http://www.zeitungsjunge.de/delphi/ ________________________________________________________________________ ¡TÚ PUEDES AYUDARNOS! Necesitamos tu ayuda para que este boletín pueda continuar y crecer. Una forma en que puedes ayudarnos es enviando este enlace a tus amigos: http://www.latiumsoftware.com/es/pascal/index.php Otra forma es votándonos en alguno de estos rankings para darle más visibilidad a nuestro sitio web y aumentar así el número de suscrip- ciones al boletín, que esperamos en el futuro se traduzca también en un mayor número de colaboraciones de artículos, trucos, etc.: http://www.sandbrooksoftware.com/cgi-bin/TopSite2/rankem.cgi?id=latium http://news.optimax.com/topdelphi/links.exe/click?id=70C517ECAE6E http://www.programmingpages.com/?r=latiumsoftwarecomenpascal http://www.top219.org/cgi-bin/vote.cgi?delphi&83 http://top100borland.com/in.php?who=20 http://top200.jazarsoft.com/delphi/rank.php3?id=latium http://213.65.224.200/cgi-bin/toplist.cgi/hits?Id=80 http://www.programacion.net/votar-enlace.php?id=474 http://www.lawebdelprogramador.com/buscar/enlace.php?id=615 Por favor vota. Son sólo unos segundos para ti que REALMENTE pueden hacer la diferencia. Necesitamos tu ayuda para poder continuar. ________________________________________________________________________ Si no has recibido el archivo con el código fuente completo de los ejemplos que se presentan en este boletín, puedes descargarlo de la siguiente dirección: http://www.latiumsoftware.com/descarga/p0025.zip ________________________________________________________________________ Página principal: http://www.latiumsoftware.com/es/pascal/index.php Página del grupo: http://espanol.groups.yahoo.com/group/boletin-pascal/ Para suscribirse / apuntarse: boletin-pascal-subscribe@gruposyahoo.com Para cancelar / removerse: boletin-pascal-unsubscribe@gruposyahoo.com Para reportar problemas con la suscripción: eds2004 @ latiumsoftware.com ________________________________________________________________________ Este boletín se provee "TAL Y COMO ESTA", sin garantía de ninguna clase. Su uso implica la aceptación de nuestros términos de licencia y de la ausencia de garantía que puedes leer en nuestro sitio web. Allí también encontrarás una nota sobre marcas registradas. Te animamos a que redis- tribuyas este boletín, siempre y cuando lo hagas en forma completa (incluyendo la información de copyright), sin modificaciones y de manera gratuita. Los artículos son copyright de sus respectivos autores y se reproducen aquí con el permiso de los mismos. ________________________________________________________________________ Latium Software http://www.latiumsoftware.com/es/index.php Copyright (c) 2001 por Ernesto De Spirito. Todos los derechos reservados ________________________________________________________________________ |
Los ejemplos completos de código fuente de este número están disponibles para descargar.
![]() |
¿Errores? ¿Omisiones? ¿Comentarios? Por favor contáctanos!






