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 14-09-2019
Avatar de oscarac
[oscarac] oscarac is offline
Miembro Premium
 
Registrado: sep 2006
Ubicación: Lima - Perú
Posts: 2.010
Poder: 20
oscarac Va por buen camino
Funcionalidad (Popup de ventanas abiertas)

buenas tardes

quisiera implementar lo siguiente a ver si me dan una mano


imaginenos un menu

Catalogos.... Operaciones......Reportes....etc... etc..... VENTANAS....
opcion1 opcion1 opcion1
opcion2 opcion2 opcion2
opcion3 opcion3 opcion3


en asunto es que si voy al menu operaciones y escojo la opcion 2 me gustaria que en el menu VENTANAS aparezca una opcion

es decir
cuando se abren muchas ventanas a veces uno se puede perder y el menu ventanas nos ayudara a "activar la ventana" que queramos


me dejo entender?


supongo que hay que crear el popup dinamicamente, la cosa es como "activar" de acuerdo a la opcion escogida en el menu Ventanas
__________________
Dulce Regalo que Satanas manda para mi.....
Responder Con Cita
  #2  
Antiguo 14-09-2019
Avatar de escafandra
[escafandra] escafandra is offline
Miembro Premium
 
Registrado: nov 2007
Posts: 2.197
Poder: 20
escafandra Tiene un aura espectacularescafandra Tiene un aura espectacular
Para añadir Items al menú puedes hacer algo como esto:


Código Delphi [-]
var
  Item: TMenuItem;
begin
  Form2.Show;
  Item:= TMenuItem.Create(MainMenu1);
  Item.Caption:= 'Opcion1';
  Ventanas.Insert(Ventanas.Count, Item);
end;


Saludos.
Responder Con Cita
  #3  
Antiguo 14-09-2019
manelb manelb is offline
Miembro
 
Registrado: mar 2017
Posts: 280
Poder: 8
manelb Va por buen camino
Pues si no recuerdo mal, y he entendido bien lo que pretendes, creo que esto es automático...

En el formulario principal debes asignar dos propiedades:
-La propiedad Menu donde se le asigna el menú principal vinculado a la ventana
-La propiedad WindowMenu que es el punto de menú donde se acumulan las ventanas creadas, y al clicar te devuelve la ventana seleccionada.

Creo recordar que esto solo funciona con ventanas MDI(no estoy seguro)

Saludos
Responder Con Cita
  #4  
Antiguo 16-09-2019
Avatar de escafandra
[escafandra] escafandra is offline
Miembro Premium
 
Registrado: nov 2007
Posts: 2.197
Poder: 20
escafandra Tiene un aura espectacularescafandra Tiene un aura espectacular
Una forma fácil de hacerlo manualmente es mediante el uso de la propiedad tag:


Código Delphi [-]
procedure TForm1.Opcion(Form: TForm);
var
  Item: TMenuItem;
begin
  Form.Show;
  Item:= TMenuItem.Create(MainMenu1);
  Item.Caption:= Form.Caption;
  Item.Tag:= integer(Form);
  Item.OnClick:= VentanaOpcion;
  Form.Tag:= integer(Item);
  Ventanas.Add(Item);
end;


Y en los formularios secundarios colocar esto en el evento OnClose:
Código Delphi [-]
procedure TForm3.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  with Form1.Ventanas do
    Delete(IndexOf(TMenuItem(self.tag)));
  TMenuItem(self.tag).Free;
end;


Automatizar esto requiere algo mas de trabajo pero se puede hacer con un par de clases y realizando sunclassing del formulario principal y de los secundarios.




Saludos.
Responder Con Cita
  #5  
Antiguo 17-09-2019
Avatar de Casimiro Notevi
Casimiro Notevi Casimiro Notevi is offline
Moderador
 
Registrado: sep 2004
Ubicación: En algún lugar.
Posts: 32.040
Poder: 10
Casimiro Notevi Tiene un aura espectacularCasimiro Notevi Tiene un aura espectacular
Cita:
Empezado por escafandra Ver Mensaje
Item.Tag:= integer(Form);
¿integer(Form) qué guarda, un puntero al formulario?
Responder Con Cita
  #6  
Antiguo 17-09-2019
Avatar de escafandra
[escafandra] escafandra is offline
Miembro Premium
 
