PDA

Ver la Versión Completa : almacenar forms abiertos en un listbox


anubis
01-03-2017, 06:56:55
Buenas,

quizá sea una pregunta de "perogrullo" pero me estoy matando la cabeza con este tema.

Tengo una aplicacion en la que los forms se abren como show en un panel principal sin problema. Puedo maximizar, minimizar, cerrar etc.
Gracias a este maravilloso foro y su ayuda.

Viendo otros post que preguntan sobre saber que forms estan abiertos, intente implementar eso mismo y cargarlos en un listbox, asi, cuando tuviera algun form "oculto" podria mostrarlo en primer plano.

Pues bien, seguro que es una tonteria pero no hay forma.

Tengo esto

procedure TPrincipal.tbutton_inventariosClick(Sender: TObject);
begin
if not assigned(finventarios) then
begin
finventarios:=tfinventarios.Create(application);
finventarios.Parent:=CONTENEDOR;
finventarios.Show;
formularios_abiertos;
end
else
begin
end;
end;


Ahi, lo que hago es preguntar si el formulario ha sido asignado y sino crea uno. Ya cuando lo cierro se libera (para no abrir el mismo 2 veces).
CONTENEDOR es un panel donde lo abro.
formularios_abiertos es donde hago lo siguiente:


procedure tprincipal.formularios_abiertos;
var
i:integer;
begin
listbox1.Clear;
for i:= Screen.FormCount -1 downto 0 do
if screen.Forms[i].Showing then
if (screen.forms[i].Caption<>'Principal') and (screen.forms[i].caption<>'acceso') then
listbox1.Items.Add(screen.Forms[i].Caption);


Funcionar funciona, porque, si hago click en una de las lineas del listbox me muestra el form, lo unico malo es que los forms estan guardados de forma inversa en el listbox y me muestra el ultimo.
Si, es porque la i va en descendente y el listbox lo guarda en ascendente. Eso lo entiendo

El chiste es que si lo pongo al reves, al seleccionar el ultimo formulario del listbox me acaba tirando un error y no precisamente de index -1.

Tambien intente usar un array para guardar los forms y luego pasarlos al listbox pero es el mismo resultado.

No debe ser complicado pero ....

Gracias.

ecfisa
01-03-2017, 09:07:26
Hola.

...
El chiste es que si lo pongo al reves, al seleccionar el ultimo formulario del listbox me acaba tirando un error y no precisamente de index -1.

Usando,

...
for i := Screen.FormCount-1 downto 0 do
if Screen.Forms[i].Showing and ( Screen.Forms[i] <> Self ) then
ListBox1.Items.Add( Screen.Forms[i].Caption );
...

muestra el título de los formularios por el órden de aparición que previamente se les dé.

¿ Cuál es el código asignado al evento OnClick del ListBox y que mensaje de error te muestra exáctamente ?

Saludos :)

anubis
01-03-2017, 16:59:54
Gracias por responder.

El problema viene en el listbox, que los recoge en orden inverso.

Creo el formulario, los recojo en un listbox y ya tengo la lista.

si le doy click a uno de los elementos, lo que hago es:


showmessage(screen.Forms[listbox1.ItemIndex].caption+' '+inttostr(listbox1.ItemIndex));
screen.Forms[listbox1.ItemIndex].Show;


Y no corresponde el numero del screen.forms con el nombre almacenado.

roman
01-03-2017, 17:26:26
Y no corresponde el numero del screen.forms con el nombre almacenado.

No tendría porqué corresponder. Tu ListBox tiene (por lo menos) dos formularios menos que ScreenForms. Los índices de una lista y la otra van a diferir.

LineComment Saludos

anubis
01-03-2017, 17:37:07
Efectivamente amigo roman, por eso se los quite después.
Perdón si los deje en el código, pero estuve haciendo un montón de pruebas sin resultado.
Como comentaba, el recorrido de los forms se hace de mas a menos y en el listbox se añaden de menos a mas, creo que va por ahi la cosa.

Lo que no entiendo es el porqué se hace así con los forms, si lo hago al revés, si funciona relativamente, pero me da un error si elijo la última opción del listbox.

edito: en una de las veces, al pulsar una de las opciones del listbox, me sale frProgressForm en una ventanita y despues mensaje que pone violación de acceso.
Imagino que, el recorrer los forms de ascendente a descendente tenga alguna explicación, porque ahí no hay error, solo que el orden no es el correcto en el listbox

