Posts Tagged ‘TApplicationEvents’

Ein globales OnClick() in Delphi

Donnerstag, September 2nd, 2010

Gestern abend konnte ich eines von Murpyhs Gesetzen verifizieren, es lautet:  „In jedem kleinen Problem, steckt ein Großes, das gerne heraus möchte.“ Eigentlich wollte ich nur eine kleine Verbesserung an der Slideshow-Funktionalität vornehmen, aber dann entdeckte ich das große Problem, welches mich den ganzen Abend beschäftigte.

Folgendes Verhalten wollte ich bei der Slideshow umsetzen:

  • die Slideshow soll über den Button in der Toolleiste aktivierbar sein
  • ein zweiter Klick auf den Button soll sie wieder deaktivieren
  • und ein Klick auf irgendein anderes Steuerelement soll die Slideshow ebenfalls deaktivieren

Die ersten beiden Punkte sind einfach umzusetzen. Das Problem liegt in dem dritten Punkt. Im alten BVASystem hatte ich die Funktionalität dadurch erreicht, indem ich bei jedem OnClick() Event ein:

procedure TfrmMain.Toolbutton1Click(Sender: TObject);
begin
 if (Slideshow.Aktiv = true) then Slideshow.Aktiv := false;
 ...
end;

eingebaut habe. Das wollte ich dieses mal aber um jeden Preis vermeiden, da die Slideshow sich im neuen Programm im Hauptdialog befindet. Dort wären weit mehr Funktionen zu erweitern, als es im alten Programm in der Vollbildansicht der Fall war.

Gelöst habe ich das Problem schlussendlich mit einem globalen OnClick Ereignis.  In der Komponetensammlung von Delphi befindet sich eine Klasse TApplicationEvents, die einen Eventhandler für OnMessage() besitzt.  Dieses Event wird vor jeder Windowsbotschaft, die die Anwendung empfängt, geworfen. Damit ist es relativ leicht möglich, hier die Botschaft „WM_LBUTTONUP“ abzufangen, die Slideshow gegebenenfalls zu deaktivieren und anschließend die Botschaft an das dementsprechende Steuerelement weiterzuleiten.

Aber Vorsicht Falle. Für den Button, der die Slideshow eh deaktivieren soll, darf entweder die Slideshow nicht im OnMessage() gestoppt werden, oder aber das Event darf den Button nie erreichen. Ich entschied mich für die zweitere Variante:

procedure TfrmMain.ApplicationEventsMessage(var Msg: tagMSG; var Handled: Boolean);
var p:TPoint;
    wc:TWinControl;
    ctrl:TControl;
begin
 case Msg.message of
  WM_LBUTTONUP:begin
   //Aktuelle Mausposition bestimmen
   P.X := Mouse.CursorPos.X;
   P.Y := Mouse.CursorPos.Y;
   //TWinControl bestimmen welches sich an der Mausposition befindet
   wc := FindVCLWindow(P);
   if Assigned(wc) then begin
    //TControl bestimmen welches sich an der Mausposition befinden
    ctrl := wc.ControlAtPos(wc.ScreenToClient((P)), false, false);
    if Assigned(ctrl) then begin
     if ctrl.Name = 'btnSlideshow' then Handled := true;
    end;
   end;
   If SlideShow.Aktiv then SlideShow.Aktiv := false;
  end;
 end;
end;

Ich bestimme also zuerst die Position der Maus auf dem Desktop. Anschließend kann ich mit „FindVCLWindow“ das TWinControl bestimmen, welches sich an der MausPosition befindet. Da die Buttons aber nicht fensterorientierte Steuerelemente sind, werden sie von der Funktion nicht entdeckt. Daher ist als zweiter Schritt ControlAtPos() notwendig. Anschließend muss nur noch überprüft werden, ob das Control der Slideshowbutton ist. Ist er es, wird das Event durch „Handled := true“ aus der Windowsbotschaftenliste gelöscht.

Am Ende muss dann nur noch die Slideshow deaktiviert werden, um das von mir gewünschte Verhalten umzusetzen.