Autor |
Beitrag |
galagher
Beiträge: 2527
Erhaltene Danke: 44
Windows 10 Home
Delphi 10.1 Starter, Lazarus 2.0.6
|
Verfasst: So 22.07.18 20:42
Hallo!
Ich habe eine Unit (keine Komponente), die ein OnDrawItem für eine TListBox bereitstellt. Ich möchte nun auf ein TForm eine ganz normale TListBox setzen, welche statt ihres eigenen OnDrawItem's jenes der Unit benutzt.
Geht das überhaupt, und wenn ja, wie?
//Edit:
Um das zu präzisieren: Ich möchte in der Unit angeben, dass eine TListBox, die ich als "var" an die Unit übergebe, das OnDrawItem der Unit benutzt.
Pseudocode:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9:
| Unit MyUnit;
procedure MyOnDrawItem procedure MachWas(var aListBox: TListBox); procedure MachWas(var aListBox: TListBox); begin aListBox.OnDrawItem := MyOnDrawItem; end; |
Wenn ich nun in Unit1 in der uses-Klausel MyUnit hinzufüge und auf TForm1 eine ListBox draufpacke, soll diese ListBox das OnDrawItem von MyUnit nutzen! Moderiert von Narses: Topic aus Delphi Language (Object-Pascal) / CLX verschoben am So 22.07.2018 um 22:04
_________________ gedunstig war's - und fahle wornen zerschellten karsig im gestrock. oh graus, es gloomt der jabberwock - und die graisligen gulpen nurmen!
|
|
jaenicke
Beiträge: 19285
Erhaltene Danke: 1743
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: So 22.07.18 22:49
Ich sehe das Problem nicht. Klappt das nicht? Meinst du den Unterschied zwischen Prozedur und Methode?
Hier mal ein vollständiges Beispiel:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55:
| unit ListBoxDrawer;
interface
uses Vcl.StdCtrls, Vcl.Controls, System.Types, Vcl.Graphics, System.SysUtils;
type TListBoxDrawer = class private class var FInstance: TListBoxDrawer; procedure DoOnDrawItem(Control: TWinControl; Index: Integer; Rect: TRect; State: TOwnerDrawState); public class constructor Create; class destructor Destroy; class procedure RegisterListBox(const ATarget: TListBox); class procedure UnregisterListBox(const ATarget: TListBox); end;
implementation
class constructor TListBoxDrawer.Create; begin FInstance := TListBoxDrawer.Create; end;
class destructor TListBoxDrawer.Destroy; begin FInstance.Free; end;
procedure TListBoxDrawer.DoOnDrawItem(Control: TWinControl; Index: Integer; Rect: TRect; State: TOwnerDrawState); var ListBoxCanvas: TCanvas; begin ListBoxCanvas := (Control as TListBox).Canvas; ListBoxCanvas.FillRect(Rect); ListBoxCanvas.TextOut(Rect.Left + 2, Rect.Top, 'Test ' + IntToStr(Index)); end;
class procedure TListBoxDrawer.RegisterListBox(const ATarget: TListBox); begin ATarget.OnDrawItem := FInstance.DoOnDrawItem; end;
class procedure TListBoxDrawer.UnregisterListBox(const ATarget: TListBox); begin ATarget.OnDrawItem := nil; end;
end. |
Und im OnCreate des Formulars z.B.: Delphi-Quelltext 1: 2: 3: 4:
| procedure TFormTest.FormCreate(Sender: TObject); begin TListBoxDrawer.RegisterListBox(ListBox1); end; |
Nebenbei:
Im Profil steht bei dir die 10.1 Starter Edition. Falls das noch aktuell ist, würde ich dir wärmstens die aktuelle 10.2 Community Edition empfehlen, die nicht so eingeschränkt wie die Starter ist.
Für diesen Beitrag haben gedankt: galagher
|
|
galagher
Beiträge: 2527
Erhaltene Danke: 44
Windows 10 Home
Delphi 10.1 Starter, Lazarus 2.0.6
|
Verfasst: Mo 23.07.18 10:57
_________________ gedunstig war's - und fahle wornen zerschellten karsig im gestrock. oh graus, es gloomt der jabberwock - und die graisligen gulpen nurmen!
|
|
jaenicke
Beiträge: 19285
Erhaltene Danke: 1743
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Mo 23.07.18 12:40
galagher hat folgendes geschrieben : | Da kann ich ja genau so gut ins OnDrawItem der TListBox den Aufruf einer OnDrawItem-Routine der anderen Unit schreiben! |
Dann müsstest du aber die Instanz aus der anderen Unit veröffentlichen. Globale Variablen wiederum sind aber keine gute Idee, so dass du dann am sinnvollsten die Referenz in der aufrufenden Klasse erzeugst. Dadurch hast du dort aber mehr Overhead.
Gehen tut es aber natürlich.
|
|
galagher
Beiträge: 2527
Erhaltene Danke: 44
Windows 10 Home
Delphi 10.1 Starter, Lazarus 2.0.6
|
Verfasst: Di 24.07.18 21:31
Ich möchte den Aufruf TListBoxDrawer.RegisterListBox in einer separaten Unit unterbringen, die ich immer wieder verwenden kann: Ich füge in einem Projekt einfach den Namen meiner Unit hinzu, und schon funktioniert alles, ich muss dann TListBoxDrawer.RegisterListBox nicht jedesmal extra aufrufen.
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10:
| procedure [...](var aListBox: TListBox); begin TListBoxDrawer.RegisterListBox(aListBox); try finally TListBoxDrawer.UnRegisterListBox(aListBox); end; end; |
Meine Frage: Wenn ich TListBoxDrawer.UnRegisterListBox(aListBox); einfach weglasse, funktioniert alles. Aber produziere ich dann ein Speicherleck?
_________________ gedunstig war's - und fahle wornen zerschellten karsig im gestrock. oh graus, es gloomt der jabberwock - und die graisligen gulpen nurmen!
|
|
jaenicke
Beiträge: 19285
Erhaltene Danke: 1743
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Mi 25.07.18 18:58
galagher hat folgendes geschrieben : | Meine Frage: Wenn ich TListBoxDrawer.UnRegisterListBox(aListBox); einfach weglasse, funktioniert alles. Aber produziere ich dann ein Speicherleck? |
Nein, das ist kein Speicherleck. Wichtig ist nur, dass das Event nicht mehr aufgerufen wird, wenn die behandelnde Klasse nicht mehr existiert. Das sollte der Fall sein. Sauberer finde ich allerdings generell bei solchen globalen Klassen, wenn man sie auch immer sauber wieder deinitialisiert, egal was sie machen. Einfach, damit man sich nicht darauf verlassen muss, dass das an genau der Stelle nicht nötig ist. Was, wenn diese globale Klasse noch mehr Funktionen bekäme, die eine Deinitialisierung nötig machen würden?
galagher hat folgendes geschrieben : | Delphi-Quelltext 1:
| procedure [...](var aListBox: TListBox); | |
Das var irritiert dabei vollkommen. Möchtest du in der anderen Prozedur wirklich eine andere Listbox Instanz dort hineinlegen und an den Aufrufer übergeben?
Für diesen Beitrag haben gedankt: galagher
|
|
galagher
Beiträge: 2527
Erhaltene Danke: 44
Windows 10 Home
Delphi 10.1 Starter, Lazarus 2.0.6
|
Verfasst: Mi 25.07.18 21:57
jaenicke hat folgendes geschrieben : | galagher hat folgendes geschrieben : | Delphi-Quelltext 1:
| procedure [...](var aListBox: TListBox); | | Das var irritiert dabei vollkommen. Möchtest du in der anderen Prozedur wirklich eine andere Listbox Instanz dort hineinlegen und an den Aufrufer übergeben? |
Das ist noch ein Relikt von vorherigen Versuchen, ich denke, const ist besser. Oder?
Der grundsätzliche Gedanke ist, ich möchte in meinem Projekt keinen Aufruf von RegisterListBox und UnregisterListBox, das soll alles automatisch gemacht werden.
Eine Unit macht das RegisterListBox und befüllt dann die ListBox, welche gleich alles ganz automatisch wie gewünscht zeichnet.
In meinem aktuellen und weiteren Projekten brauche ich dann nichts weiter zu machen, als in der uses-Klausel den Unitnamen einzufügen.
Nur: Wo setze ich nun das UnregisterListBox? Gleich anschliessend, ist die Darstellung im DoOnDrawItem klarerweise weg. Im finalization der Unit? Geht nicht, da kann ich ja die ListBox nicht als Parameter übergeben. Gar nicht? Ja, würde gehen, wie ich jetzt weiss.
_________________ gedunstig war's - und fahle wornen zerschellten karsig im gestrock. oh graus, es gloomt der jabberwock - und die graisligen gulpen nurmen!
|
|
galagher
Beiträge: 2527
Erhaltene Danke: 44
Windows 10 Home
Delphi 10.1 Starter, Lazarus 2.0.6
|
Verfasst: Mi 25.07.18 22:42
_________________ gedunstig war's - und fahle wornen zerschellten karsig im gestrock. oh graus, es gloomt der jabberwock - und die graisligen gulpen nurmen!
|
|
jaenicke
Beiträge: 19285
Erhaltene Danke: 1743
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Do 26.07.18 06:40
galagher hat folgendes geschrieben : | Der grundsätzliche Gedanke ist, ich möchte in meinem Projekt keinen Aufruf von RegisterListBox und UnregisterListBox, das soll alles automatisch gemacht werden. |
Du könntest (z.B. mit Hilfe der Delphi Detours Library) einen Hook auf die Methode TCustomListBox.DrawItem setzen und somit deine Änderung direkt in jede TListBox deiner Anwendung injizieren.
Vielleicht ließe sich ja auch mit den VCL Styles etwas erreichen, je nachdem was du machen möchtest?
|
|
galagher
Beiträge: 2527
Erhaltene Danke: 44
Windows 10 Home
Delphi 10.1 Starter, Lazarus 2.0.6
|
Verfasst: Do 26.07.18 19:59
_________________ gedunstig war's - und fahle wornen zerschellten karsig im gestrock. oh graus, es gloomt der jabberwock - und die graisligen gulpen nurmen!
|
|
jaenicke
Beiträge: 19285
Erhaltene Danke: 1743
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Do 26.07.18 23:18
|
|
galagher
Beiträge: 2527
Erhaltene Danke: 44
Windows 10 Home
Delphi 10.1 Starter, Lazarus 2.0.6
|
Verfasst: Fr 27.07.18 08:05
jaenicke hat folgendes geschrieben : | Aber das hört sich eher so an als ob du die Listbox als Datenspeicher missbrauchst. |
Nein, die Daten werden aus einer Datei in eine TStringList eingelesen und diese benutze ich, um darin nach Text(teilen) zu suchen. Die Funde werden dann in der ListBox dargestellt, aber ohne die Zahl.
Das alles klappt mittlerweile perfekt!
_________________ gedunstig war's - und fahle wornen zerschellten karsig im gestrock. oh graus, es gloomt der jabberwock - und die graisligen gulpen nurmen!
|
|
|