Ver Mensaje Individual
  #10  
Antiguo 31-03-2010
andres1569 andres1569 is offline
Miembro
 
Registrado: may 2003
Posts: 908
Reputación: 21
andres1569 Va por buen camino
Hola:

Te pongo aquí una explicación detallada de este asunto, espero te resulte útil. Al final te pongo un link a un curso muy recomendado para iniciarse en esto de crear componentes propios.

En efecto, tal como dice esa explicación, puedes crear tus propios editores de propiedades (descendientes de TPropertyEditor) y editores de componentes (descienden de TComponentEditor).

Los primeros se usan desde el Object Inspector, y resultan útiles cuando la propiedad en cuestión tiene cierta complejidad, o incluso si siendo simple quieres darle un tratamiento personalizado. No creo que te haga falta, por ejemplo, para introducir un Integer, o un String ... pero sí en cambio es útil tener un editor de fuentes que en realidad es una clase TFontProperty que lanza un cuadro de diálogo donde editar la fuente (TFontDialog). Lo mismo cuando quieres introducir el nombre de un archivo, aparte de poderlo introducir escribiendo, puedes crear p.e. un "TFileNameProperty" que muestre al usuario un TOpenDialog de donde elegir el archivo deseado. Básicamente hay que sobreescribir los métodos GetValue y SetValue para controlar cómo se toma el valor de la propiedad y cómo se asigna luego. Otro método que suele sobreescribirse es GetAttributes, donde puedes fijar algunas características tales como si se desplegará una lista con los posibles valores en el ObjectInspector, si dicha lista aparecerá ordenada, o si la propiedad podrá editarse simultaneamente para más de un componente. Y puesto que ya hay muchas clases que vienen "de fábrica" descendientes de TPropertyEditor, como TStringEditor, TClassEditor, TIntegerEditor ... conviene que elijas de cuál te resulta más ventajoso heredar.

Los TComponentEditor permiten crear editores para todo el componente, normalmente llaman a un formulario donde de forma más amena se pueden alterar unas cuantas, sino todas, las propiedades del componente, un ejemplo es el editor del componente TChart (el que aparece cuando haces doble click sobre el mismo), que además muestra el resultado final a medida que vas cambiando valores. Los editores de componentes se lanzan desde el menú flotante del componente, por ello es básico reescribir los métodos GetVerbCount (donde indicas cuántas entradas de menú vas a utilizar), GetVerb (donde indicas el caption de cada entrada del menú) y ExecuteVerb (que según el parámetro Index indica la entrada de menú seleccionada por el usuario para que actúes en consecuencia). Recuerda que la primera entrada del menú es la que se ejecutará por defecto cuando hagas doble click sobre el componente.

Es importante conocer algunas de la propiedades con las que contamos en estas clases. En concreto la propiedad Designer, que se refiere al diseñador del IDE. Si por ejemplo lanzas un editor de una propiedad / componente que altera algún valor, debes hacer una llamada a Designer.Modified para indicarle a Delphi que se han producido cambios (así no te llevarás la sorpresa de que no te pida guardar al salir). Puede que necesites también hacer uso del método GetComponent de ambas clases, que devuelve -según el índice que le pases como parámetro- el componente que se está editando, ya que puede ser que se estén editando varios simultaneamente.

Otra cosa IMPORTANTE, hay un antes y un después de Delphi 6 en esto de los editores de diseño:

Antes de Delphi 6, las clases TPropertyEditor y TComponentEditor venían, junto con una ristra de descendientes, en la unit DsgnIntf.pas y esta unit podías incluso incluirla en un ejecutable final, de hecho hay varias utilidades por ahí que permitían mostrar algo parecido a un ObjectInspector en runtime para cambiar propiedades durante la ejecución del programa, estas utilidades hacían uso de dicha unit que les simplificaba mucho el trabajo.

Desde Delphi 6 esto ya no está permitido, pues entonces Borland decidió que vulneraba derechos de copyright relacionados con el IDE de Delphi, y que no podían ir incluidos en ningún ejecutable final. Por ese motivo, se trasladaron todas esas clases a varias units, a saber: DesignEditors, DesignMenus y DesignWindows (puedes encontrar los fuentes en la carpeta de Delphi \Source\ToolsAPI). Esto también implica que a partir de la versión 6, si quieres crear tus propios editores de propiedades/componentes, debes crear un paquete exclusivamente de diseño, donde puedes referenciar dichas unidades, y para distribución creas otro paquete, de ejecución exclusivamente. La única diferencia entre uno y otro estará en que el de diseño llevará dichos editores, y es el que registrarás en el IDE de Delphi para que te aparezcan en la paleta. Ten en cuenta, también, que estas tres units no están accesibles como archivos .DCU, sino que residen en un package llamado designide.dcp. Esto implica que para poder usarlas en el package de diseño debes incluir el package designide.dcp en su cláusula Requires.

