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

Unidad 11. Editores de Componentes. 
delphi.gif - 582,0 K
 
Volver al índice Por Luis Roche emailed.gif - 15503,0 K

Bverde.gif - .325 KIntroducción
En esta unidad aprenderemos a crear editores de componentes. Comenzaremos viendo las similitudes y diferencias existentes entre editores de propiedades y editores de componentes y, a continuación, desarrollaremos un ejemplo práctico: un editor de componentes para el componente (valga la redundancia) creado en la unidad anterior, TDigitalDisplay.

Bverde.gif - .325 KDiferencias entre editores de propiedades y editores de componentes

Como vimos en las unidades 8 y 9, un editor de propiedades trabaja sobre la representación en forma de string del valor de una propiedad de un componente determinado. El objetivo de esta manipulación es permitir al usuario del componente leer y almacenar el valor de la propiedad que está siendo editada. Adicionalmente un editor de propiedades puede transformar el valor de la propiedad convirtiéndola a un valor más apropiado para el funcionamiento interno del componente. Un ejemplo es el editor que construimos para las propiedades de tipo de fecha, que muestra al usuario la fecha en formato "legible" (dd/mm/aa), pero que opera internamente con un float (TDateTime).

Pero la propia filosofía de diseño de los editores de propiedades trae consigo una ventaja que es a su vez una desventaja: un editor de propiedades sólo conoce del componente la propiedad a editar y nada más. Recordemos que un editor de propiedades leía y almacenaba el valor de la propiedad por medio de los métodos GetOrdValue, SetOrdValue, GetFloatValue, etc. Estos métodos proporcionan el único medio de comunicación que el editor tiene con el componente. Un editor de componentes, en cambio, puede acceder al componente directamente, ya que una de las propiedades que todo editor de componentes tiene es la propiedad Component, que devuelve una referencia al componente que está siendo editado. De este modo, el editor de componentes puede acceder a todos los métodos y propiedades que el componente define como published o public (e incluso a los definidos como private o protected sí el editor reside en la misma unidad que el componente).

Por tanto, si queremos manipular una única propiedad de un componente utilizaremos un editor de propiedades. Si, por el contrario, queremos manipular varias propiedades al mismo tiempo, o ejecutar algún método del componente, deberemos utilizar un editor de componentes. Además, los editores de componentes permiten definir nuevas acciones en forma de menú ítems que se añaden al menú contextual que aparece al seleccionar un componente y hacer click sobre él con el botón derecho del ratón (p.e. el editor de componente TPageControlEditor permite añadir/eliminar nuevas páginas del componente TPageControl). Otra posibilidad que permiten los editores de componentes es ejecutar una acción determinada cuando el usuario hace doble click sobre un componente determinado (p.e. el editor de componentes TMenuEditor ejecuta el editor de menús del componente TMainMenu). A modo de resumen la figura siguiente muestra como se comunican los editores de propiedades y de componentes con el componente a editar:

Bverde.gif - .325 KEstructura de un editor de componentes

Todos los editores de componentes descienden de una clase base denominada TComponentEditor. La declaración de dicha clase es la siguiente:

TComponentEditor = class
private
  FComponent: TComponent;
  FDesigner: TFormDesigner;
public
  constructor Create(AComponent: TComponent; ADesigner: TFormDesigner); virtual;
  procedure Edit; virtual; 
  procedure ExecuteVerb(Index: Integer); virtual;
  function GetVerb(Index: Integer): string; virtual;
  function GetVerbCount: Integer; virtual;
  procedure Copy; virtual;
  property Component: TComponent read FComponent;
  property Designer: TFormDesigner read FDesigner;
end;

Vamos a ver con más detenimiento las propiedades y métodos declarados en esta clase:

No parece complicado, ¿verdad? De hecho, es más sencillo escribir editores de componentes que editores de propiedades. Y vamos a demostrarlo escribiendo nuestro primer editor de componentes.

Bverde.gif - .325 KUn editor para el componente TDigitalDisplay

