Einzelnen Beitrag anzeigen

Benutzerbild von sakura
sakura

Registriert seit: 10. Jun 2002
Ort: München
11.412 Beiträge
 
Delphi 11 Alexandria
 
#4
  Alt 26. Jun 2002, 14:42
Hi DP-ler,

jetzt geht es endlich richtig los, der erste echte Schritt in Richtung Komponentenerstellung. Ich habe mich entschieden, vor dem Debug Manager noch eine kleine Runde Komponenten-Basics einzuschieben. Sonst wäre es wahrscheinlich doch ein wenig schwer mitzukommen, wenn man noch nie Komponenten entwickelt hat. Und schließlich ist das ja auch das gewollte Publikum des Artikels.

Estellen einer Komponente

Fangen wir mal ganz vorne an und das auch praktisch. Delphi starten und/oder eventuell geöffnete Projekte schließen. Nochmal: ich nutze die englische Delphi Version, es sollte aber nicht schwer sein, entsprechende Schritte nachzuvollziehen.

Im Menü Datei|Neu wählen wir jetzt das Item Komponente aus und klicken den OK Schalter.
http://www.gatenetwork.com/delphi-sa...ourse03_01.gif

Anschließend präsentiert Delphi uns einen Dialog, der uns die Möglichkeit gibt, den "Rahmen" der Komponente zu erstellen. Dort füllen wir die Startdaten der Komponente ein.
http://www.gatenetwork.com/delphi-sa...ourse03_02.gif

Delphi erstellt uns jetzt eine kleine Unit, welche wie folgt aussieht.
Code:
unit SenselessSample;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;

type
  TSenselessSample = class(TComponent)
  private
    { Private declarations }
  protected
    { Protected declarations }
  public
    { Public declarations }
  published
    { Published declarations }
  end;

procedure Register;

implementation

procedure Register;
begin
  RegisterComponents('gate(n)etwork', [TSenselessSample]);
end;

end.
Damit ist der erste Schritt schon geschafft, wir haben eine Komponente, die, in ihrer jetzigen Form, völlig unnütz ist, aber bereits funktioniert. Aber den Test sparen wir uns noch ein paar Minuten. Etwas sinnvolles wäre doch ganz nett, oder?

Der Aufbau unserer Unit

Was sehen wir jetzt eigentlich? Die Unit für unsere Komponente ist genau wie jede andere Unit aufgebaut.
  • unit - Der Name der Unit, der gleich dem Namen des PAS Datei ist.
  • uses - Die am häufigsten genutzten Units werden automatisch eingebunden. Editieren dieser Liste ist, wie sonst auch, möglich.
  • type - Die Typen Deklarationen starten hier.
  • TSenselessSample = class(TComponent)An dieser Stelle deklarieren wir unsere Komponente. Abgeleitet von TComponent, ist es eine non-visual Komponente, welche der Anwender unseres Programmes nicht direkt zu sehen bekommt (Siehe 2. Posting).
    Folgende Bereiche gehören zu der Komponente.
    • private - In diesem Bereich werden alle Variablen und Methoden deklariert, welche nur innerhalb dieser Klasse für die direkte Verwendung zur Verfügung stehen.
    • protected - In diesem Bereich werden alle Eigenschaften, Variablen und Methoden deklariert, welche nur innerhalb dieser Klasse und aller davon abgeleiteten Klassen zur Verfügung stehen.
    • public - In diesem Bereich werden alle Eigenschaften und Methoden deklariert, welche auch ausserhalb der Klasse zur Verfügung stehen. Diese Eigenschaften und Methoden können somit überall im Projekt (in Verbindung mit einem Objekt der entsprechenden Klasse) verwendet werden. Es wird i.A. als schlechte Programmierpraktik angesehen, wenn in diesem Bereich Variablen deklariert werden, da die Klasse keine Kontrolle über diese haben würde.
      Code:
      Bsp:
      Memo1.Clear;
    • published - In diesem Bereich werden alle Eigenschaften, welche auch im Design-Modus im Object Inspektor manipuliert werden können. (z.B: TLabel.Caption, TEdit.Text; TForm.Icon, ...)
  • procedure Register; - Es wird die Prozedure deklariert, welche Delphi später aufruft, um unsere Komponente in die Palette zu laden.
  • implementation - Hier fängt der eigentliche Teil unserer Unit an - der ganze ausgeführte Code.
  • procedure Register; - Schon wieder. Na gut, hier melden wir die Komponente wirklich an. In diesem Fall wird eine Palette mit dem Namen "gate(n)etwork" eingerichtet.
  • end. - Alles hat mal ein Ende, auch unsere unit