A todo esto, a partir de Delphi 6 la unit DsgnIntf.pas cambia de nombre a DesignIntf.pas, y alberga únicamente interfaces y algunos tipos útiles, y reside también en el package designide.dcp (no hay un archivo .dcu tampoco para esta unit). A propósito, los fallos que mucha gente tenía con una unit llamada "proxies" también tenían que ver con esta remodelación.

Te pongo aquí un ejemplo de editor de propiedades y otro de editor de componentes. Como sabrás, no basta con definir dichas clases sino que, obviamente, hay que asignarlas al componente y/o propiedad deseada, para que Delphi sepa dónde aplicarlos. Para ello se usan los procedimientos RegisterPropertyEditor y RegisterComponentEditor (en ambos en el parámetro ComponentClass se debe indicar a qué clase -y derivados- afectará).

Código Delphi [-]
type

  TAliasProperty = class(TStringProperty)
  public
    function GetAttributes : TPropertyAttributes;  override;
    procedure GetValues(Proc: TGetStrProc);   override;
  end;

  TMiComponenteEditor = class(TComponentEditor)
  public
    procedure ExecuteVerb(Index: Integer);   override;
    function GetVerb(Index: Integer) : String;   override;
    function GetVerbCount : Integer;   override;
  end;

  procedure Register;

implementation

procedure Register;
begin
// registramos el componente
  RegisterComponents('MiPaleta', [TMiComponente]);
// registramos el editor de la propiedad 'Alias' para TMiComponente
  RegisterPropertyEditor(TypeInfo(String), TMiComponente, 'Alias', TAliasProperty);
// registramos el editor de TMiComponente
  RegisterComponentEditor(TMiComponente, TMiComponenteEditor);
end;

{ TAliasProperty }

function TAliasProperty.GetAttributes: TPropertyAttributes;
begin
  result := [paValueList, paMultiSelect, paSortList];
end;

// TGetStrProc es un tipo de función que recibe un valor cadena para añadir 
// a una lista que mostrará al usuario desde el ObjectInspector
procedure TAliasProperty.GetValues(Proc: TGetStrProc);
var
  AliasList : TStringList;
  i : Integer;
begin
  AliasList := TStringList.Create;
  Session.ConfigMode := cmAll;   // que busque todos los alias posibles
  Session.GetAliasNames(AliasList);
  try
    for i:=0 to AliasList.Count - 1 do Proc(AliasList[i]);
  finally
    AliasList.Free;
  end;
end;  

{ TMiComponenteEditor }

// según el parámetro Index, lanzamos la acción / formulario correspondiente
procedure TMiComponenteEditor.ExecuteVerb(Index: Integer);
begin
  Case Index of
    0 : with TMiComponenteForm.Create(nil) do
          try
            if ShowModal = mrOk then Designer.Modified;
          finally
            Free;
          end; 
    1 : with TMiComponentePreviewForm.Create(nil) do
          try
            ShowModal;
          finally
            Free;
          end; 
  end;
end;

// según el parámetro Index, indicamos el Caption de cada entrada del menú
function TMiComponenteEditor.GetVerb(Index: Integer): String;
begin
  Case Index of
    0 : result := 'Editar valores ...';
    1 : result := 'Previsualizar';
  end;
end;

// devuelve el número de entradas que vamos a añadir al menú 
function TMiComponenteEditor.GetVerbCount: Integer;
begin
  result := 2;
end;

Por último, un consejo: los componentes debes crearlos en unidades aparte de la de los editores. Para los editores creas una unidad dedicada exclusivamente, llamémosla MisComponentesReg.pas, ahí es donde añades en la cláusula Uses las units DesignIntf, DesignEditors -DesignMenus y DesignWindows pueden ser optativas- o bien DsgnIntf si trabajas en versiones anteriores a la 6. En dicha unit además es donde registrarás los componentes por lo que deberá llevar enlazadas todas las units donde los tengas implementados. En realidad, al package de diseño sólo le tienes que añadir esta unidad para que Delphi te los instale en la paleta.

Otra aclaración: puesto que a partir de Delphi no se permiten crear aplicaciones que usen las unidades propias del diseño, a la hora de depurar la unit MisComponentesReg.pas, esto no lo podrás hacer incluyéndola en un proyecto tal cual y compilando, ya que te saltará el error del tipo: "File not found DesignIntf.dcu'. Debes crear el package de diseño, incluirle dicha unit, y compilar el package cada vez que quieras depurarla.

Te pongo un enlace a un "Curso de creación de componentes" que me fue muy útil en su día, confeccionado por Luis Roche:

http://www.publispain.com/supertutor...os/4/ccind.htm

Saludos
__________________
Guía de Estilo
Responder Con Cita