![]() |
![]() |
| Volver al índice | Por
Luis Roche |
Introducció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.
Diferencias
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:
Estructura
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.
Un 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.
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:
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:
Registro
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.
Có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.
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í