Entwickler-Ecke
Delphi Language (Object-Pascal) / CLX - Komponente um Prozeduren erweitern
Coder - Mi 20.06.07 19:12
Titel: Komponente um Prozeduren erweitern
Hi
Ich würde gern eine Komponente um Prozeduren erweitern, um diese in eine eigene Unit zu schreiben.
Jedoch soll es nicht zu kompliziert werden d.h. ich will die erweiterte Komponente nicht extra in der IDE installieren sondern die Eigenschaften des Vorfahren übernehmen, der bereits auf die Form gesetzt und richtig konfiguriert ist.
Dazu hab ich dieses Testprojekt erstellt.
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: 25: 26: 27: 28: 29: 30:
| type TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); private public end;
TMyButton = class(TButton) procedure MakeCaption; end;
var Form1: TForm1;
implementation
{$R *.dfm}
procedure TMyButton.MakeCaption; begin Self.Caption := '123'; end;
procedure TForm1.Button1Click(Sender: TObject); begin TMyButton(Button1).MakeCaption; end; |
Das funktioniert soweit gut.
Aber kann ich den TButton in ein TMyButton "umwandeln" und dabei die Eigenschaften in einem Rutsch übernehmen, sodass ich mir das
TMyButton( sparen kann?
MfG
mkinzler - Mi 20.06.07 19:14
Garnicht, da Sender ja vom typ TComponent ist.
Coder - Fr 03.08.07 02:33
Das obere Beispiel funktioniert.
Aber jetzt hab ich mal probiert der Komponente eine weitere Variable anzuhängen:
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: 25: 26: 27:
| type TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); private public end;
TMyButton = class(TButton) public s: String; end;
var Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject); begin TMyButton(Button1).s := 'Test'; ShowMessage(TMyButton(Button1).s); end; |
So bekommt man eine Zugriffsverletzung.
Das erscheint mir auch irgendwie logisch, da wohl gar kein Speicher für die Variable reserviert wird, oder?
Wie könnte man das lösen, eine Komponente mit Funktionen, Prozeduren und Variablen einfach zu erweitern?
Ich will vermeiden, dass jemand, der meinen Quellcode kompilieren will, erst noch zig Komponenten in seine IDE installieren muss.
MfG
Logikmensch - Fr 03.08.07 07:01
Dass das nicht richtig funzt liegt daran, dass ein Nachkomme auch als solcher Createt werden muss, damit die Instanz richtig initialisiert wird. Mit Deinem TypeCast umgehst Du dies. Da Dein Button1 nur als TButton instantiiert ist, fehlen ihm Deine neuen Eigenschaften, sprich Deine neue Variable und dann gibt's unweigerlich Probleme. Das Prinzip der OOP richtig zu benutzen ist Grundlage für gutes OOP, daher ist mir jetzt nicht klar, was Du eigentlich einsparen willst.
Wenn Du ohne eine neue Kompo zu erstellen, eine Kompo um weitere Fähigkeiten erweitern willst, dann erstelle diese nicht mit dem Formulardesigner, sondern zur Laufzeit. Das Hinzufügen eines Buttons ist doch recht simpel:
innerhalb des private-Abschnitts Deines Forms eintragen:
Delphi-Quelltext
1: 2: 3:
| ... Fmybutton:Tmybutton; ... |
innerhalb des FormCreate-Events einzutragen:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7:
| .... Fmybutton:=Tmybutton.Create(Self); Fmybutton.Parent:=Self; Fmybutton.Caption:='TestKnopf'; Fmybutton.OnClick:=mybuttonclick; ... |
innerhalb des FormDestroy-Events einzutragen:
Delphi-Quelltext
1: 2: 3:
| ... Fmybutton.Free; ... |
So, dann kannst Du Dich in Deinem Code mit Deinem Knöpfchen freuen. :-)
Coder - Fr 03.08.07 15:06
Jupp. Nur muss ich dann die ganzen Eigenschaften der Komponente auch zur Laufzeit setzen.
Beim VirtualTreeView sind das ne ganze Menge.
Und das ist mir für nur eine zusätzliche Eigenschaft zu viel Aufwand.
Da lass ichs lieber mit OOP.
MfG
Logikmensch - Fr 03.08.07 18:13
Stimmt, bei komplexeren Objekten ist es mit dem Designer einfacher. Ich verstand Dich so, dass es Dir "nur" um einen Button geht.
Ich hoffe, ich war nicht zu schroff. Meine ersten Versuche in der Komponentenentwicklung gingen in dieselbe Richtung wie Dein Typecast-Versuch. :-)
Reinhard Kern - Fr 03.08.07 18:19
Coder hat folgendes geschrieben: |
Jupp. Nur muss ich dann die ganzen Eigenschaften der Komponente auch zur Laufzeit setzen.
Beim VirtualTreeView sind das ne ganze Menge.
Und das ist mir für nur eine zusätzliche Eigenschaft zu viel Aufwand.
Da lass ichs lieber mit OOP.
MfG |
Hallo,
ich mache mir einfach eine neue Klasse, die die zugrundeliegende Komponente "benutzt" und erweitert:
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: 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: 56:
| TRKStringGrid = class (TComponent) private FHeader : TLabel; public FStringGrid : TStringGrid; NextRow,NextEntry : integer; constructor create (AOwner : TComponent; myGrid : TStringGrid); procedure AssignHeader (H : TLabel); procedure SetHeader (txt : string); procedure SetHItem (txt : string; cix : integer); procedure SetC0Item (txt : string; rix : integer); procedure SetC0Number (rn : integer); procedure SetColText (txt : string; cix : integer); procedure SetColTextDef (deftxt,txt : string; cix : integer); procedure SetColInt (v : longint; cix : integer); procedure SetColBool (b : boolean; cix : integer); procedure Sort (col : integer; num,renumber : boolean); procedure Pack; procedure AutoAlign (h_only : boolean); procedure ClearGrid (irows : integer); procedure NewRow; procedure NewEntry; function GetSelectedRow : integer; function GetSelectedCode : ShortString; function GetColText (c,r : integer) : ShortString; function GetColInt (c,r : integer) : longint; end;
implementation
uses BLMain;
constructor TRKStringGrid.create (AOwner : TComponent; myGrid : TStringGrid); begin inherited create (AOwner); FStringGrid := myGrid; end;
procedure TRKStringGrid.AssignHeader (H : TLabel); begin FHeader := H; end;
procedure TRKStringGrid.SetHeader (txt : string); begin if Assigned (FHeader) then FHeader.Caption := txt; end; procedure TRKStringGrid.SetHItem (txt : string; cix : integer); begin if Assigned (FStringGrid) then FStringGrid.Cells [cix,0] := txt; end;
..... |
Geht in beliebigem Umfang, ich muss eben nur beim Create mit der Komponente "connecten". Im Beispiel sind es 2, ein Label und ein Stringgrid. Das Label benutze ich nur manchmal.
Gruss Reinhard
dummzeuch - So 05.08.07 13:52
Titel: den Compiler reinlegen
Hi,
es gibt auch die Moeglichkeit, dem Compiler vorzuspiegeln, dass eine Komponente von einem abgeleiteten Typ ist:
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: 25:
| unit Unit1;
interface
[...]
type TEdit = class(Controls.TEdit) public procedure doSomething; end;
type TForm1=class(TForm) Edit1: TEdit; [...] end; [...]
implementation
[...] procedure TEdit.doSomething; [...] end. |
Damit hat ploetzlich jede TEdit-Komponente auf dem Formular eine zusaetzliche Methode doSometing, die man wie folgt aufrufen kann:
Delphi-Quelltext
1: 2: 3: 4:
| procedure TForm1.Edit1Click(Sender: TObject); begin (Sender as TEdit).doSomething; end; |
Auf diese Weise kann man einer Komponente auch neue Eventhandler, Messagehandler und Properties verpassen. Funktioniert wunderbar, auch ueber Unit-Grenzen hinweg, man muss nur den Gueltigkeitsbereich des Typs beachten.
Im Grunde genommen ist dies nichts anderes als das Erstellen einer abgeleiteten Komponente, nur dass man sie nicht in der IDE installiert und dass sie denselben Namen hat wie eine Standard-Komponente.
twm
Moderiert von
Tino: Code- durch Delphi-Tags ersetzt
Reinhard Kern - So 05.08.07 19:46
Titel: Re: den Compiler reinlegen
dummzeuch hat folgendes geschrieben: |
Hi,
es gibt auch die Moeglichkeit, dem Compiler vorzuspiegeln, dass eine Komponente von einem abgeleiteten Typ ist:
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: 25:
| unit Unit1;
interface
[...]
type TEdit = class(Controls.TEdit) public procedure doSomething; end;
type TForm1=class(TForm) Edit1: TEdit; [...] end; [...]
implementation
[...] procedure TEdit.doSomething; [...] end. |
Damit hat ploetzlich jede TEdit-Komponente auf dem Formular eine zusaetzliche Methode doSometing, die man wie folgt aufrufen kann:
Delphi-Quelltext 1: 2: 3: 4:
| procedure TForm1.Edit1Click(Sender: TObject); begin (Sender as TEdit).doSomething; end; |
Auf diese Weise kann man einer Komponente auch neue Eventhandler, Messagehandler und Properties verpassen. Funktioniert wunderbar, auch ueber Unit-Grenzen hinweg, man muss nur den Gueltigkeitsbereich des Typs beachten.
Im Grunde genommen ist dies nichts anderes als das Erstellen einer abgeleiteten Komponente, nur dass man sie nicht in der IDE installiert und dass sie denselben Namen hat wie eine Standard-Komponente.
twm |
Hallo,
damit verarschst du nicht den Compiler, der unterscheidet sicher zwischen Unit1.TEdit und Controls.TEdit, sonst würde das ganze ja auch nicht funktionieren. Für dein Unit1.TEdit wird ganz ordentlich eine erweiterte Methodentabelle angelegt mit doSomething.
Vielmehr legst du die kleinen Hilfsprogramme wie den Object-Inspector herein - nehme ich an, sonst wäre das ganze wenig sinnvoll. Wenn ich Unit1.TEdit nicht damit bearbeiten kann, habe ich ja nichts von der ganzen Übung.
Gruss Reinhard
BenBE - So 05.08.07 20:26
Ich bezweifle, dass das wirklich stabil laufen wird, da die VCL Komponenten-Klassen allein an ihrem Namen unterscheided, wodurch nicht sicher ist, ob Controls.TEdit oder Unit1.TEdit in der DFM-Resource gemeint ist. Und selbst wenn es korrekt aufgelöst wird, besteht immer noch das Problem, ob er wirklich eine Instanz von Unit1.TEdit angelegt wurde (oder doch nur von Controls.TEdit, was dann ziemlich krachen würde ...
dummzeuch - So 05.08.07 21:07
Hi,
BenBE hat folgendes geschrieben: |
Ich bezweifle, dass das wirklich stabil laufen wird, da die VCL Komponenten-Klassen allein an ihrem Namen unterscheided, wodurch nicht sicher ist, ob Controls.TEdit oder Unit1.TEdit in der DFM-Resource gemeint ist. Und selbst wenn es korrekt aufgelöst wird, besteht immer noch das Problem, ob er wirklich eine Instanz von Unit1.TEdit angelegt wurde (oder doch nur von Controls.TEdit, was dann ziemlich krachen würde ... |
Es funktioniert und ist stabil. Was nicht klappt, sind neue published properties, die wuerden nicht gestreamt.
twm
Coder - Mi 08.08.07 16:08
Klasse! Dann werd ich das so machen wie dummzeuch.
Danke an alle :zustimm:
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2025 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!