Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   OOP (https://www.clubdelphi.com/foros/forumdisplay.php?f=5)
-   -   Cómo codificar en el evento (incluso asignado) de un componente (https://www.clubdelphi.com/foros/showthread.php?t=26752)

dec 02-11-2005 18:08:15

Cómo codificar en el evento (incluso asignado) de un componente
 
Hola,

Tengo un problema, o, por mejor decir, una inquietud, puesto que no sé si resolviendo esta se creará aquél. Estoy pensando en crear un sencillo componente que habría de heredar de un "TWebBrowser". Para lo que quisiera conseguir, necesitaría de codificar en su evento "OnBeforeNavigate2".

Bien. O mal. El caso es que llego hasta crear un método capaz de responder a dicho evento, de tal forma que cuando creo una instancia de la clase "TDecWebBrowser" pueda hacer algo más o menos así:

Código Delphi [-]
 type
   TDecWebBrowser = class(TWebBrowser)
   private
     // Método que responderá al evento
     procedure AntesDeNavegar(parametros: TParams);
   public
     constructor Create(AOwner: TComponent); override;
   end;
 
 implementation
 
 constructor TDecWebBrowser.Create(AOwner: TComponent);
 begin
   inherited;
   // Asigno el método al evento
   OnBeforeNavigate2 := AntesDeNavegar;
 end;
 
 procedure TDecWebBrowser.AntesDeNavegar(parametros: TParams);
 begin
   // TODO
   // Aquí llegaríamos si el usuario no asignó también
   // un método para este evento: de ser así prevalece-
   // ría su método sobre este, y no solamente eso, si-
   // no que este no llegaría a ejecutarse.
 end;
O sea, al crear el componente asigno a su evento "OnBeforeNavigate2" el método privado del mismo componente, que es en donde necesito codificar determinadas cuestiones. En donde ahora pone "TODO".

¿Y? Pues que si cuando hago uso del componente asigno al evento "OnBeforeNavite2" un manejador del evento, adiós al que asigné cuando se creó el componente. ¿Se entiende? Si algo no se entiende, por favor, no dejéis de decirlo y trataré de ofrecer la información tan precisamente como pueda. Gracias de antemano a todos y saludos.

P.D. No corre ninguna prisa. ;)

Lepe 02-11-2005 20:06:38

Se supone, que el evento OnBeforeNavigate2 es del propio TWebBrower, entonces, tu componente ha de quitarlo de la zona published. Simplemente declarandolo en tu componente en la zona Private.

A continuación creas tu propio evento OnBeforeNavigate y lo haces published. Y este nuevo evento será el que vea el usuario de tu componente, es decir:
Código Delphi [-]
type
   TDecWebBrowser = class(TWebBrowser)
   private
     Fnuevo : .....;

     // Método que responderá al evento
     procedure AntesDeNavegar(parametros: TParams);
     property OnBeforeNavigate2 .... // este es el antiguo
   public
     constructor Create(AOwner: TComponent); override;
   published
      property OnBeforeNavigateNuevo:.... read FNuevo write FNuevo;
   end;
 
 implementation
 
 constructor TDecWebBrowser.Create(AOwner: TComponent);
 begin
   inherited;
   // Asigno el método al evento
   OnBeforeNavigate2 := AntesDeNavegar;
 end;
procedure TDecWebBrowser.AntesDeNavegar(parametros: TParams);
 begin
   // TODO
   // Aquí llegaríamos si el usuario no asignó también
   // un método para este evento: de ser así prevalece-
   // ría su método sobre este, y no solamente eso, si-
   // no que este no llegaría a ejecutarse.
  if Assigned(FOnBeforeNavigateNuevo) then
      FOnBeforeNavigateNuevo(.....parametros que lleve);
 end;
El usuario programador que use tu componente no sabrá que existe AntesDeNavigar (ni falta que le hace) ;).

Espero te sirva.

andres1569 02-11-2005 20:07:40

Cita:

Empezado por dec
Tengo un problema, o, por mejor decir, una inquietud, puesto que no sé si resolviendo esta se creará aquél.

Frase para la historia, :D

Bueno, el problema que se te plantea se resolvería adecuadamente si tuvieras acceso al método supuestamente protegido y supuestamente llamado DoBeforeNavigate o algo así, virtual claro, que no sé si existe. Como supongo que ya lo has buscado y no has encontrado nada parecido, lo único que se me ocurre es un pequeño truco, que no evita que se pueda machacar tu evento en tiempo de ejecución, pero que quizás en la práctica resulte suficiente (si vas a utilizar tú el componente, no es problema pues sabes lo que no debes hacer, si es un componente para terceros tendrá una puerta trasera donde puedan machacar tu código de forma involuntaria):

Sobrescribes el método Loaded, y ahí almacenas en una variable del tipo adecuado el evento que haya sido fijado en tiempo de diseño:

Código Delphi [-]
procedure TDecWebBrowser.Loaded;
begin
  inherited Loaded;
  EventoAnterior := OnBeforeActivate2;
  OnBeforeActivate2 := AntesDeNavegar;
end;

En el método AntesDeNavegar haces una llamada a EventoAnterior, claro.

Bueno, es algo chapucilla, pero puede servirte.

Saludos

dec 02-11-2005 20:37:38