En la unidad anterior desarrollamos el componente TDigitalDisplay, al cual dotamos de una serie de propiedades (color de los dígitos, color de fondo, alineación...) que lo hacen ampliamente configurable para el usuario. Esta profusión de posibilidades puede hacer difícil al usuario la elección de valores adecuados para que el aspecto del componente sea "estéticamente bonito" :-) Aquí es donde un editor de componentes puede ayudar: en vez de obligar al usuario a estar continuamente jugando con las distintas propiedades hasta encontrar una combinación satisfactoria, mediante un form auxiliar podemos mostrar al usuario de forma inmediata los cambios que vaya realizando al componente. Una vez que el usuario elija la apariencia que desea darle, pulsa sobre OK y el componente real adoptará la forma deseada. Si al usuario no le convence la apariencia, pulsa Cancel y se olvida de tener que ir propiedad por propiedad asignando los valores antiguos. De este modo es mucho más sencillo el trabajo en tiempo de diseño con el componente y todos contentos ¿verdad? ;-) Manos a la obra entonces.

Comencemos por diseñar el form auxiliar que servirá para que el usuario "juegue" con las propiedades del componente:

Como se puede observar en la figura, el form consta de un componente TDigitalDisplay utilizado para mostrar los diversos cambios efectuados a las propiedades, y una serie de controles que permiten alterar el valor de dichas propiedades.

La construcción de este form es muy sencilla, por lo que muestro directamente el código fuente del mismo:

type
  TfrmDigitalDisplay = class(TForm)
    gbMuestra: TGroupBox;
    DigitalDisplay1: TDigitalDisplay;
    rgAlineacion: TRadioGroup;
    gbValor: TGroupBox;
    lValor: TLabel;
    eValor: TEdit;
    cbRellenarCeros: TCheckBox;
    eDigitos: TEdit;
    lDigitos: TLabel;
    upDigitos: TUpDown;
    gbColores: TGroupBox;
    lFondo: TLabel;
    lDigitosOn: TLabel;
    lDigitosOff: TLabel;
    pColorFondo: TPanel;
    pColorOn: TPanel;
    pColorOff: TPanel;
    bbAceptar: TBitBtn;
    bbCancelar: TBitBtn;
    ColorDialog1: TColorDialog;
    gbDimensiones: TGroupBox;
    lAncho: TLabel;
    eAncho: TEdit;
    lAlto: TLabel;
    eAlto: TEdit;
    procedure pColorFondoClick(Sender: TObject);
    procedure pColorOnClick(Sender: TObject);
    procedure pColorOffClick(Sender: TObject);
    procedure rgAlineacionClick(Sender: TObject);
    procedure eDigitosChange(Sender: TObject);
    procedure upDigitosClick(Sender: TObject; Button: TUDBtnType);
    procedure cbRellenarCerosClick(Sender: TObject);
    procedure eValorChange(Sender: TObject);
    procedure eAnchoChange(Sender: TObject);
    procedure eAltoChange(Sender: TObject);
  private
  public
  end;
...
implementation
...
{---------------------------------------}
{ Form del Editor de propiedades        }
{---------------------------------------}

procedure TfrmDigitalDisplay.pColorFondoClick(Sender: TObject);
begin
  ColorDialog1.Color:=pColorFondo.Color;
  if ColorDialog1.Execute then
    pColorFondo.Color:=ColorDialog1.Color;
  DigitalDisplay1.BckgndColor:=pColorFondo.Color;
end;

procedure TfrmDigitalDisplay.pColorOnClick(Sender: TObject);
begin
  ColorDialog1.Color:=pColorOn.Color;
  if ColorDialog1.Execute then
    pColorOn.Color:=ColorDialog1.Color;
  DigitalDisplay1.DigitOnColor:=pColorOn.Color;
end;

procedure TfrmDigitalDisplay.pColorOffClick(Sender: TObject);
begin
  ColorDialog1.Color:=pColorOff.Color;
  if ColorDialog1.Execute then
    pColorOff.Color:=ColorDialog1.Color;
  DigitalDisplay1.DigitOffColor:=pColorOff.Color;
