|
Antwort |
Registriert seit: 20. Nov 2009 Ort: Baden Württemberg 77 Beiträge Delphi 2009 Architect |
#1
Erstellen einer VCL-Komponente nach einem vorgefertigten Muster.
Ich hatte zugegebenermaßen große Probleme das Konzept der Klassen und Komponenten zu verstehen. Ganz simpel ausgedrückt ist es eigentlich am einfachsten das Konzept zu verstehen, wenn man ausprobiert und einfach mal Testet, welche Möglichkeiten man für sich selbst nutzen kann. Ich habe hier einfach mal ein Standard-Pattern für eine VCL-Komponente zusammengeschrieben, mit dem ich seit geraumer Zeit relativ gut arbeiten kann. Kleines Lippenbekenntnis vorab. Auch ich habe mir die meisten Ideen im Netz zusammengekramt. Unter anderem haben mir Leute aus diesem Forum mit Tipps geholfen. Denjenigen ein Dankeschön. Ich habe sie so aufbereitet, dass ich jederzeit loslegen kann. Leider habe ich keine Quellen parat, da dies über die Zeit hin gewachsen ist. Aber, dies ist keine Doktorarbeit und so ... Jetzt aber los. Die Typendeklaration TShape gibt uns die Möglichkeit, einfache, nicht rechteckige Formen für unsere VCL-Komponente zu bestimmen. Type TShape =(bsRect, bsRounded, bsEllipse); 1. Die Typendeklaration der Komponente selbst. Ich gehe einfach einmal auf die wichtigsten Elemente ein. TVCL_Component (nennt es einfach wie ihr wollt) wird von der Klasse TGraphicControl, ein Bestandteil von Delphi abgeleitet. 2. Im „Private“-Teil benenne ich die Variable, die ich verwende sowie einige Procedures, die ich nach außen hin schütze, indem ich sie in diesen Bereich schreibe. 3. „Public“ – In den Public-Teil kommen die wichtigen „3“. Create, Destroy und Paint. Ich will jetzt nicht erklären, warum man einen Constructor oder einen Destructor benötigt. Die Paint-Prozedur aber sollte in zumindest einem Satz erklärt werden. „Man braucht sie, um etwas von der Komponente sichtbar zu machen.“ Eigenlich logisch, sonst ist es nämlich keine VCL-Komponente. Ich muss aber noch etwas mehr dazu sagen. Man muss ein „Zauberwort“ sagen, damit man die Wirkung der Komponente bereits im Design-Modus beobachten kann „if (csDesigning in ComponentState) then …“. Ansonsten sieht man einfach nur ein Rechteck und dabei wissen wir doch, dass es bei anderen Komponenten auch funktioniert. 4. In den „Published“-Teil packen wir nun endlich alle Prozeduren und Properties, die wir nach außen hin sichtbar darstellen. Wir kennen sie bereits von anderen Komponenten aus dem Objektinspektor. Manche Prozeduren wie MouseDown werden mit den Events gesteuert. Auf diese können wir dann letztlich reagieren wie wir wollen. Nachdem es VCL, also sichtbare Komponenten sind, wollen wir sicherlich mit der Maus arbeiten. Also sind diese Funktionen wichtig. Was noch? Klar, die Properties. Davon gehen wir einfach zwei Tyen. Die, die wir zuweisen können z.B. die Font und Farben, wo andererseits die Ereignisfunktionen sind, die dann, wenn wir sie einsetzen auf Ereignisse reagieren. type TVCL_COMPONENT = class(TGraphicControl) private FColor : TColor; // Variable Hintergrundfarbe FFlat : Boolean; FRadius : Integer; // Variable Radius der Form (rounded) FShape : TBtnShape; // Variable Form der Komponente FMouseOver : boolean; // Variable Maus wird über die Kompo bewegt FPushDown : boolean; // Variable Maus wird über der Kompo gedrückt Points : array [0..MaxPoints] of TPoint; // Variable Point-Array für Polygonale Form procedure SetColor(const Value: TColor); // Setzt die Farbe des Hintergrundes procedure SetRadius(const Value: Integer); // Setzt den Radius der Ecken procedure SetShape(const Value: TBtnShape); // Setzt die Form der Komponente procedure SetFlat(const Value: boolean); // Zeigt die Komponente flach oder erhaben protected public constructor Create(AOwner: TComponent); override; destructor Destroy; override; procedure Paint; Override; // MUSS bei VCL-Komponenten published procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override; procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);override; procedure CMMouseLeave(var Message: TMessage); message CM_MouseLeave; procedure CMMouseEnter(var Message: TMessage); message CM_MouseEnter; property Radius : Integer read FRadius write SetRadius; property Color : TColor read FColor write SetColor; property Shape : TBtnShape read FShape write SetShape; property Flat : boolean read FFlat write SetFlat; Property Caption; property Font; // Setzt den Font property Hint; // Zeigt einen Hint property ShowHint; // Enabling des Hint property Visible; // Sichtbar oder nicht // Reaktion auf die Standardevents der VCL-Komponenten property OnClick; property OnDblClick; property OnMouseActivate; property OnMouseDown; property OnMouseEnter; property OnMouseLeave; property OnMouseMove; property OnMouseUp; end; procedure Register; implementation procedure Register; begin RegisterComponents (‘Komponententitel’, [TVCL_COMPONENT]); End; //================================================== ============================ //== Class-Definition TVCL_COMPONENT //================================================== ============================ constructor TVCL_COMPONENT.Create(AOwner: TComponent); begin inherited; Width := 300; Height:= 400; end; //__________________________________________________ ___________________________ destructor TVCL_COMPONENT.Destroy; begin inherited; end; //__________________________________________________ ___________________________ Procedure TVCL_COMPONENT.Paint; var xw, yh : integer; rgn : Hrgn; R, RCap : Trect; hiColor, loColor : Tcolor; procedure drawcaption; begin Rcap := Rect(0, 0, width-0, height-0); canvas.font.Assign(Font); canvas.brush.style := bsClear; if Fmouseover then Begin canvas.font.color := clBlack; End; if FPushDown then Begin Rcap := Rect(1, 1, width+1, height+1); canvas.font.color := clBlack; End; DrawText (canvas.handle, @Caption[1], -1, Rcap, DT_SINGLELINE or DT_VCENTER or DT_CENTER or DT_END_ELLIPSIS); end; procedure drawframe; begin if Fmouseover or FPushDown then with canvas do begin canvas.font.color := clBlack; brush.color:= FColor; brush.style:= bssolid; case Fshape of bsRect : Rectangle(0,0,xw,yh); bsRounded : RoundRect(0,0,xw,yh,FRadius,FRadius); bsEllipse : Ellipse(0,0,xw,yh); end; end; if ((Not Fmouseover) And (Not FPushDown)) then with canvas do begin brush.color:=FColor; brush.style:=bssolid; pen.color:=loColor; canvas.font.color := Canvas.Font.Color; case Fshape of bsRect : Rectangle(0,0,xw,yh); bsRounded : RoundRect(0,0,xw,yh,FRadius,FRadius); bsEllipse : Ellipse(0,0,xw,yh); end; end; SelectClipRgn(Canvas.handle,rgn); //________________________________ // Hier Code einfügen //________________________________ drawcaption; end; begin R := Rect(0, 0, width, height); if FPushDown then begin RCap.left := Rcap.left + 1; RCap.top := RCap.top + 1; RCap.Right := RCap.right + 1; RCap.Bottom := Rcap.Bottom + 1; end; xw := width-1; yh := height-1; // Gezeichnet wird während des Design, wichtig für den Entwickler if (csDesigning in ComponentState) then begin hiColor :=HiCol; locolor :=LoCol; canvas.pen.style :=pssolid; drawframe; end else // Gezeichnet wird, wenn die Komponente "flach" dargestellt wird begin hiColor :=HiCol; locolor :=LoCol; canvas.pen.style:=psclear; drawframe; end; SelectClipRgn(Canvas.handle,0); DeleteObject(rgn); End; //__________________________________________________ ____________________________ procedure TVCL_COMPONENT.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin FPushDown:=true; invalidate; inherited; end; //__________________________________________________ ____________________________ procedure TVCL_COMPONENT.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin FPushDown:=false; invalidate; inherited; end; //__________________________________________________ ____________________________ procedure TVCL_COMPONENT.CMMouseLeave(var Message: TMessage); begin FMouseOver := false; invalidate; end; //__________________________________________________ ____________________________ procedure TVCL_COMPONENT.CMMouseEnter(var Message: TMessage); begin FMouseOver := true; invalidate; end; //__________________________________________________ ____________________________ procedure TVCL_COMPONENT.SetColor(const Value: TColor); begin if value <> FColor then begin FColor := Value; invalidate; end; end; //__________________________________________________ ____________________________ procedure TVCL_COMPONENT.SetRadius(const Value: Integer); begin FRadius := Value; invalidate; end; //__________________________________________________ ____________________________ procedure TVCL_COMPONENT.SetShape(const Value: TBtnShape); begin FShape := Value; invalidate; end; //__________________________________________________ ____________________________ procedure TVCL_COMPONENT.SetFlat(const Value: boolean); begin FFlat := Value; end; Nicht vergessen, “procedure Register;” wie jede andere Prozedur einer Unit in den Bereich von Implementation einbinden. So, und jetzt stellen wir uns die große Gretchenfrage: “Wie krieg ich das denn nun endlich in eine Komponente, die ich dann auch anwenden kann. 1. Delphi öffnen 2. Ein neues Package anlegen. 3. Eine neue Unit anlegen (wir wollen doch einigermaßen sauber arbeiten. 4. Den Code von hier in die neue Unit kopieren. 5. Den Teil mit der Registrierung nicht vergessen. 6. Kompilieren 7. Im Menü „Komponente“ – „Packages installieren“ [hinzufügen] … suchen und bestätigen 8. Ein Programm schreiben, in das ihr eure neue Komponente einfügen könnt. 9. Erweitern, erweitern und schließlich erweitern. Dies ist, um es nochmal zu erklären, einfach nur ein Pattern, ein Muster, das ich hier für all jene bereitstellen möchte, die wie ich eigentlich keine Vorstellung hatten, wie das aussehen könnte und kein Tutorial, in dem ich zum n-ten mal erklären möchte andere schon ganz gut getan haben. Macht ein paar Versuche und das Verständnis kommt beim ausprobieren, so wie der Hunger beim Essen.
Wolfgang
Grüße und Danke Wolfgang |
Zitat |
Registriert seit: 1. Aug 2007 Ort: Ludwigshafen am Rhein 1.529 Beiträge Delphi 7 Professional |
#2
Tja, auch bei dem besten Tutorial sollten keine DELPHI-Tags für Delphi-Code fehlen. Und da das nach Tutorial aussieht, gehört es auch in die Sparte Tutorial.
Bernhard
Bernhard
Iliacos intra muros peccatur et extra! |
Zitat |
rollstuhlfahrer |
Öffentliches Profil ansehen |
Mehr Beiträge von rollstuhlfahrer finden |
Registriert seit: 11. Okt 2003 Ort: Elbflorenz 44.063 Beiträge Delphi 12 Athens |
#3
Delphi-Quellcode:
Leere Methoden sind eher unschön und stellen nur einen unnötigen Overhead dar.
begin
inherited; end; Aber OK, da dieses als Vorlage für eine Erweiterung dient, könnte man es nochmal durchgehn lassen, da hier ja geplant ist, diesen Code direkt zu erweitern und nicht durch Vererbung. if value <> FColor then . Wieso prüfst du in manchen Settern auf eine Veränderung und in Anderen nicht?
Delphi-Quellcode:
Wieso wird in dem WITH eigentlich nochmal namentlich darauf zugegriffen?
with canvas do
begin ... canvas.xyz TVCL_COMPONENT . Finde ich als Namen etwas unglücklich gewählt: - einmal die etwas delphi-untypische Schreibweise ... CamelCase ist da verbreiteter - und dann ist ist dieses ja nicht die einzige VCL-Komponente Wenn man hier auch noch den OOP-Gedanken etwas mehr durchsetzt (vorallem die Vererbung), dann wäre TBaseVisualComponent ("Basis" für Grundkomponente) doch passender?
Delphi-Quellcode:
Ansonsten kann es nie schaden, wenn man sich beim Schreiben an die Schreibweise der Deklarationen hält bzw. einen einheitlichen Schreibstil nutzt. (vorallem für solche Vorlagen)
canvas.font.Assign(Font);
canvas.brush.style := bsClear; //besser (da es so auch deklariert wurde) Canvas.Font.Assign(Font); Canvas.Brush.Style := bsClear; OK, wenn man schon alles kleinschreiben will, wieso tanzt dann Assign aus der Reihe? Eine ordentliche Codeformatierung erübrigt solche Kennzeichnungen ala //________________________ . // Hier Code einfügen . Diesen Teil in eine eigene virtuelle protected Pethode ausgelagert und man kann der OOP folgend von der Basiskomponente ableiten und seinen Code durch überschreiben (override) hinzufügen. Da man eventuell sowieso das Zeichnen noch implementieren "muß", könnte man dieses auch noch als Abstract deklarieren. Es kann auch nicht schaden, wenn solche eingebetteten Prozeduren, wie drawcaption und drawframe ( Schreibweise? ) ebenfalls als Private oder Protected in die Klassendeklaration auslagert.
Delphi-Quellcode:
DrawComponent > die virtuelle Methode, zum Überschreiben/Einfügen des Zeichencodes
...
canvas.pen.style:=psclear; drawframe; end; DrawCaption > wird ja eh ganz am Ende von DrawFrame aufgerufen, warum also soein verschachtelter Aufruf, wo es auch direkt geht
Delphi-Quellcode:
canvas.font.color := Canvas.Font.Color;
.
...
Pen.Style := psClear; DrawFrame; DrawComponent; DrawCaption; end; Sieht zwar unterschiedlich aus, aber eigentlich macht dieser Code nicht wirklich was. Font.Color := Font.Color; // ohne Canvas, da doch im WITH drin . In deinem Code entsteht vermutlich nicht gleich eine Exception, aber für den Code der Anderen, welcher hier ja noch eingefügt werden soll, kann man sowas nicht wirklich sicherstellen, daher würde ich noch einen Resourcenschutzblock empfehlen (vorallem für rgn).
Neuste Erkenntnis:
Seit Pos einen dritten Parameter hat, wird PoSex im Delphi viel seltener praktiziert. Geändert von himitsu ( 6. Aug 2011 um 00:05 Uhr) |
Zitat |
Ansicht |
Linear-Darstellung |
Zur Hybrid-Darstellung wechseln |
Zur Baum-Darstellung wechseln |
ForumregelnEs ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.
BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus. Trackbacks are an
Pingbacks are an
Refbacks are aus
|
|
Nützliche Links |
Heutige Beiträge |
Sitemap |
Suchen |
Code-Library |
Wer ist online |
Alle Foren als gelesen markieren |
Gehe zu... |
LinkBack |
LinkBack URL |
About LinkBacks |