Hola,

Gracias a ambos por responder, en primer lugar. Tomando la manera que dice Lepe, queda algo pendiente y es que no se acaba de quitar del "inspector de objetos" el evento "OnBeforeNavigate2", aun cuando lo declaro como una propiedad privada con el fin de ocultarlo, precisamente,... ¿qué estoy olvidando?

Es decir, el tema parece ir bien, pero, ahora en el "inspector de objetos" tengo dos eventos "OnBeforeNavigate" (que yo preparo en el componente) y "OnBeforeNavigate2" (que ya incorpora el componente de que heredo, y acaso sería bien ocultar)... Además, si el usuario asigna algún método a este último evento, adiós al primero... claro está... ¿Cómo oculto, pues, el evento "OnBeforeNavigate2"?

Gracias de nuevo a ambos. El componente no es ninguna maravilla, pero, si llego a darle cabo, más o menos, ya sabéis que lo incluiré en DecComp. ;) Gracias otra vez. ;)

Crandel 02-11-2005 20:43:33

Cita:

Empezado por Lepe
Se supone, que el evento OnBeforeNavigate2 es del propio TWebBrower, entonces, tu componente ha de quitarlo de la zona published. Simplemente declarandolo en tu componente en la zona Private.

Pues no se puede disminuir los privilegios !!!!

En la mayoria de los casos se debe ir una clase mas arriba, antes que se haga publico la propiedad, pero en este caso no tienes una clase más arriba que te sirva.

La opción que te queda es sobreescribir la propiedad.

Aca te hice un ejemplo de como sería:
Código Delphi [-]
 type
   TDecWebBrowser = class (TWebBrowser)
   private
     // donde almacenamos el puntero al evento del usuario
     FOnBeforeNavigate2: TWebBrowserBeforeNavigate2;
     // Método que responderá al evento
     procedure AntesDeNavegar(parametros: TParams);
   public
     constructor Create(AOwner: TComponent); override;
   published
     // evento que sobreescribimos
     property OnBeforeNavigate2: TWebBrowserBeforeNavigate2 read FOnBeforeNavigate2 write FOnBeforeNavigate2;
   end;

 implementation
 
 constructor TDecWebBrowser.Create(AOwner: TComponent);
 begin
   inherited;
   // Asigno el método al evento
   inherited OnBeforeNavigate2 := AntesDeNavegar;
 end;
 
 procedure TDecWebBrowser.AntesDeNavegar(parametros: TParams);
 begin
   // TODO
   // esto puede ir antes o depues del TODO, de acuerdo al orden de ejecución
   FOnBeforeNavigate2({todos los parametros}); 
 end;

creo que con esto debería funcionar sin problemas

Suerte

dec 02-11-2005 20:53:29

Hola,

A quien le pueda resultar interesante, aquí tienen una posible forma de ocultar propiedades, dictada por Neftali. En todo caso, Crandel, lo que propones funciona exactamente como esperaba. Muchísimas gracias a todos. De verdad. ;)

dec 02-11-2005 23:41:27

Hola,

Bueno, pues, he llamado al engendro TDecBrowser y pueden conseguirlo aquí mismo. Gracias de nuevo a todos. ;)

roman 03-11-2005 06:52:14

No sé, no sé. A mi como que me da más algo del estilo:

Código Delphi [-]
unit DecBrowser;

interface

uses
  OleCtrls, SHDocVw, ActiveX;

type
  TDecBrowser = class(TWebBrowser)
  protected
    procedure InvokeEvent(DispID: TDispID; var Params: TDispParams); override;
  end;

implementation

procedure TDecBrowser.InvokeEvent(DispID: TDispID; var Params: TDispParams);
begin
  inherited;

  if DispId = 250 then // 250 - OnBeforeNavigate2
  begin
    {
      Aquí tu código
    }
  end;
end;

end.

Si inherited se llama antes o después ya dependerá de tus necesidades específicas.

// Saludos

Crandel 03-11-2005 07:15:54

Cita:

Empezado por roman
No sé, no sé. A mi como que me da más algo del estilo:
...

Supuse desde un principio que en este tema no podía dejar de faltar la participación de Roman :D

Lo que comentas lo pense al prpicipio, pero el tema es que el método InvokeEvent es llamado muchas veces al parecer, lo cual se estaria llamando constantemente a nuestro método override, para no hacer nada.

Si la idea es capturar este único evento, lo haria con mi propuesta, si se quiere capturar varios más, lo haria a la forma recdomendada por roman.

roman 03-11-2005 07:25:53

Cita:

Empezado por Crandel
pero el tema es que el método InvokeEvent es llamado muchas veces al parecer, lo cual se estaria llamando constantemente a nuestro método override, para no hacer nada.

¿Y?

¿Te imaginas cuántas veces se llama a WndProc? Pero esto no es óbice para no redefinirlo de ser necesario incluso si el comportamiento añadido es muy poco.

Por otra parte, aunque es cuestión de puntos de vista, los métodos y/o propiedades de una clase no debieran esconderse (aun suponiendo que realmente se puede); la herencia de clases se supone que está para añadir funcionalidad no para quitarla.

// Saludos

Crandel 03-11-2005 07:43:29

Cita:

