Club Delphi  
    FTP   CCD     Buscar   Trucos   Trabajo   Foros

Retroceder   Foros Club Delphi > Principal > OOP
Registrarse FAQ Miembros Calendario Guía de estilo Temas de Hoy

Grupo de Teaming del ClubDelphi

Respuesta
 
Herramientas Buscar en Tema Desplegado
  #1  
Antiguo 05-10-2005
OzzyzzO OzzyzzO is offline
Miembro
 
Registrado: may 2003
Ubicación: Argentina
Posts: 12
Poder: 0
OzzyzzO Va por buen camino
Exclamation Problemas al guardar componentes con TStream!!

Hola!

Estoy desarrollando una aplicación en la que creo componentes en forma dinámica, y al cerrar el form los guardo en un archivo de texto para volver a recuperarlos en el momento en que vuelva a ejecutar la aplicación. Esto lo logro con una función que saqué de la ayuda de delphi con una pequeña modificación:

Código Delphi [-]
// Esta función convierte el componente a un string (¿obvio no?)
function ComponentToString(Component: TComponent): string;
var
  BinStream:TMemoryStream;
  StrStream: TStringStream;
  s: string;
begin
  BinStream := TMemoryStream.Create;
  try
    StrStream := TStringStream.Create(s);
    try
      BinStream.WriteComponent(Component);
      BinStream.Seek(0, soFromBeginning);
      ObjectBinaryToText(BinStream, StrStream);
      StrStream.Seek(0, soFromBeginning);
      Result:= StrStream.DataString;
    finally
      StrStream.Free;

    end;
  finally
    BinStream.Free
  end;
end;

// Esta función convierte un string  a componente nuevamente
function StringToComponent(Value: string; Component: TComponent): TComponent;//acá meti la mano
var
  StrStream:TStringStream;
  BinStream: TMemoryStream;
begin
  StrStream := TStringStream.Create(Value);
  try
    BinStream := TMemoryStream.Create;
    try
      ObjectTextToBinary(StrStream, BinStream);
      BinStream.Seek(0, soFromBeginning);
      Result := BinStream.ReadComponent(Component);//acá meti la mano

    finally
      BinStream.Free;
    end;
  finally
    StrStream.Free;
  end;
end;

Ahora bien, funciona todo correctamente mientras guarde un componente que no tiene subcomponentes, por ejemplo, un TButton. El problema surge cuando intento recuperar un componente con subcomponentes, es decir un componente de mi creación, derivado de TCustomPanel, que tiene un TImage, un TStaticText y un TPopupMenu; el error que surge es: "class TImage not found".

Estas funciones las encontré en el ejemplo de la ayuda del método WriteComponent del componente TStream.

Se que en otro hilo del foro se describió otra forma de realizar esto, pero mi pequeño cerebrito fué incapaz de procesar esa información ya habiendo empezado con este método .

Espero haber sido lo suficientemente claro.

Agradezco a todo aquel que aporte alguna pista.
__________________
Juan Pablo
Responder Con Cita
  #2  
Antiguo 05-10-2005
Avatar de delphi.com.ar
delphi.com.ar delphi.com.ar is offline
Federico Firenze
 
Registrado: may 2003
Ubicación: Buenos Aires, Argentina *
Posts: 5.932
Poder: 27
delphi.com.ar Va por buen camino
Cita:
Empezado por OzzyzzO
...El problema surge cuando intento recuperar un componente con subcomponentes, es decir un componente de mi creación, derivado de TCustomPanel, que tiene un TImage, un TStaticText y un TPopupMenu; el error que surge es: "class TImage not found"....
Cuando dices "...tiene un TImage..." te refieres que tiene embebido un TImage, o que tiene una propiedad del tipo TImage donde enlazas otro componente?...
Si esta embebido, queda en tu componente implementar los métodos y propiedades para leer y guardar los datos de sus objetos, pero si no te funciona WriteComponent / ReadComponent, tampoco tendría que funcionar cuando carga / lee el dfm el propio IDE de Delphi o tu aplicación compilada.

Saludos!
__________________
delphi.com.ar

Dedique el tiempo suficiente para formular su pregunta si pretende que alguien dedique su tiempo en contestarla.
Responder Con Cita
  #3  
