Ver Mensaje Individual
  #6  
Antiguo 03-09-2004
Avatar de roman
roman roman is offline
Moderador
 
Registrado: may 2003
Ubicación: Ciudad de México
Posts: 20.269
Reputación: 10
roman Es un diamante en brutoroman Es un diamante en brutoroman Es un diamante en bruto
Pues tal como lo describes puedes hacerlo prácticamente tal cual con Delphi. De hecho me he perdido un poco en la explicación y ya no sé si lo tienes o no resuelto.

Algunas ideas:

Como creo que ya has observado tendrás que crear componentes (o descendientes de componentes) para poder manejar los mensajes. El punto es que Delphi traduce los mensaje tipo Windows (los que envías con SendMessage) en eventos pero no implementa uno genérico que tú puedas definir durante el diseño.

Según he entendido, tú idea a grandes rasgos es como sigue:

Cita:
Ante determinada acción sobre algún control, éste envía un mensaje al formulario quien a su vez lo transmite a todas las componentes.

Cada componente puede o no implementar código para manejar el mensaje y, de hacerlo, actuará de acuerdo al valor de los parámetros.
El problema aquí es que cada componente tendrá que manejar un doble case:

Código Delphi [-]
case WParam of
  1:
    case LParam of
      1: ... ;
      2: ... ;
      ...
    end;

  2:
    case LParam of
      1: ... ;
      2: ... ;
      ...
    end;

    ...
end;

que para mi gusto hace el código muy ilegible y difícil de mantener aun cuando no todas las componentes implementen todas las etiquetas.

Quiero suponer que posiblemente WParam lo usas para el tipo de acción a realizar (limpiar, cargar, guardar, etc) y LParam afecta el cómo se realiza la acción.

Lo primero entonces es separar las acciones en distintos mensajes:

Código Delphi [-]
const
  CM_CLEAN = WM_USER + 1;
  CM_LOAD = WM_USER + 2;
  CM_SAVE = WM_USER + 3;
  ...

y crear manejadores para cada mensaje:

Código Delphi [-]
procedure CMClean(Msg: TMessage); message CM_CLEAN;
procedure CMLoad(Msg: TMessage); message CM_LOAD;
procedure CMSave(Msg: TMessage); message CM_SAVE;
...

Las componentes que no manejen una acción en particular simplemente no implementan el correspondiente manejador.

Lo siguiente es convertir estos mesajes en eventos:

Código Delphi [-]
type
  TCleanEvent = procedure(params, var Proceed: Boolean);
  TLoadEvent = procedure(params, var Proceed: Boolean);
  SaveEvent =  procedure(params, var Proceed: Boolean);
  ...

¿Por qué en eventos?

Cada componente podría manejar de alguna manera estándar un determinado mensaje pero al convertirlo en evento se da la oportunidad al programador de modificar el comportamiento en tiempo de diseño.

El parámetro Proceed quizá no te sea necesario pero ante algo tan general como lo que planteas yo lo pondría para poder parar el mensaje de ser necesario. La idea es:

Una componente envía uno de estos mensajes al formulario quien normalmente lo pasará a todas sus componentes. El programador puede detener la transmisión del mensaje en el correspondiente evento del formulario poniendo Proceed := false si por alguna circunstancia ve que no conviene transmitirlo. Así mismo, cuando una componente recibe un mensaje, puede determinar no pasarlo a los que le siguen.

Esto es similar, por ejemplo, a cuando intentas cerrar una aplicación MDI como Word. El sistema manda el mensaje WM_CLOSE a la ventana principal quien a su vez lo manda a todos los documentos. Si alguna de éstos tiene cambios sin guardar presenta un mensaje al usuario. Si éste decide cancelar entonces el mensaje WM_CLOSE ya no se sigue difundiendo.

params en las declaraciones de arriba significa cualesquiera parámetros que consideres conveniente para la acción. Podrías usar WParam y LParam pero esto te deja un código poco legible. Considera que un mensaje como WM_KEYDOWN, Delphi lo connvierte a fin de cuentas en el evento OnKeyDown cuyos parámetros son mucho más entendibles.

Ahora concretemos.

Comencemos por el formulario, quien debe encargarse de transmitir los mensajes a todas sus componentes:

Código Delphi [-]
interface

type
  TXForm = class(TForm)
  private
    FOnClean: TCleanEvent;
    FOnLoad: TLoadEvent;
    FOnSave: TSaveEvent;

    procedure CMClean(Msg: TMessage); message CM_CLEAN;
    procedure CMLoad(Msg: TMessage); message CM_LOAD;
    procedure CMSave(Msg: TMessage); message CM_SAVE;

  protected
    procedure BroadcastMessage(Msg: TMessage);

  published
    property OnClean: TCleanEvent read FOnClean write FOnClean;
    property OnLoad: TLoadEvent read FOnLoad write FOnLoad;
    property OnSave: SaveEvent read FOnSave write FOnSave;
  end;

implementation

{
  En cada manejador, el formulario llama al correspondiente evento
  y transmite el mensaje sólo si Proceed es true después de la llamada
}

procedure TXForm.CMClean(Msg: TMessage); message CM_CLEAN;
var
  Proceed: Boolean;

begin
  Proceed := true;

  if Assigned(FOnClean) then FOnClean(params, Proceed);
  if Proceed then BroadcastMessage(Msg);
end;

procedure TXForm.CM_Load(Msg: TMessage); message CM_LOAD;
var
  Proceed: Boolean;

begin
  Proceed := true;

  if Assigned(FOnLoad) then FOnLoad(params, Proceed);
  if Proceed then BroadcastMessage(Msg);
end;

procedure TXForm.CM_SAVE(Msg: TMessage); message CM_SAVE;
var
  Proceed: Boolean;

begin
  Proceed := true;

  if Assigned(FOnSave) then FOnSave(params, Proceed);
  if Proceed then BroadcastMessage(Msg);
end;

{
  BroadcastMessage transmite el mensaje a cada control.
  Si un control devuelve false entonce se detiene la
  transmisión.
}
procedure TXForm.BroadcastMessage(Msg: TMessage);
var
  Proceed: Boolean;
  Control: TControl;
  I: Integer;

begin
  Proceed := true;

  for I := 0 to ComponentCount - 1 do
    if Components[i] is TControl then
    begin
      Control := TControl(Components[i]);
      Proceed := Boolean(Control.Perform(Msg.Msg, Msg.WParam, Msg.LParam));
      if not Proceed then break;
    end;
end;

Finalmente vemos como sería el caso de una componente:

Código Delphi [-]
interface

type
  TXDBGrid = class(TTDBGrid)
  private
    FOnClean: TCleanEvent;
    FOnLoad: TLoadEvent;
    FOnSave: TSaveEvent;

    procedure CMClean(Msg: TMessage); message CM_CLEAN;
    procedure CMLoad(Msg: TMessage); message CM_LOAD;
    procedure CMSave(Msg: TMessage); message CM_SAVE;

  published
    property OnClean: TCleanEvent read FOnClean write FOnClean;
    property OnLoad: TLoadEvent read FOnLoad write FOnLoad;
    property OnSave: TSaveEvent read FOnSave write FOnSave;
  end;

implementation

{
  Cada manejador llama al evento correspondiente. Si luego de la
  llamada Proceed es false asignamos 0 a Msg.Result a fin de que
  la llamada a Perform por parte del formulario devuelva 0 (false)
  y se detenga la transmisión del mensaje a los otros controles.
}
procedure TXDBGrid.CMClean(Msg: TMessage);
var
  Proceed: Boolean;

begin
  Proceed := true;
  if Assigned(FOnClean) then FOnClean(params, Proceed);
  Msg.Result := Integer(Proceed);
end;

...

Desde luego queda la sensación de que estamos repitiendo todo lo que hace la VCL de Delphi con los mensajes de Windows pero bueno, es el resultado de querer algo tan genérico.

// Saludos
Responder Con Cita