Delphi-PRAXiS

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)

Guido Eisenbeis 14. Mär 2007 23:38


"inherited" für umgeleitetes Event
 
Beispiel für ein umgeleitetes Event:

Delphi-Quellcode:
unit Unit1; // Enthält einen Button, dessen Click-Event
            // und das FormCreate-Event.
...

implementation

{$R *.dfm}

uses
  Unit2;


procedure TForm1.Button1Click(Sender: TObject);
begin
  ShowMessage('Ich bin im Original-Event.');
end;


procedure TForm1.FormCreate(Sender: TObject);
begin
  Button1.OnClick := Unit2.MyEventReceiver.MyOwnButton1Click;
end;


end.
Delphi-Quellcode:
unit Unit2;

interface

type
  TMyEventReceiver = class
    procedure MyOwnButton1Click(Sender: TObject);
  end;

var
  MyEventReceiver: TMyEventReceiver;


implementation

uses
  Dialogs;


procedure TMyEventReceiver.MyOwnButton1Click(Sender: TObject);
begin
  ShowMessage('Ich bin im umgeleiteten Event.');
end;


end.
Wie kann man nun zusätzlich zu dem umgleitete Event das Original-Event auslösen?

Also quasi wie ein "inherited" bei überschriebenen Klassen-Methoden. Und zwar so, dass kein Fehler ausgelöst wird, wenn es das Original-Event nicht gibt.

Guido.

Kedariodakon 15. Mär 2007 00:24

Re: "inherited" für umgeleitetes Event
 
Nunja, falls da nochmehr OnClick-Events dazu kommen sollten, heißt das Unwort des Jahres "Observer"...
Sprich, leite den TButton ab und versehe diesen dann mit "Observer" Eigenschaften, am Besten natürlich mit hilfe von Interfaces (geht aber auch ohne)...
Als Ergebniss hast du dann einen Button der soviele OnClick-Events hat wie du magst...

Alles andere wär auch nur wildes rumgeflicke...
Beispiel wär, dass du die Klasse "TMyEventReceiver" mit einer Methode versiehst, die das OnClick-Event bespielt und sich den alten Wert merkt und ihn entsprechend ausführt...

Da mal so ein Flickwerk:

Delphi-Quellcode:
Unit Unit2;

Interface

Uses StdCtrls, Classes;

Type
  TMyEventReceiver = Class
  Private
    { Private-Deklaration }
    FOwnerButton: TButton; //  Von wo kommt das Event?
    FOldOnClick: TNotifyEvent; // Speichern des alten Events
    Procedure SetOwnerButton( Const Value: TButton );
  Protected
    { Protected-Deklaration }
    Procedure InternOnClick( Sender: TObject );
    Property OwnerButton: TButton     Read FOwnerButton Write SetOwnerButton;
    Property OldOnClick: TNotifyEvent Read FOldOnClick Write FOldOnClick;
    { ... }
    Procedure MyOwnButton1Click( Sender: TObject );
  Public                    
    { Public-Deklaration }
    Destructor Destroy; Override;
    Procedure SetButtonOnClick( Const aButton: TButton );
    Procedure Reset;
  End;

Var MyEventReceiver: TMyEventReceiver;

Implementation

Uses Dialogs;

Destructor TMyEventReceiver.Destroy;
Begin
  Reset; //  Vor dem freigeben reseten...
  Inherited;
End;

Procedure TMyEventReceiver.SetButtonOnClick( Const aButton: TButton );
Begin
  OwnerButton := aButton; //  initialisieren mit dem neuen Button...
End;

Procedure TMyEventReceiver.Reset;
Begin
  If Assigned( OwnerButton ) Then Begin //  Wenn schonmal initialisiert, dann zurücksetzen
    OwnerButton.OnClick := OldOnClick; //  Alter Click
    FOwnerButton       := Nil;        //  Owner nil'en
  End;
  OldOnClick := Nil;                  //  OldOnClick Speicher nil'en
End;

Procedure TMyEventReceiver.SetOwnerButton( Const Value: TButton );
Begin
  Reset; //  Vor einem neubespielen reseten...
  If Assigned( Value ) Then Begin //  Werte setzen nur, wenn auch was übergeben wurde...
    FOwnerButton := Value;
    OldOnClick   := Value.OnClick;
    Value.OnClick := InternOnClick;
  End;