roman
01-03-2017, 17:42:11
Y ¿cuál es el error? Creo que aún no lo has dicho.

LineComment Saludos

anubis
01-03-2017, 18:53:21
Si, perdón, lo edité después.

Edito: en una de las veces, al pulsar una de las opciones del listbox, me sale frProgressForm en una ventanita y despues mensaje que pone violación de acceso.
Imagino que, el recorrer los forms de ascendente a descendente tenga alguna explicación, porque ahí no hay error, solo que el orden no es el correcto en el listbox

ecfisa
01-03-2017, 19:37:29
Hola.

Creo que el cargar los punteros a formularios dentro del ListBox te va a simplificar el asunto, te pongo un ejemplo :

....
uses Unit2, Unit3, Unit4, Unit5;

procedure TForm1.LoadForms;
var
i : Integer;
begin
ListBox1.Clear;
for i := Screen.FormCount-1 downto 0 do
if Screen.Forms[i] <> Self then
begin
Screen.Forms[i].Parent := Panel1;
Screen.Forms[i].Show;
end;
for i := Screen.FormCount-1 downto 0 do
if Screen.Forms[i].Showing and ( Screen.Forms[i] <> Self ) then
ListBox1.AddItem( Screen.Forms[i].Caption, Pointer(Screen.Forms[i]) );
// ListBox1.Sorted := True; //(opcional)
Randomize;
end;

procedure TForm1.FormShow(Sender: TObject);
begin
LoadForms;
end;

procedure TForm1.ListBox1Click(Sender: TObject);
const
COLORS: array[0..3] of TColor = (clRed, clLime, clYellow, clBlue);
var
i, ix: Integer;
begin
ix := ListBox1.ItemIndex; // selección actual
if ListBox1.ItemIndex <> -1 then
begin
// restaurar color en todos los form.
for i := 0 to ListBox1.Count-1 do
TForm(ListBox1.Items.Objects[i]).Color := clBtnFace;
// pintar form seleccionado con su color
if ix <> -1 then
TForm(ListBox1.Items.Objects[ix]).Color := Colors[ix];
end;
end;

procedure TForm1.btnMergeClick(Sender: TObject);
var
i, x: Integer;
begin
for i := 0 to ListBox1.Items.Count-1 do
begin
x := Random(ListBox1.Items.Count);
ListBox1.Items.Move(i, x);
end;
end;


Salida del ejemplo:
https://s17.postimg.org/o5uw2q8an/anubis.gif

De este modo simpre habrá correspondencia entre el nombre mostrado en el ListBox y el objeto sobre el que accionar, espero te sirva.

Saludos :)

ecfisa
01-03-2017, 20:00:43
Me olvidaba... para completar el ejemplo, liberar form al azar

...
procedure TForm1.btnLiberaClick(Sender: TObject);
begin
TForm(ListBox1.Items.Objects[Random(Screen.FormCount)]).Free;
LoadForms; // actualizar ListBox
end;


Saludos :)

anubis
02-03-2017, 01:24:13
gracias por el detalle ;).

Estaba intentando probar la unit que posteaste y me sale, a la hora de compilar:
ListBox1.AddItem( Screen.Forms[i].Caption, Pointer(Screen.Forms[i]));

uprincipal.pas(182,75) Error: Incompatible type for arg no. 2: Got "Pointer", expected "TObject"

algo le falta o sobra ;)

roman
02-03-2017, 15:44:42
uprincipal.pas(182,75) Error: Incompatible type for arg no. 2: Got "Pointer", expected "TObject"


Le sobra el Pointer.

LineComment Saludos

ecfisa
02-03-2017, 18:40:56
Hola.

uprincipal.pas(182,75) Error: Incompatible type for arg no. 2: Got "Pointer", expected "TObject"

Rehice la prueba en Lazarus y efectivamente no tolera ese moldeo, Delphi (donde hice el ejemplo) no tiene inconveniente.

Para evitar el error en Lazarus podrías cambiar "Pointer" por "TObject" o, como correctamente apunta roman, quitar directamente el moldeo.

Saludos :)

roman
02-03-2017, 19:04:00
Rehice la prueba en Lazarus y efectivamente no tolera ese moldeo, Delphi (donde hice el ejemplo) no tiene inconveniente.


De todas formas, el moldeo es innecesario ya que el segundo parámetro de AddItem está declarado como TObject.

LineComment Saludos