end;

procedure TfrmDigitalDisplay.rgAlineacionClick(Sender: TObject);
begin
  case rgAlineacion.ItemIndex of
    0 : DigitalDisplay1.Alignment:=taRightJustify;
    1 : DigitalDisplay1.Alignment:=taLeftJustify;
    2 : DigitalDisplay1.Alignment:=taCenter;
  end;
end;

procedure TfrmDigitalDisplay.eDigitosChange(Sender: TObject);
begin
  DigitalDisplay1.Digits:=StrToInt(eDigitos.Text);
end;

procedure TfrmDigitalDisplay.upDigitosClick(Sender: TObject; Button: TUDBtnType);
begin
  DigitalDisplay1.Digits:=StrToInt(eDigitos.Text);
end;

procedure TfrmDigitalDisplay.cbRellenarCerosClick(Sender: TObject);
begin
  DigitalDisplay1.LeadingZeros:=cbRellenarCeros.Checked;
end;

procedure TfrmDigitalDisplay.eValorChange(Sender: TObject);
begin
  DigitalDisplay1.Value:=eValor.Text;
end;

procedure TfrmDigitalDisplay.eAnchoChange(Sender: TObject);
begin
  DigitalDisplay1.Width:=StrToInt(eAncho.Text);
end;

procedure TfrmDigitalDisplay.eAltoChange(Sender: TObject);
begin
  DigitalDisplay1.Height:=StrToInt(eAlto.Text);
end;

Una vez diseñado el form, vamos a crear el editor de componentes propiamente dicho.

Bverde.gif - .325 KImplementado el editor

Nuestro editor de componentes deriva directamente de TComponentEditor. La declaración del mismo es la siguiente:

  TDigitalDisplayEditor = class(TComponentEditor)
    function GetVerbCount : integer; override;
    function GetVerb(Index : integer) : string; override;
    procedure ExecuteVerb(Index : integer); override;
    procedure PrepararForm(aForm : TfrmDigitalDisplay);
    procedure ActualizarComponente(aForm : TfrmDigitalDisplay);
  end;

Por convención, los editores de componentes finalizan con la palabra Editor (TTableEditor, TPageControlEditor, ...)

Nuestro editor añadirá una única opción al menú contextual del componente. Dicha opción se activará cuando el usuario seleccione la opción o haga doble click sobre el componente, tal y cómo se explico anteriormente. Por tanto definimos el método GetVerbCount de la siguiente manera:

function TDigitalDisplayEditor.GetVerbCount : integer;
begin
  Result:=1;
end;

Nuestro método se limita a comunicar que sólo se añade un ítem al menú contextual. A continuación indicamos que el ítem a añadir debe tener la leyenda "&Edit"

function TDigitalDisplayEditor.GetVerb(Index : integer) : string;
begin
  Result:='E&dit...';
end;

Y ahora la parte más interesante. El método ExecuteVerb se debe encargar de las siguientes tareas:

  1. Crear el form diseñado en la sección anterior.
  2. Asignar a los controles del form los valores de las propiedades que actualmente tiene el componente real (entendiendo por componente real el que el usuario ha seleccionado de la paleta de componente y ha pinchado sobre el form correspondiente).
  3. Mostrar, de forma modal, el form de edición.
  4. Si el usuario sale del form aceptando los cambios (OK) se debe actualizar el componente real con los valores que el usuario ha elegido en el form.
  5. Debemos comunicar a Delphi que el componente ha cambiado. Este paso es muy importante. Para ello, ejecutamos la sentencia Designer.Modified
  6. Liberar el form.

Todos estos pasos son típicos sea cuál sea el editor de componentes que estemos desarrollando, de forma que os recomiendo que os familiaricéis con ellos. Pos supuesto, si no se necesita el form auxiliar, se obvian los pasos correspondientes ;) La secuencia mostrada queda codificada en nuestro editor en concreto de la siguiente manera:

procedure TDigitalDisplayEditor.ExecuteVerb(Index : integer);

  procedure CopyDigitalDisplay(Dest, Source : TDigitalDisplay);
  {procedimiento auxiliar que copia los valores de las propiedades de un
   componente digital display a otro}
  begin
    Dest.Digits:=Source.Digits;
    Dest.LeadingZeros:=Source.LeadingZeros;
    Dest.Value:=Source.Value;
    Dest.Alignment:=Source.Alignment;
    Dest.BckgndColor:=Source.BckgndColor;
    Dest.DigitOnColor:=Source.DigitOnColor;
    Dest.DigitOffColor:=Source.DigitOffColor;
    Dest.Width:=Source.Width;
    Dest.Height:=Source.Height;
  end;

Var
  aForm : TfrmDigitalDisplay;
begin
  {Creamos el form del editor de propiedades}
  aForm:=TfrmDigitalDisplay.Create(Application);
  try
    {Asignamos el caption correspondiente}
    aForm.Caption:=Component.Owner.Name+'.'+Component.Name+' '+aForm.Caption;
    {Asignamos a los controles del form los valores correspondientes del componente
     a editar}
    PrepararForm(aForm);
    {Actualizamos el DigitalDisplay utilizado como muestra para que presente el mismo
     aspecto que tiene el componente real}
    CopyDigitalDisplay(aForm.DigitalDisplay1, Component As TDigitalDisplay);
    {Si el usuario acepta los cambios efectuados al form}
    if aForm.ShowModal = mrOK then
    begin
      {Actualizamos el componente en base a los controles del form}
      ActualizarComponente(aForm);
      {Informamos a Delphi que el componente ha cambiado}
      Designer.Modified;
    end;
  finally
    {Liberamos el form}
    aForm.Free;
  end;
end;

Cabe destacar lo siguiente:

Bverde.gif - .325 KRegistro del editor de componentes y consideraciones finales.

Nos quedan por ver un par de detalles. El primero es el registro del editor de componentes. Para ello se debe ejecutar, dentro del procedimiento Register, la sentencia RegisterComponentEditor. Este método toma dos parámetros, el primero es la referencia la clase del componente al que se debe asociar el editor, mientras que el segundo especifica el nombre del editor. En nuestro caso lo registramos así:

procedure Register;
{Registro del editor de componentes}
begin
  RegisterComponentEditor(TDigitalDisplay,TDigitalDisplayEditor);
end;

El segundo aspecto a tratar es dónde situar el editor de componentes. Aquí se aplican los mismos principios que explicamos en la primera unidad en que estudiamos los editores de propiedades. Ya que los editores de componentes sólo se necesitan en tiempo de diseño no se deben situar en la misma unidad en que resida el componente, sino en una unidad independiente. Lo que sí se suele hacer es situarlo en la misma unidad donde reside el form auxiliar, tal y como lo hacemos nosotros con nuestro editor.

Bverde.gif - .325 KCódigo fuente completo del editor de componente

unit DigitalDisplayEditor;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, Buttons, ExtCtrls, ComCtrls, DigitalDisplay, DsgnIntf;