Antiguo 05-10-2005
OzzyzzO OzzyzzO is offline
Miembro
 
Registrado: may 2003
Ubicación: Argentina
Posts: 12
Poder: 0
OzzyzzO Va por buen camino
Ante todo gracias por responder.

Los componentes que están dentro del componente (el TImage, el TStaticText, etc) los creo en el medo AfterConstruction de mi componente:

Código Delphi [-]
procedure TDigitalDisplay.AfterConstruction;
var
  MenuItem: TMenuItem;
begin
  StaticText1 := TStaticText.Create(self);
  StaticText2 := TStaticText.Create(self);
  StaticText3 := TStaticText.Create(self);
  Image1 := TImage.Create(self);
  PopupMenu1 := TPopupMenu.Create(self);
  MenuItem := TMenuItem.Create(PopupMenu1);
  with MenuItem do
  begin
    Caption := 'Configurar';
    OnClick := PopupConfigurar;
  end;

//...
//...
//...

  inherited AfterConstruction;
end;

En cuanto al dfm, si, el IDE lo lee sin problemas. Un dato más: a las funciones para guardar y leer las llamo desde los eventos OnClose y OnCreate del form respectivamente, aunque también probé de llamarlas mediante un botón y tampoco funcionó. Además, el metodo write funciona, pero el Read es el que da el error.

Saludos.
__________________
Juan Pablo
Responder Con Cita
  #4  
Antiguo 06-10-2005
rounin rounin is offline
Miembro
 
Registrado: sep 2005
Posts: 43
Poder: 0
rounin Va por buen camino
Procedures Stream.WriteComponent and ReadComponent are
purposed for deal mainly with Root components (Form, DataModule, ...).

Somewhen I was experimenting with components
and wrote procedures for read/write
non-root components from stream:

Código Delphi [-]
 
 
{*******************************************************************************
* *
* Reonid sources *
* Categoty: other *
* (c) reonid@yahoo.com *
* *
*******************************************************************************}
unit Comp2Stm;
interface
uses
Classes, Controls;
 
function LoadComponentFromStream(ARoot, AOwner: TComponent; AParent: TWinControl;
SrcTxtStream: TStream; AComp: TComponent = nil): TComponent;
 
procedure SaveComponentToStream(AComp, ARoot: TComponent; DestTxtStream: TStream);
 
implementation
 
type
TDummy = class
class procedure ReaderSetName(Reader: TReader; Component: TComponent; var Name: string);
end;
 
class procedure TDummy.ReaderSetName(Reader: TReader; Component: TComponent; var Name: string);
begin
if (Reader.Owner <> nil) and
(Reader.Owner.FindComponent(Name) <> nil) then Name := '';
// The simplest way to resolve the name conflicts
end;
 
function LoadComponentFromStream(ARoot, AOwner: TComponent; AParent: TWinControl;
SrcTxtStream: TStream; AComp: TComponent = nil): TComponent;
var
BinStream: TStream;
Reader: TReader;
Comp: TComponent;
begin
BinStream := TMemoryStream.Create;
try
Reader := TReader.Create(BinStream, 4096);
 
//SrcTxtStream.Position := 0;
ObjectTextToBinary(SrcTxtStream, BinStream);
try
Reader.Position := 0;
Reader.Owner := AOwner;
Reader.Parent := AParent;
Reader.Root := ARoot;
// Owner, Parent and Root for new Component
// Reader will search in RTTI-tables of the Root component 
// classes and event handlers etc.
 
Reader.OnSetName := TDummy.ReaderSetName;
// Äëÿ ðàçðåøåíèÿ êîíôëèêòîâ èì¸í
// For the name conflicts resolution
 
//Reader.OnFindMethod := ;
//Reader.OnFindComponentClass := ;
 
Reader.BeginReferences;
try
Reader.ReadSignature;
Comp := Reader.ReadComponent(AComp);
Result := Comp;
Reader.FixupReferences;
// Initializes references on other components,
// that saved in DFM as names.
finally
Reader.EndReferences;
end;
finally
Reader.Free;
end;
finally
BinStream.Free;
end;
end;
 
procedure SaveComponentToStream(AComp, ARoot: TComponent; DestTxtStream: TStream);
var BinStream: TStream;
Writer: TWriter;
begin
BinStream := TMemoryStream.Create();
try
Writer := TWriter.Create(BinStream, 4096);
try
Writer.Root := ARoot;
Writer.WriteSignature;
Writer.WriteComponent(AComp);
Writer.WriteListEnd;
finally
Writer.Free;
end;
BinStream.Position := 0;
ObjectBinaryToText(BinStream, DestTxtStream);
finally
BinStream.Free;
end;
end;
 
end.

PS: When Reader reads a component from stream,
initially it reads the Class Name of the component.
Then (if the component has not been created before reading)
Reader tries to find the Class Reference by Class Name.

First, it searches the class in the RTTI-table of Root component.
If there is any component of the same class in the Root,
then success.

Second, the Reader searches the class in the
registered classes. If the class have been registered by
RegisterClass (RegisterClasses), then success.

Third, the Reader let user to resolve the Class Name
to the Class Reference by himself
(it calls OnFindComponentClass event handler).

If all this attempts have failed, the Reader raise EClassNotFound
"class <Class Name> not found".
Use RegisterClasses to solve it.

Última edición por rounin fecha: 06-10-2005 a las 14:23:22.
Responder Con Cita
  #5  
Antiguo 06-10-2005
OzzyzzO OzzyzzO is offline
Miembro
 
Registrado: may 2003
Ubicación: Argentina
Posts: 12
Poder: 0
OzzyzzO Va por buen camino
Thanks! Rounin. I think that your solution will help me. Very good explanation. Thanks again!

Saludos
__________________
Juan Pablo
Responder Con Cita
  #6  
Antiguo 06-10-2005
Avatar de vtdeleon
vtdeleon vtdeleon is offline
Miembro
 
Registrado: abr 2004
Ubicación: RD & USA
Posts: 3.236
Poder: 24
vtdeleon Va por buen camino
Saludos

Rounin, Sabes escribir en español?, Lo digo porque en este foro la gran mayoría hablamos(o escribimos) en español. Entonces los forista que consulten este hilo y tenga pocos conocimiento del ingles estarán despistado.

Ante todo, Es muy grata y muy buena tu ayuda (hasta a mi me has ayudado). Espero seguir viendote por aqui
__________________
Van Troi De León
(Not) Guía, Code vB:=Delphi-SQL, ¿Cómo?
Viajar en el tiempo no es teóricamente posible, pues si lo fuera, ya estarían aqui contándonos al respecto!
Responder Con Cita
  #7  
Antiguo 06-10-2005
rounin rounin is offline
Miembro
 
Registrado: sep 2005
Posts: 43
Poder: 0
rounin Va por buen camino
Saludos,

Estoy estudiando español,
(yo periodicamente voy en comisiones de servicio a España)
pero mi nivel de español no es bastante alto,
y lo escribir en español todavía es dificil para mi.
Lo siento.
Responder Con Cita
  #8  
Antiguo 06-10-2005
Avatar de vtdeleon
vtdeleon vtdeleon is offline
Miembro
 
Registrado: abr 2004
Ubicación: RD & USA
Posts: 3.236
Poder: 24
vtdeleon Va por buen camino
Saludos
Cita:
Empezado por rounin
pero mi nivel de español no es bastante alto,
Cómo va ser. Se entiende perfectamente
Cita:
Empezado por rounin
y lo escribir en español todavía es dificil para mi.
Aquí te serviría de práctica
__________________
Van Troi De León
(Not) Guía, Code vB:=Delphi-SQL, ¿Cómo?
Viajar en el tiempo no es teóricamente posible, pues si lo fuera, ya estarían aqui contándonos al respecto!
Responder Con Cita
  #9  
Antiguo 07-10-2005
rounin rounin is offline
Miembro
 
Registrado: sep 2005
Posts: 43
Poder: 0
rounin Va por buen camino
Saludos,

pienso que en tu caso necesita incluir flag csSubcomponent
en tus subcomponentes:

Código:
  TDigitalDisplay = class(TCustomPanel)
  private
	FImage1: TImage;
	FStaticText1: TStaticText;
	FPopupMenu1: TPopupMenu;
	{...}
  published
	property StaticText1: TStaticText read FStaticText1; {r/o}
	property Image1: TImage read FImage1;
	property PopupMenu1: TPopupMenu read FPopupMenu1;
	{...}
  end;
 
 