Empezado por roman
¿Te imaginas cuántas veces se llama a WndProc? Pero esto no es óbice para no redefinirlo de ser necesario incluso si el comportamiento añadido es muy poco.

Por supuesto, que no va haber diferencia ni de velocidad, ni otra cosa. Sólo me parecio mejor opción para un solo evento, además de parecerse más a la consigna "Cómo codificar en el evento (incluso asignado) de un componente".
Cita:

Empezado por roman
aunque es cuestión de puntos de vista, los métodos y/o propiedades de una clase no debieran esconderse (aun suponiendo que realmente se puede); la herencia de clases se supone que está para añadir funcionalidad no para quitarla.

no hable de esconder ni de quitar nada, todo esta en su lugar.

roman 03-11-2005 07:51:18

Cita:

Empezado por Crandel
todo esta en su lugar.

En esto estoy de acuerdo. Yo lo estaba entendiendo mal.

// Saludos

Lepe 03-11-2005 08:56:47

Cita:

Empezado por Crandel
Pues no se puede disminuir los privilegios !!!!

En la mayoria de los casos se debe ir una clase mas arriba, antes que se haga publico la propiedad, pero en este caso no tienes una clase más arriba que te sirva.

Exacto, ese precisamente es el problema, como has visto, en este caso concreto no se puede ir una clase más arriba o toca implementar muchas propiedades.

En cuanto a lo demás que habeís hablado.... simplemente me callo :D

saludos ;)

dec 03-11-2005 08:59:03

Hola,

Probé, probé, previamente, con el método "InvokeEvent", lo que ocurre que es no llegué a dar con una forma de usarlo, puesto que solamente me fijé en el método "InvokeEvent" de la clase "TInternetExplorer". Eso me confundió y no llegué a ver que la clase "TWebBrowser" también tiene su "InvokeEvent". De momento, para lo que es, se queda como está, pero, de todos modos, muchas gracias a todos por vuestras aportaciones, valiosas todas ellas.

dec 03-11-2005 09:08:33

Hola,

Solamente añadir que anoche me dormí pensando en esto (vaya una vida más alucinante la mía, últimamente) que dijo Crandel:


Cita:

Empezado por Crandel
La opción que te queda es sobreescribir la propiedad.

Aunque, a decir verdad, mi sueño era más romántico, si se me permite, puesto que era "la opción es heredar la propiedad..."...

Y es que, ¿de verdad esta instrucción no parece una maravilla, por decirlo así?

Código Delphi [-]
 inherited OnBeforeNavigate2 := AntesDeNavegar;
Aún no he conseguido desentrañarla, y puede que no lo haga nunca, pero, desde luego, trataré de quedarme con la idea. En fin, trataré de quedarme con todo lo que se va diciendo por aquí, ¡porque lo vale! ¿Os he dado ya las gracias? Pues ahí van otra vez: a todos, muchas gracias. ;)

Neftali [Germán.Estévez] 03-11-2005 10:34:20

Cita:

Empezado por roman
...los métodos y/o propiedades de una clase no debieran esconderse (aun suponiendo que realmente se puede); la herencia de clases se supone que está para añadir funcionalidad no para quitarla.

Ahí no estoy de acuerdo contigo Román, no creo que sea cuestión de puntos de vista (porque no creo que siempre se pueda hacer A o B), creo que es cuestión de necesidades (a veces necesitarás que esté y otras veces no).
Si necesito heredar de un componente y la propiedad Color (por decir una) no tiene sentido en mi componente heredado, no creo que haya que dejarla visible/accesible.
¿Qué pasa si esa propiedad altera el comportamiento de tu componente de una forma no deseada?
¿Añado código para inutilizarla? Pero estonces estaré añadiendo código para deshabilitar una propiedad que sí veo y tengo accesible, pero que no necesito... ¿raro no?

dec 03-11-2005 11:56:19

Hola,

Bueno. Pues el componente está muy bien, o eso me parece, en cuanto a que puede resultar útil, pero, me encuentro con un problema que paso a explicar. Se trata de las acciones que pueden asociarse al componente.

Puede asociarse con el componente "DecBrowser" un componente "TActionList". No hay problema en hacerlo, y tampoco con el componente, es decir, que todo parece ir bien.

El problema está en quitar el componente de un formulario en tiempo de diseño. Añádase en un formulario vació el componente "DecBrowser"; añádase también un "TActionList" al formulario; asígnese este último componente a la propiedad "Acciones" del "DecBrowser"; quítese del formulario ahora el "TActionList", y, a continuación, trátese de quitar el componente "DecBrowser" del formulario...

Perdón por las "violaciones de acceso", "errores abstractos" y demás. No era mi intención, se lo juro. Estoy probando cosas. Por ejemplo, si a la hora de crear la variable que contendrá la propiedad "Acciones" hago algo así:

Código Delphi [-]
   constructor TDecBrowser.Create(AOwner: TComponent);
   begin
     inherited Create(AOwner);
     FEnlaces := TStringList.Create;
     FAcciones := TActionList.Create(Forms.Application); // Aquí
     inherited OnBeforeNavigate2 := ComprobarEnlaces;
   end;
En lugar de algo como esto otro:

Código Delphi [-]
   constructor TDecBrowser.Create(AOwner: TComponent);
   begin
     inherited Create(AOwner);
     FEnlaces := TStringList.Create;
     FAcciones := TActionList.Create(Self); // Aquí
     inherited OnBeforeNavigate2 := ComprobarEnlaces;
   end;
La cosa parece pintar mejor, sin embargo, aún no he dado con la solución para el problema que he comentado arriba. Sigo en ello, a ver si soy capaz. Cualquier ayuda será bienvenida. Por cierto, en el código del componente actualmente hay algo como esto:

Código Delphi [-]
   property Acciones: TActionList read FAcciones write FAcciones;
Y lo he cambiado por algo así:

Código Delphi [-]
  property Acciones: TActionList read GetAcciones write SetAcciones;
Con sus correspondientes métodos:

Código Delphi [-]
  // Devolvemos la lista de acciones asociada.
  //
  function TDecBrowser.GetAcciones: TActionList;
  begin
    Result := FAcciones;
  end;
  
  // Establecemos la lista de acciones.
  //
  procedure TDecBrowser.SetAcciones(value: TActionList);
  begin
    FAcciones := value;
  end;
Pero, como pueden ver, son algo superfluos los métodos de arriba, puesto que para ese viaje no hacían falta tantas alforjas, como suele decirse. Pero, es que esto otro, aunque compilar compila, no funciona:

Código Delphi [-]
  // Establecemos la lista de acciones.
  //
  procedure TDecBrowser.SetAcciones(value: TActionList);
  begin
    FAcciones.Assign(value); // ni así
    FAcciones.Assign(TPersistent(value)); // ni así tampoco
  end;
Me aparece tal que este error:


Cita:

---------------------------
Error
---------------------------
Cannot assign a TActionList to a TActionList.
---------------------------
OK
---------------------------

Lepe 03-11-2005 13:28:43

Mientras roman llega y no llega, doy mi opinión :D.

Tu no tienes que crear Facciones ni mucho menos. Delphi se encarga de crear el TActionList cuando lo agregas a tu form y "crea" los editores y formularios (editor de propiedades). Y también destruirá el TActionList cuando lo quites en tiempo de diseño de tu forma. Tú lo único que tienes que hacer es asignar el actionList y cuando necesites acceder, comprobar que no es nil.

Código Delphi [-]
constructor TDecBrowser.Create(AOwner: TComponent);
  begin
    inherited Create(AOwner);
    FEnlaces := TStringList.Create;
    FAcciones := nil;
    inherited OnBeforeNavigate2 := ComprobarEnlaces;
  end;

y dejar tu propiedad como:
Código Delphi [-]
  property Acciones: TActionList read FAcciones write FAcciones;
Si se quita el TActionList del form supongo (comprobar en delphi (1*)) que el IDE le asigna nil a tu propiedad Facciones, y como en tu código siempre compruebas que FAcciones es distinto de nil (con assigned si quieres), tú componente jamás dará violaciones de acceso.

(1*) - Para comprobar este punto, te sirve mejor el métod SetAcciones, para poner un punto de ruptura y comprobar que es realmente nil

A menos que tu componente lleve una propiedad Acciones, que al pulsar en los 3 puntitos, deba aparecer el editor de propiedades de un TActionList.:confused: Este método me parece un poco extraño, pero es la única explicación de hacer un TActionList.Create dentro de tu componente.

No sé si me he explicado bien.

saludos

dec 03-11-2005 13:46:30

Hola,

Lo he podido solucionar (de momento, al menos, parece funcionar bien, no se produce el error antes mencionado y el componente va también bien en tiempo de ejecución) sobreescribiendo el método "Notification" de "TComponent".

Primeramente creo una variable privada "FAcciones" de tipo "TCustomActionList". Luego una propiedad "Acciones" del mismo tipo, y que va a leer y a escribirse en "FAcciones". En el constructor del componente hago lo siguiente:

Código Delphi [-]
 // Se crea este componente.
 //
 constructor TDecBrowser.Create(AOwner: TComponent);
 begin
   inherited Create(AOwner);
   FEnlaces := TStringList.Create;
   FAcciones := TActionList.Create(Forms.Application);
   inherited OnBeforeNavigate2 := ComprobarEnlaces;
 end;
Creo la instancia de "TActionList" en "Application", porque, de no hacerlo así, de hacerlo en "Self" (el propio componente) en el "inspector de objetos" de Delphi la propiedad no queda muy bien al comienzo. Puede verse. No es el caso ahora.

En el destructor del componente libero la variable "FAcciones" sin problemas, tal que así:

Código Delphi [-]
 // Se destruye este componente.
 //
 destructor TDecBrowser.Destroy;
 begin
   FEnlaces.Free;
   FAcciones.Free;
   inherited Destroy;
 end;
Y el quid de la solución está en el método "Notification", como queda dicho, sobreescrito para que quede de este modo:

Código Delphi [-]
 // Notificaciones al componente.
 //
 procedure TDecBrowser.Notification(Component: TComponent;
   Operation: TOperation);
 begin
   inherited;
   if (csDesigning in ComponentState) then
   begin
     if (Operation = opRemove) then
     begin
       if (Component = FAcciones) then
       begin
         FAcciones.RemoveFreeNotification(Self);
         FAcciones := TCustomActionList.Create(Forms.Application);
       end;
     end;
   end;
 end;