Hinweis: Ein Hinweis sei mir noch gestattet. In den jeweiligen Abschnitten (private, protected, ...) müssen alle Variablen stets vor den Methoden (procedure, function) und Eigenschaften (property) deklariert werden.

"Filling in the meat"

Wollen wir dieser sinnlosen Komponente mal noch ein wenig Inhalt geben. Dazu deklarieren wir insgesamt sechs Eigenschaften. Zwei geben uns die Möglichkeit Fließkommazahlen entgegenzunehmen, vier weitere zeigen jeweils auf eine TLabel Komponente.

Dazu füllen wir den Bereich zwischen private und end; wie folgt aus.
Code:
  published
    { Published declarations }
    property X: Double;
    property Y: Double;
    property Result_Add: TLabel;
    property Result_Sub: TLabel;
    property Result_Mul: TLabel;
    property Result_Div: TLabel;
  end;
Der Bezeichner property teilt Delphi mit, das es sich um eine Eigenschaft der Klasse handelt. In diesem Fall habe ich die Eigenschaften X, Y, Result_Add, Result_Sub, Result_Mul und Result_Div deklariert. Noch sind wir aber nicht fertig. Jetzt positionieren wir den Cursor innerhalb des gerade eingefügten Bereiches und lassen Delphi ein bisschen für uns arbeiten. Einfach mal die Tasten [STRG]+[SHIFT]+[C] drücken und Delphi "completes" unsere Klasse und die ganze Unit.

Code:
unit SenselessSample;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;

type
  TSenselessSample = class(TComponent)
  private
    FX: Double;
    FY: Double;
    FResult_Add: TLabel;
    FResult_Sub: TLabel;
    FResult_Mul: TLabel;
    FResult_Div: TLabel;
    procedure SetResult_Add(const Value: TLabel);
    procedure SetResult_Div(const Value: TLabel);
    procedure SetResult_Mul(const Value: TLabel);
    procedure SetResult_Sub(const Value: TLabel);
    procedure SetX(const Value: Double);
    procedure SetY(const Value: Double);
    { Private declarations }
  protected
    { Protected declarations }
  public
    { Public declarations }
  published
    { Published declarations }
    property X: Double read FX write SetX;
    property Y: Double read FY write SetY;
    property Result_Add: TLabel read FResult_Add write SetResult_Add;
    property Result_Sub: TLabel read FResult_Sub write SetResult_Sub;
    property Result_Mul: TLabel read FResult_Mul write SetResult_Mul;
    property Result_Div: TLabel read FResult_Div write SetResult_Div;
  end;

procedure Register;

implementation

procedure Register;
begin
  RegisterComponents('gate(n)etwork', [TSenselessSample]);
end;

{ TSenselessSample }

procedure TSenselessSample.SetResult_Add(const Value: TLabel);
begin
  FResult_Add := Value;
end;

procedure TSenselessSample.SetResult_Div(const Value: TLabel);
begin
  FResult_Div := Value;
end;

procedure TSenselessSample.SetResult_Mul(const Value: TLabel);
begin
  FResult_Mul := Value;
end;

procedure TSenselessSample.SetResult_Sub(const Value: TLabel);
begin
  FResult_Sub := Value;
end;

procedure TSenselessSample.SetX(const Value: Double);
begin
  FX := Value;
end;

procedure TSenselessSample.SetY(const Value: Double);
begin
  FY := Value;
end;

end.
Da wir uns entschieden haben, TLabel zu nutzen müssen wir die uses Klausel ein wenig erweitern.
Code:
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;

wird zu

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;
Was ist alles geschehen?

Schauen wir uns einfach mal die aufgefüllte Zeile einer property an.

Code:
    property X: Double;


wird zu

    property X: Double read FX write SetX;
