Delphi-PRAXiS
Seite 2 von 2     12   

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   GUI-Design mit VCL / FireMonkey / Common Controls (https://www.delphipraxis.net/18-gui-design-mit-vcl-firemonkey-common-controls/)
-   -   Delphi "inherited" für umgeleitetes Event (https://www.delphipraxis.net/88383-inherited-fuer-umgeleitetes-event.html)

Kedariodakon 17. Mär 2007 16:57

Re: "inherited" für umgeleitetes Event
 
Zitat:

Zitat von Guido Eisenbeis
Davon habe ich auch alles verstanden bis auf eins:

In Unit1 wird ein Feld "FButon1EventHandler" angelegt. Bis hierhin ist das klar. Dann wird aber noch ein Property "Buton1EventHandler" angelegt, das lediglich in das Feld hineinschreibt oder daraus ausliest. Wozu dient der indirekte Weg über dieses Property?

Öhm ka, hab ich einfach so gemacht, kann man auch ohne Property machen... Ist also nichts wildes, hatte glaub ich vorher noch eine SetButon1EventHandler um Irgendwas zu testen...



Zitat:

Zitat von Guido Eisenbeis
Die brennende Frage ist nun: Warum funktioniert das? Oder funktioniert es nicht wirklich?

Jain, funktionieren schon aber nicht sauber!, zum einen:

Du erzeugst jedesmal solch eienn Eventhandler:
Delphi-Quellcode:
procedure InitClickEventRetouring(Ctrl: TObject);
begin
  MyEventReceiver := TMyEventReceiver.Create; // <- "Knackpunkt" ! 
  { ... }
end;
Aber du gibst schlussendlich nur einen frei!
Delphi-Quellcode:
finalization
  MyEventReceiver.Free;
Da musst du dir was anderes einfallen lassen, z.b. eine Liste mit Eventhandlern oder ähnliches....


So der nächste wichtige Punkt, du überprüfst nicht ob es vorher überhaupt ein Event gab!
Delphi-Quellcode:
procedure TMyEventReceiver.ClickIntern(Sender: TObject);
begin
  { ... }
  OnClickOrig(Sender);
end;
Das solltest du ändern in:
Delphi-Quellcode:
procedure TMyEventReceiver.ClickIntern(Sender: TObject);
begin
  { ... }
  If Assigned( OnClickOrig ) Then OnClickOrig(Sender);
end;
Sprich du schaust erstmal ob der Methodenzeichner belegt wurde, bevor du ihn ausführst...


Als leztes nur eine kleine Anmerkung:
Delphi-Quellcode:
procedure InitClickEventRetouring(Ctrl: TObject);
begin
  {...}
  TButton(Ctrl).OnClick := MyEventReceiver.ClickIntern;
end;
Hier solltest du eventuell nachschaun, ob Ctrl überhaupt ein Wert hat! Aber viel wichtiger, udem kann da ein Wert übergeben werden, der gar keine OnClick Ereigniss hat, bzw. schon gar nicht ein TButton ist!

Da gehört also sowas hin wie:
Delphi-Quellcode:
procedure InitClickEventRetouring(Ctrl: TObject);
begin
  {...}
  If Assigned( Ctrl ) And ( Ctrl Is TButton ) Then TButton(Ctrl).OnClick := MyEventReceiver.ClickIntern;
end;
das wärs erstmal von meinen Anmerkungen....

Bye Christian

Guido Eisenbeis 18. Mär 2007 01:04

Re: "inherited" für umgeleitetes Event
 
Zitat:

Zitat von Kedariodakon
Öhm ka, hab ich einfach so gemacht, kann man auch ohne Property machen...

Das hab ich mir gedacht, jetzt hab ich Sicherheit! Thank you!

Zitat:

Zitat von Kedariodakon
Zitat:

Zitat von Guido Eisenbeis
Die brennende Frage ist nun: Warum funktioniert das? Oder funktioniert es nicht wirklich?

Jain, funktionieren schon aber nicht sauber!, zum einen:

Du erzeugst jedesmal solch eienn Eventhandler:
...
Aber du gibst schlussendlich nur einen frei!
...

DAS war an dieser Stelle die wichtige Frage! Danke für die Info! Ich hasse es, wenn andere Programmierer Anwendungen schreiben, die zwar irgendwie "funktionieren" aber man merkt am Programmverhalten, dass da was nicht sauber läuft. Da bleiben beim Beenden TrayIcons oder Taskbareinträge zurück oder das Programm bleibt zwischendurch hängen. Deshalb bemühe ich mich in meinen Programmen sauber zu arbeiten!

Zitat:

Zitat von Kedariodakon
Da musst du dir was anderes einfallen lassen, z.b. eine Liste mit Eventhandlern oder ähnliches....

Mit Arrays habe ich schon oft gearbeitet. Diesmal werde ich mir mal TList vornehmen. :-D


Zitat:

Zitat von Kedariodakon
So der nächste wichtige Punkt, du überprüfst nicht ob es vorher überhaupt ein Event gab!
...
Das solltest du ändern in:
Delphi-Quellcode:
procedure TMyEventReceiver.ClickIntern(Sender: TObject);
begin
  { ... }
  If Assigned( OnClickOrig ) Then OnClickOrig(Sender);
end;
Sprich du schaust erstmal ob der Methodenzeichner belegt wurde, bevor du ihn ausführst...

Das hatte ich vorher. :mrgreen: Das ging beim Testen/Probieren verloren.

Das ist ein sehr wichtiger Punkt, denn darum geht es in diesem Thread ja: Es soll ein (Click-)Event abgefangen und zurückgeleitet werden, falls auch was zum Zurückleiten da ist. Denn sonst könnte ich ja einfach im Original-(Click-)Event meinen ausgelagerten Code aufrufen. Der ausgelagete Code soll aber auch ausgeführt werden, wenn im Form garkein (Click-)Event existiert.


Zitat:

Zitat von Kedariodakon
Als leztes nur eine kleine Anmerkung:
Delphi-Quellcode:
procedure InitClickEventRetouring(Ctrl: TObject);
begin
  {...}
  TButton(Ctrl).OnClick := MyEventReceiver.ClickIntern;
end;
Hier solltest du eventuell nachschaun, ob Ctrl überhaupt ein Wert hat! Aber viel wichtiger, udem kann da ein Wert übergeben werden, der gar keine OnClick Ereigniss hat, bzw. schon gar nicht ein TButton ist!

Da gehört also sowas hin wie:
Delphi-Quellcode:
procedure InitClickEventRetouring(Ctrl: TObject);
begin
  {...}
  If Assigned( Ctrl ) And ( Ctrl Is TButton ) Then TButton(Ctrl).OnClick := MyEventReceiver.ClickIntern;
end;
Bye Christian

Dazu habe ich mir auch schon Gedanken gemacht. Das kannst du daran erkennen, dass ich "Ctrl: TObject" verwendet habe. Prinzipiell könnte ich mit den obigen Codes und deiner Abfrage "If Assigned(Ctrl) ..." arbeiten. Aber du weißt ja wie das ist: hat man mal Blut geleckt, will man mehr! Hast du eine Idee, wie ich das OnClick allgemeiner gestalten kann? Also so, dass es für Buttons, Labels, Panels usw. gleichermaßen funktioniert? Ich hatte schon experimentiert, das OnClick selbst anstatt das Control zu übergeben. Das scheiterte jedoch daran, dass es sich nicht als "Var" übergeben lässt und ohne "Var" sich nichts zuweisen lässt.

Ein TypeCasting "(Ctrl as TButton).OnClick := ..." wirft ja bei Fehlern eine Exception während "TButton(Ctrl).OnClick := ..." gutmütig über ein übergebenes Label hinwegsieht. Das war schonmal mein Ansatz, den Code für mehrere Control-Typen benutzen zu können. Ich hätte es aber gerne noch sauberer!

Gruß,
Guido.

Kedariodakon 18. Mär 2007 01:44

Re: "inherited" für umgeleitetes Event
 
Zitat:

Zitat von Guido Eisenbeis
Mit Arrays habe ich schon oft gearbeitet. Diesmal werde ich mir mal TList vornehmen. :-D

Nimm lieber eine TObjectList, die gibt gleich alle Objecte frei, wenn sie freigegeben wird (zumindest standardmäßig...

Zitat:

Zitat von Guido Eisenbeis
Aber du weißt ja wie das ist: hat man mal Blut geleckt, will man mehr! Hast du eine Idee, wie ich das OnClick allgemeiner gestalten kann? Also so, dass es für Buttons, Labels, Panels usw. gleichermaßen funktioniert?



Delphi-Quellcode:
procedure InitClickEventRetouring(Ctrl: TObject);
begin
  {...}
  If Assigned( Ctrl ) And ( Ctrl Is TButton ) Then TButton(Ctrl).OnClick := MyEventReceiver.ClickIntern;
end;
ich schrieb weiter oben schonmal woher das Onlick kommt! Nimm statt TObject ein TControl...

Desweiteren hättest du die möglichkeit explezit nach den Klassen zu schauen ungefär so:

Delphi-Quellcode:
Procedure InitClickEventRetouring( Ctrl: TControl );
Begin
  If         ( Ctrl Is TButton ) Then Begin
    // Mach was mit dem TButton
  End Else If ( Ctrl Is TLabel ) Then Begin
    // Mach was mit dem TLabel
  End Else If ( Ctrl Is TPanel ) Then Begin
    // Mach was mit dem TPanel
  End Else Begin
    // tja is was anderes, mach halt was anderes...
  End;
End;

Bye Christian

Guido Eisenbeis 18. Mär 2007 03:52

Re: "inherited" für umgeleitetes Event
 
Liste der Anhänge anzeigen (Anzahl: 1)
Zitat:

Zitat von Kedariodakon
Nimm lieber eine TObjectList, die gibt gleich alle Objecte frei, wenn sie freigegeben wird (zumindest standardmäßig...

Habe ich auch so gesehen. Hier der neue Code (Unit1 hat sich nicht verändert):

Delphi-Quellcode:
unit Unit2;

interface

procedure InitClickEventRetouring(Ctrl: TObject);

implementation

uses
  Dialogs, Classes, StdCtrls, Contnrs;

type
  TClickEvnt = procedure (Sender: TObject) of object;
  TMyEventReceiver = class
    private
      FClickOrig: TClickEvnt;
      procedure ClickIntern(Sender: TObject);
    public
      property OnClickOrig: TClickEvnt read FClickOrig write FClickOrig;
  end;

var
  EvRecvList: TObjectList;

procedure TMyEventReceiver.ClickIntern(Sender: TObject);
begin
  ShowMessage('Umgeleitetes Event von:  ' + TButton(Sender).Name);
  If (Assigned(OnClickOrig)) Then
    OnClickOrig(Sender);
end;

procedure InitClickEventRetouring(Ctrl: TObject);
var
  MyEventReceiver: TMyEventReceiver;
begin
  try
    MyEventReceiver := TMyEventReceiver.Create;
    MyEventReceiver.OnClickOrig := TButton(Ctrl).OnClick;
    TButton(Ctrl).OnClick := MyEventReceiver.ClickIntern;
    EvRecvList.Add(MyEventReceiver);
  except
    ShowMessage('Error in ClickEventRetouring code');
  end;
end;

initialization
  EvRecvList := TObjectList.Create;

finalization
  EvRecvList.Free;

end.
Zitat:

Zitat von Kedariodakon
Zitat:

Zitat von Guido Eisenbeis
Hast du eine Idee, wie ich das OnClick allgemeiner gestalten kann? Also so, dass es für Buttons, Labels, Panels usw. gleichermaßen funktioniert?

ich schrieb weiter oben schonmal woher das Onlick kommt! Nimm statt TObject ein TControl...

Leider führt TControl sein OnClick nicht nach außen! Es ist protected.

Zitat:

Zitat von Kedariodakon
Desweiteren hättest du die möglichkeit explezit nach den Klassen zu schauen ungefär so:

Delphi-Quellcode:
Procedure InitClickEventRetouring( Ctrl: TControl );
Begin
  If         ( Ctrl Is TButton ) Then Begin
    // Mach was mit dem TButton
  End Else If ( Ctrl Is TLabel ) Then Begin
    // Mach was mit dem TLabel
  End Else If ( Ctrl Is TPanel ) Then Begin
    // Mach was mit dem TPanel
  End Else Begin
    // tja is was anderes, mach halt was anderes...
  End;
End;

Das wäre auch meine "Notlösung".

Gruß,
Guido.

Kedariodakon 18. Mär 2007 11:44

Re: "inherited" für umgeleitetes Event
 
Liste der Anhänge anzeigen (Anzahl: 1)
Zitat:

Zitat von Guido Eisenbeis
Leider führt TControl sein OnClick nicht nach außen! Es ist protected.

Das mag war sein, aber die Speicherstelle von OnClick sollte sich nicht verändern... Heißt du kannst auf TButton oder TPanel casten und kommst zum protected OnClick-Event... (aber das ist unsauber!... :wink:

Zitat:

Zitat von Guido Eisenbeis
Das wäre auch meine "Notlösung".

Nunja, das ist die einzig wahre, um erhlich zu sein...


Desweiteren hast du noch eine unstimmigkeit in deinem Code!
Delphi-Quellcode:
procedure InitClickEventRetouring(Ctrl: TObject);
var
  MyEventReceiver: TMyEventReceiver;
begin
  try
    MyEventReceiver := TMyEventReceiver.Create;
    MyEventReceiver.OnClickOrig := TButton(Ctrl).OnClick;
    TButton(Ctrl).OnClick := MyEventReceiver.ClickIntern;
    EvRecvList.Add(MyEventReceiver);
  except
    ShowMessage('Error in ClickEventRetouring code');
  end;
end;
Du hast zwar einen Try..Except Block, aber das Object steht im Fehlerfall sicher nicht in der Liste und wird schlussendlich nicht freigegeben!
Du solltest es gleich nach dem Erzeugen in die Liste packen....

Eventuell ist es auch sinvoller den ursprünglichen Fehlercode nach außen zu geben!

ich hab das mal alles umgebaut... so sieht meine Lösung aus:
Delphi-Quellcode:
Procedure InitClickEventRetouring( Ctrl: TControl );
Var MyEventReceiver: TMyEventReceiver;
Begin
  If Assigned( Ctrl ) Then Begin //  Wenn kein Object pbergeben wurde können wir uns gleich alles sparen...
    MyEventReceiver := TMyEventReceiver.Create; //  Receiver erzeugen...
    EvRecvList.Add( MyEventReceiver );         //  Receiver gleich in die Liste einfügen, damit es nicht verloren geht...
    Try
      If         ( Ctrl Is TButton ) Then Begin
        //  Es ist ein TButton
        MyEventReceiver.OnClickOrig := TButton( Ctrl ).OnClick;
        TButton( Ctrl ).OnClick    := MyEventReceiver.ClickIntern;
      End Else If ( Ctrl Is TLabel ) Then Begin
        //  Es ist ein TLabel
        MyEventReceiver.OnClickOrig := TLabel( Ctrl ).OnClick;
        TLabel( Ctrl ).OnClick     := MyEventReceiver.ClickIntern;
      End Else If ( Ctrl Is TPanel ) Then Begin
        //  Es ist ein TPanel
        MyEventReceiver.OnClickOrig := TPanel( Ctrl ).OnClick;
        TPanel( Ctrl ).OnClick     := MyEventReceiver.ClickIntern;
      End Else Begin
        //  Es ist ein was anderes, aber da wir mit einem TControl arbeiten, Casten wir einfach auf TButton ;=)
        MyEventReceiver.OnClickOrig := TButton( Ctrl ).OnClick;
        TButton( Ctrl ).OnClick    := MyEventReceiver.ClickIntern;
      End;  
    Except
      On Error: Exception Do Begin //  Jeder Fehler ist eine Exception...
        ShowMessage( 'Error in ClickEventRetouring code: "' + Error.Message + '"' ); //  zusätzlich geben wir den Fehler mit aus!
      End;
    End;
  End;
End;
BTW: Wenn du sowieso am testen bist, es gibt sicher auch eine Möglichkeit über die RTTI...
Du könntest dort in der Tabelle anch dem "OnClick" Property suchen und es dann benutzen, falls vorhanden...
Aber das wird alles nicht super schnell sein...
Ich würd das wenn, nur im letzten Else Block bei mir machen, um wirklich sicher zu sein, dass da was ist...
Ev. macht es dann auch sinn mit einem TObject zu arbeiten...


Bye Christian

Guido Eisenbeis 19. Mär 2007 20:17

Re: "inherited" für umgeleitetes Event
 
Hallo Christian.

Die letzen beiden Tage war ich sehr beschäftigt und bin noch nicht dazu gekommen zu antworten. Sorry dafür.

Zitat:

Zitat von Kedariodakon
Zitat:

Zitat von Guido Eisenbeis
Leider führt TControl sein OnClick nicht nach außen! Es ist protected.

Das mag war sein, aber die Speicherstelle von OnClick sollte sich nicht verändern... Heißt du kannst auf TButton oder TPanel casten und kommst zum protected OnClick-Event... (aber das ist unsauber!... :wink:

Klar soweit. :)

Zitat:

Zitat von Kedariodakon
Zitat:

Zitat von Guido Eisenbeis
Das wäre auch meine "Notlösung".

Nunja, das ist die einzig wahre, um erhlich zu sein...

Sehe ich genauso! Deshalb "Notlösung" auch in Anführungszeichen.

Zitat:

Zitat von Kedariodakon
Desweiteren hast du noch eine unstimmigkeit in deinem Code!
...
Du hast zwar einen Try..Except Block, aber das Object steht im Fehlerfall sicher nicht in der Liste und wird schlussendlich nicht freigegeben!
Du solltest es gleich nach dem Erzeugen in die Liste packen....

Eventuell ist es auch sinvoller den ursprünglichen Fehlercode nach außen zu geben!

ich hab das mal alles umgebaut...

Auch das sehe ich genauso (nachdem du es aufgedeckt hast :-D *Dickes-Lob* ).

Deine an sich schon gute Routine habe ich so übernommen, habe mich aber dazu entschlossen bei einem Fehler die EventReceiver-Instanz garnicht erst in die Liste aufzunhemen.

Delphi-Quellcode:
procedure InitClickEventRetouring(Ctrl: TControl);
var
  MyEventReceiver: TMyEventReceiver;
begin
  if Assigned(Ctrl) then // wurde ein Objekt übergeben ?
  begin
    MyEventReceiver := nil;

    try
      MyEventReceiver := TMyEventReceiver.Create;
      MyEventReceiver.OnClickOrig := TButton(Ctrl).OnClick;
      TButton(Ctrl).OnClick := MyEventReceiver.ClickIntern;
    except

      On Error: Exception Do // Bei allen Fehlern den Speicher
      begin                  // freigeben und Nachricht ausgeben.
        // MyEventReceiver.Free;
        FreeAndNil(MyEventReceiver);
        ShowMessage('Error in ClickEventRetouring code with message:'
                     + #13#10 + #13#10 + '"' + Error.Message + '"');
      end;
    end;

    // nur in Liste einfügen, wenn kein Fehler aufgetreten ist
    if (Assigned(MyEventReceiver)) then
      EvRecvList.Add(MyEventReceiver);
  end;
end;

Zitat:

Zitat von Kedariodakon
BTW: Wenn du sowieso am testen bist, es gibt sicher auch eine Möglichkeit über die RTTI...

Mit der RTTI bin ich noch nicht vertraut. Da müsste ich mich erst einarbeiten. Trotzdem danke für den Hinweis!

Danke für deine Unterstützung!

Gruß,
Guido.

Edit: TObject durch TControl ersetzt und "Free" durch "FreeAndNil" ersetzt.

Kedariodakon 19. Mär 2007 20:47

Re: "inherited" für umgeleitetes Event
 
Autsch :cyclops:

Deine If-Abfrage:
Delphi-Quellcode:
if (Assigned(MyEventReceiver)) then EvRecvList.Add(MyEventReceiver);
Wird bei einem Fehler dein Freigegebenes Objekt trotzdem aufnehmen...

Delphi-Quellcode:
MyEventReceiver.Free;
Zerstört nähmlich nur das Objekt und setzt es nicht Nil, was du aber mit Assigned abfragst...

Nimm stattdesen:
Delphi-Quellcode:
FreeAndNil( MyEventReceiver )
;)

Nunja Zerstören musst du das Object ja eh, ob du das nun im Exvept Teil machst oder am Programmende ;)

Bye Christian

Guido Eisenbeis 19. Mär 2007 23:10

Re: "inherited" für umgeleitetes Event
 
Zitat:

Zitat von Kedariodakon
Nunja Zerstören musst du das Object ja eh, ob du das nun im Exvept Teil machst oder am Programmende ;)

Das stimmt. Iss gehüpft wie gehoppelt! :bounce2:

Zitat:

Zitat von Kedariodakon
Nimm stattdesen:
Delphi-Quellcode:
FreeAndNil( MyEventReceiver )

Hey, cool! :thumb: Das hatte ich übersehen und FreeAndNil war mir noch nicht geläufig. Habe es in meinem Code eingebaut und im Beitrag 16 editiert (mit Hinweis :-) ).

Ciao,
Guido.

Kedariodakon 19. Mär 2007 23:16

Re: "inherited" für umgeleitetes Event
 
So sieht das alles sauber aus. Ich denke so können wir den Code auf die öffentlichkeit loslassen :zwinker:

Ich hoffe du hast ein wenig was gelernt :party:

Bye Christian

Guido Eisenbeis 20. Mär 2007 11:35

Re: "inherited" für umgeleitetes Event
 
Ein kleiner "Schriebfheler" hat sich eingeschlichen: Überall im Code/Posting wo "Retouring" steht, sollte das durch "Rerouting" ersetzt werden. :warn: :-D

@Christian.

Gelernt dabei: einiges.
Dank dafür: vielen! :cheers:

Ciao,
Guido.


Alle Zeitangaben in WEZ +1. Es ist jetzt 08:29 Uhr.
Seite 2 von 2     12   

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz