delphi.gif - 582,0 K
Curso de creación de componentes en Delphi

Unidad 7. TDBViewer: Un visualizador rápido de bases de datos.

delphi.gif - 582,0 K
Volver al índice Por Luis Roche emailed.gif - 15503,0 K

dbviewer.gif En esta unidad transformaremos un form en un componente. En concreto, crearemos un form para visualizar rápidamente bases de datos y lo integraremos dentro de un componente. De este modo en nuestros proyectos, cuando en una pantalla deseemos una opción de visulizar una base de datos, nos bastará con pinchar el componente en el form, asignar sus propiedades, llamar al método execute y el resto del trabajo lo hará el propio componente.

Esta conversión de cuadros de diálogo en componentes es una opción muy potente ya que nos permite reutilizar el código de nuestros diversos proyectos de una forma rápida y sencilla. Seguro que en cuanto conozcamos la técnica de cómo lograrlo se nos ocurrirán infinitas posibilidades para convertir pantallas que utilizamos a menudo en componentes. Baste con decir que yo tengo un componente que encapsula ¡toda una aplicación de 10 o 12 pantallas!.

Hay que hacer notar que en el desarrollo del form utilizo algunas funciones de Delphi 2 que no existen en Delphi 1, con lo cúal el código fuente no es directamente compilable en Delphi 1. Si alguno quiere convertirlo a 16 bits no debe tener demasiados problemas y puede ser un buen ejercicio ;)


Bverde.gif - .325 KObjetivo del componente

Nuestro objetivo es crear un componente que el usuario pueda añadir en un proyecto y para el que pueda definir propiedades en tiempo de diseño. El componente "adaptador" asociado con el form se encargará de crearlo y mostrarlo en tiempo de ejecución. De este modo logramos una alta reutilización del form.

El form que vamos integrar en el componente es un visualizador / editor de tablas de bases de datos. A continuación se muestra el componente en tiempo de ejecución y se comentan sus funciones principales.

El proceso de creación de nuestro componente lo vamos a dividir en los siguientes pasos, los cuales iremos viendo en detalle en las siguientes secciones:
Bverde.gif - .325 KDiseño del form

La creación del form visualizador la haremos de la manera tradicional, es decir, en Delphi, haremos click en File|New Form. Una vez nos aparezca el form en blanco, introduciremos los diversos elementos que lo conforman. No voy a explicar ahora todos los pasos a seguir para diseñar el form, sino que me centrare en los más relevantes. La unidad correspondiente a dicho form y el fichero DFM asociado (frmView.pas y frmView.dfm) puedes bajartelo desde el índice del curso de modo que no tienes que diseñarlo enteramente.

La sección de interface del form es la siguiente:

unit frmView;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, DB, DBTables, Grids, DBGrids, ExtCtrls, Buttons, Menus;

type
  TfrmVisor = class(TForm)
    tTable: TTable;
    dsDataSource: TDataSource;
    dbGrid: TDBGrid;
    pTop: TPanel;
    pSeleccionTabla: TPanel;
    pBuscar: TPanel;
    gbTablas: TGroupBox;
    Alias: TLabel;
    Tabla: TLabel;
    cbAlias: TComboBox;
    cbTables: TComboBox;
    gbBuscar: TGroupBox;
    cbFields: TComboBox;
    eSearch: TEdit;
    Buscar: TSpeedButton;
    BuscarSiguiente: TSpeedButton;
    PopupMenu1: TPopupMenu;
    Igual1: TMenuItem;
    Distinto1: TMenuItem;
    Menor1: TMenuItem;
    Mayor1: TMenuItem;
    N1: TMenuItem;
    Activar1: TMenuItem;
    Eliminar1: TMenuItem;
    procedure FormCreate(Sender: TObject);
    procedure FillcbFields;
    procedure Inicializar;
    procedure cbAliasChange(Sender: TObject);
    procedure cbTablesChange(Sender: TObject);
    procedure BuscarClick(Sender: TObject);
    procedure BuscarSiguienteClick(Sender: TObject);
    procedure dbGridColEnter(Sender: TObject);
    procedure PopupMenu1Popup(Sender: TObject);
    procedure Activar1Click(Sender: TObject);
    procedure Eliminar1Click(Sender: TObject);
    procedure Igual1Click(Sender: TObject);
    procedure Distinto1Click(Sender: TObject);
    procedure Menor1Click(Sender: TObject);
    procedure Mayor1Click(Sender: TObject);
    private
  public
    FReadOnly, FFiltrar, FBuscar : boolean;
  end;