constructor TDigitalDisplay.Create(AOwner: TComponent);
 
   function CreateSubComponent(AClass: TComponentClass): TComponent;
   begin
	 Result := AClass.Create(Self);
	 Result.SetSubComponent(True); {!!!!!!!!}
	 Result.FreeNotification(Self); 
	 if Result is TControl then TControl(Result).Parent := Self;
   end;
 
var
  MenuItem: TMenuItem;
begin
  inherited;
  {...}
  FStaticText1 := CreateSubComponent(TStaticText) as TStaticText;
  FStaticText2 := CreateSubComponent(TStaticText) as TStaticText;
  FStaticText3 := CreateSubComponent(TStaticText) as TStaticText;
  FImage1	  := CreateSubComponent(TImage) as TImage;
  FPopupMenu1  := CreateSubComponent(TPopupMenu) as TPopupMenu;
 
  MenuItem := TMenuItem.Create(PopupMenu1);
  FPopupMenu1.Items.Add(MenuItem);

  with MenuItem do
  begin
	Caption := 'Configurar';
	OnClick := PopupConfigurar;
  end;

end;
 
procedure TDigitalDisplay.Notification(AComponent: TComponent; Operation: TOperation);
begin
  inherited;
  if (AComponent = FStaticText1) and (Operation = opRemove) then FStaticText1 := nil;
  if (AComponent = FStaticText2) and (Operation = opRemove) then FStaticText2 := nil;
  if (AComponent = FStaticText3) and (Operation = opRemove) then FStaticText3 := nil;
  if (AComponent = FPopupMenu1) and (Operation = opRemove) then FPopupMenu1 := nil;
  if (AComponent = FImage1)	 and (Operation = opRemove) then FImage1	 := nil;
end;
PS Porque creas subcomponentes en AfterConstruction?
Porque no en Create?
Responder Con Cita
  #10  
Antiguo 20-10-2005
OzzyzzO OzzyzzO is offline
Miembro
 
Registrado: may 2003
Ubicación: Argentina
Posts: 12
Poder: 0
OzzyzzO Va por buen camino
Lightbulb

Cita:
Empezado por rounin
PS Porque creas subcomponentes en AfterConstruction?
Porque no en Create?
Perdón por no contestar , estuve con mucho trabajo.

Los subcomponentes los creo en el metodo AfterConstruction porque en el constructor Create el componente aun no está creado y no puedo asignarle la variable Self a la propiedad Parent del subcomponente, ni tampoco pasarlo como Owner en la creación del subcomponente. O almenos eso entendí.

Saludos y éxitos con el español.
__________________
Juan Pablo
Responder Con Cita
  #11  
Antiguo 20-10-2005
rounin rounin is offline
Miembro
 
Registrado: sep 2005
Posts: 43
Poder: 0
rounin Va por buen camino
Puedes hacer eso en Create después inherited.
En este lugar el componente esta creado totalmente.
(sólo las propiedades no ya esta cargado de DFM y
la ventana no ya esta creado).

Última edición por rounin fecha: 21-10-2005 a las 12:51:23.
Responder Con Cita
  #12  
Antiguo 20-10-2005
OzzyzzO OzzyzzO is offline
Miembro
 
Registrado: may 2003
Ubicación: Argentina
Posts: 12
Poder: 0
OzzyzzO Va por buen camino
Cita:
Empezado por rounin
Puedes hacer eso en Create después inherited.
Es cierto Rounin ... ¿Como no me dí cuenta? ... bueno supongo que el apuro y la falta de experiencia tuvieron algo que ver .

Gracias de nuevo.

Y con respecto a:
Cita:
Empezado por vtdeleon
... Entonces los forista que consulten este hilo y tenga pocos conocimiento del ingles estarán despistado.
¿Eso lo dijiste por mi respueta? je...je...

Saludos
__________________
Juan Pablo
Responder Con Cita
Respuesta



Normas de Publicación
no Puedes crear nuevos temas
no Puedes responder a temas
no Puedes adjuntar archivos
no Puedes editar tus mensajes

El código vB está habilitado
Las caritas están habilitado
Código [IMG] está habilitado
Código HTML está deshabilitado
Saltar a Foro


La franja horaria es GMT +2. Ahora son las 21:17:40.


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