AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren

Leere VCL-Komponente zur freien Benutzung

Ein Thema von Ookami · begonnen am 5. Aug 2011 · letzter Beitrag vom 5. Aug 2011
Antwort Antwort
Benutzerbild von Ookami
Ookami

Registriert seit: 20. Nov 2009
Ort: Baden Württemberg
77 Beiträge
 
Delphi 2009 Architect
 
#1

Leere VCL-Komponente zur freien Benutzung

  Alt 5. Aug 2011, 20:28
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
  Mit Zitat antworten Zitat
Benutzerbild von rollstuhlfahrer
rollstuhlfahrer

Registriert seit: 1. Aug 2007
Ort: Ludwigshafen am Rhein
1.529 Beiträge
 
Delphi 7 Professional
 
#2

AW: Leere VCL-Komponente zur freien Benutzung

  Alt 5. Aug 2011, 21:56
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!
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
38.608 Beiträge
 
Delphi 10.4 Sydney
 
#3

AW: Leere VCL-Komponente zur freien Benutzung

  Alt 5. Aug 2011, 23:46
Delphi-Quellcode:
begin
  inherited;
end;
Leere Methoden sind eher unschön und stellen nur einen unnötigen Overhead dar.
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:
with canvas do
begin
  ...
  canvas.xyz
Wieso wird in dem WITH eigentlich nochmal namentlich darauf zugegriffen?

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:
canvas.font.Assign(Font);
canvas.brush.style := bsClear;

//besser (da es so auch deklariert wurde)
Canvas.Font.Assign(Font);
Canvas.Brush.Style := bsClear;
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)
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:
  ...
  canvas.pen.style:=psclear;
  drawframe;
end;
DrawComponent > die virtuelle Methode, zum Überschreiben/Einfügen des Zeichencodes
DrawCaption > wird ja eh ganz am Ende von DrawFrame aufgerufen, warum also soein verschachtelter Aufruf, wo es auch direkt geht
Delphi-Quellcode:
  ...
  Pen.Style := psClear;
  DrawFrame;
  DrawComponent;
  DrawCaption;
end;
canvas.font.color := Canvas.Font.Color; .
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).
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
Delphi-Tage 2005-2014

Geändert von himitsu ( 6. Aug 2011 um 00:05 Uhr)
  Mit Zitat antworten Zitat
Themen-Optionen Thema durchsuchen
Thema durchsuchen:

Erweiterte Suche
Ansicht

Forumregeln

Es 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

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +2. Es ist jetzt 16:38 Uhr.
Powered by vBulletin® Copyright ©2000 - 2021, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2021 by Daniel R. Wolf