procedure PascalStr(var s : string);
procedure ComaStr(var s : string);
function Condicion(AField : TField; AOp : string) : string;
procedure AddFilter(AField : TField; AOp : string);

var
  frmVisor: TfrmVisor;

Lo primero que conviene hacer notar es la sección pública. En ella definimos tres variables de tipo booleano: FReadOnly, FFiltrar y FBuscar. Estas tres variables forman el interface entre el form y el componente. Así, a la propiedad ReadOnly que implementaremos en el componente le corresponde la variable FReadOnly del form y, de forma análoga ocurre con las propiedades PermitirBucar (AllowSearch) y PermitirFiltrar (AllowFilter) del componente. Será el componente, y no el form quién en su método execute asignará los valores correspondientes a dichas variables.

Conviene que para seguir la explicación que viene ahora tengas a la vista el código fuente completo del form ya que voy a hacer referencias contínuas al mismo.

El método Inicializar es el que se encarga de ajustar la visualización del form según las variables FReadOnly, FFiltrar y FBuscar. Este método será llamado por el propio componente una vez asignado el valor a dichas variables y su cometido es el siguiente:

Veamos ahora como se efectúa la visualización de una tabla. En el evento OnCreate del form se comienza por llenar el combo box cbAlias con todos los alias disponibles. A continuación, de todos los existentes, se selecciona el primero y se fuerza el relleno del combo box cbTables con todas las tablas existentes en el alias (método cbAliasChange). Dicho relleno fuerza la llamada al método cbTablesChange, el cúal se encarga de activar la tabla (también se aprovecha para llenar el combo box cbFields con los campos de la tabla seleccionada). De este modo, el grid, que ya está conectado al datasource y a la tabla correspondiente muestra dicha tabla. Posteriormente si el usuario cambia la selección del alias o de la tabla el proceso se repite y se muestra la nueva tabla seleccionada.

Por simplicidad se ha omitido el tratamiento de errores en la apertura de tablas, ya que no es el objetivo de esta lección. Pero deberías añadir como mínimo un bloque try..except cuando se activa la tabla y actuar en función del tipo de error que se puede producir. Por mi parte, como acabo de decir, no he introducido dicho tratamiento y dejo que delphi se las tenga que ver con las excepciones ya que, al fin y al cabo, para eso está, ¿no? ;)

Pasamos ahora a la búsqueda de registros. Para realizarla, el método BuscarClick se basa en el contenido del edit eSearch que contiene la cadena a buscar y en el combo box cbFields que tiene el campo por el que hay que buscar. La búsqueda se realiza mediante los métodos del objeto TTable FindFirst y FindNext, los cuales supongo los conoces de sobra. En todo caso, encontrarás información sobre los mismos en la ayuda en línea de Delphi.

Nos queda por ver como implementar el filtrado de registros. Hay diversas formas de hacerlo, y me he decantado por utilizar un menu popup que permiter activar, desactivar y añadir nuevas condiciones al filtro. Concretamente el menu tiene las opciones siguientes: añadir una condición del tipo Campo = valor, Campo > Valor, Campo < valor y Campo <> Valor, activar el filtro y eliminar el filtro. Las diversas condiciones del filtro se unen entre sí mediante el operador AND.