type
  TfrmDigitalDisplay = class(TForm)
    gbMuestra: TGroupBox;
    DigitalDisplay1: TDigitalDisplay;
    rgAlineacion: TRadioGroup;
    gbValor: TGroupBox;
    lValor: TLabel;
    eValor: TEdit;
    cbRellenarCeros: TCheckBox;
    eDigitos: TEdit;
    lDigitos: TLabel;
    upDigitos: TUpDown;
    gbColores: TGroupBox;
    lFondo: TLabel;
    lDigitosOn: TLabel;
    lDigitosOff: TLabel;
    pColorFondo: TPanel;
    pColorOn: TPanel;
    pColorOff: TPanel;
    bbAceptar: TBitBtn;
    bbCancelar: TBitBtn;
    ColorDialog1: TColorDialog;
    gbDimensiones: TGroupBox;
    lAncho: TLabel;
    eAncho: TEdit;
    lAlto: TLabel;
    eAlto: TEdit;
    procedure pColorFondoClick(Sender: TObject);
    procedure pColorOnClick(Sender: TObject);
    procedure pColorOffClick(Sender: TObject);
    procedure rgAlineacionClick(Sender: TObject);
    procedure eDigitosChange(Sender: TObject);
    procedure upDigitosClick(Sender: TObject; Button: TUDBtnType);
    procedure cbRellenarCerosClick(Sender: TObject);
    procedure eValorChange(Sender: TObject);
    procedure eAnchoChange(Sender: TObject);
    procedure eAltoChange(Sender: TObject);
  private
  public
  end;

  TDigitalDisplayEditor = class(TComponentEditor)
    function GetVerbCount : integer; override;
    function GetVerb(Index : integer) : string; override;
    procedure ExecuteVerb(Index : integer); override;
    procedure PrepararForm(aForm : TfrmDigitalDisplay);
    procedure ActualizarComponente(aForm : TfrmDigitalDisplay);
  end;

var
  frmDigitalDisplay: TfrmDigitalDisplay;

procedure Register;

implementation

{$R *.DFM}

{---------------------------------------}
{ Form del Editor de propiedades        }
{---------------------------------------}

procedure TfrmDigitalDisplay.pColorFondoClick(Sender: TObject);
begin
  ColorDialog1.Color:=pColorFondo.Color;
  if ColorDialog1.Execute then
    pColorFondo.Color:=ColorDialog1.Color;
  DigitalDisplay1.BckgndColor:=pColorFondo.Color;
end;

procedure TfrmDigitalDisplay.pColorOnClick(Sender: TObject);
begin
  ColorDialog1.Color:=pColorOn.Color;
  if ColorDialog1.Execute then
    pColorOn.Color:=ColorDialog1.Color;
  DigitalDisplay1.DigitOnColor:=pColorOn.Color;
end;

procedure TfrmDigitalDisplay.pColorOffClick(Sender: TObject);
begin
  ColorDialog1.Color:=pColorOff.Color;
  if ColorDialog1.Execute then
    pColorOff.Color:=ColorDialog1.Color;
  DigitalDisplay1.DigitOffColor:=pColorOff.Color;
end;

procedure TfrmDigitalDisplay.rgAlineacionClick(Sender: TObject);
begin
  case rgAlineacion.ItemIndex of
    0 : DigitalDisplay1.Alignment:=taRightJustify;
    1 : DigitalDisplay1.Alignment:=taLeftJustify;
    2 : DigitalDisplay1.Alignment:=taCenter;
  end;
end;

procedure TfrmDigitalDisplay.eDigitosChange(Sender: TObject);
begin
  DigitalDisplay1.Digits:=StrToInt(eDigitos.Text);
end;

procedure TfrmDigitalDisplay.upDigitosClick(Sender: TObject; Button: TUDBtnType);
begin
  DigitalDisplay1.Digits:=StrToInt(eDigitos.Text);
end;

procedure TfrmDigitalDisplay.cbRellenarCerosClick(Sender: TObject);
begin
  DigitalDisplay1.LeadingZeros:=cbRellenarCeros.Checked;
end;

procedure TfrmDigitalDisplay.eValorChange(Sender: TObject);
begin
  DigitalDisplay1.Value:=eValor.Text;
end;

procedure TfrmDigitalDisplay.eAnchoChange(Sender: TObject);
begin
  DigitalDisplay1.Width:=StrToInt(eAncho.Text);
end;

procedure TfrmDigitalDisplay.eAltoChange(Sender: TObject);
begin
  DigitalDisplay1.Height:=StrToInt(eAlto.Text);
end;

{---------------------------------------}
{ Editor de propiedades                 }
{---------------------------------------}

function TDigitalDisplayEditor.GetVerbCount : integer;
begin
  Result:=1;
end;

function TDigitalDisplayEditor.GetVerb(Index : integer) : string;
begin
  Result:='E&dit...';
end;

procedure TDigitalDisplayEditor.ExecuteVerb(Index : integer);

  procedure CopyDigitalDisplay(Dest, Source : TDigitalDisplay);
  {procedimiento auxiliar que copia los valores de las propiedades de un
   componente digital display a otro}
  begin
    Dest.Digits:=Source.Digits;
    Dest.LeadingZeros:=Source.LeadingZeros;
    Dest.Value:=Source.Value;
    Dest.Alignment:=Source.Alignment;
    Dest.BckgndColor:=Source.BckgndColor;
    Dest.DigitOnColor:=Source.DigitOnColor;
    Dest.DigitOffColor:=Source.DigitOffColor;
    Dest.Width:=Source.Width;
    Dest.Height:=Source.Height;
  end;

Var
  aForm : TfrmDigitalDisplay;
begin
  {Creamos el form del editor de propiedades}
  aForm:=TfrmDigitalDisplay.Create(Application);
  try
    {Asignamos el caption correspondiente}
    aForm.Caption:=Component.Owner.Name+'.'+Component.Name+' '+aForm.Caption;
    {Asignamos a los controles del form los valores correspondientes del componente
     a editar}
    PrepararForm(aForm);
    {Actualizamos el DigitalDisplay utilizado como muestra para que presente el mismo
     aspecto que tiene el componente real}
    CopyDigitalDisplay(aForm.DigitalDisplay1, Component As TDigitalDisplay);
    {Si el usuario acepta los cambios efectuados al form}
    if aForm.ShowModal = mrOK then
    begin
      {Actualizamos el componente en base a los controles del form}
      ActualizarComponente(aForm);
      {Informamos a Delphi que el componente ha cambiado}
      Designer.Modified;
    end;
  finally
    {Liberamos el form}
    aForm.Free;
  end;
end;

procedure TDigitalDisplayEditor.PrepararForm(aForm : TfrmDigitalDisplay);
{Este método asigna a los controles del form los valores correspondientes
 del componente a editar}
begin
  with Component As TDigitalDisplay do
  begin
    aForm.eDigitos.Text:=IntToStr(Digits);
    aForm.cbRellenarCeros.Checked:=LeadingZeros;
    aForm.eValor.Text:=Value;
    case Alignment of
      taRightJustify : aForm.rgAlineacion.ItemIndex:=0;
      taLeftJustify : aForm.rgAlineacion.ItemIndex:=1;
      taCenter : aForm.rgAlineacion.ItemIndex:=2;
    end;
    aForm.pColorFondo.Color:=BckgndColor;
    aForm.pColorOn.Color:=DigitOnColor;
    aForm.pColorOff.Color:=DigitOffColor;
    aForm.eAncho.Text:=IntToStr(Width);
    aForm.eAlto.Text:=IntToStr(Height);
  end;
end;

procedure TDigitalDisplayEditor.ActualizarComponente(aForm : TfrmDigitalDisplay);
{Este método actualiza el componente real en base a los valores de  los controles
 del form }
begin
  with Component As TDigitalDisplay do
  begin
    Digits:=StrToInt(aForm.eDigitos.Text);
    LeadingZeros:=aForm.cbRellenarCeros.Checked;
    Value:=aForm.eValor.Text;
    case aForm.rgAlineacion.ItemIndex of
      0 : Alignment:=taRightJustify;
      1 : Alignment:=taLeftJustify;
      2 : Alignment:=taCenter;
    end;
    BckgndColor:=aForm.pColorFondo.Color;
    DigitOnColor:=aForm.pColorOn.Color;
    DigitOffColor:=aForm.pColorOff.Color;
    Width:=StrToInt(aForm.eAncho.Text);
    Height:=StrToInt(aForm.eAlto.Text);
  end;
end;

procedure Register;
{Registro del editor de componentes}
begin
  RegisterComponentEditor(TDigitalDisplay,TDigitalDisplayEditor);
end;

end.


Luis Roche revueltaroche@redestb.es

Ultima modificación 25.01.1998

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