Delphi hat für uns die üblichste Behandlungsart einer Eigenschaft eingetragen. Das heisst, wenn jemand die Eigenschaft X ermitteln will, so wird der Wert der Variablen FX direkt zurückgegeben. Delphi hat auch gleich die Variable FX für uns im private Bereich deklariert. Danke Borland. Ausserdem hat Delphi auch gleich definiert, wie Änderungen der Eigenschaft übernommen werden. Damit wir bei eventuellen Änderungen reagieren können, hat Delphi das Setzen der Variable nicht direkt erledigt, sondern eine "Wrapper" Methode SetX erstellt. Diese Methode übernimmt das Speichern der Änderungen.
Code:
procedure TSenselessSample.SetX(const Value: Double);
begin
  FX := Value;
end;
Diese Art der Variablenbehandlung kommt uns jetzt noch sehr zu gute.

"And Action!"

Jetzt fügen wir noch eine Methode zum private Bereich hinzu, damit wir die die Hauptfunktionalität noch angenehm verpacken können.
Code:
  private
    ...
    procedure UpdateLabels;
    { Private declarations }
  protected
Und noch einmal [STRG]+[SHIFT]+[C] für die automatische Code-[b]C[/c]ompletion drücken!
Code:
procedure TSenselessSample.UpdateLabels;
begin

end;
Jetzt haben wir unsere Hauptmethode fast fertig. Es müssen nur noch ein paar Zeilen Code hinein und das Ergebnis sieht wie folgt aus.
Code:
procedure TSenselessSample.UpdateLabels;
begin
  // summe ermitteln und darstellen
  if Assigned(FResult_Add) then
  try
    // label für summe wurde zugewiesen
    FResult_Add.Caption := FloatToStr(FX + FY);
  except
    on E: Exception do
      // oops
      FResult_Add.Caption := E.Message;
  end;
  // differenz ermitteln und darstellen
  if Assigned(FResult_Sub) then
  try
    // label für differenz wurde zugewiesen
    FResult_Sub.Caption := FloatToStr(FX - FY);
  except
    on E: Exception do
      // oops
      FResult_Sub.Caption := E.Message;
  end;
  // produkt ermitteln und darstellen
  if Assigned(FResult_Mul) then
  try
    // label für produkt wurde zugewiesen
    FResult_Mul.Caption := FloatToStr(FX * FY);
  except
    on E: Exception do
      // oops
      FResult_Mul.Caption := E.Message;
  end;
  // quotient ermitteln und darstellen
  if Assigned(FResult_Div) then
  try
    // label für quotient wurde zugewiesen
    FResult_Div.Caption := FloatToStr(FX / FY);
  except
    on E: Exception do
      // oops
      FResult_Div.Caption := E.Message;
  end;
end;
Für jedes Ergebnis überprüfen wir zuerst, ob ein entsprechendes Label vorhanden ist und anschließend versuchen wir das Ergebnis zu ermitteln und darzustellen. Wenn das fehlschlagen sollte, liefern wir eine entsprechende Fehlermeldung zurück.

Jetzt tragen wir in alle Set Methoden noch als letzten den Aufruf zu unserer UpdateLabels; Methode ein und sind soweit fertig. Einen Schönheitsfehler müssen wir noch beseitigen. Da es geschehen kann, dass eines der zugewiesenen Labels vom Form entfernt wird, müssen wir abschliessend noch dieses Ereignis abfangen und die entsprechden FResult_XXX Variabel auf nil setzen! Dazu überlaufen wir die Standard Notification Methode.
Code:
  protected
    { Protected declarations }
    procedure Notification(
      aComponent: TComponent; Operation: TOperation
    ); override;
Den kompletten Code könnt Ihr jetzt lesen.
Code:
unit SenselessSample;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;

type
  TSenselessSample = class(TComponent)
  private
    FX: Double;
    FY: Double;
    FResult_Add: TLabel;
    FResult_Sub: TLabel;
    FResult_Mul: TLabel;
    FResult_Div: TLabel;
    procedure SetResult_Add(const Value: TLabel);
    procedure SetResult_Div(const Value: TLabel);
    procedure SetResult_Mul(const Value: TLabel);
    procedure SetResult_Sub(const Value: TLabel);
    procedure SetX(const Value: Double);
    procedure SetY(const Value: Double);
    procedure UpdateLabels;
    { Private declarations }
  protected
    { Protected declarations }
    procedure Notification(
      aComponent: TComponent; Operation: TOperation
    ); override;
  public
    { Public declarations }
  published
    { Published declarations }
    property X: Double read FX write SetX;
    property Y: Double read FY write SetY;
    property Result_Add: TLabel read FResult_Add write SetResult_Add;
    property Result_Sub: TLabel read FResult_Sub write SetResult_Sub;
    property Result_Mul: TLabel read FResult_Mul write SetResult_Mul;
    property Result_Div: TLabel read FResult_Div write SetResult_Div;
  end;