El método Activar1Click y Eliminar1Click no necesitan demasiada explicación ya que se limitan a poner la propiedad Filtered del objeto TTable a True o False según corresponda.

Los restantes métodos del menú (Igual1Click...) añaden úna nueva condición al filtro. Para ello, se utiliza el procedimiento AddFilter, que recibe como parámetros el campo sobre el que realizar la nueva condición de filtro (que es la columna activa del dbgrid al pulsar el botón derecho del ratón) y el operador (=,<,> o <> según corresponda. Dicho método, después de formatear correctamente la cadena, la añade a la propiedad filter del objeto TTable y efectúa un refresh para que el nuevo filtro se active.

Y con esto acabamos la parte correspondiente al diseño del form. Fácil, ¿verdad? De todos modos, si téneis alguna duda, el código fuente está bastante documentado. Y si aún así no lo veis claro, ya sabeis, le dais al teclado y me mandáis un e-mail.

Bverde.gif - .325 KCreación del componente

Empecemos ahora con el componente, el cúal es realmente sencillo. Consta de tres propiedades y dos métodos, los cuáles, a esta altura del curso no deben tener ningún secreto para vosotros:

Y eso es todo. ¿Era sencillo o no? Esta simplicidad os la encontrareis en general siempre que estéis transformando un form en componente. Basta con crear unas cuantas propiedades que actuarán sobre el form y definir un método (que por cierto, no tiene porque llamarse execute) para lanzar el form y ¡voila!

¡Ah! que no se os olvide añadir a la clausula uses del componente (en la sección de implementación) la unidad del form. En nuestro caso:

...
implementation

uses frmView;
...
Bverde.gif - .325 KLa conexión entre el componente y el form

Como acabamos de decir, la ejecución del form se lleva a cabo a través del método Execute del componente:

procedure TDBViewer.Execute;
begin
  {Crear el form}
  frmVisor:=TFrmVisor.Create(Application);
  try
    {Asignar las propiedades del componente a las variables
     correspondientes del form}
    frmVisor.FReadOnly:=FReadOnly;
    frmVisor.FBuscar:=FAllowSearch;
    frmVisor.FFiltrar:=FAllowFilter;
    {Inicializar el form}
    frmVisor.Inicializar;
    {Mostrar el form de forma modal}
    frmVisor.ShowModal;
  finally
    {Liberar el form}
    frmVisor.Free;
  end;
end;

El método comienza por crear el form cuyo propietario será la aplicación. A continuación se realiza la conexión entre el form y el componente. Para ello se van asignado a las variables FReadOnly, FBuscar y FFiltrar del form las correspondientes del componente. Una vez realizada esta asignación, debemos hacer que el form se visualice correctamente en base a estos valores (recordar que el form se ha creado, pero aún no se ha mostrado). Esto se consigue llamando al método Inicializar del form (este método lo vimos en la sección Diseño del form).

Ya sólo nos queda mostrar el form de forma modal y "esperar" a que el usuario se canse de jugar con él, momento en el cuál aprovecharemos para destruir el form mediante la llamada al método free.

Como último aspecto a destacar conviene hacer notar el bloque try..finally que nos asegura que, pase lo que pase, la memoria ocupada al crear el form, se liberará correctamente.

Bverde.gif - .325 KInstrucciones de uso del componente y conclusiones

Una vez registrado el componente en la paleta de la forma habitual, su uso es muy sencillo. Cuando queramos utilizarlo en un proyecto, basta con pinchar el componente DBViewer donde deseemos, asignar sus propiedades a nuestra elección (bien sea en tiempo de diseño o de ejecución) y ejecutar el método Execute. Dicho método lo llamaremos desde nos interese (bien sea mediante un botón, en el form create, etc).

Como conclusión, si nos olvidamos de lo que hace el form en si (que puede ser desde un simple about box a todo un proyecto encapsulado en él), nos daremos cuenta que el proceso de convertirlo en componente es muy simple. A partir de las dos unidades que tengamos (una para el form y otra para el componente), añadiremos las propiedades necesarias para personalizar la apariencia del form y crearemos un método (execute) que se encargará de crear y mostrar el form.

Por último me gustaría que si añadis nuevas funciones al form, me lo hagáis saber. Para el curso he escogido un diseño simple, pero con dos o tres cosas más se puede convertir en un componente realmente útil. Así que aquí van algunas ideas: añadirle algunos eventos, tales como OnOpenTable, OnTableError, etc; añadirle la posibilidad de imprimir la base de datos; añadirle un visor de campos memo o de imagenes... ¡Todo depende de vuestras necesidades! :)

