|
Tutorial geschlossen
|
Hi DP-ler,
wollen wir mal wieder. Die Beiträge zum Thema Threads habe ich leider nicht mehr und zur Zeit steht mir auch der Kragen nicht so sehr danach. Deshalb werde ich ganz einfach ein bisschen zum Thema Komponenten-Erstellung schreiben. Wenn Ihr Wünsche habt, zu bestimmten Komponenten und Features, welche im Laufe der Zeit hier auftauchen sollten, so lasst es mich einfach wissen. Am besten per Private Message. Kommentare zu dem hier geschriebenen einfach in den Thread schreiben. Mit der Zeit werde ich diese dann zusammenfassen, damit der Überblick nicht verloren geht. Nun aber zum Tutorial In diesem Tutorial möchte ich Euch ein wenig der Komponentenentwicklung näher bringen. Angefangen werde ich mit ein wenig trockener Theorie und dann geht es langsam weiter. Anfangen werden wir mit einer non-visual Komponente, welche es ermöglicht Benutzereinstellungen in der Registry zu speichern, weiter geht es mit einer Graphic Control, welche sich auf dem Form darstellt, aber keinen Fokus erhalten kann. Anschliessend werden wir eine interaktive Komponente erstellen, mit Collections arbeiten und abschliessend eine DB-Komoponente. Was sonst noch? An den Bereich der Komponentenentwicklung schliessen sich weitere Delphi-Bereiche an
Einige dieser Bereiche werden wir gesondert betrachten und andere werden direkt in die oben genannten Bereich mit einfließen. Was brauchen wir, um mitzumachen Ein wenig Zeit, Geduld und Delphi. Für Teile des Kurses sind mindestens die Professional Versionen nötig. Das meiste sollte ab Version 4 laufen, erstellt werden alle Beispiele mit Delphi 5 Enterprise. In Delphi 6 gab es ein paar Umbenennungen und Teilungen der vorhandenen Units. Einfach ausprobieren... Ausserderm sollten die Grundlagen der Objekt-orientierten Programmierung vorliegen, damit wir nicht bei nil anfangen. Meine Delphi Version ist Englisch, daher werden alle Screen Shots in Englisch sein. Wird schon schief gehen...
Ich bin nicht zurück, ich tue nur so
|
Delphi 12 Athens |
#2
Hi DP-ler,
getrennt von der Einleitung gibt es jetzt erst einmal die erste Runde Theorie. Damit es nicht zu trocken wird, fangen wir mit einer kleinen Grafik an, welche so ähnlich im Delphi 5 Entwickler Handbuch (31-2) zu finden ist, da das aber nicht bei jedem vorliegt, habe ich diese schnell noch einmal erstellt. ![]() Alle Klassen, welche in Delphi erstellt werden sind ultimativ von der Basis-Klasse TObject abgeleitet. Die Klasse TObject definiert die grundlegenden Methoden für jedes Objekt, welches in OO-Pascal (der Sprache von Delphi) erstellt wird. Abgeleitet von TObject ist die Klasse TPersistent. Diese Klasse stellt grundlegende Methoden zum Kopieren (Assign/AssignTo) und zum Streamen nicht-publizierter Eigenschaften zur Verfügung. Die Basis dieser Klasse ermöglicht es der Delphi-IDE mit Drag-&-Drop und dem Object Inspector uns Programmierern mit der VCL zu arbeiten. VCL VCL ist die Abkürzung für die Visual Component Library. Die VCL ist die Sammlung aller installierten Komponenten, welche via Drag-&-Drop auf dem Form im Design-Modus angeordnet werden können. Die Nutzung der VCL macht es uns Programmierern leicht, sich auf das Eigentliche, die Erstellung der Programmlogik, zu konzentrieren, da die Erstellung der Benutzerschnittstellen in einem Bruchteil der Zeit erledigt ist. Dieser Vorteil bringt natürlich auch seine (Geschmackssache: großen) Nachteile mit sich. Die Programme (EXE Dateien) werden um einiges größer, da immer das komplette Handling der VCL mitkompiliert wird. Aber wozu gibt es große Festplatten? Ausserdem werden die Programme meist auch etwas langsamer gegenüber nonVCL-Applikationen. Aber der Unterschied ist nur gering - und ausserdem gibt es schnelle Prozessoren. Wer trotzdem Interesse an nonVCL Programmen hat, sollte sich mal auf ![]() Aber nun zurück zu den Komponenten und der VCL... Was ist der Unterschied zwischen Komponenten und Controls? Zurückblickend auf die obige Grafik ist die Antwort eigentlich schnell gefunden. Alle Controls sind Komponenten, da sie von TComponent abgeleitet werden, aber nicht alle Komponenten sind Controls. Was heisst das jetzt für uns? Die Klasse TComponent führt alle Methoden ein, welche die Delphi IDE benötigt, um Komponenten in der VCL Palette darzustellen. Das heisst für uns, dass alle VCL Komponenten, welche wir erstellen wollen generell von der Klasse TComponent abgeleitet werden müssen. Ein solches Beispiel ist zum Beispiel die Komponente TTimer von der System Palette. Eine Komponente, welche direkt von TComponent, nicht von TControl, abgeleitet wurde, wird im Delphi-Form-Designer generell durch ein kleines Quadrat dargestellt. Darunter erscheint, sofern man diese Option eingschaltet hat, der Name der Komponente. Diese Komponenten sind zur Laufzeit des Programmes nicht sichtbar und arbeiten im Hintergrund. Solche Komponenten werden direkt durch den Code vom Programmierer gesteuert. Auch die Dialog-Komponenten, wie zum Beispiel TOpenDialog gehören in die Gruppe der Komponenten, nicht zu den Controls. Die Komponente ist zur Laufzeit nicht zu sehen. Sie kann lediglich einen Dialog anzeigen, welcher ansonsten nicht viel mit der Komponente zu tun hat. Was sind jetzt Controls? Eigentlich ist das jetzt recht einfach - der ganze Rest der Komponenten der VCL Palette, also all jene, welche auch zur Laufzeit zu sehen sind. Da wären die grafischen Controls wie das TLabel und TPaintBox und die "windowed" Controls wie TEdit und TButton. TGraphicControl und TWinControl Was soll dieser Unterschied? Eigentlich recht einfach. TGraphicControl ist die Basis aller Controls, welche sich auf dem Form darstellen können, aber keinen Windows-Handle haben und somit auch nicht den Fokus erhalten können. TWinControl steht nicht für Controls welche durch das OS (Windwows) zur Verfügung gestellt werden, sondern für "windowed" Controls. Das heisst, diese Controls "registrieren" sich bei Windows und erhalten einen Windows-Handle. Dadurch sind solche Controls in der Lage Windows-Botschaften zu erhalten. Diese Controls können auch, sofern das vorgesehen ist, den Fokus durch den Benutzer erhalten. Für nächsten Mittwoch ist die nächste Runde angesetzt. Dann werden wir nicht gleich mit dem Erstellen einer Komponente anfangen, sondern noch ein wenig Theorie pauken. Thema werden die Exceptions sein. Bis dann! [edit 1]Luckies Link auf den aktuellsten Stand gebracht ![]()
Daniel Lizbeth
|
Delphi 12 Athens |
#3
Hi DP-ler,
wie versprochen fangen wir mit den Exceptions an. Exceptions sind Delphis Weg, um Fehler "abzufangen" und dem Anwender einer Anwendung darzustellen. Mit Hilfe der Konstrukte try...except...end und try...finally...end kann der Programmier im Falle einer Exception (eines Fehlers) diesen abfangen bzw. notwendige "Aufräumarbeiten" werden durchgeführt, bevor der Fehler angezeigt wird. Dieser Abschnitt des Kurses bildet die Grundlage für den nächsten. Wir werden als erstes eine Komponente beginnen, welche es uns ermöglicht die Standardfehlermeldung von Delphi zu umgehen und eigene Wege zu beschreiten. try...except...end Es gibt in der Programmierung immer wieder Bereiche, in welchen man davon ausgehen muss, dass es füher oder später zu Problemen im Code kommen wird. Die wohl üblichste Fehlerquelle ist eine falsche Eingabe durch den Benutzer. Fangen wir doch mal mit einem sehr einfachen Beispiel an. Der Nutzer soll eine Zahl eingeben, welche später für Rechenoperationen genutzt wird. Wenn der Nutzer aber etwas anderes (oder nichts) eingibt, dann liegt kein gültiger Wert für die Rechenoperation vor. Um das Beispiel ein wenig zu verdeutlichen, werden wir die folgende Prozedur etwas genauer betrachten.
Code:
Zugegeben, die Funktion ist sehr einfach. Aber anhand dieser Funktion lässt sich das ganze sehr leicht darstellen. Übergeben wird der Parameter Zahl als String. Zahl wird in eine Fließkommazahl (StrToFloat) umgewandelt. Dieser Wert wird dann genutzt als Teiler für den Wert 1000 genutzt.
function DivideIt_01(Zahl: String): String;
begin Result := FloatToStr(1000 / StrToFloat(Zahl)); end; Es gibt jetzt zwei typische, potentielle Fehlerquellen.
Code:
Das Programm versucht die Anweisung Result := FloatToStr(1000 / StrToFloat(Zahl)); auszuführen. Sollte dabei ein Problem auftreten, wird der Code innerhalb des except Blockes ausgeführt. In diesem Fall geben wir einen "freundlichen" Hinweis zurück.
function DivideIt_02(Zahl: String): String;
begin try Result := FloatToStr(1000 / StrToFloat(Zahl)); except Result := 'Der übergebene Wert ist leider ungültig'; end; end; Hinweis: Der EXCEPT Block wird nur im Falle einer aufgetretenen Exception ausgeführt. try...finally...end Oft genug geschieht es in der Programmierung, dass wir einige Resourcen (Speicher) des Computers in Anspruch nehmen müssen. Anschließend sollten wir diese Resourcen stets wieder freigeben. Wieder ein denkbar einfaches Beispiel
Code:
Dieses Beipsiel liefert uns die Anzahl der Zeilen in einer (Text-)Datei zurück. In diesem Beispiel nutze ich eine Stringliste. Als erstes muss diese initialisiert werden. Anschliessend kann die Datei geladen werden, um die Anzahl der Zeilen zu ermitteln. Am Ende wird die Stringliste wieder freigegeben.
function CountLines(FileName: String): Integer;
var Strings: TStringList; begin // ressourcen reservieren Strings := TStringList.Create; // datei in die string liste laden Strings.LoadFromFile(FileName); // zeilenanzahl zurückliefern Result := Strings.Count; // string liste wieder zerstören Strings.Free; end; Was passiert jetzt, wenn die Datei nicht existiert oder der Nutzer nicht das Recht hat diese zu öffnen? Es wird eine Exception (ein Fehler) in der Zeile Strings.LoadFromFile(FileName); erzeugt und die Verarbeitung abgebrochen. Die Zeile zum freigeben der Stringliste Strings.Free; wird nie ausgeführt, dass heißt, es ist ein kleines Speicherloch entstanden... Da kommt der try...finally...end Block recht gelegen. Dieser Block wird immer ausgeführt. Damit können wir als Programmierer garantieren, dass alle Ressourcen wieder freigegeben werden. In unserem Beispiel sähe das wie folgt aus.
Code:
Auch wenn ein Fehler beim Laden der Datei auftritt, ist garantiert, dass die Stringliste zerstört wird, unabhängig davon, ob alle vorhergehenden Schritte ausgeführt worden sind, oder nicht.
function CountLines(FileName: String): Integer;
var Strings: TStringList; begin // ressourcen reservieren Strings := TStringList.Create; try // datei in die string liste laden Strings.LoadFromFile(FileName); // zeilenanzahl zurückliefern Result := Strings.Count; finally // string liste wieder zerstören Strings.Free; end; end; Hinweis: Der FINALLY Block wird immer ausgeführt. Auch wenn mitten im Code Befehle zum Abbrechen der Prozedur oder einer Schleife (z.B. Exit, Break) auftauchen. Exceptions erzeugen Um das Erzeugen eigener Exceptions zu demonstrieren, kehren wir zu unserem ersten Beispiel zurück.
Code:
Das Erzeugen einer Exception ist denkbar einfach. Erstellt wird eine Exception durch die Einbindung des reservierten Wortes raise. Als Parameter wird ein Exception Object (bzw. ein davon abgeleitetes Objekt) erstellt. Im obigen Beispiel testen wir, ob ein leerer String übergeben wird; in diesem Fall erstellen eine Exception mit einer entsprechenden Fehlermeldung.
function DivideIt_03(Zahl: String): String;
begin if Zahl = '' then raise Exception.Create('Ein leerer Parameter ist unbrauchbar!'); Result := FloatToStr(1000 / StrToFloat(Zahl)); end; Eigene Exception-Typen deklarieren Um im späteren Verlauf eines Programmes aufgetretene Fehler leichter zu erkennen, gibt Delphi uns die Möglichkeit eigene Fehlerklassen zu erstellen.
Code:
In diesem Beispiel erstellen wir eine Fehlerklasse für leere (unbrauchbare) Parameter. Wenn wir diese Klasse als Fehlerklasse in unser letztes Beispiel integrieren, dann sieht die Funktion wie folgt aus.
type
EEmptyParam = class(Exception) public constructor Create; end; { EEmptyString } constructor EEmptyParam.Create; begin inherited Create('Ein leerer Parameter ist unbrauchbar!'); end;
Code:
Für den Anwender des Programmes ergeben sich daraus keine Änderungen, aber sehr wohl für den Programmierer, wie wir im folgenden Abschnitt (Excpetions erkennen) sehen werden.
function DivideIt_04(Zahl: String): String;
begin if Zahl = '' then raise EEmptyParam.Create; Result := FloatToStr(1000 / StrToFloat(Zahl)); end; Exceptions erkennen Nachdem wir die Exceptions verstanden, erstellt und abgefangen haben, müssen wir diese für korrektes und sinnvolles Handling nur noch wiedererkennen. Dazu gibt es in einem try...except...end Block die Möglichkeit eine Exception-Typ-Überprüfung vorzunehmen.
Code:
Durch die Überprüfung on E: Exceptiontyp können wir den Typ eines Fehlers ermitteln und entsprechend reagieren. Die folgende Liste nennt die häufigsten Fehlertypen
function DivideIt_05(Zahl: String): String;
begin try if Zahl = '' then raise EEmptyParam.Create; Result := FloatToStr(1000 / StrToFloat(Zahl)); except // leere parameter erkennung on E: EEmptyParam do Result := 'Leerer Parameter'; // division durch 0 erkannt on E: EZeroDivide do Result := 'Division durch Null (0)'; // übergebener parameter ist keine zahl on E: EConvertError do Result := 'Keine Zahl (NaN)'; // was gibt es sonst noch ??? on E: Exception do Result := '??? ' + E.Message; end; end;
Das Beispiel... ![]() Der nächste Schritt Im nächsten Teil dieses Tutorials fangen wir mit der Erstellung einer Komponente an. Diese Komponente ermöglicht es uns das Aussehen von Fehlermeldungen zu beeinflussen oder gar komplett Kontext-abhängige Fehlerbehandlungen durchzuführen. Mal sehn was kommt... Tippfehler gefixt! FuckRacism
Daniel Lizbeth
|
Delphi 12 Athens |
#4
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. ![]() 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. ![]() Delphi erstellt uns jetzt eine kleine Unit, welche wie folgt aussieht.
Code:
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?
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. Der Aufbau unserer Unit Was sehen wir jetzt eigentlich? Die Unit für unsere Komponente ist genau wie jede andere Unit aufgebaut.
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:
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.
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;
Code:
Da wir uns entschieden haben, TLabel zu nutzen müssen wir die uses Klausel ein wenig erweitern.
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.
Code:
Was ist alles geschehen?
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs; wird zu uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; Schauen wir uns einfach mal die aufgefüllte Zeile einer property an.
Code:
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.
property X: Double;
wird zu property X: Double read FX write SetX;
Code:
Diese Art der Variablenbehandlung kommt uns jetzt noch sehr zu gute.
procedure TSenselessSample.SetX(const Value: Double);
begin FX := Value; end; "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:
Und noch einmal [STRG]+[SHIFT]+[C] für die automatische Code-[b]C[/c]ompletion drücken!
private
... procedure UpdateLabels; { Private declarations } protected
Code:
Jetzt haben wir unsere Hauptmethode fast fertig. Es müssen nur noch ein paar Zeilen Code hinein und das Ergebnis sieht wie folgt aus.
procedure TSenselessSample.UpdateLabels;
begin end;
Code:
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.
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; 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:
Den kompletten Code könnt Ihr jetzt lesen.
protected
{ Protected declarations } procedure Notification( aComponent: TComponent; Operation: TOperation ); override;
Code:
Installieren der Komponente
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. 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. ![]() Anschliessend seht Ihr das Fenster zum kompilieren und installieren des Packages. ![]() "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, ![]() ![]()
Daniel Lizbeth
|
Delphi 12 Athens |
#5
Hi DP-ler,
lang ist es her, aber jetzt kann ich mich auch mal wieder diesem Tutorial ein wenig widmen. Seit dem letzten Mal ist nun auch Delphi 7 auf dem Markt und deshalb möchte ich, neben der normalen Komponentenerstellung auch ModelMaker etwas genauer unter die Lupe nehmen. ModelMaker ist bereits seit mehreren Jahren verfügbar und liefert für Delphi ab Version 1 die Möglichkeit UML für Delphi zu erstellen, Projekte zu verwalten und sogar automatisch Hilfedateien für erstellte Komponenten zu erzeugen. Es wird also interessant. Für diejenigen, welche ein "ältere" Delphiversion nutzen, empfehle ich einen Besuch bei ![]() Hinweis: Dieses Mal werde ich keine Neuerungen in der Komponentenenticklung zeigen, sondern lediglich unsere letzte Komponente (TSenselessSample) noch einmal erstellen und Zurhilfenahme von ModelMaker. Let's get started Startet also Delphi und aus der Delphi IDE heraus Modelmaker. Wer zwei Monitore an seinem Rechner hat, ist jetzt klar im Vorteil. ![]() In ModelMaker wählt aus dem Menü File, den zweiten Punkt New from Default. Wie Ihr merkt, ich habe mal wieder die englische Delphiversion bestellt. Aber die mit der deutschen Variante werdens schon schaffen. What you see is what you get oder so ähnlich. Auf der linken Seite von ModelMaker seht ihr oben die verschiedenen Design-Module (Ansicht DM), welche Euch zur Verfügung stehen. Darunter befinden sich die Deklarationen (Ansicht Dekl) innerhalb der aktuell ausgewählten Klasse. Rechts ist die Arbeitsfläche. Die Darstellung der Arbeitsfläche hängt generell von der aktuellen Aufgabe ab. Nach dem Laden des "Default Templates" seht ihr normalerweise ein einfaches Diagramm, welches die wichtigsten Klassen für Delphi Projekte darstellt. Let's get started with working Als wir die Komponente in Delphi erstellt haben, haben wir im Dial Neu den Punkt Component ausgewählt. Anschließend haben wir als Ancestor den Typ TComponent ausgewählt, den Namen unserer Komponente auf TSenselessSample gestzt, eine Quelldatei festgelegt, und ausgesucht, auf welcher Palettenseite die Komponente installiert wird. Diese Schritte werden wir jetzt in ModelMaker nachvollziehen. In der Ansicht DM wählt die Klasse TComponent aus, drückt die rechte Maustaste und wählt den Menüpunkt Add Descendant, setzt den Namen auf TSenselessSample. ModelMaker fügt die neue Komponente automatisch an der richtigen Stelle in der Hierarchie ein. Via Drag-n-Drop zieht die neue Komponente in das Standard Diagramm. ModelMaker erkennt automatisch die Beziehung zur Basiskomponente und erstellt einen visuellen Link. Übung Ändert manuell den visuellen Link und beobachtet, wie sich die Struktur der Klassenübersicht automatisch anpasst. Erstellen der Unit für die Komponente Wählt in der Ansicht DM den Reiter Units aus. Im Bereich Classes not assigned to units ist unsere Komponente TSenselessSample zu sehen. Klickt mit der rechten Maustaste in die Ansicht und wählt Add Unit aus dem Kontextmenü. Wählt einen neuen Namen und Ordner für die Unit aus. Im unteren Bereich fügt Ihr die TSenselessSample Komponente zur Unit hinzu und definiert auf welcher Palettenseite diese dargestellt werden soll. Soweit, so gut! Automatisches erstellen der Unit In der oberen Toolbar seht Ihr neben den Standard Neu, Laden, Speichern, ... Buttons ein geöffnetes und ein geschlossenes Schloss. Klickt auf das geöffnete Schloss, um ModelMaker zu erlauben, den Source Code zu generieren. In der Ansicht DM, im Bereich Units, wählt die neue Unit aus, klickt mit der rechten Maustaste auf den entsprechenden Eintrag und wählt den Menüpunkt Enable Auto Generation. Ein grüner Pfeil ist davor zu sehen. Im Hauptmenü klickt jetzt auf den Punkt Delphi und selektiert den Eintrag Open Unit. Jetzt könnte Ihr den erzeugten Quellcode in Delphi sehen. Sehr ähnlich dem Quellcode, wie wir ihn auch das letzte Mal erstellt haben. Aber noch ist nicht annähernd die Funktionalität vorhanden, wie wir diese bei der letzten Version von TSenselessSample hatten. Erstellen der Properties Ob Ihr jetzt in der Ansicht DM :: Classes, oder Ansicht DM :: Units arbeitet ist gar nicht so wichtig, wichtig ist jedoch, dass Ihr sicher stellt, dass unsere Komponente TSenselessSample ausgewählt ist. Im unteren Bereich seht die Ansicht Dekl (Deklarationen). Die ersten vier Icons stehen für die Funktionen
Über das dritte Icon fügen wir jetzt sechs Eigenschaften (Properties) hinzu.
Wenn Ihr Euch jetzt den Source Code in Delphi anschaut, dann ist dieser jetzt bereits erheblich angewachsen. Alle Deklarationen sind bereits eingefügt und die wichtigsten Code Einträge (SetXXX) sind auch schon da. Nur compilieren würde zur Zeit nicht gehen, da wir im Code auf den Datentyp (Klassentyp) TLabel verweisen. Deshalb müssen wir eine manuelle Anpassung im Code vornehmen. Diese darf aber nicht in Delphi geschehen, sondern muss in ModelMaker geschehen, da sonst bei der nächsten Erstellung des Source Codes die Änderung wieder verloren wäre. Hinweis: Es gibt auch die Möglichkeit Code nach ModelMaker zu re-importieren, aber das gehört jetzt nicht zu diesem Kurs. Genaueres dazu findet Ihr auf der Companion Tools CD 1, bei den Dokumentationen. Da ist auch das komplette Handbuch für ModelMaker als PDF, mit knapp 120 Seiten Umfang. Ändern des Unit Source Codes Wählt in der Ansicht DM :: Units unsere Unit aus. Auf der Arbeitsfläche müsst Ihr, sofern es nicht bereits geschehen ist, oben den Reiter Unit Code anwählen. Jetzt seht Ihr den Unit Code, welcher als Vorlage für unsere Komponente dient. Dort seht Ihr auch diese uses Klausel, welche angepasst werden muss, wie auch schon im letzten Beitrag geschehen.
Code:
Jetzt können wir den nächsten Schritt nehmen.
[b]uses[/b]
SysUtils, Windows, Messages, Classes, Graphics, Controls, Forms, Dialogs; [color=#0000FF] // wird zu // [/color] [b]uses[/b] SysUtils, Windows, Messages, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; Die Methode UpdateLabels Um für alle Labels Ergebnisse anzeigen zu können, fügen wir die Methode UpdateLabels zum Model. Anstatt das Icon Property in der Ansicht Dekl zu nutzen, klicken wir dieses Mal auf des zweite Icon (Methode). Der Dialog unterscheidet sich nur geringfügig von der letzten Version. Folgende Einstellungen sind vorzunehmen
Jetzt müssen wir noch den Code einfügen, welcher ausgeführt werden soll. Dazu klickt in der Arbeitsfläche auf den Reiter Implementation und fügt den folgenden Code ein.
Code:
Putting UpdateLabels to work
[color=#000080]// summe ermitteln und darstellen[/color]
[b]if[/b] Assigned(FResult_Add) [b]then[/b] [b]try[/b] [color=#000080]// label für summe wurde zugewiesen[/color] FResult_Add.Caption := FloatToStr(FX + FY); [b]except[/b] [b]on[/b] E: Exception [b]do[/b] [color=#000080]// oops[/color] FResult_Add.Caption := E.Message; [b]end[/b]; [color=#000080]// differenz ermitteln und darstellen[/color] [b]if[/b] Assigned(FResult_Sub) [b]then[/b] [b]try[/b] [color=#000080]// label für differenz wurde zugewiesen[/color] FResult_Sub.Caption := FloatToStr(FX - FY); [b]except[/b] [b]on[/b] E: Exception [b]do[/b] [color=#000080]// oops[/color] FResult_Sub.Caption := E.Message; [b]end[/b]; [color=#000080]// produkt ermitteln und darstellen[/color] [b]if[/b] Assigned(FResult_Mul) [b]then[/b] [b]try[/b] [color=#000080]// label für produkt wurde zugewiesen[/color] FResult_Mul.Caption := FloatToStr(FX * FY); [b]except[/b] [b]on[/b] E: Exception [b]do[/b] [color=#000080]// oops[/color] FResult_Mul.Caption := E.Message; [b]end[/b]; [color=#000080]// quotient ermitteln und darstellen[/color] [b]if[/b] Assigned(FResult_Div) [b]then[/b] [b]try[/b] [color=#000080]// label für quotient wurde zugewiesen[/color] FResult_Div.Caption := FloatToStr(FX / FY); [b]except[/b] [b]on[/b] E: Exception [b]do[/b] [color=#000080]// oops[/color] FResult_Div.Caption := E.Message; [b]end[/b]; Jetzt müssen wir noch einstellen, das UpdateLabels immer aufgerufen wird, wenn sicher einer der Parameter (Werte, Labels) ändert. Dazu müsst Ihr jetzt in der Ansicht für jede der Zugriffsmethoden (Access Methods) (SetResult_Add, SetResult_Div, SetResult_Mul, SetResult_Sub, SetX, SetY) folgende Aufgaben erledigen. Zugriffsmethode in der Ansicht Dekl markieren (auswählen). In der Arbeitsfläche den Reiter Implemantation auswählen. Links unten seht Ihr jetzt die aktuellen Code Abschnitte, in logische Bereiche getrennt.
Code:
Zwischen Abschnitt 2 und Abschnitt 3 müssen wir jetzt unseren Aufruf zur Methode UpdateLabels plazieren. Klickt dazu mit der rechten Maustaste in den linken unteren Bereich und wählt den Menüpunkt Add Section. Anschließend zieht diesen per Drag-n-Drop zwischen den zweiten und den dritten Bereich.
[color=#FF0000]// Abschnitt 1[/color]
if FResult_Add <> Value then begin [color=#FF0000]// Abschnitt 2[/color] FResult_Add := Value; [color=#FF0000]// Abschnitt 3[/color] end; Im Vergleich zu den anderen Bereichen ist dieser nicht "rot-weiss" markiert, sonder grün. Grüne Bereiche sind User-Code, die "rot-weissen" Bereiche werden durch ModelMaker verwaltet. In unseren Bereich fügen wir jetzt einfach den Aufruf zur Methode UpdateLabels; ein. Diesen Vorgang jetzt für die restlichen Zugriffsmethoden ausführen ![]() Und nun noch die Methode Notification Gleich der Methode UpdateLabels wird diese mit folgenden Paramtern eingerichtet.
Das Häckchen "Call inherited" fügt den Aufruf zur vorhergehenden Instanz der Methode ein.
Code:
Das Häckchen "Inheritance restricted" bewirkt, dass alle Einstellungen der Methode von der vorhergehenden Definition (TComponent) übernommen wird.
[b]inherited[/b] Notification(aComponent, Operation);
Jetzt müssen wir noch ein wenig User Code einpflegen. Also wieder in der Arbeitsfläche den Reiter Implementation auswählen, "Add Section" im Kontextmenü auswählen und den folgenden Code mit Copy & Paste einfügen.
Code:
Fertig! Jetzt die Komponente, wie bereits im letzten Teil des Kurses beschrieben, installieren und glücklich sein.
[b]if[/b] (Operation = opRemove) [b]then[/b]
[b]begin[/b] [b]if[/b] aComponent = FResult_Add [b]then[/b] FResult_Add := [b]nil[/b]; [b]if[/b] aComponent = FResult_Sub [b]then[/b] FResult_Sub := [b]nil[/b]; [b]if[/b] aComponent = FResult_Mul [b]then[/b] FResult_Mul := [b]nil[/b]; [b]if[/b] aComponent = FResult_Div [b]then[/b] FResult_Div := [b]nil[/b]; [b]end[/b]; Anbei ist das ModelMaker Projekt als Download. Mit dieser Datei könnt Ihr die Komponente vollständig erstellen. Viel Spass, bis bald. ... ![]()
Daniel Lizbeth
|
Ansicht |
![]() |
![]() |
![]() |
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 |
![]() |
![]() |