End;

Procedure TMyEventReceiver.InternOnClick( Sender: TObject );
Begin
  If Assigned( OldOnClick ) Then OldOnClick( Sender ); //  Altes Event auslösen, falls es belegt war
  MyOwnButton1Click( Sender ); //  Deine Funktion...
End;

Procedure TMyEventReceiver.MyOwnButton1Click( Sender: TObject );
Begin
  ShowMessage( 'Ich bin im umgeleiteten Event.' );
End;

end.
Der Aufruf in Unit1 sieht das so aus:
Delphi-Quellcode:
unit Unit1; // Enthält einen Button, dessen Click-Event
            // und das FormCreate-Event.
...

implementation

{$R *.dfm} 

uses
  Unit2;


procedure TForm1.Button1Click(Sender: TObject);
begin
  ShowMessage('Ich bin im Original-Event.');
end;


procedure TForm1.FormCreate(Sender: TObject);
begin
  Unit2.MyEventReceiver.SetButtonOnClick( Button1 );
end;
end.
Hoffe es funktioniert (ist ungetestet...)...

Bye Christian

Guido Eisenbeis 15. Mär 2007 01:48

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

Also ich sehe das (bis jetzt noch) nicht als Flickwerk! :-D

Ich habe es aber auch nur überflogen.

Zum Thema Observer hat Google leider "keinen" Treffer erzielt. *scherz*

Ok, jetzt im Ernst: Bei meiner Recherche nach dem Thema zeigte Freund Goolge zu diesem Stichwort 59.800.000 Treffer, die von einer englischen Zeitung bis zum Space-Observer reichten. Selbst nach Eingrenzung mit "Delphi Observer" zeigte er immer noch 524.000 Treffer.

Zum Glück stach da ein Beitrag der DP hervor, mit dem Titel "Events in einer Liste" bzw. "Ergänzung zu Events in einer Liste". Ich gehe mal davon aus, dass du das gemeint hast.

Jetzt gehe ich erstmal offline, mache heia-heia :xmas: und sehe mir beide Möglichkeiten morgen an. Danach melde ich mich wieder.

Bis hierhin schon mal danke!

Gruß,
Guido.

Kedariodakon 15. Mär 2007 08:26

Re: "inherited" für umgeleitetes Event
 
Klick mich! FEST!!! :drunken:

bye Christian

Guido Eisenbeis 16. Mär 2007 02:50

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

Heute arbeite ich schon den ganzen Tag an dieser Sache. Und schon die halbe Nacht. Ich habe alle Beiträge durchglesen, die du geschrieben und auf die du verlinkt hast. Ebenso habe ich alle Codes aus diesen Sites (inklusive dem "Flickwerk") in jeweils ein eigenes Testprojekt implementiert und versucht zum Laufen zu bringen. Leider ohne Erfolg.

Zitat:

Zitat von Kedariodakon
Hoffe es funktioniert (ist ungetestet...)...

Respekt, falls das bedeutet, dass du das aus dem Kopf heraus (also ohne IDE) geschrieben hast!

Auch diesen Code habe ich nicht zum Laufen bekommen. Ich habe auch eine Code für Multicast angeguckt, aber das ist nicht das Geeignete für mein Problem.

Als Test habe ich das Beispiel ganz oben genommen, wo ein ButtonClick von Form1 in Unit2 umgeleitet wird. Derzeit ist es ein wenig frustrierend, nach all den Stunden Arbeit daran. :pale:

Im Moment würde ich mich schon darüber freuen, wenn du mir ein funktionierendes/getestetes "Flickwerk" posten würdest!

Edit: Natürlich sind auch Lösungen von alle anderen willkommen!

Gruß,
Guido.

Muetze1 16. Mär 2007 08:18

Re: "inherited" für umgeleitetes Event
 
Merke dir einfach nur einen Original vorhandenen Eventhandler, welcher im OnClick stand, bevor du deinen zuweist. In deinem kannst du dann den Originalhandler aufrufen.

Delphi-Quellcode:
Type
  TFormClass = Class(TForm)
  ...
  Private
    lOldEventHandler: TNotifyEvent;
  ...
  End;

...

Procedure TFormClass.FormCreate(Sender: TObject)
Begin
  lOldEventHandler := Button1.OnClick;
  Button1.OnClick := MyNewHandler;
End;

Procedure TFormClass.MyNewHandler(Sender: TObject)
Begin
  // my code

  // call old handler (if found)
  If Assigned(lOldEventHandler) Then
    lOldEventHandler(Sender);
End;

Kedariodakon 16. Mär 2007 08:50

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

Zitat von Guido Eisenbeis
Respekt, falls das bedeutet, dass du das aus dem Kopf heraus (also ohne IDE) geschrieben hast!

Danke, aber da ist eigendlich nichts dabei...

Zitat:

Zitat von Guido Eisenbeis
Im Moment würde ich mich schon darüber freuen, wenn du mir ein funktionierendes/getestetes "Flickwerk" posten würdest!

ich hab den obrigen Code genau so übernommen und eigendlich funktioniert es...


Nun und "Muetze1"s Codeschnippsel macht genau das selbe, nur eben in der Form :wink:


Bye Christian

Guido Eisenbeis 16. Mär 2007 19:01

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

Ein (funktionierendes) Demo sagt mehr als tausend Worte! :thumb:

Nachdem ich die 'dof'-Datei gelöscht hatte, lieft das Demo dann auch! Danach habe ich (kurz) den Code aus dem obigen Download mit dem verglichen, den ich aus deinem Posting in ein eigenes Testprojekt gesetzt habe.

Nun ja, warum mein Testprojekt nicht funktionieren konnte, ist mir nun klar:

Delphi-Quellcode:
unit Unit1; // Enthält einen Button, dessen Click-Event
            // und das FormCreate-Event.

... // <- KEIN Eventhandler.

implementation

{$R *.dfm}

uses
  Unit2;

procedure TForm1.Button1Click(Sender: TObject);
begin
  ShowMessage('Ich bin im Original-Event.');
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Unit2.MyEventReceiver.SetButtonOnClick( Button1 );
end;

end.
Den Code aus deinem Posting habe ich soweit verwendet, wie er zu ersehen war. Natürlich hatte ich keinen EventHandler in Unit1 deklariert/initalisiert! Der ging in den drei Punkten unter (siehe oben).

Ich habe mittlerweile eine andere Lösung erarbeitet. Nun werde ich diese andere Lösung noch überarbeiten/durchchecken. Danach werde ich deinen/Muetze1's Code durcharbeiten, damit ich verstehe wie da was gemacht wird.

Danach melde ich mich mit den Ergebnissen.

Grüße,
Guido.

Kedariodakon 16. Mär 2007 23:29

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


Nunja, wir haben nichts wildes gemacht...

Wie du mitbekommen hast, wird deine OnClick Methode im OnClick (vom Typ TNotifyEvent) im TButton abgelegt...

Deklariert wurde sie aber schon in TControl im Protected Bereich, außerhalb sichtbar wird sie erst im TButton in dem OnClick Published wird...

Delphi-Quellcode:
Type
  TControl = class(TComponent)
  { ... }
  protected
    property OnClick: TNotifyEvent read FOnClick write FOnClick stored IsOnClickStored;
  { ... }
  end;

{ ... }

Type
  TWinControl = class(TControl)
  { ... }
  end;

{ ... }

Type
  TButtonControl = class(TWinControl)
  { ... }
  end;

{ ... }

Type
  TButton = class(TButtonControl)
  { ... }
  published
    property OnClick;
  { ... }
  end;
So nun ist natürlich noch viel interessanter, was TNotifyEvent nun eigendlich ist!
Delphi-Quellcode:
TNotifyEvent = procedure(Sender: TObject) of object;
Da sieht man, dass TNotifyEvent ein Methodenzeiger ist, sprich der Inhalt soll zu solch einer Methode verweisen...


So, also was haben wir gemacht?
Muetze1 und ich haben also nichts anderes gemacht als uns eine Variable anzulegen, natürlich vom Typ TNotifyEvent und haben in diesem die OnClick Methode des TButtons gespeichert...
Anschliessend haben wir das OnClick des TButtons mit userer Methode überschrieben, ihm sozusagen einen neuen Methodenzeiger verpasst...

So, in unserer neuen Funktion haben wir sozusagen unseren Code ausgeführt und anschließend bzw. davor versucht die alte gespeicherte Methode aufgerufen, die wir gesichert haben...

Da ja ev. keine vorhanden gewesen sein könnte überprüfen wir natürlich mit Assigned( "TButtonOnClickSaveVariable" ) ob der Inhalt der gespeicherten alten OnClickMethode (bzw. den Zeiger dorthin) nicht NIL ist... Ist er nicht NIL rufen wir die Methode auf.

Das war es schon, keine Hexerei, kein Hockuspokus...
Ich hoffe die Erklärung konnte dir helfen... :zwinker:

Bye Christian

Guido Eisenbeis 17. Mär 2007 15:58

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

Du hast scheinbar ein gutes Händchen dafür, wann ich welche Infos brauche! ;-)
Die haben geholfen!

Nun zum neuesten Stand:

Mittlerweile habe ich meinen eigenen Code überarbeitet und fertiggestellt und deinen Code verstanden. Die Bearbeitung meines Codes habe ich durchgeführt, bevor ich deinen Code studiert habe. Ich suchte zum einen nach einer einfacheren Lösung, zum anderen wollte ich verstehen was gemacht wird. Der Hammer ist nun, dass mein Code vom Prinzip her fast mit deinem Code identisch ist!
:party:

Wie gesagt, danach habe ich deinen/euren Code studiert. 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?


Zu meinem Code:

Er hat nicht die Komfort-Features deines Codes, wie zum Beispiel "Reset". Dafür ist er einfacher gehalten. Und so sieht er aus:

Delphi-Quellcode:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Label1: TLabel;
    procedure Label1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

uses
  Unit2;

procedure TForm1.FormCreate(Sender: TObject);
begin
  InitClickEventRetouring(Label1);
  InitClickEventRetouring(Button1);
end;

procedure TForm1.Label1Click(Sender: TObject);
begin
  ShowMessage('Original-Event von (Label): ' + TLabel(Sender).Name);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  ShowMessage('Original-Event von (Button): ' + TButton(Sender).Name);
end;

end.
Delphi-Quellcode:
unit Unit2;

interface

uses
  StdCtrls;

procedure InitClickEventRetouring(Ctrl: TObject);

implementation

uses
  Dialogs, Classes;

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
  MyEventReceiver: TMyEventReceiver;

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

procedure InitClickEventRetouring(Ctrl: TObject);
begin
  MyEventReceiver := TMyEventReceiver.Create; // <- "Knackpunkt" !
  MyEventReceiver.OnClickOrig := TButton(Ctrl).OnClick;

  TButton(Ctrl).OnClick := MyEventReceiver.ClickIntern;
end;

initialization

finalization
  MyEventReceiver.Free;

end.
Als ich den Code erstellte, wollte ich ihn so anlegen, dass mehrere (verschiedene) Controls benutzt werden können. Wie man sehen kann, sind auch zwei Init-Aufrufe im FormCreate der Unit1. Einer für Label1 und einer für Button1. Zu diesem Zweck hatte ich vor, ein Array oder eine Liste mit MyEventReceiver-Instanzen zu erstellen.

Während des Programmierens kompiliere ich das Projekt bei bestimmten Gelegenheiten, um Fehler zu ermitteln die der Compiler erkennt. Als ich den obigen Code kompilierte, hatte ich eigentlich erwartet, dass entweder der Compiler an der mit "Knackpunkt" markierten Stelle einen Fehler wirft, oder dass der zweite Init-Aufruf mit der Create-Zuweisung den Wert in "MyEventReceiver" überschreibt. Dem war jedoch nicht so! Alles funktionierte einwandfrei, das jeweilige ClickInner wurde ausgelöst und das richtige Original-OnClick aufgerufen!

Die brennende Frage ist nun: Warum funktioniert das? Oder funktioniert es nicht wirklich?

Beim ersten Create wird Speicher reserviert. Wird vielleicht beim zweiten Create der neu reservierte Speicher wirklich reserviert und der vorherige ist nur "zufällig" noch da, bis ihn irgendein sonstiger Befehl oder ein anderes Programm überschreibt? Und wenn im fialization-Abschnitt "MyEventReceiver" freigegeben wird, wird dann auch sämtlicher Speicher freigegeben? Also auch der vom ersten Create?

Gruß.
Guido.

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 23:02 Uhr.

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