A lo mejor es una burrada. Lo que estoy haciendo es comprobar que el componente que se quita de mi componente, valga la redundancia, es "FAcciones", si es así notifico su eliminación al IDE de Delphi (supongo algo así, no estoy muy puesto, para qué nos vamos a engañar) y a continuación creo de nuevo una instancia de "FAcciones", por si el usuario quiere añadir de nuevo un componente "TActionList".

Eso era lo que se apreciaba antes con error: al quitar el "TActionList" del componente "DecBrowser" en tiempo de diseño podía verse junto con el mensaje de error (Access violation, Abstract error...) cómo "desaparecía" la propiedad "Acciones" del "inspector de objetos"...

En fin. No sé qué tal la solución. Ahora revisaré cuanto dice Lepe, pues seguro será interesante. Ya podéis descargar, por cierto, el componente actualizado en mi página Web personal: dec.clubdelphi.com.

dec 03-11-2005 14:34:42

Hola,

He probado lo que sugieres Lepe, pero, o bien no llegué a hacerlo del todo como se debe, o, en cualquier caso, no podía hacerlo: no podía ni ubicar siquiera el componente en el formulario: ¿Violación de acceso? Ahora mismo no recuerdo el problema en concreto...

He preparado (*) una demostración de que el componente funciona como se espera, más o menos, tanto en tiempo de diseño como en ejecución, si bien se mira. Es algo pesado (como 1 MB) pero con las conexiones que tenéis de 100 MB debe ser pan comido para vosotros.

Demostración del componente DecBrowser (formato Flash, 1 MB apróx.)

(*) Con la ayuda del programa Wink, estupendo, por otro lado, hay que decirlo. ;)

roman 03-11-2005 16:23:09

Cita:

Empezado por Neftali
¿Qué pasa si esa propiedad altera el comportamiento de tu componente de una forma no deseada?

Entonces quizá no estés heredando de la clase correcta.

Al hacer este tipo de manipulaciones rompes el polimorfismo. Si tienes una clase base A, cualquier tratamiento genérico sobre un conjunto de As presupone que todos los descendientes se comportarán como A, al menos en lo que el contrato de A establece.

// Saludos

jachguate 03-11-2005 19:07:25

Dec: Es la primera vez que leo este hilo, aún cuando lo visité alguna vez en la semana, no contaba con el tiempo para revisarlo completo. Lo que no termino de entender es, para que creas una instancia de TActionList, si al parecer, luego le asignarás un puntero a la propiedad, a otro TActionList, creado, manejado y destruido por la forma (o por un tercero).

Si lo único que se necesita es mantener una referencia a un ActionList, no hará falta crear o destruir uno al momento de crear/destruir la instancia de TDecWebBrowser y si el mecanismo de notificación que ya has implementado, que es la manera estándar de "desconectar" componentes con relaciones horizontales.

Si por el contrario, es tu WebBrowser quien crea la lista de acciones, no hará falta notificar, pues será el propio componente el que se encargue de crearla/destruirla, y el usuario del componente, cuando mucho, podrá añadir/eliminar elementos a este ActionList. Si ese es el caso, entonces proteges el miembro de la clase con métodos de asignación de la propiedad:

Código Delphi [-]
property Acciones : TActionList read FAcciones write SetAcciones;
...

procedure TDecWebBrowser.SetAcciones(value : TActionList);

begin
  //no hacemos que nuestro miembro apunte a value, lo "procesamos"
  //de otra forma, sea asignandolo directamente, copiando las acciones, 
  //o de la manera que convenga a nuestros intereses.
  FAcciones.Assign(value);
end;

Hasta luego.

;)

andres1569 03-11-2005 19:43:22

Cita:

Empezado por roman
Al hacer este tipo de manipulaciones rompes el polimorfismo. Si tienes una clase base A, cualquier tratamiento genérico sobre un conjunto de As presupone que todos los descendientes se comportarán como A, al menos en lo que el contrato de A establece.

Recuerdo un debate en estos foros hace muuucho tiempo sobre un tema parecido (Román también estabas por allí, ;) )

En este caso concreto no parece un problema enmascarar una propiedad (el método OnBeforeActivate) puesto que es para ampliar su comportamiento, se ofrece al usuario del componente la misma funcionalidad que tenía en el ancestro, incluso con el mismo nombre para el evento (según el código que ha puesto Crandel).

Lo que no tengo muy claro (no lo he probado pero supongo que fallará), es qué pasaría si accediéramos polimórficamente a varios objetos TWebBrowser, entre ellos un TDecWebBrowser, para asignar su evento OnBeforeActivate, algo así:

Código Delphi [-]
for i:=0 to ComponentCount - 1 do
  if Components[i] is TWebBrowser then
     TWebBrowser(Components[i]).BeforeActivate := MiMetodo;

Al llegar al componente de tipo TDecWebBrowser, ¿se modificaría el evento redefinido o el correspondiente al ancestro? Me inclino por esto segundo, no sé si alguien ha hecho sus pruebas al respecto. :rolleyes:

Quizás esa sea una de las pegas de enmascarar una propiedad / método, que se está induciendo a un posible error.

Saludos

dec 03-11-2005 20:13:51

Hola,


Cita:

Empezado por Juan Antonio
Dec: Es la primera vez que leo este hilo, aún cuando lo visité alguna vez en la semana, no contaba con el tiempo para revisarlo completo. Lo que no termino de entender es, para que creas una instancia de TActionList, si al parecer, luego le asignarás un puntero a la propiedad, a otro TActionList, creado, manejado y destruido por la forma (o por un tercero).

Bueno. Se ha hecho eso no porque yo vea que es la mejor solución, sino porque se trataba de solucionar un problema... puede que no de la mejor manera, pero, verás ahora porqué digo esto.


Cita:

Empezado por Juan Antonio
Si lo único que se necesita es mantener una referencia a un ActionList, no hará falta crear o destruir uno al momento de crear/destruir la instancia de TDecWebBrowser y si el mecanismo de notificación que ya has implementado, que es la manera estándar de "desconectar" componentes con relaciones horizontales.

Pues es el caso que haciéndolo de ese modo no funciona (Acabo de probarlo de nuevo). Lo he dicho más arriba. El problema se presenta si sitúas en un formulario un "TDecBrowser" y un "TActionList". Acto seguido asignas a la propiedad "Acciones" del "TDecBrowser" el "TActionList" y aun después quitas del formulario el "TActionList"... "violaciones de acceso" variadas e incluso la necesidad (si no le has cogido el truco, y aún así...) de reiniciar Delphi.


Cita:

Empezado por Juan Antonio
Si por el contrario, es tu WebBrowser quien crea la lista de acciones, no hará falta notificar, pues será el propio componente el que se encargue de crearla/destruirla, y el usuario del componente, cuando mucho, podrá añadir/eliminar elementos a este ActionList. Si ese es el caso, entonces proteges el miembro de la clase con métodos de asignación de la propiedad: (...)

No se trata de eso, es decir, no veo la necesidad de que el "TDecBrowser" maneje internamente las acciones... para eso está ya el componente "TAcionList". Por cierto, trata de localizar un componente en la VCL (y en la JVCL también) que cuente con una propiedad de tipo "TActionList"... yo no he podido encontrarlo antes, cuando lo he intentado para tratar de fijarme cómo hacía para torear con esa propiedad.

Pero, me parece que comienzo a desvariar... Gracias de nuevo a todos. ;)

roman 03-11-2005 20:29:39

Cita:

Empezado por dec
Pues es el caso que haciéndolo de ese modo no funciona (Acabo de probarlo de nuevo). Lo he dicho más arriba. El problema se presenta si sitúas en un formulario un "TDecBrowser" y un "TActionList". Acto seguido asignas a la propiedad "Acciones" del "TDecBrowser" el "TActionList" y aun después quitas del formulario el "TActionList"... "violaciones de acceso" variadas e incluso la necesidad (si no le has cogido el truco, y aún así...) de reiniciar Delphi.

Creo que estás perdiendo el punto que señala Juan Antonio.

Estas violaciones de acceso se corrigen, como tú mismo ya lo descubriste, con el método Notification, que está precisamente para eso. Es justo el mecanismo que usa, por ejemplo, un DataSource y su propiedad DataSet.

Pero el caso es que tu código crea una instancia de TActionList:

Código Delphi [-]
FAcciones := TActionList.Create(Forms.Application);

y esto es lo que resulta innecesario si sólo te interesa la referencia al control y no que tu componente la mantenga por sí misma.

// Saludos

dec 03-11-2005 20:51:21

Hola,


Cita:

Empezado por roman
Creo que estás perdiendo el punto que señala Juan Antonio.

Estas violaciones de acceso se corrigen, como tú mismo ya lo descubriste, con el método Notification, que está precisamente para eso. Es justo el mecanismo que usa, por ejemplo, un DataSource y su propiedad DataSet.

No, no; si sé perfectamente por dónde va Juan Antonio. Entiendo perfectamente que solamente necesito una referencia y no manejar por mi cuenta un "TActionList"... ¡pero es que me está dando los errores que digo y no consigo hacerlo de otro modo que como he dicho! ;)

Ahora pienso en la cuestión de hacer uso del método "Notification", de otro modo que como lo hago ahora, claro está, supongo, no sé si bien:

Código Delphi [-]
 
 // Notificaciones al componente.
 //
 procedure TDecBrowser.Notification(Component: TComponent;
   Operation: TOperation);
 begin
   inherited;
   if (csDesigning in ComponentState) then
   begin
     if (Operation = opRemove) then
     begin
       if (Component = FAcciones) then
       begin
         FAcciones.RemoveFreeNotification(Self);
         FAcciones := TCustomActionList.Create(Forms.Application);
       end;
     end;
   end;
 end;
El problema ahora mismo está en que si dejo sin crear la variable "FAcciones" al crear el componente ni siquiera puedo poner en un formulario un "TDecBrowser": violaciones de acceso a go-go. Recuerdo que antes busqué por la paleta de componentes alguno que tuviera una propiedad del tipo "TActionList" y que no di con ninguno...

¿Estáis probando esto? Quiero decir, ¿probáis puede hacerse como decís? Porque si es así os agradecería el propio código, más que nada, porque entonces estoy haciendo algo y aun algos mal o no me entero de nada en absoluto,... ambas cosas son posibles. :D

dec 03-11-2005 20:53:22

Hola,

Quizá me gustaría añadir por aquello de "si algo funciona, no lo toques" (es broma) que el componente ya no da el problema anteriormente descrito: funciona bien, tanto en tiempo de diseño como en tiempo de ejecución. No digo esto, obviamente, sino para dejar constancia del hecho, por si resultara de alguna utilidad. ;)

roman 03-11-2005 21:01:05

Vamos a ver.

En tu código tienes:

FAcciones.RemoveFreeNotification(Self);

¿En qué momento agregas tal notificación?

Por otro lado, te das cuenta que cuando Notification se llama con opRemove es porque la componente se está destruyendo. Llamar entonces un método de una componente que se está destruyendo no creo que sea muy seguro.

Yo te recomiendo que te revises en la VCL el código para la propiedad PopupMenu de TControl que te dejará perfectamente claro el uso de FreeNotification y Notification.

EDITO:

No se está destruyendo la componente. Ya se destruyó:

Cita:

Empezado por ayuda de Delphi
opRemove - The specified object has been destroyed, and its memory is about to be freed.

// Saludos

dec 03-11-2005 21:09:12

Hola,


Cita:

Empezado por roman
FAcciones.RemoveFreeNotification(Self);

¿En qué momento agregas tal notificación?

No sé... ¿no la agrega el IDE de Delphi a la que añade el "TActionList" a la propiedad "Acciones" del "TDecBrowser"?


Cita:

Empezado por roman
Por otro lado, te das cuenta que cuando Notification se llama con opRemove es porque la componente se está destruyendo. Llamar entonces un método de una componente que se está destruyendo no creo que sea muy seguro.

No he inventado nada. Lo he visto en algún componente. Lo digo porque yo no estoy muy puesto que digamos en estos temas y me he fijado en algunos componentes para tratar de solucionar la cuestión planteada aquí. De todos modos debes estar e lo cierto: quitando esa instrucción el componente se comporta como se espera: no falla.


Cita:

Empezado por roman
Yo te recomiendo que te revises en la VCL el código para la propiedad PopupMenu de TControl que te dejará perfectamente claro el uso de FreeNotification y Notification.

Pues así lo haré. No porque no lo halla hecho ya, pero, porque muy probablemente no lo hice del todo ni bien. En ello estamos. ;)

maeyanes 03-11-2005 21:12:16

Creo que más bien le falta indicarle al TActionList que le notifique al componente TDecBrowser cuando se está destruyendo, esto se haría de la siguiente forma:

Código Delphi [-]
procedure TDecBrowser.SetActionList(Value: TActionList);
begin
  if FActionList <> Value then
  begin
    FActionList := Value;
    if Assigned(Value) then
      Value.FreeNotification(Self)
  end
end;

Ya con eso desde el Notification puedes eliminar la referencia al TActionList cuando este se destruya:

Código Delphi [-]
procedure TDecBrowser.Notification(AComponent: TComponent;
  Operation: TOperation);
begin
  if (Operation = opRemove) and (AComponent = FActionList) then
    FActionList := nil
end;

Saludos...

roman 03-11-2005 21:16:30

Cita:

Empezado por maeyanes
Creo que más bien le falta indicarle al TActionList que le notifique al componente TDecBrowser cuando se está destruyendo

¡Exacto! A eso me refería con "agregar la notificación". Y ésta es justo la manera en que se hace con el PopupMenu y otras propiedades similares.

// Saludos

Crandel 03-11-2005 21:18:00

Cita:

Empezado por andres1569
(según el código que ha puesto Crandel).
Código Delphi [-]
for i:=0 to ComponentCount - 1 do
  if Components[i] is TWebBrowser then
     TWebBrowser(Components[i]).BeforeActivate := MiMetodo;
Al llegar al componente de tipo TDecWebBrowser, ¿se modificaría el evento redefinido o el correspondiente al ancestro? Me inclino por esto segundo, no sé si alguien ha hecho sus pruebas al respecto. :rolleyes:

yo también me inclino por porque se va a estar modificando el evento del ancestro, porque se llama directamente al BeforeActivate del TWebBrowser.

Cuidado DEC !!!

Punto a favor de hacerlo con la sugerencia de Roman.

roman 03-11-2005 21:23:13

Cita:

Empezado por Crandel
yo también me inclino por porque se va a estar modificando el evento del ancestro, porque se llama directamente al BeforeActivate del TWebBrowser.

No estaría tan seguro. Pero aún así, habiendo un método protegido que controla al evento, prefiero redefinir aquél en lugar de lidiar con una propiedad "artificial". Cuestión de enfoques.

// Saludos

roman 03-11-2005 21:25:28

Cita:

Empezado por dec
Lo he visto en algún componente.

¿De la VCL?

// Saludos

maeyanes 03-11-2005 21:33:10

Cita:

Empezado por roman
No estaría tan seguro. Pero aún así, habiendo un método protegido que controla al evento, prefiero redefinir aquél en lugar de lidiar con una propiedad "artificial". Cuestión de enfoques.

// Saludos

Yo también prefiero usar el enfoque de redefinir los métodos controladores de los eventos, claro, cuando estos existen.


Saludos...

dec 03-11-2005 21:33:17

Hola,