Bverde.gif - .325 KCódigo fuente del componente.
unit DBView;          { (c) 1997 by Luis Roche }

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;

type
  TDBViewer = class(TComponent)
  private
    FReadOnly : boolean;
    FAllowSearch : boolean;
    FAllowFilter : boolean;
  protected
  public
    constructor Create(AOwner : TComponent); override;
    procedure Execute;
  published
    property ReadOnly : boolean read FReadOnly write FReadOnly default False;
    property AllowSearch : boolean read FAllowSearch write FAllowSearch default True;
    property AllowFilter : boolean read FAllowFilter write FAllowFilter default True;
  end;

procedure Register;

implementation

uses frmView;

constructor TDBViewer.Create(AOwner : TComponent);
begin
  inherited Create(AOwner);
  {Asignar la propiedades por defecto}
  FReadOnly:=False;
  FAllowSearch:=True;
  FAllowFilter:=True;
end;

procedure TDBViewer.Execute;
begin
  {Crear el form}
  frmVisor:=TFrmVisor.Create(Application);
  try
    {Asignar las propiedades del componente a las variables
     correspondientes del form}
    frmVisor.FReadOnly:=FReadOnly;
    frmVisor.FBuscar:=FAllowSearch;
    frmVisor.FFiltrar:=FAllowFilter;
    {Inicializar el form}
    frmVisor.Inicializar;
    {Mostrar el form de forma modal}
    frmVisor.ShowModal;
  finally
    {Liberar el form}
    frmVisor.Free;
  end;
end;

procedure Register;
begin
  RegisterComponents('Curso', [TDBViewer]);
end;

end.
Bverde.gif - .325 KCódigo fuente del form.

Se muestra a continuación el código integro de la unidad del form. El fichero dfm junto con el resto del componente lo podeis bajar directamente en formato zip desde el índice del curso.

unit frmView;            { (c) 1997 by Luis Roche }

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, DB, DBTables, Grids, DBGrids, ExtCtrls, Buttons, Menus;

type
  TfrmVisor = class(TForm)
    tTable: TTable;
    dsDataSource: TDataSource;
    dbGrid: TDBGrid;
    pTop: TPanel;
    pSeleccionTabla: TPanel;
    pBuscar: TPanel;
    gbTablas: TGroupBox;
    Alias: TLabel;
    Tabla: TLabel;
    cbAlias: TComboBox;
    cbTables: TComboBox;
    gbBuscar: TGroupBox;
    cbFields: TComboBox;
    eSearch: TEdit;
    Buscar: TSpeedButton;
    BuscarSiguiente: TSpeedButton;
    PopupMenu1: TPopupMenu;
    Igual1: TMenuItem;
    Distinto1: TMenuItem;
    Menor1: TMenuItem;
    Mayor1: TMenuItem;
    N1: TMenuItem;
    Activar1: TMenuItem;
    Eliminar1: TMenuItem;
    procedure FormCreate(Sender: TObject);
    procedure FillcbFields;
    procedure Inicializar;
    procedure cbAliasChange(Sender: TObject);
    procedure cbTablesChange(Sender: TObject);
    procedure BuscarClick(Sender: TObject);
    procedure BuscarSiguienteClick(Sender: TObject);
    procedure dbGridColEnter(Sender: TObject);
    procedure PopupMenu1Popup(Sender: TObject);
    procedure Activar1Click(Sender: TObject);
    procedure Eliminar1Click(Sender: TObject);
    procedure Igual1Click(Sender: TObject);
    procedure Distinto1Click(Sender: TObject);
    procedure Menor1Click(Sender: TObject);
    procedure Mayor1Click(Sender: TObject);
    private
  public
    FReadOnly, FFiltrar, FBuscar : boolean;
  end;