Registrado: nov 2007
Posts: 2.197
Poder: 20
escafandra Tiene un aura espectacularescafandra Tiene un aura espectacular
Cita:
Empezado por Casimiro Notevi Ver Mensaje
¿integer(Form) qué guarda, un puntero al formulario?
Exacto. El MenuItem guarda un puntero al formulario hijo y éste guarda un puntero a su MenuItem.

Saludos.
Responder Con Cita
  #7  
Antiguo 17-09-2019
Avatar de Casimiro Notevi
Casimiro Notevi Casimiro Notevi is offline
Moderador
 
Registrado: sep 2004
Ubicación: En algún lugar.
Posts: 32.040
Poder: 10
Casimiro Notevi Tiene un aura espectacularCasimiro Notevi Tiene un aura espectacular
Responder Con Cita
  #8  
Antiguo 18-09-2019
Avatar de escafandra
[escafandra] escafandra is offline
Miembro Premium
 
Registrado: nov 2007
Posts: 2.197
Poder: 20
escafandra Tiene un aura espectacularescafandra Tiene un aura espectacular
Explico un poco más el sistema:

La idea más sencilla es hacerlo manualmente, de forma que se cree un MemuItem por cada formulario secundario y que cada Item tenga una forma de conocer su formulario asignado. Al mismo tiempo cada formulario debe conocer su MenuItem para proceder a su destrucción al cerrarse. La manera más sencilla de guardar estas cosas en el tag de cada componente mediante un puntero. De esta forma un OnClicMenuItem traería fácilmente a primer plano el formulario solicitado y si éste se cierra, podría destruir su MenuItem puesto que lo tiene guardado en su Tag.

Código Delphi [-]
// Este procedimiento se ejecutará cada vez que creemos o mostremos un formulario
procedure TForm1.Opcion(Form: TForm);
var
  Item: TMenuItem;
begin
  Form.Show;
  Item:= TMenuItem.Create(MainMenu1);
  Item.Caption:= Form.Caption;
  Item.Tag:= integer(Form);
  Item.OnClick:= VentanaOpcion;
  Form.Tag:= integer(Item);
  Ventanas.Add(Item);
end;

procedure TForm1.Opcion1Click(Sender: TObject);
begin
  Opcion(Form2);
end;

procedure TForm1.Opcion21Click(Sender: TObject);
begin
  Opcion(Form3);
end;

// Este procedimiento traerá al primer plano el formulario solicitado en el menú.
procedure TForm1.VentanaOpcion(Sender: TObject);
begin
  TForm((Sender as TMenuItem).Tag).BringToFront;
end;


En el formulario secundario pondremos el sigueinte codico en el evento OnClose:
Código Delphi [-]
procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  with Form1.Ventanas do
    Delete(IndexOf(TMenuItem(self.tag)));
  TMenuItem(self.tag).Free;
 // Action:= caFree;
end;

Esto es muy sencillo pero para cada nuevo formulario que implementemos debe ser ajustado manualmente. Si queremos automatizar esto un poco más, nos tenemos que complicar un poco con técnicas de sunclassing del formulario principal y de los secundarios para ser capaces de responder a los mensajes WM_COMMAND enviados a los MenuItems del Formulario principal, responder apropiadamente y para conocer cuando se va a cerrar un formulario secundario.

Saludos.
Responder Con Cita
  #9  
Antiguo 18-09-2019
Avatar de escafandra
[escafandra] escafandra is offline
Miembro Premium
 
Registrado: nov 2007
Posts: 2.197
Poder: 20
escafandra Tiene un aura espectacularescafandra Tiene un aura espectacular
Automatizando las cosas

Veamos la funcionalidad que buscamos:
1.- Añadir elementos al menú de forma automática al añadir formularios hijos nuevos.
2.- Respuesta automática al seleccionar una opción de menú, colocando su formulario en primer plano.
3.- Eliminación automática del menú, al eliminar un formulario hijo

Con este planteamiento necesitamos controlar los mensajes que recibe el formulario principal para detectar el Mensaje WM_COMMAND que se envía al seleccionar una opción TMenuItem y hacer lo mismo en los hijos, esta vez para detectar WM_CLOSE. ¿Como hacemos esto?

