Autor |
Beitrag |
Mathematiker
Beiträge: 2622
Erhaltene Danke: 1447
Win 7, 8.1, 10
Delphi 5, 7, 10.1
|
Verfasst: Di 15.07.14 23:57
Hallo,
ich habe eine Methode die von mehreren Stellen aufgerufen wird. Mit dem in der Hilfe beschriebenen
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
|
Verfasst: 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:
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:
Delphi-Quelltext 1: 2: 3: 4: 5:
| ... PRIVATE Var fUpdateStatusbar : TUpdateStatusbarEvent; ... |
Danach erstelle ich ein entsprechendes Property im Public-Abschnitt:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9:
| ... PUBLIC
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:
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:
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:
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
Beiträge: 2622
Erhaltene Danke: 1447
Win 7, 8.1, 10
Delphi 5, 7, 10.1
|
Verfasst: Mi 16.07.14 07:34
Hallo Perlsau,
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
|
Verfasst: 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
Beiträge: 305
Erhaltene Danke: 61
Win 7
Delphi 10.2 Tokyo Enterprise
|
Verfasst: 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
Beiträge: 653
Erhaltene Danke: 160
|
Verfasst: 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
Beiträge: 6388
Erhaltene Danke: 146
Windows 7 + Windows 10
Sydney Prof + CE
|
Verfasst: Mi 16.07.14 10:18
@ WasWeiß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 baumina lösen. Wobei ich die Idee von Perlsau auch interessant finde und sicher in meinem Hinterkopf bleibt.
|
|
WasWeißDennIch
Beiträge: 653
Erhaltene Danke: 160
|
Verfasst: 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.
Delphi-Quelltext 1: 2: 3: 4:
| type TDingens = (dTop, dLeft, dBottom, dRight);
TDingensEvent = procedure(Sender: TObject; Dingens: TDingens) of object; |
Delphi-Quelltext 1: 2: 3: 4: 5:
| type TSomeForm = class(TForm) ... private procedure DoDingens(Sender: TObject; Dingens: TDingens); |
Button1 würde dann z.B.
Delphi-Quelltext 1:
| DoDingens(self, dTop); |
aufrufen und Checkbox42
Delphi-Quelltext 1:
| DoDingens(self, dBottom); |
Für diesen Beitrag haben gedankt: Mathematiker
|
|
baumina
Beiträge: 305
Erhaltene Danke: 61
Win 7
Delphi 10.2 Tokyo Enterprise
|
Verfasst: Mi 16.07.14 10:45
Das entspricht dem was ich schrieb, nur versteh ich nicht warum du TDingensEvent dazu brauchst.
|
|
WasWeißDennIch
Beiträge: 653
Erhaltene Danke: 160
|
Verfasst: 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
Beiträge: 305
Erhaltene Danke: 61
Win 7
Delphi 10.2 Tokyo Enterprise
|
Verfasst: 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
Beiträge: 653
Erhaltene Danke: 160
|
Verfasst: 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
|
Verfasst: 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:
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:
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:
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
Beiträge: 305
Erhaltene Danke: 61
Win 7
Delphi 10.2 Tokyo Enterprise
|
Verfasst: Mi 16.07.14 11:39
|
|
Mathematiker
Beiträge: 2622
Erhaltene Danke: 1447
Win 7, 8.1, 10
Delphi 5, 7, 10.1
|
Verfasst: Mi 16.07.14 11:55
Hallo,
vielen Dank für die Antworten.
Im Moment habe ich bauminas und Perlsaus Ideen genutzt. Insbesondere der Vorschlag
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
Beiträge: 653
Erhaltene Danke: 160
|
Verfasst: 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.
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
Beiträge: 2622
Erhaltene Danke: 1447
Win 7, 8.1, 10
Delphi 5, 7, 10.1
|
Verfasst: Mi 16.07.14 12:01
Hallo,
WasWeißDennIch hat folgendes geschrieben : | Mich beschleicht das Gefühl, dass aus Gründen der Code-Ersparnis herumgemurkst werden soll. |
Oh je, Du hast mich erwischt.
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
Beiträge: 653
Erhaltene Danke: 160
|
Verfasst: 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
Beiträge: 1555
Erhaltene Danke: 70
Win7 Enterprise 64bit, Win XP SP2
Turbo Delphi
|
Verfasst: 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
Beiträge: 1600
Erhaltene Danke: 232
Delphi 2 - RAD-Studio 10.1 Berlin
|
Verfasst: Do 24.07.14 22:01
Mathematiker hat folgendes geschrieben : | 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.
|
|