Autor |
Beitrag |
Coder
      
Beiträge: 1383
Erhaltene Danke: 1
WinXP
D2005 PE
|
Verfasst: Mi 20.06.07 19:12
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
      
Beiträge: 4106
Erhaltene Danke: 13
Delphi 2010 Pro; Delphi.Prism 2011 pro
|
Verfasst: Mi 20.06.07 19:14
Garnicht, da Sender ja vom typ TComponent ist.
_________________ Markus Kinzler.
|
|
Coder 
      
Beiträge: 1383
Erhaltene Danke: 1
WinXP
D2005 PE
|
Verfasst: 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
      
Beiträge: 390
Win XP
Delphi 2007 Prof., XE2, XE5
|
Verfasst: 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. 
_________________ Es gibt keine Probleme - nur Lösungen!
|
|
Coder 
      
Beiträge: 1383
Erhaltene Danke: 1
WinXP
D2005 PE
|
Verfasst: 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
      
Beiträge: 390
Win XP
Delphi 2007 Prof., XE2, XE5
|
Verfasst: 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. 
_________________ Es gibt keine Probleme - nur Lösungen!
|
|
Reinhard Kern
      
Beiträge: 591
Erhaltene Danke: 14
|
Verfasst: 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:
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
      
Beiträge: 593
Erhaltene Danke: 5
Delphi 5 ent, Delphi 6 bis Delphi XE8 pro
|
Verfasst: 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
      
Beiträge: 591
Erhaltene Danke: 14
|
Verfasst: 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
      
Beiträge: 8721
Erhaltene Danke: 191
Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
|
Verfasst: 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 ...
_________________ Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
|
|
dummzeuch
      
Beiträge: 593
Erhaltene Danke: 5
Delphi 5 ent, Delphi 6 bis Delphi XE8 pro
|
Verfasst: 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 
      
Beiträge: 1383
Erhaltene Danke: 1
WinXP
D2005 PE
|
Verfasst: Mi 08.08.07 16:08
Klasse! Dann werd ich das so machen wie dummzeuch.
Danke an alle 
|
|
|