Autor Beitrag
Mathematiker
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 2622
Erhaltene Danke: 1447

Win 7, 8.1, 10
Delphi 5, 7, 10.1
BeitragVerfasst: Di 15.07.14 23:57 
Hallo,
ich habe eine Methode die von mehreren Stellen aufgerufen wird. Mit dem in der Hilfe beschriebenen
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
procedure TMainForm.Button1Click(Sender: TObject);
begin
  if Sender = Button1 then ...;
  if Sender = Button2 then ...;
end;

kann ich ja den Sender lokalisieren.
Mein Problem ist nun, dass bei einigen Sendern verschiedene Ereignisse, z.B. OnBottomClick und OnTopClick die Methode rufen sollen. Beiden Ereignissen ist z.B. Button1Click zugewiesen. Aus verschiedenen Gründen (Übersichtlichkeit mit weniger Methoden, kleinere Exe durch Vermeidung von "fast" doppeltem Code, ...) möchte ich nicht für jedes Ereignis eine einzelne Methode verwenden.

Seht Ihr eine Möglichkeit außer dem Sender auch das Ereignis zu identifizieren?

Danke für Ideen und beste Grüße
Mathematiker
Perlsau
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: Mi 16.07.14 00:51 
Falls du mit "von mehreren Stellen aus" eigentich "von mehreren Units/Forms aus" meinst: Erstelle dir doch selber ein Ereignis, das jedesmal, wenn du deine Methode benötigst, diese aufruft. Ich hab z.B. in meinen Mainforms zumeist eine Statusbar, die von allen möglichen Forms und Units aktualisiert wird, ohne daß diese Forms und Units die Mainform-Klasse kennen. Z.B. im Datenmodul, wo diverse Querys im AfterScroll-Ereignis meine Methode zur Aktualisierung der Statusbar in der Mainform aufrufen.

Dazu erstelle ich erst einmal einen neuen Typ noch vor der Klassendeklaration im Datenmodul:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
UNIT UnitData;

INTERFACE

USES
  SysUtils, Classes, ...;

TYPE
  TUpdateStatusbarEvent = Procedure of Object;
  ...

TYPE
  TDatMod = CLASS(TDataModule)
  ...


Dann spendiere ich meinem Datenmodul eine private Variable vom Typ TUpdateStatusbarEvent:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
  ...
    PRIVATE { Private-Deklarationen }
      Var
        fUpdateStatusbar : TUpdateStatusbarEvent;
  ...


Danach erstelle ich ein entsprechendes Property im Public-Abschnitt:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
  ...
    PUBLIC  { Public-Deklarationen  }

      Function  VerbindenDatenbank : Boolean;
      Function  VerbindenQueries   : Boolean;
  ...
      Property OnUpdateStatusbar : TUpdateStatusbarEvent Read fUpdateStatusbar Write fUpdateStatusbar;
  ...
  END;


Und zu guter Letzt (was das Datenmodul betrifft) deklariere und implementiere ich ein Ereignis AfterScroll für ein Query:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
Procedure TDatMod.Query_MessungAfterScroll(DataSet: TDataSet);
begin
  ...
  If Assigned(fUpdateStatusbar) Then fUpdateStatusbar;
end;


Jetzt nehm ich mir die Mainform vor, wo sich die Statusbar befindet, deren Aktualisierung von diesem Ereignis ausgelöst werden soll. Dort deklariere und implementiere ich erst einmal eine private Methode, die die Aktualisierung der Statusbar ausführen soll:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
Procedure TFormMain.StatusBarAktuell;
begin
  StatBarMain.Panels[1].Text := ...;
  StatBarMain.Panels[2].Text := ...;
  ...
end;


Nun muß ich nur noch dem in UnitData (Datenmodul) deklarierten Ereignis eine Ereignisbehandlung zuweisen, wofür ich sinnigerweise die eben erstellte Aktualisierungsmethode verwende:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
Procedure TFormMain.FormActivate(Sender: TObject);
begin
  ...
  DatMod.OnUpdateStatusbar := StatusBarAktuell;
  ...
end;


Jetzt wird jedesmal, wenn die Datenmenge im Query gescrollt wird, die Statusbar aktualisiert. Diese Methode kann ich quasi von jeder anderen Methode im Datenmodul aus aufrufen. Um die Aktualisierungsmethode aus einer anderen Unit heraus aufzurufen, mach ich dort einfach dasselbe wie im Datenmodul und weise danach auch diesem Ereignis in der Hauptunit die Statusbar-Aktualisierungsmethode als Ereignisbehandlung zu. In deinem Fall rufst löst du das Ereignis einfach in deinen Buttonklicks aus.

Wenn es wirklich wichtig ist, welcher Button geklickt wurde, kannst du ja ein Read-Only-Property deklarieren, das einen entsprechenden Hinweis liefert. Ich habe z.B. eine Unit GlobalUtils, die eine Klasse Globalix enthält, in der neben häufig benötigten allgemeinen Methoden ein paar wenige globale Variablen gehalten werden. Eine davon ist z.B. ProgMode, die je nach Modus des Anwendung (die bei mir häufig aus etlichen verschachtelten PageControls besteht) z.B. andere Bereiche der Statusbar-Aktualisierung ausführt (Case GL.ProgMode Of). Genauso verwende ich dieselbe globale Variable ProgMode in einer Datenbank-Klasse, die nahezu alle Bearbeitungen in der Datenbank erledigt – mit nur einem Query: Je nach ProgMode wird dem Query das tatsächlich benötigte Query zugewiesen, womit ich für zahlreiche Tabellen immer denselben Code verwenden kann.


Zuletzt bearbeitet von Perlsau am Mi 16.07.14 09:51, insgesamt 1-mal bearbeitet

Für diesen Beitrag haben gedankt: Mathematiker
Mathematiker Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 2622
Erhaltene Danke: 1447

Win 7, 8.1, 10
Delphi 5, 7, 10.1
BeitragVerfasst: Mi 16.07.14 07:34 
Hallo user profile iconPerlsau,

Danke für die ausführliche Antwort.
Ich versuche heute Deine Ideen umzusetzen. Dass es anspruchsvoll wird, hatte ich mir schon gedacht.
Insbesondere die Idee "Query" muss ich mir genauer ansehen.

Beste Grüße
Mathematiker
Perlsau
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: Mi 16.07.14 08:40 
Moin,

das Query war doch nur als Beispiel aufgeführt. Soweit ich weiß, befaßt du dich nicht mit Datenbanken – oder inzwischen doch?

Beim Verwenden von selbstdeklarierten Ereignissen handelt es sich um eine durchaus übliche Vorgehensweise. Das ist nicht wirklich kompliziert, zumindest scheint es mir nicht so komplex zu sein wie so manche mathematische Gleichungen und Funktionen (was vielleicht daran liegen könnte, daß ich kein Mathematiker bin).

Eine weitere Möglichkeit, Ereignisse auszulösen, besteht in sog. Callback-Funktionalität: Irgend eine Methode irgend einer Klasse benötigt dazu eine Funktion als Parameter, z.B. bei Bass die Sound-Stream-Erzeugung. Die Call-Back-Funktion wird dann ausgelöst, wenn gewisse Streambewegungen auftreten (z.B. wenn ein Sample abgespielt wird).

Für diesen Beitrag haben gedankt: Mathematiker
baumina
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 305
Erhaltene Danke: 61

Win 7
Delphi 10.2 Tokyo Enterprise
BeitragVerfasst: Mi 16.07.14 09:19 
Ich würde OnBottomClick und OnTopClick trennen und von dort jeweils eine Procedure DoXXX(Sender, AusBottomClick) aufrufen. Also einfach selbst sagen aus welchem Ereignis raus der Aufruf stattfindet.

Für diesen Beitrag haben gedankt: Mathematiker
WasWeißDennIch
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 653
Erhaltene Danke: 160



BeitragVerfasst: Mi 16.07.14 10:02 
Ich verstehe die Anforderung nicht. Den Sender-Parameter auszuwerten ist ja in gewissen Fällen noch sinnvoll, aber wozu muss man das auslösende Ereignis wissen?
jasocul
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 6386
Erhaltene Danke: 146

Windows 7 + Windows 10
Sydney Prof + CE
BeitragVerfasst: Mi 16.07.14 10:18 
@user profile iconWasWeißDennIch: Wenn der Source für beide Ereignisse nahezu gleich ist, kann man auf diese Weise alles in eine Methode packen und nur die geringfügige Abweichung vom auslösenden Ereignis abhängig machen.

Ich würde es vermutlich auch so, wie user profile iconbaumina lösen. Wobei ich die Idee von user profile iconPerlsau auch interessant finde und sicher in meinem Hinterkopf bleibt.
WasWeißDennIch
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 653
Erhaltene Danke: 160



BeitragVerfasst: Mi 16.07.14 10:36 
Tut mir leid, aber irgendwie klingt das in meinen Ohren unsauber, wahrscheinlich, weil ich noch nie in der Verlegenheit war, ein Problem auf diese Weise anzugehen. Wenn man schon ein eigenes Ereignis deklariert, kann man dem doch auch einen weiteren Parameter spendieren, den man im auslösenden Ereignis entsprechend setzt.
ausblenden Delphi-Quelltext
1:
2:
3:
4:
type
  TDingens = (dTop, dLeft, dBottom, dRight);

  TDingensEvent = procedure(Sender: TObject; Dingens: TDingens) of object;

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
type
  TSomeForm = class(TForm)
  ...
  private
    procedure DoDingens(Sender: TObject; Dingens: TDingens);

Button1 würde dann z.B.
ausblenden Delphi-Quelltext
1:
DoDingens(self, dTop);					

aufrufen und Checkbox42
ausblenden Delphi-Quelltext
1:
DoDingens(self, dBottom);					

Für diesen Beitrag haben gedankt: Mathematiker
baumina
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 305
Erhaltene Danke: 61

Win 7
Delphi 10.2 Tokyo Enterprise
BeitragVerfasst: Mi 16.07.14 10:45 
Das entspricht dem was ich schrieb, nur versteh ich nicht warum du TDingensEvent dazu brauchst.
WasWeißDennIch
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 653
Erhaltene Danke: 160



BeitragVerfasst: Mi 16.07.14 10:54 
OK, das hab ich vermischt. Allerdings verstehe ich meinerseits nicht, wieso Du das auslösende Event übergeben willst statt der Daten, die von Interesse wären.
baumina
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 305
Erhaltene Danke: 61

Win 7
Delphi 10.2 Tokyo Enterprise
BeitragVerfasst: Mi 16.07.14 11:01 
Kann ich jetzt nicht beurteilen was dem TE an dieser Stelle wichtiger ist, wirklich zu wissen woher er kommt oder lieber zu wissen was zu machen ist.
WasWeißDennIch
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 653
Erhaltene Danke: 160



BeitragVerfasst: Mi 16.07.14 11:08 
Genau deshalb habe ich ja oben die Frage gestellt, möglicherweise wird das Konzept noch einmal durchdacht ;)
Perlsau
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: Mi 16.07.14 11:32 
Ist doch eigentlich ganz einfach: Stellen wir uns vor, wir haben 2 Buttons, die beide ungefähr dasselbe auslösen sollen, mit einem kleinen Unterschied: Bei Button1 soll der Wert 1 in eine private Variable Wertspeicher geschrieben werden, bei Button2 der Wert 2. Beide Buttons rufen nun die Procedure Auswerten auf, die folgendermaßen implementiert ist:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
Procedure FormMain.Auswerten(Wert : Integer);
begin
  Wertspeicher := Wert;
end;


Nun muß ich lediglich bei jeder Ereignisbehandlung für jeden Button den entsprechenden Wert mitgeben und gut ist:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
Procedure FormMain.Button1Click(Sender: TObject);
begin
  Auswerten(1);
end;

Procedure FormMain.Button2Click(Sender: TObject);
begin
  Auswerten(2);
end;


Oder wenn's um Strings geht:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
Procedure FormMain.Auswerten(Wert : Integer);
begin
  Case Wert Of
   1 : Wertspeicher := 'Der obere Button wurde geklickt';
   2 : Wertspeicher := 'Der untere Button wurde geklickt';
  End;
end;

Für diesen Beitrag haben gedankt: Mathematiker
baumina
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 305
Erhaltene Danke: 61

Win 7
Delphi 10.2 Tokyo Enterprise
BeitragVerfasst: Mi 16.07.14 11:39 
Und woher weiß er dann den Unterschied von welchem Event er kommt wenn er folgendes in der dfm zugewiesen hatte?

ausblenden Delphi-Quelltext
1:
2:
3:
          
OnClick = Button1Click
OnEnter = Button1Click


Deswegen muss er sie trennen:

ausblenden Delphi-Quelltext
1:
2:
3:
          
OnClick = Button1Click
OnEnter = Button1Enter
Mathematiker Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 2622
Erhaltene Danke: 1447

Win 7, 8.1, 10
Delphi 5, 7, 10.1
BeitragVerfasst: Mi 16.07.14 11:55 
Hallo,
vielen Dank für die Antworten.
Im Moment habe ich user profile iconbauminas und user profile iconPerlsaus Ideen genutzt. Insbesondere der Vorschlag
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
Procedure FormMain.Auswerten(Wert : Integer);
begin
  Wertspeicher := Wert;
end;
Procedure FormMain.Button1Click(Sender: TObject);
begin
  Auswerten(1);
end;
Procedure FormMain.Button2Click(Sender: TObject);
begin
  Auswerten(2);
end;

ist genau das Richtige und naheliegend. Irgendwie habe ich mich wohl zu blöd angestellt.

Zur Erklärung, warum ich das Ganze mache: Seit einigen Wochen habe ich mir mein Programm vorgenommen und gehe alle 580000 Zeilen Quellcode durch. Eine ziemliche Arbeit, aber notwendig.
Da ich vor 20 Jahren mit dem Programm begonnen habe, sind die frühesten Units/Algorithmen aus heutiger Sicht extrem laienhaft konstruiert, auch wenn sie fehlerfrei funktionieren. Damals konnte/wusste ich es nicht besser.
Also muss Alles kontrolliert werden. Neben gefundenen totem Code und umständlichen Lösungen gibt es einige fast gleiche Routinen, die oft ziemlich umfangreiche Berechnungen enthalten. Und die versuche ich zu vereinheitlichen. In der ganzen Zeit hat sich einiges angesammelt.
Meine konkrete Frage betraf eine Fremdkomponente (RXSpinbutton), die für die zwei Pfeilschalter die Ereignisse OnBottomClick und OnTopClick auswertet.

Mittlerweile sind schon 6000 Programmzeilen "weg". Schöner Nebeneffekt ist, dass die Exe von 9120 kbyte auf 8760 kByte reduziert ist. Ist zwar bei den heutigen Speichergrößen nicht notwendig, aber mir gefällt es.

Nochmals Danke für die vielen Hinweise
Beste Grüße
Mathematiker
WasWeißDennIch
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 653
Erhaltene Danke: 160



BeitragVerfasst: Mi 16.07.14 11:57 
Und nochmal die Frage: wieso muss man das Event kennen? Mich beschleicht das Gefühl, dass aus Gründen der Code-Ersparnis herumgemurkst werden soll. Ich kenne die genaue Anforderung ja nicht, aber möglicherweise wäre auch eine Property samt zugehörigem Event denkbar.
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
type
  TMyType = (mtOne, mtTwo, mtThree);  
  
  TMyClass = class
  private
    FMyProp: TMyType;
    FOnMyPropChanged: TNotifyEvent;
    procedure SetMyProp(const Value: TMyType);
  public
    property MyProp: TMyType read FMyProp write SetMyProp;
    property OnMyPropChanged: TMyPropChangedEvent read FOnMyPropChanged write FOnMyPropChanged;
  end;

...

procedure TMyClass.SetMyProp(const Value: TMyType);
begin
  if Value <> FMyProp then
    begin
      FMyProp := Value;
      if Assigned(FOnMyPropChanged) then
        FOnMyPropChanged(self);
    end;
end;


Nun kann man der Eigenschaft OnMyPropChanged eine Methode zuweisen, welche immer dann aufgerufen wird, wenn sich MyProp ändert. Auf der GUI-Seite kann man verschiedenen Controls dieselbe Methode zuweisen, in der dann z.B. mtTwo zugewiesen wird, wieder anderen Controls eine andere Methode, die den Wert mtOne setzt usw.

Für diesen Beitrag haben gedankt: Mathematiker
Mathematiker Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 2622
Erhaltene Danke: 1447

Win 7, 8.1, 10
Delphi 5, 7, 10.1
BeitragVerfasst: Mi 16.07.14 12:01 
Hallo,
user profile iconWasWeißDennIch hat folgendes geschrieben Zum zitierten Posting springen:
Mich beschleicht das Gefühl, dass aus Gründen der Code-Ersparnis herumgemurkst werden soll.

Oh je, Du hast mich erwischt. :oops:
Das hatte ich wirklich vor, aber mit den hier genannten Hinweisen kann ich darauf verzichten.

Unabhängig davon, dass ich es nicht mehr brauche, interessiert es mich doch noch, ob man auch das auslösende Ereignis einfach(!) ermitteln kann.

Danke für die Hinweise
Beste Grüße
Mathematiker
WasWeißDennIch
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 653
Erhaltene Danke: 160



BeitragVerfasst: Mi 16.07.14 12:04 
Wenn man das nicht selbst mitgibt (z.B. als Parameter, siehe baumina), wüsste ich nicht, wie man das bewerkstelligen sollte.
Boldar
ontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic starofftopic star
Beiträge: 1555
Erhaltene Danke: 70

Win7 Enterprise 64bit, Win XP SP2
Turbo Delphi
BeitragVerfasst: Mi 16.07.14 15:22 
Naja, theoretisch könnte man möglicherweise den Callstack analysieren, aber das wäre wohl sehr unsauber. Wahrscheinlich hilft aber nicht einmal das weiter.
Delphi-Laie
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 1600
Erhaltene Danke: 232


Delphi 2 - RAD-Studio 10.1 Berlin
BeitragVerfasst: Do 24.07.14 22:01 
user profile iconMathematiker hat folgendes geschrieben Zum zitierten Posting springen:
Mittlerweile sind schon 6000 Programmzeilen "weg". Schöner Nebeneffekt ist, dass die Exe von 9120 kbyte auf 8760 kByte reduziert ist. Ist zwar bei den heutigen Speichergrößen nicht notwendig, aber mir gefällt es.


Mir auch, denn ich bin ein Freund kompakten Codes. Ist für mich eleganter, ja sogar ästhetischer.

Ganz simpel, wenn auch unelegant, ist die Verwendung eines möglichst versionsniedrigen Delphis, um die Compilatsgrößen zu verringern. VCL-frei zu programmieren, wie es Luckie favorisiert, ist natürlich noch viel wirksamer, aber wesentlich einarbeitungs- und umsetzungsaufwendiger.