PDA

Ver la Versión Completa : Acceder a objetos creados en tiempo de ejecución


newtron
13-11-2020, 19:59:00
Hola a tod@s.


En un formulario tengo una serie de objetos que se crean en tiempo de ejecución y posteriormente tengo que cambiarles alguna de sus propiedades. El tema es que esos objetos que se crean en tiempo de ejecución no me aparecen en el "componentcount" del formulario y no puedo hacer un bucle para localizarlos por su nombre, la verdad no sé por qué.


¿Alguien me podría decir de qué manera puedo acceder a ellos para modificarles cosas?


Gracias y un saludo

Casimiro Notevi
13-11-2020, 20:53:48
Haz una búsqueda por FindComponent
Avisa si necesitas ayuda.

Caminante
13-11-2020, 21:18:41
Hola

Lo primero seria saber que clase de objetos estas creando. Sin descienden de TComponent y les asignas un parent o son descendientes de TObject.

Podrias almacenar los objetos en un TObjectList y asi puedes tener acceso a ellos. Bueno depende de la forma que los estes creando.

Saludos

newtron
14-11-2020, 11:28:31
Hola y gracias por vuestras respuestas.


Los objetos que estoy creando son unos componentes propios que tienen un "TImage" y un "Caption" dentro de un recuadro para poder mostrar botones con imágenes y un texto con colores configurables, etc.


Estuve mirando el comando "FindComponent" pero no me funcionaba, no sé si hay algún uses que no estaba poniendo o cual era el problema pero no pude llegar a utilizarlo. ¿Algún ejemplo tontuno y/o hay que usar algún uses en especial?


Gracias y un saludo

Edito: Ok, ya veo como es la sintaxis. Pruebo y si tengo algún problema ya comento.
Gracias.

kuan-yiu
16-11-2020, 11:08:17
¿Cómo los creas? ¿Puedes poner ese trozo de código?

newtron
16-11-2020, 12:22:50
Claro, te lo paso aunque le he dado otra solución porque, como siempre, tengo el tiempo escaso y le estaba dando muchas vueltas a este tema.




procedure TFormPrincipal.MuestraArticulos(Grupo: String);
var
Boton: TNTSB2;
TextoEnIdioma: String;
Posicion,Aumento: SmallInt;
TopP,LeftP: SmallInt;
ImagenBMP: TBitMap;
begin
TopP:=3;
LeftP:=3;
if Debughook=0 then
Aumento:=2
else
Aumento:=1;
ScrollBox3:=TScrollBox.Create(nil);
ScrollBox3.Left := ScrollBox2.Left;
ScrollBox3.Top := ScrollBox2.Top;
ScrollBox3.Height := ScrollBox2.Height;
ScrollBox3.Width := ScrollBox2.Width;
ScrollBox3.Parent := FormPrincipal;
FormMain.EDBQuery1.SQL.Clear;
FormMain.EDBQuery1.SQL.Add('SELECT FAMILIA,CODIGO,DESCRIPCIO,IDIOMA1 FROM ARTICULOS A LEFT JOIN GRUPART B ON A.CODIGO=B.ARTICULO WHERE A.CODIGO='+QuotedStr(Grupo)+' ORDER BY B.POSICION');
FormMain.EDBQuery1.ExecSQL;
while not FormMain.EDBQuery1.Eof do begin
if DlgPropiedades.Idioma=1 then begin
TextoEnIdioma:=FormMain.EDBQuery1.FieldByName('DESCRIPCIO').AsString;
end else if DlgPropiedades.Idioma=2 then begin
TextoEnIdioma:=FormMain.EDBQuery1.FieldByName('IDIOMA1').AsString;
end;
// Creo botones artículos
Boton:=TNTSB2.Create(nil);
Boton.Name := 'A'+FormMain.EdbQuery1.FieldByName('FAMILIA').AsString+FormMain.EdbQuery1.FieldByName('CODIGO').AsSt ring;
Boton.Tipo := tTituloSimple;
Boton.Caption := TextoEnIdioma;
Boton.Color := clWhite;
Boton.Parent := ScrollBox3;
Boton.Top := TopP;
Boton.Left := LeftP;
Boton.Width := Trunc(134*Aumento);
Boton.Height := Trunc(162*Aumento);
Boton.NTValor := 'A*'+FormMain.EdbQuery1.FieldByName('FAMILIA').AsString+FormMain.EdbQuery1.FieldByName('CODIGO').AsS tring;
Boton.OnClick := FormTomarLlevar.BotonPulsado;
LeftP:= LeftP + Boton.Width +3;
if LeftP> (Boton.Width*2)+20 then begin
TopP:= TopP + Boton.Height +3;
LeftP:=3;
end;
Posicion:=DlgPropiedades.ListaArticulos.IndexOf(FormMain.EdbQuery1.FieldByName('FAMILIA').AsString+F ormMain.EdbQuery1.FieldByName('CODIGO').AsString);
DlgPropiedades.ImageListArticulos.GetBitmap(Posicion,Boton.Glyph);
FormMain.EDBQuery1.Next;
end;
ScrollBox2.Visible := fALSE;
ScrollBox3.BringToFront;
PanelLadoD.BringToFront;
end;

Neftali [Germán.Estévez]
16-11-2020, 12:50:58
Se me ocurre que puede ser por el tema del Owner.
Segun la ayuda componentcount:
Indicates the number of components owned by the component.


Si estás creando los componentes sin Owner, el ComponentCount/Components[i] no los encontrará. Y lo mismo pasará si estás utilizando el ComponentCount/Components[i]sobre un contenedor al que no pertenecen.

Por otro lado yo recomiendo por temas de optimización (y para más seguridad) que en lugar de utilizar FindComponent, crees una lista personal (TList/TStringist/TObjectList/TList<Tcomponent>,...) para almacenar, gestionar, buscar,... los componentes creados en runtime.

ACTUALIZACIÓN:
Ahora que veo el código, se me ocurren 2 opciones:

(1) Crear los componentes con un Owner.
(2) Añadirlos a una lista propia para gestionarlo.

newtron
16-11-2020, 13:13:10
Germán.


Efectivamente he tirado por la opción de crear una lista para guardar los componentes. De una forma o de otra viene bien conocer el problema por si tengo que "recular" en el manejo de todo esto y volver a la búsqueda de los componentes creados.


Gracias a todos y un saludo.

kuan-yiu
16-11-2020, 13:24:30
Yo te recomiendo que huyas de esto:
Boton:=TNTSB2.Create(nil); // Esto no
Boton:=TNTSB2.Create(Padre); // Mejor esto. Padre sería lo que estimes oportuno: tPanel, tForm, Sender...

Te evita un montón de problemas con el manejo de la memoria, con el borrado...

Neftali [Germán.Estévez]
16-11-2020, 13:25:00
Yo lo he usado muchas veces y por ejemplo, utilizar una TStringList ordenada, con el nombre de los componentes, mejora infinitamente la velocidad (más cuanto mayor sea el número de componentes) ya que la búsqueda en una lista ordenada es dicotómica, en lugar de secuencial.

Casimiro Notevi
16-11-2020, 13:43:41
También puedes asignar a la propiedad "Tag" del botón el código del artículo, así cuando se pulse un botón no hay que buscar nada, solamente tomar su tag y ya tienes el código del artículo.

ecfisa
16-11-2020, 16:26:55
Hola.

Y, si usas la clase TStringList como te sugiere Neftali, mediante el método AddObject (http://docwiki.embarcadero.com/Libraries/Sydney/en/System.Classes.TStringList.AddObject) puedes almacenar tanto el nombre del objeto como el objeto mismo.

...
Image := TImage.Create;
//...
Image.Name := Format('Image%d', [i]);
Lista.AddObject(Image.Name, TObject(Image));
...


Saludos :)

Ñuño Martínez
16-11-2020, 18:54:13
Coñe, me apunto lo del TStringList, que no me acordaba yo que podía usarse como contenedor de objetos. La de dolores que me va a ahorrar (cuando me acuerde de usarlo).

Neftali [Germán.Estévez]
16-11-2020, 19:50:06
Y, si usas la clase TStringList como te sugiere Neftali, mediante el método AddObject (http://docwiki.embarcadero.com/Libraries/Sydney/en/System.Classes.TStringList.AddObject) puedes almacenar tanto el nombre del objeto como el objeto mismo.


+1

TStringList, esa gran desconocida...
Nosotros en su día hicimos pruebas de velocidad con otras listas tipo TDictionary e implemenaciones de las listas hash y TStringList resultó en muchos casos más rápida que estas.

newtron
24-11-2020, 19:29:19
Buenas compañeros.


De nuevo con este tema. Una vez medio resuelto el tema almacenando las imágenes en "Imagelist" veo que, o no sé o las imágenes no puedo guardarlas con una resolución mayor de X con lo que al mostrarlas grandes se ven muuuuuuuuuuuuuuuuuu mal.


Estoy pensando en guardarlas en una lista tal y como habéis apuntado. Creo una lista de "Timage" tal y como apuntaba mi querido amigo ecfisa pero después no soy capaz de asignarla al componente TImage. Esto es lo que estoy haciendo (ejemplo tontuno):



..
Lista:=TStringList.Create(nil);
Imagen:=TImage.Create(nil);

Imagen.Picture.LoadFromFile('D:\TEMP\1.JPG');
Lista.AddObject('IMAGEN1', TObject(Imagen));
..



Entendiendo que esto anterior sea correcto luego no sé cómo asignarla al TImage.


¿Alguien me puede ilustrar?


Gracias y un saludo

ecfisa
24-11-2020, 21:19:46
Hola estimado amigo :)

Una vez asignado el objeto puedes hacer

Lista := TStringList.Create;
Imagen := TImage.Create(nil);

Imagen.Picture.LoadFromFile('D:\TEMP\1.JPG');
Lista.AddObject('IMAGEN1', TObject(Imagen));

// Ej1: Mostrar el TImage creado (igual a hacer: Imagen.Parent := Self;)
TImage(Lista.Objects[0]).Parent := Self;

// Ej2: Asignar la imágen a otro TImage
Image1.Picture.Assign(TImage(Lista.Objects[0]).Picture);

Para referirte al objeto imagen almacenado, usa el moldeo TImage(Lista.Objects[n]).

Otro ejemplo similar que asigna una imagen cargada en un TStringList a un TImage creado en tiempo de diseño:

var
pic: TPicture;
Lista: TStringList;
begin
Lista := TStringList.Create;
pic := TPicture.Create;

Lista.AddObject('Imagen1', TObject(pic));
TPicture(Lista.Objects[0]).LoadFromFile('C:\TMP\IMAGEN.JPG');

Image1.Picture.Assign(TPicture(Lista.Objects[0]));
...


Saludos :)

newtron
25-11-2020, 09:07:40
Daniel, gracias compañero. Como siempre es un placer saber de ti. :)

Neftali [Germán.Estévez]
25-11-2020, 09:09:16
Como añadido a lo que ha comentado [ecfisa], también puedes probar, por ejemplo, con Streams.

Puedes cargar las imágenes en la lista utilizando lo siguiente:

ts := TFileStream.Create('f:\__Imagenes__\Imagenes Varias\Logotipos\Logotipo.jpg', fmOpenReadWrite);
Lista.AddObject(i.ToString, ts);



Y asignarla a un TImage con este:

ts := TStream(Lista.Objects[0]);
Image1.Picture.LoadFromStream(ts);




De forma similar debes poder utilizar TStringList, TObjectList, TList<TStream>, TArray<TStream>.

newtron
25-11-2020, 13:17:57
Gracias Germán.


Ya había pensado también en la posibilidad de usar Streams pero, como casi siempre, os adelantáis.


De momento estoy intentando resolver el tema con TStringList pero ahora se me ocurre que me vendría bien poder buscar dentro de la lista por el nombre del objeto pero no consigo dar con la sintaxis, si es una lista de strings se busca con "IndexOf" pero en este caso que es una lista de objetos.... ¿cómo se haría la búsqueda por el nombre del objeto?


Gracias y un saludo

movorack
25-11-2020, 14:57:03
Hola.

Al agregar el objeto al StringList debes colocarle un identificador. El IndexOf verifica ese identificador.


procedure TForm1.ListarObjetos(Sender: TObject);
const
//Algunos nombres están repetidos
Nombres : Array [0..7] of string = ('Obj1', 'Obj2', 'Obj3', 'Obj1', 'Obj4', 'Obj2', 'Obj5', 'Obj1');
var
i: integer;
ListaObjetos: TStringList;
Nombre: string;
begin
ListaObjetos := TStringList.Create;
try
for i := Low(Nombres) to High(Nombres) do
begin
Nombre := Nombres[i].Trim;

if Nombre.IsEmpty or (ListaObjetos.IndexOf(Nombre.ToUpper) >= 0) then
Continue;

ListaObjetos.AddObject(Nombre.ToUpper, TObject.Create);
end;

//En este punto se deben tener 5 objetos en la lista
finally
ListaObjetos.Free;
end;
end;

Neftali [Germán.Estévez]
25-11-2020, 15:46:36
...pero ahora se me ocurre que me vendría bien poder buscar dentro de la lista por el nombre del objeto pero no consigo dar con la sintaxis


Bueno, por eso hemos comentado lo del TStringList, porque te permite almacenar una cadena asociada a cada objeto.
Como deriva de TStrings, también posee el método IndexOf o el método Find (si está ordenada), que funcionan de la misma manera.

newtron
25-11-2020, 17:04:19
:o ¡¡Andalaleche!! Y yo pensando en la manera de hacer esa búsqueda siendo un TObject.


Gracias a todos y un saludo.

movorack
25-11-2020, 17:29:57
También puedes usar un TDictionary o crear tu propio objeto basado en este.


type
TListaObjetos = class(TDictionary<String,Tobject>)
public
function Conteo: string;
end;

function TListaObjetos.Conteo: string;
begin
Result := Format('Tengo %d elementos', [Self.Count]);
end;

procedure TForm1.ListarObjetos(Sender: TObject);
const
//Algunos nombres están repetidos
Nombres : Array [0..7] of string = ('Obj1', 'Obj2', 'Obj3', 'Obj1', 'Obj4', 'Obj2', 'Obj5', 'Obj1');
var
i: integer;
ListaObjetos: TListaObjetos;
Nombre: string;
begin
ListaObjetos := TListaObjetos.Create;
try
for i := Low(Nombres) to High(Nombres) do
begin
Nombre := Nombres[i].Trim;

if Nombre.IsEmpty or ListaObjetos.ContainsKey(Nombre.ToUpper) then
Continue;

ListaObjetos.Add(Nombre.ToUpper, TObject.Create);
end;

//En este punto se deben tener 5 objetos en la lista
ShowMessage(ListaObjetos.Conteo);
finally
ListaObjetos.Free;
end;
end;

newtron
25-11-2020, 18:05:42
Gracias compañero. Ya tengo montado todo el tema con el TStringList.


Saludos