Esto requiere hacer SubClassing del formulario principal y de los hijos. Básicamente un SubClassing realiza un cambio en la función de tratamiento de mensajes Windows de una ventana para cambiar su funcionalidad. Hacer un SubClassing en delphi es muy sencillo pues los TWinControl disponen de una función privada y virtual denominada WndProc que equivale a la función de tratamiento de mensajes Windows a nivel API, es decir, maneja mensajes del tipo WM_PAINT y no eventos como OnPaint. A su vez y para facilitar el SubClassing a alto nivel, los TWinColtrol disponen de un puntero público a un procedimiento denominado WindowProc que apunta a WndProc. Si hacemos que WindowProc apunte a nuestra función de tratamiento de mensajes, ya hemos hecho el SubClassing, con una simple asignación.

Vamos a comenzar por los formularios hijos. Generalmente un SubClassing no pretende reescribir toda la función de tratamiento de mensajes sino añadir o cambiar alguna cosa y aprovechar la función original de esa ventana (WndProc) para que haga el resto del trabajo. Es por ello que para cada formulario deberemos conocer su WndProc original y escribir un procedimiento específico para su SubClassing. Esto lo vamos a hacer con una clase de la que crearemos una instancia para cada formulario hijo.

Dado queremos que los formularios hijos informen al formulario principal cuando se van a cerrar, lo primero que vamos a hacer es escribir una clase para realizar el SubClassing. Crearemos un objeto para cada formulario hijo dotándole de una nueva función de tratamiento de mensajes que será un procedimiento que llamaremos SubClassWndProc. Éste va a añadir funcionalidad al formulario y cederá el control a su WndProc original. Necesitamos tantas versiones de SubClassWndProc como formularios hijos y es por ello que lo encapsulamos en una clase de la que crearemos las instancias que nos hagan falta.
Código Delphi [-]
  TMForm = class(TForm);    // Clase interpuesta

  TSubclasWindow = class
  private
    Form: TMForm;
    procedure SubClassWndProc(var Message: TMessage);
  public
    MainForm: TForm;
    constructor Create(AForm: TMForm);
    destructor Destroy; override;  
  end;

implementation

constructor TSubclasWindow.Create(AForm: TMForm);
begin
  MainForm:= nil;
  Form:= TMForm(AForm);
  Form.WindowProc:= SubClassWndProc;    // Realizamos el SubClassing
end;

destructor TSubclasWindow.Destroy;
begin
  Form.WindowProc:= Form.WndProc;    // Deshacemos el SubClassing
  inherited Destroy;
end;

procedure TSubclasWindow.SubClassWndProc(var Message: TMessage);
begin
  if Message.Msg = WM_CLOSE then
  begin
    if MainForm <> nil then
      PostMessage(MainForm.Handle, WM_MYCLOSE, WPARAM(Form), 0);
  end;
  Form.WndProc(Message);
end;

Como vemos, esto nos permite tratar WM_CLOSE y enviar al Formulario principal un mensaje personalizado WM_MYCLOSE que informa en WParam cual formulario hijo se va a cerrar. Luego cede el control al WndProc original que al ser privado, lo trampeamos con una clase interpuesta (TMForm). Como necesitamos una función SubClassWndProc para cada formulario hijo, crearemos una instancia para cada uno y las controlaremos en una lista TList.

El SubClassing del formulario principal que es único, lo haremos sobre la clase o componente núcleo de nuestro sistema, y se dedicará a mnejar los mensajes W_COMMAND enviados desde el menú. También dará respuesta a nuestro mensaje personalizado WM_MYCLOSE, enviado desde un formulario hijo que se cierra. Esta sería la forma de tratar esos mensajes:


Código Delphi [-]
procedure TMultiWindowMenuControl.SubClassWndProc(var Message: TMessage);
var
  Pos: integer;
begin
  if Message.Msg = WM_MYCLOSE then
    Delete(TForm(Message.WParam))    // Borra de la lista el formulario que se cierra
  else if Message.Msg = WM_COMMAND then
  begin
    Pos:= GetMenuItenPos(LOWORD(Message.WParam));   // Encuentro el ItemMenu que es seleccionado por su ID en WParam
    if Pos >= 0 then
      TSubclasWindow(List.Items[Pos]).Form.BringToFront;    // Pongo el formulario en primer plano
  end;
  Form.WndProc(Message);        // Permitimos respuesta al resto de mensajes con la funcion original 
end;

La funcionalidad completa que tendremos en nuestra clase TMultiWindowMenuControl será así:.
1.- Un cosntructor que capture el Formulario principal, prepare su SubClassing y cree un TList para almacenar objetos TSubclasWindow.
2.- Un destructor que también deshaga los SubClassing.
3.- Asignar el MenuItem principal del que colgaremos los MenuItem para los formularios hijos
4.- Un procedimiento Add para añadir un formulario hijo
5.- Un procedimiento Delete para eliminar un formulario hijo
6.- Un procedimiento Clear que elimine el control de todos los formularios hijos
7.- Una forma de encontrar el MenuItem señalado por el ID que proporciona WM_COMMAND y que llamará GetMenuItenPos
8.- Una respuesta al mensaje WM_MYCLOSE que libere la opcion de menu correspondiente.

El código completo de la clase:
Código Delphi [-]
type
  TMultiWindowMenuControl = class(TComponent)
  private
    Form: TMForm;
    FMenuItem: TMenuItem;
    List: TList;
    procedure SubClassWndProc(var Message: TMessage);
    function  GetMenuItenPos(MenuID: Cardinal): Cardinal;
  public
    procedure Add(NewForm: TForm);
    procedure Delete(AForm: TForm);
    procedure Clear;
    procedure SetMenuItem(NewMenuItem: TMenuItem);
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  published
    property MenuItem: TMenuItem write SetMenuItem;
  end;

implementation

constructor TMultiWindowMenuControl.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FMenuItem:= nil;
  List:= TList.Create;
  Form:= TMForm(AOwner);
  Form.WindowProc:= SubClassWndProc;
end;

destructor TMultiWindowMenuControl.Destroy;
begin
  Form.WindowProc:= Form.WndProc;
  Clear;
  List.Free;
  inherited Destroy;
end;

procedure TMultiWindowMenuControl.Clear;
var
  Item: TMenuItem;
begin
  While List.Count > 0 do
  begin
    TSubclasWindow(List.Items[0]).Free;
    List.Delete(0);
    Item:= FMenuItem.Items[0];
    FMenuItem.Delete(0);
    Item.Free;
  end;
end;

procedure TMultiWindowMenuControl.SetMenuItem(NewMenuItem: TMenuItem);
var
  Item: TMenuItem;
begin
  // Si FMenuItem tenía Items, los traspaso al nuevo MenuItem
  while (FMenuItem <> nil) and (FMenuItem.Count > 0) do
  begin
    Item:= FMenuItem.Items[0];
    FMenuItem.Delete(0);
    NewMenuItem.Add(Item);
  end;
  FMenuItem:= NewMenuItem;
end;

procedure TMultiWindowMenuControl.Add(NewForm: TForm);
var
  i: integer;
  SubClass: TSubclasWindow;
  Item: TMenuItem;
begin
  if FMenuItem <> nil then
  begin
    // Comprobando si existe
    for i:=0 to List.Count-1 do
      if TSubclasWindow(List.Items[i]).Form = NewForm then break;
    if (i < List.Count) and (List.Count > 0) then exit;

    SubClass:= TSubclasWindow.Create(TMForm(NewForm));
    SubClass.MainForm:= Form;
    List.Add(SubClass);
    Item:= TMenuItem.Create(FMenuItem);
    Item.Caption:= NewForm.Caption;
    FMenuItem.Add(Item);
  end;
end;

procedure TMultiWindowMenuControl.Delete(AForm: TForm);
var
  i: integer;
  Item: TMenuItem;
begin
  //Buscando para borrar
  i:= 0;
  While (i < List.Count) and (TSubclasWindow(List.Items[i]).Form <> AForm) do inc(i);
  if i < List.Count then
  begin
    TSubclasWindow(List.Items[i]).Free;
    List.Delete(i);
    Item:= FMenuItem.Items[i];
    FMenuItem.Delete(i);
    Item.Free;
  end;
end;

procedure TMultiWindowMenuControl.SubClassWndProc(var Message: TMessage);
var
  Pos: integer;
begin
  if Message.Msg = WM_MYCLOSE then
    Delete(TForm(Message.WParam))
  else if Message.Msg = WM_COMMAND then
  begin
    Pos:= GetMenuItenPos(LOWORD(Message.WParam));
    if Pos >= 0 then
      TSubclasWindow(List.Items[Pos]).Form.BringToFront;
  end;
  Form.WndProc(Message);