procedure Register;

implementation

procedure Register;
begin
  RegisterComponents('gate(n)etwork', [TSenselessSample]);
end;

{ TSenselessSample }

procedure TSenselessSample.Notification(
  aComponent: TComponent; Operation: TOperation
);
begin
  inherited Notification(aComponent, Operation);
  if (Operation = opRemove) then
  begin
    if aComponent = FResult_Add then
      FResult_Add := nil;
    if aComponent = FResult_Sub then
      FResult_Sub := nil;
    if aComponent = FResult_Mul then
      FResult_Mul := nil;
    if aComponent = FResult_Div then
      FResult_Div := nil;
  end;
end;

procedure TSenselessSample.SetResult_Add(const Value: TLabel);
begin
  FResult_Add := Value;
  UpdateLabels;
end;

procedure TSenselessSample.SetResult_Div(const Value: TLabel);
begin
  FResult_Div := Value;
  UpdateLabels;
end;

procedure TSenselessSample.SetResult_Mul(const Value: TLabel);
begin
  FResult_Mul := Value;
  UpdateLabels;
end;

procedure TSenselessSample.SetResult_Sub(const Value: TLabel);
begin
  FResult_Sub := Value;
  UpdateLabels;
end;

procedure TSenselessSample.SetX(const Value: Double);
begin
  FX := Value;
  UpdateLabels;
end;

procedure TSenselessSample.SetY(const Value: Double);
begin
  FY := Value;
  UpdateLabels;
end;

procedure TSenselessSample.UpdateLabels;
begin
  // summe ermitteln und darstellen
  if Assigned(FResult_Add) then
  try
    // label für summe wurde zugewiesen
    FResult_Add.Caption := FloatToStr(FX + FY);
  except
    on E: Exception do
      // oops
      FResult_Add.Caption := E.Message;
  end;
  // differenz ermitteln und darstellen
  if Assigned(FResult_Sub) then
  try
    // label für differenz wurde zugewiesen
    FResult_Sub.Caption := FloatToStr(FX - FY);
  except
    on E: Exception do
      // oops
      FResult_Sub.Caption := E.Message;
  end;
  // produkt ermitteln und darstellen
  if Assigned(FResult_Mul) then
  try
    // label für produkt wurde zugewiesen
    FResult_Mul.Caption := FloatToStr(FX * FY);
  except
    on E: Exception do
      // oops
      FResult_Mul.Caption := E.Message;
  end;
  // quotient ermitteln und darstellen
  if Assigned(FResult_Div) then
  try
    // label für quotient wurde zugewiesen
    FResult_Div.Caption := FloatToStr(FX / FY);
  except
    on E: Exception do
      // oops
      FResult_Div.Caption := E.Message;
  end;
end;

end.
Installieren der Komponente

Der letzte wichtige Schritt ist die Installation der Komponente in der Delphi Palette. Ausserdem könnte man jetzt noch ein Icon für die Komponente erstellen, aber dazu kommen wir ein anderes Mal, wenn es nicht gerade diese sinnlose Komponente ist.

Um die Komponente zu installieren, wählt im Menü Komponenten|Komponente installieren. Delphi trägt automatisch alles ein und schlägt Euch das Standard User-Komponenten Packet vor. Das ist auch in Ordnung. Mit OK bestätigen.

http://www.gatenetwork.com/delphi-sa...ourse03_03.gif

Anschliessend seht Ihr das Fenster zum kompilieren und installieren des Packages.

http://www.gatenetwork.com/delphi-sa...ourse03_04.gif

"Kompilieren" und anschließend "Installieren" drücken. Fertig.

Der Test des Sinnlosen

Alle offenen Projekte schließen und eine neue Anwendung anlegen. 4 Labels auf das Form packen und von der "gate(n)etwork" Seite unsere Komponente auswählen und auf das Form packen. Jetzt nur noch die vier Labels den entsprechenden Eigenschaften zuordnen und ein wenig im Objekt Inspektor rumspielen.

Viel Spass, hier gibt es alles noch zum Download

Daniel W.
Ich bin nicht zurück, ich tue nur so