procedure PascalStr(var s : string);
procedure ComaStr(var s : string);
function Condicion(AField : TField; AOp : string) : string;
procedure AddFilter(AField : TField; AOp : string);

var
  frmVisor: TfrmVisor;

implementation

{$R *.DFM}

procedure TfrmVisor.FormCreate(Sender: TObject);
begin
  {Rellenar combo box con los alias disponibles}
  Session.GetAliasNames(cbAlias.Items);
  {Seleccionar el primer alias}
  cbAlias.ItemIndex:=0;
  {Provocar el relleno del combo box con el nombre de las tablas}
  cbAliasChange(Sender);
  {Deshabilitar el botón de buscar siguiente}
  BuscarSiguiente.Enabled:=False;
end;

procedure TfrmVisor.Inicializar;
{Procedimiento que se encarga de ajustar visualmente el form a las
 propiedades del componente}
begin
  dbGrid.ReadOnly:=FReadOnly;
  pBuscar.Visible:=FBuscar;
  if FFiltrar then
    dbGrid.PopupMenu:=PopupMenu1
  else
    dbGrid.PopupMenu:=nil;
end;

procedure TfrmVisor.cbAliasChange(Sender: TObject);
begin
  {Rellenar combo box con las tablas existentes en el alias seleccionado}
  Session.GetTableNames(cbAlias.Items[cbAlias.ItemIndex],'*.*',True,True,cbTables.Items);
  {Seleccionar la primera tabla}
  cbTables.ItemIndex:=0;
  {Provocar la apertura de la tabla}
  cbTablesChange(Sender);
end;

procedure TfrmVisor.cbTablesChange(Sender: TObject);
begin
  {Asignar propiedades del objeto tTable}
  tTable.Active:=False;
  tTable.DatabaseName:=cbAlias.Text;
  tTable.TableName:=cbTables.Text;
  {Activar la tabla}
  tTable.Active:=True;
  {Rellenar combo box con los campos existentes en la tabla}
  FillcbFields;
end;

procedure TfrmVisor.FillcbFields;
{Procedimiento que se encarga de rellenar combo box con los
 campos existentes en la tabla seleccionada}
Var
  i : integer;
begin
  cbFields.Items.Clear;
  cbFields.Text:='';
  for i:=0 to tTable.FieldCount-1 do
    cbFields.Items.Add(tTable.Fields[i].DisplayLabel);
end;

procedure TfrmVisor.BuscarClick(Sender: TObject);
{Procedimiento que busca un texto determinado en el
 campo seleccionado.}
Var
  F : TField;
  s : string;
begin
  {Si no hay seleccionado ningún campo donde buscar, salir}
  if cbFields.ItemIndex=-1 then
    exit;
  with tTable do
  begin
    {Obtener cadena a buscar y campo donde buscar}
    s:=eSearch.Text;
    F:=Fields[cbFields.ItemIndex];
    {Convertir la cadena a buscar al formato adecuado}
    case F.DataType of
      FtString, FtDate, FtTime, FtDateTime : PascalStr(s);
      FtFloat : ComaStr(s);
    end;
    {Realizar la búsqueda propiamente dicha}
    Filter:='['+F.FieldName+']='+s;
    FindFirst;
    {Habilitar el botón buscar siguiente sólo si la búsqueda
     ha tenido éxito}
    BuscarSiguiente.Enabled:=Found;
  end;
end;

procedure TfrmVisor.BuscarSiguienteClick(Sender: TObject);
{Procedimiento que busca la siguiente aparición del
 texto correspondiente}
begin
  tTable.FindNext;
  BuscarSiguiente.Enabled:=tTable.Found;
end;

procedure PascalStr(var s : string);
{Rutina de propósito general que se encarga de formatear adecuadamente
 cadenas que contienen apóstrofes en su interior (p.e. O'Hara)}
Var
  i : integer;
begin
  for i:=Length(s) downto 1 do
    if s[i]='''' then Insert('''',s,i);
  s:=''''+s+'''';
end;

procedure ComaStr(var s : string);
{Rutina que sustituye la coma decimal por el punto decimal}
Var
  i : integer;
begin
  for i:=Length(s) downto 1 do
    if s[i]=',' then s[i]:='.';
end;

procedure TfrmVisor.dbGridColEnter(Sender: TObject);
begin
  {Al entrar en una columna del grid, actualizar el combo box
   con el campo donde buscar}
  cbFields.ItemIndex:=dbGrid.SelectedField.Index;
end;

procedure TfrmVisor.PopupMenu1Popup(Sender: TObject);
{Mostrar de forma adecuada el menu popup al pulsar el botón
 derecho del ratón sobre el grid}
begin
  with tTable do
  begin
    Activar1.Checked:=Filtered;
    Activar1.Enabled:=Filter<>'';
    Eliminar1.Enabled:=Filter<>'';
  end;
end;

procedure TfrmVisor.Activar1Click(Sender: TObject);
{Activar el filtrado de registros}
begin
  tTable.Filtered:=not tTable.Filtered;
  tTable.Refresh;
end;

procedure TfrmVisor.Eliminar1Click(Sender: TObject);
{Desactivar el filtrado de registros}
begin
  tTable.Filtered:=False;
  tTable.Filter:='';
  tTable.Refresh;
end;

procedure TfrmVisor.Igual1Click(Sender: TObject);
begin
  AddFilter(dbGrid.SelectedField,'=');
end;

procedure TfrmVisor.Distinto1Click(Sender: TObject);
begin
  AddFilter(dbGrid.SelectedField,'<>');
end;

procedure TfrmVisor.Menor1Click(Sender: TObject);
begin
  AddFilter(dbGrid.SelectedField,'<=');
end;

procedure TfrmVisor.Mayor1Click(Sender: TObject);
begin
  AddFilter(dbGrid.SelectedField,'>=');
end;

procedure AddFilter(AField : TField; AOp : string);
{Añade una nueva condición al filtro}
Var
  s : string;
begin
  {Poner en s la condición a buscar}
  s:=Condicion(AField,AOp);
  with AField.DataSet do
  begin
    if Filter='' then Filter:=s
    else Filter:=Filter+' AND ' + s;
    Filtered:=True;
    Refresh;
  end;
end;

function Condicion(AField : TField; AOp : string) : string;
{Formatea la condición a buscar de forma adecuada}
Var
  Valor : string;
begin
  Valor:=AField.AsString;
  case AField.DataType of
    FtString, FtDate, FtTime, FtDateTime : PascalStr(Valor);
    FtFloat : ComaStr(Valor);
  end;
  Condicion:=Format('([%s] %s %s)',[AField.FieldName,AOp,Valor]);
end;

end.

Luis Roche revueltaroche@redestb.es
Ultima modificación 15.05.1997

Otros Links de Interés:

Cocina  -  Videos  -  Juegos Gratis  - Postales cachondas  - Cine  - Programas Gratis  -  Letras de Canciones

Listas de todos los Tutoriales Gratis. 1998- 2007 - -

Los tutoriales y cursos aquí reunidos son una recopilación de los mejores encontrados en Internet.
El crédito y copyright de los mismos si lo hubiere corresponde al autor de cada uno de ellos.
Si tu tutorial o curso está aquí, y deseas darlo de baja de esta recopilación o quieres añadir el tuyo, envíanos un mensaje desde
aquí

Publispain - Fun