end;

// Encuentra la posición del Item mediante su MenuID
// devuelve -1 si no aparece
function TMultiWindowMenuControl.GetMenuItenPos(MenuID: Cardinal): Cardinal;
var
  Count: Cardinal;
begin
  Result:= Cardinal(-1);
  if FMenuItem <> nil then
  begin
    Count:= GetMenuItemCount(FMenuItem.Handle);
    if (GetMenuItemID(FMenuItem.Handle, 0) <> Cardinal(-1)) and (Count > 0) then
    begin
      Result:= 0;
      while (Result < Count) and (MenuID <> GetMenuItemID(FMenuItem.Handle, Result)) do
        inc(Result);
    end;
    if Result >= Count then Result:= Cardinal(-1);
  end;  
end;

Ambas clases se colocarán un una sola unit y la funcionalidad será tan simple como tener un formulario principal con un menú que nos permita crear formularios hijos y con un MenuItem del que se colgarán los formularios hijos de forma automática. Además, tendrá un objeto de la clase TMultiWindowMenuControl.



Código Delphi [-]
// Constructor del formulario principal
procedure TForm1.FormCreate(Sender: TObject);
begin
  MultiWindowMenuControl:= TMultiWindowMenuControl.Create(self);
  MultiWindowMenuControl.MenuItem:= Ventanas;
end;

//Opciones de menú para crear formularios hijos
procedure TForm1.Opcion1Click(Sender: TObject);
begin
  MultiWindowMenuControl.Add(Form2);
  Form2.Show;
end;

procedure TForm1.Opcion2Click(Sender: TObject);
begin
  MultiWindowMenuControl.Add(Form3);
  Form3.Show;
end;
//..............

No hay que hacer nada más para tener el control de los formularios hijos de forma automática en un menú.


Todo el código lo he probado en delphi 7 y Berlin.

En este enlace teneis el código y un ejemplo.

Saludos.

Última edición por escafandra fecha: 18-09-2019 a las 23:32:28.
Responder Con Cita
  #10  
Antiguo 20-09-2019
Avatar de escafandra
[escafandra] escafandra is offline
Miembro Premium
 
Registrado: nov 2007
Posts: 2.197
Poder: 20
escafandra Tiene un aura espectacularescafandra Tiene un aura espectacular
Tras descubrir un bug, me veo obligado a publicar su corrección. El método SetMenuItem queda como sigue:



Código Delphi [-]
procedure TMultiWindowMenuControl.SetMenuItem(NewMenuItem: TMenuItem);
var
  Item: TMenuItem;
begin
  // Si FMenuItem tenía Items, los traspaso al nuevo MenuItem
  if FMenuItem = NewMenuItem then exit;
  if NewMenuItem <> nil then
  begin
    while (FMenuItem <> nil) and (FMenuItem.Count > 0) do
    begin
      Item:= FMenuItem.Items[0];
      FMenuItem.Delete(0);
      NewMenuItem.Add(Item);
    end;
  end
  else
    Clear;
  FMenuItem:= NewMenuItem;
end;


El código completo corregido se encuentra aquí: MultiWindowMenuControl_01.zip.




Saludos.

Última edición por escafandra fecha: 20-09-2019 a las 00:57:27.
Responder Con Cita
  #11  
Antiguo 20-09-2019
Avatar de Casimiro Notevi
Casimiro Notevi Casimiro Notevi is offline
Moderador
 
Registrado: sep 2004
Ubicación: En algún lugar.
Posts: 32.040
Poder: 10
Casimiro Notevi Tiene un aura espectacularCasimiro Notevi Tiene un aura espectacular
¡¡¡Gracias!!!
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

Temas Similares
Tema Autor Foro Respuestas Último mensaje
Mdi no repetir ventanas abiertas José Luis Garcí Varios 15 05-03-2009 08:02:05
Barra de ventanas abiertas Vlady OOP 3 10-02-2009 10:40:12
For a todas las ventanas abiertas. rauros Varios 1 03-08-2008 22:07:59
Cantidad de VEntanas abiertas Paradiso Varios 1 22-06-2006 02:42:51
Ventanas abiertas Isaac Varios 3 11-02-2004 16:44:37


La franja horaria es GMT +2. Ahora son las 10:28:55.


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