Yo quisiera verlos en mi caso. He probado lo que me decís en vuestros últimos mensajes. He probado de varias formas. Comprendo que la solución que he logrado no parece la más efectiva o elegante, pero, hasta el momento es la única que funciona. Con todolo demás que he probado, y, repito, he probado, estoy probando y probaré, lo único que obtengo son errores de este jaez:


Cita:

---------------------------
Error
---------------------------
Access violation at address 0083D670 in module 'vcl70.bpl'. Read of address 00000030.
---------------------------
OK
---------------------------
Lamento ser tan tonto... ¿podría alguien probar en su Delphi (no hay prisa ninguna por mi parte) a retocar el componente de tal modo que funcione correctamente, sobre todo en cuanto al problema que inició este Hilo?

roman, confirmo que estaba demás la instrucción:

Código Delphi [-]
 FAcciones.RemoveFreeNotification(Self);
Quisiera decir que esta frase no me ha caído muy bien:


Cita:

Empezado por roman
Y ésta es justo la manera en que se hace con el PopupMenu y otras propiedades similares.

Opino que propiedades similares, lo que se dice propiedades similares... no sé yo si lo serán un "TPopupMenu" y un "TActionList". En fin, tampoco diré otra cosa.


Cita:

Empezado por yo mismo
Lo he visto en algún componente.

Cita:

Empezado por roman
¿De la VCL?

Pues diría que sí, precisamente. No sé porqué intuía que me preguntarías algo así. Y esta tarde me la he pegado en parte con el tema, y, efectivamente, creo haberlo visto en un componente de la VCL, pero, eso sí, no recuerdo ahora mismo... con lo cual esto que digo queda invalidado en buena medida.

roman 03-11-2005 21:39:50

Cita:

Empezado por dec
Quisiera decir que esta frase no me ha caído muy bien

:confused:


Cita:

Opino que propiedades similares, lo que se dice propiedades similares... no sé yo si lo serán un "TPopupMenu" y un "TActionList".
Por similares me refiero a que en ambos casos- al menos hasta donde alcanzo a entender de lo que quieres hacer -se trata de propiedades que referencian otras componentes pero que en sí mismas no son mantenidas (creadas, destruídas, etc.) por el control.

// Saludos

dec 03-11-2005 21:47:37

Hola,


Cita:

Empezado por roman
:confused:

Era una forma de decir. No sé porqué sabía que esa frase iba a causarte esa expresión. Si lo sé no la digo. ;)


Cita:

Empezado por roman
Por similares me refiero a que en ambos casos- al menos hasta donde alcanzo a entender de lo que quieres hacer -se trata de propiedades que referencian otras componentes pero que en sí mismas no son mantenidas (creadas, destruídas, etc.) por el control.

Comprendo. Pero, sigo diciendo que no he sido capaz de hacerlo de otro modo que como lo he hecho. Y que cuando intento otra cosa (lo que parece razonable) falla, al menos hasta ahora.

Por otro lado me estoy pegando con el método "InvokeEvent". Me gustaría utilizarlo, un poco irracionalmente, puesto que el componente, lo diré una vez más, funciona aparentemente bien.

Encuentro este problema:

Código Delphi [-]
   procedure TDecBrowser.InvokeEvent(DispID: TDispID;
     var Params: TDispParams);
   begin
     inherited;
     if DispId = 250 then // 250 - OnBeforeNavigate2
     begin
       // En correspondencia con los parámetros del evento
       // en cuestión, yo precisaría manejar dos parámetros:
       // Params[1] (URL) y Params[6] (Cancel) pero con el
       // el evento puedo hacerlo, esto es, sé hacerlo, lo
       // que no en este método, pues, ¿cómo conseguir algo así?
       // if Params[1] = 'http://unaUrl' then
            {...}
       // El dato contenido en Params[1] ha de ser de tipo
       // "OleVariant".
     end;
   end;
Ahora mismo estoy actuando así en el evento que me interesa y es necesario:

Código Delphi [-]
   procedure TDecBrowser.ComprobarEnlaces(Sender: TObject;
     const pDisp: IDispatch; var URL, Flags, TargetFrameName,
     PostData, Headers: OleVariant; var Cancel: WordBool);
   var
     i: integer;
     enlace: string;
   begin
     Cancel := false;
     for i := 0 to FAcciones.ActionCount-1 do
     begin
       enlace := Format(rsPosibleEnlace,
         [FAcciones.Actions[i].Name]);
       if (URL = AnsiLowerCase(enlace)) then
       begin
         Cancel := true;
         FAcciones.Actions[i].Execute;
       end;
     end;
     if Assigned(FOnBeforeNavigate2) then
     begin
       FOnBeforeNavigate2(Sender, pDisp, URL, Flags,
         TargetFrameName, PostData, Headers, Cancel);
     end;
   end;

maeyanes 03-11-2005 21:56:12

¿Cuál es la última versión de tu componente?, para que le eche un vistazo... :)

roman 03-11-2005 21:59:16

Cita:

Empezado por dec
puesto que el componente, lo diré una vez más, funciona aparentemente bien.

Bueno, entonces no nos hagas caso. Si te funciona bien y estás a gusto con el resultado pues ¿a qué darle más vueltas?

// Saludos


La franja horaria es GMT +2. Ahora son las 14:55:40.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2026, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi