Komponente schreiben, Teil 2

Komponente schreiben, Teil 2


In diesem Artikel wird behandelt wie erweiterte Eigenschaften, schreiben Schreiben von benutzerdefinierten streaming für diese Eigenschaften und Untereigenschaften.




Dieser Artikel erschien ursprünglich in Delphi-Entwickler



Copyright Pinnacle Publishing, Inc. Alle Rechte vorbehalten.







Dieser Artikel ist Teil eines dreiteiligen Artikels auf Komponenten. Teil 1 behandelt die einfache Erstellung von Komponenten, Teil zwei behandelt wie erweiterte Eigenschaften, schreiben Schreiben von benutzerdefinierten streaming für diese Eigenschaften und Untereigenschaften. Der letzte Teil deckt Eigenschaft / Komponenten-Editoren, wie spezielle Editoren für die Komponente zu schreiben / -Eigenschaft, und wie Sie schreiben 'versteckt' Komponenten.



Oft ist es notwendig, Komponenten zu schreiben, die erweiterte Funktionen ausführen. Diese Komponenten müssen häufig entweder verweisen auf andere Komponenten, benutzerdefinierte Eigenschaft Datenformate, aufweisen oder eine Eigenschaft, die eine Liste der Werte, anstatt einen einzelnen Wert besitzt. In diesem Teil werden wir verschiedene Beispiele für diese Themen, beginnend mit der einfachsten erkunden.



Verweise auf Komponenten

Einige Komponenten müssen auf andere Komponenten zu verweisen. TLabel besitzt zum Beispiel eine 'FocusControl'-Eigenschaft. Beim aufnehmen ein kaufmännisches und-Zeichen in der Eigenschaft 'Beschriftung' unterstreicht es den nächsten Buchstaben (& Hallo wird Hello), drücken die Tastenkombination ALT-H auf Ihrer Tastatur löst ein Ereignis in Ihrer Bezeichnung. Wenn die 'FocusControl'-Eigenschaft festgelegt wurde, wird Fokus angegebene Steuerelement übergeben werden.



Solche Eigenschaft in Ihrem eigenen Komponente zu haben ist ganz einfach. Alles, was Sie zu tun ist, deklarieren Sie eine neue Eigenschaft, und legen den Eigenschaftentyp auf der niedrigsten Basisklasse, die es annehmen kann (TWinControl erlaubt jedem Nachfahre des TWinControl verwendet werden), aber es gibt folgen.











Typ

TSimpleExample = Klasse(TComponent)

private

FFocusControl: TWinControl;

geschützt

Verfahren SetFocusControl (const Value: TWinControl); virtuelle;

öffentliche

veröffentlicht

Eigenschaft FocusControl: TWinControl lesen FFocusControl schreiben SetFocusControl;

end;



Verfahren TSimpleExample.SetFocusControl (const Value: TWinControl);

beginnen

FFocusControl: = Value;

end;





Das obige Beispiel zu nehmen. Dies ist ein ganz einfaches Beispiel (also den Komponentennamen) wie man eine Komponente schreiben, die eine andere Komponente verweist. Haben Sie diese Eigenschaft in der Komponente zeigt Objektinspektor eine Combobox mit eine Liste der Komponenten, die den Kriterien (alle Komponenten stammen von TWinControl) entsprechen.



Unsere Komponente kann so etwas zu tun











Verfahren TSimpleExample.DoSomething;

beginnen

if (Assigned(FocusControl)) und

(FocusControl.Enabled) dann

FocusControl.Setfocus;

end;





Zunächst geprüft, ob die Eigenschaft zugewiesen wurde, wenn also wir den Fokus darauf setzen, aber es Situationen, gibt wenn die Eigenschaft nicht NULL ist noch die Komponente, auf die, der er verweist, nicht mehr gültig ist. Dies geschieht häufig, wenn eine Eigenschaft eine Komponente verweist, die zerstört worden ist.



Zum Glück bietet uns Delphi mit einer Lösung. Wenn eine Komponente zerstört wird, benachrichtigt er seines Besitzers (Kontaktformular), dass es zerstört wird. An dieser Stelle wird jede Komponente, die im Besitz der gleichen Formulars zu dieser Veranstaltung informiert. Wir müssen eine Standardmethode TComponent genannt 'Benachrichtigung' überschreiben, um dieses Ereignis zu überfüllen.











Typ

TSimpleExample = Klasse(TComponent)

private

FFocusControl: TWinControl;

geschützt

Verfahren Benachrichtigung (AComponent: TComponent;

Betrieb: TOperation); außer Kraft setzen;

Verfahren SetFocusControl (const Value: TWinControl); virtuelle;

öffentliche

veröffentlicht

Eigenschaft FocusControl: TWinControl lesen FFocusControl schreiben SetFocusControl;

end;



Verfahren TSimpleExample.SetFocusControl (const Value: TWinControl);

beginnen

FFocusControl: = Value;

end;



Verfahren TSimpleExample.Notification (AComponent: TComponent;

Betrieb: TOperation);

beginnen

erbte; //Never vergessen zu nennen

if (Operation = OpRemove) und

(AComponent = FocusControl) dann

FFocusControl: = null;

end;





Jetzt wird unsere referenzierte Komponente zerstört wir informiert werden, an welcher Stelle können wir unsere Referenz zu null festlegen. Beachten Sie jedoch, dass 'jede Komponente, die im Besitz der gleichen Formulars dieser Veranstaltung auch informiert wird' , ich sagte



Dies führt uns ein weiteres Problem. Wir erfahren nur, dass die Komponente zerstört wird, wenn es die gleiche Form gehört. Es ist möglich, unser Eigentum auf Komponenten über andere Formen (oder auch ohne ein Eigentümer überhaupt) zeigen zu lassen, und wenn diese Komponenten zerstört werden, werden wir nicht benachrichtigt. Wieder einmal gibt es eine Lösung.



TComponent stellt eine Methode namens 'FreeNotification'. Der Zweck des FreeNotification ist, sagen die Komponente (FocusControl) uns im Auge behalten, wenn es zerstört wird.



Eine Implementierung würde wie folgt aussehen.











Typ

TSimpleExample = Klasse(TComponent)

private

FFocusControl: TWinControl;

geschützt

Verfahren Benachrichtigung (AComponent: TComponent;

Betrieb: TOperation); außer Kraft setzen;

Verfahren SetFocusControl (const Value: TWinControl); virtuelle;

öffentliche

veröffentlicht

Eigenschaft FocusControl: TWinControl lesen FFocusControl schreiben SetFocusControl;

end;



Verfahren TSimpleExample.SetFocusControl (const Value: TWinControl);

beginnen

if Wert <> FFocusControl dann

beginnen

if Assigned(FFocusControl) dann

FFocusControl.RemoveFreeNotification(Self);



FFocusControl: = Value;



if Assigned(FFocusControl) dann

FFocusControl.FreeNotification(Self);

Ende;

end;



Verfahren TSimpleExample.Notification (AComponent: TComponent;

Betrieb: TOperation);

beginnen

if (Operation = OpRemove) und

(AComponent = FocusControl) dann

FFocusControl: = null;

end;





Beim Festlegen der Eigenschaft unserer FocusControl prüfen wir zuerst, wenn es bereits zu einer Komponente festgelegt ist. Wenn sie bereits festgelegt ist müssen wir die ursprüngliche Komponente mitteilen, die wir nicht mehr brauchen, zu wissen, wann es zerstört wird. Sobald unser Eigentum auf den neuen Wert festgelegt wurde, informieren wir der neuen Komponente fordern wir eine Benachrichtigung wenn es freigegeben wird. Der Rest unseres Codes bleibt gleich wie die referenzierte Komponente noch unsere Benachrichtigungsmethode nennt.



Sätze

Dieser Abschnitt ist wirklich ganz einfach und dauert nicht lang zu decken. Ich zweifle nicht, dass Sie bereits mit der Erstellung Ihrer eigenen ordinalen Typen vertraut sind.











Typ

TComponentOption = (CoDrawLines,

CoDrawSolid,

CoDrawBackground);





Eigenschaften dieses Typs werden eine Combobox mit einer Liste aller möglichen Werte zeigen, aber manchmal müssen Sie eine Kombination aus viele (oder alle) dieser Werte einstellen. Dies waren Sätze kommen in spielen











Typ

TComponentOption = (CoDrawLines,

CoDrawSolid,

CoDrawBackground);

TComponentOptions = set TComponentOption;





Veröffentlichen eine Eigenschaft vom Typ TComponentOptions würde dazu führen, dass ein [+] neben unseren Eigenschaftsnamen erscheinen. Wenn Sie klicken, um die Eigenschaft zu erweitern, sehen Sie eine Liste der Optionen. Für jedes Element im TComponentOption finden Sie eine boolesche Eigenschaft, Sie können einschließen / Elemente aus Ihrer Gruppe ausschließen, indem Sie dessen Wert auf True / False festlegen.



Es ist einfach zu überprüfen / ändern von Elementen in einer Menge von innerhalb unserer Komponente wie folgt.











Wenn CoDrawLines in OurComponentOptions

dann DrawTheLines;





oder











Verfahren TSomeComponent.SetOurComponentOptions (const Value: TComponentOptions);

beginnen

if (CoDrawSolid Wert) und

(CoDrawBackground Wert) dann

{eine Ausnahme auslösen}



FOurComponentOptions: = Value;

Ungültig wird;

end;





Binary-Eigenschaften

Manchmal ist es notwendig, Ihre eigenen streaming-Routinen zum Lesen und Schreiben von benutzerdefinierten Eigenschaftentypen (Dies ist wie Delphi liest / schreibt die Eigenschaften Top und Left für nicht sichtbare Komponenten ohne tatsächlich veröffentlichen diese Eigenschaften im Objektinspektor) zu schreiben.



Beispielsweise habe ich einmal eine Komponente, um ein Formular basierend auf ein Bitmap-Bild prägen. Mein Code zum Zeitpunkt einer Fenster-Region eine Bitmap konvertieren war extrem langsam und wäre möglicherweise keine Nutzung zur Laufzeit. Meine Lösung war, die Daten zur Entwurfszeit zu konvertieren und Streamen die binären Daten, die aus der Umwandlung geführt haben. Zum Erstellen von binären Eigenschaften ist ein Prozess in drei Schritten.



1. schreiben Sie eine Methode zum Schreiben der Daten.

2. schreiben Sie eine Methode zum Lesen der Daten.

3. sagen Sie Delphi, wir eine binäre Eigenschaft haben und pass unserer lesen / Methoden schreiben.











Typ

TBinaryComponent = Klasse(TComponent)

private

FBinaryData: Zeiger;

FBinaryDataSize: DWord;

Verfahren WriteData(S: TStream);

Verfahren ReadData(S: TStream);

geschützt

Verfahren DefineProperties(Filer: TFiler); außer Kraft setzen;

öffentliche

Konstruktor Create(AOwner: TComponent); außer Kraft setzen;

end;





DefineProperties wird von Delphi aufgerufen, wenn unsere Komponente übertragen werden muss. Alles, was wir tun müssen, ist diese Methode überschreiben, und fügen Sie eine Eigenschaft mit TFiler.DefineProperty oder TFiler.DefineBinaryProperty.











Verfahren TFiler.DefineBinaryProperty (const Name: String;

ReadData, WriteData: TStreamProc; HasData: Boolean);



Konstruktor TBinaryComponent.Create(AOwner: TComponent);

beginnen

geerbt;

FBinaryDataSize: = 0;

end;



Verfahren TBinaryComponent.DefineProperties(Filer: TFiler);

var

HasData: Boolean;

beginnen

geerbt;

HasData: = FBinaryDataSize <> 0;

Filer.DefineBinaryProperty ('BinaryData', ReadData,

WriteData, HasData);

end;



Verfahren TBinaryComponent.ReadData (S: TStream);

beginnen

S.Read (FBinaryDataSize, SizeOf(DWord));

if FBinaryDataSize > 0 dann beginnen

GetMem (FBinaryData, FBinaryDataSize);

S.Read (FBinaryData ^, FBinaryDataSize);

end;

end;



Verfahren TBinaryComponent.WriteData (S: TStream);

beginnen

//This wird nicht aufgerufen, wenn FBinaryDataSize = 0



S.Write (FBinaryDataSize, Sizeof(DWord));

S.Write (FBinaryData ^, FBinaryDataSize);

end;





Erstens überschreiben wir DefineProperties. Sobald wir dies getan haben, definieren wir eine binäre Eigenschaft mit den Werten-



-BinaryData: der unsichtbare Eigenschaftenname verwendet werden.

-ReadData: das Verfahren für das Lesen der Daten verantwortlich.

-WriteData: das Verfahren zum Schreiben der Daten verantwortlich.

-HasData: Wenn dies falsch ist, das WriteData-Verfahren wird nicht einmal aufgerufen.



Persistenz

Eine kurze Erklärung der Persistenz ist in Ordnung, als wir es in den folgenden Abschnitten beziehen soll. Persistenz ist macht es möglich, dass Delphi zum Lesen und Schreiben der Eigenschaftendes aller seiner Bestandteile. TComponent abgeleitet wird von einer Klasse namens TPersistent. TPersistent ist einfach eine Delphi-Klasse kann mit seinen Eigenschaften gelesen und geschrieben von Delphi, was bedeutet, dass alle Nachkommen von TPersistent auch diese dieselbe Funktion haben.



Sammlungen

Wenn wir in diesem Artikel weiterkommen decken wir Komponenteneigenschaften mehr Komplexität. Sammlungen sind eine der komplexesten 'standard' Delphi-Eigenschaftentypen. Wenn Sie einem TDBGrid auf ein Formular fallen und seine Eigenschaften im Objektinspektor betrachten, sehen Sie eine Eigenschaft namens 'Spalten'.



Spalten ist die Auflistungseigenschaft, wenn Sie klicken Sie auf [..] sehen Sie ein kleines Fenster pop-up. Dieses Fenster ist der standard-Eigenschaft-Editor für TCollection-Eigenschaften (und Nachkommen der TCollection).



columns editor



Wenn Sie klicken Sie auf 'Neu' sehen Sie ein neues Element hinzugefügt (ein TColumn-Element), klicken Sie auf dieses Element wählt es in Objektinspektor damit Sie seine Eigenschaften ändern können / Veranstaltungen. Wie geschieht dies?



Die Columns-Eigenschaft steigt von TCollection. TCollection ist ähnlich wie ein Array, welches eine Liste von TCollectionItem enthält. Da TCollection von TPersistent abstammt ist es ist in der Lage, diese Liste von Elementen zu streamen, ebenso TCollectionItem ist auch von TPersistent abstammen und seine Eigenschaften kann auch streamen. So ist haben wir ein Array-ähnliche Element von streaming aller seiner Elemente und deren Eigenschaften fähig.



Das erste, was man beim Erstellen eigener Struktur basierend auf TCollection / TCollectionItem ist unser CollectionItem zu definieren.



(Siehe OurCollection.pas)











Typ

TOurCollectionItem = Klasse(TCollectionItem)

private

FSomeValue: String;

geschützt

Funktion GetDisplayName: String; außer Kraft setzen;

öffentliche

Verfahren Assign(Source: TPersistent); außer Kraft setzen;

veröffentlicht

Eigenschaft SomeValue: String

FSomeValue lesen

Schreiben Sie FSomeValue;

end;





Was wir hier getan haben, ist ein Nachkomme des TCollectionItem erstellen. Wir haben eine token Eigenschaft mit dem Namen 'SomeValue' hinzugefügt, überschreiben die GetDisplayName-Funktion (um den Text, der im Standard-Editor angezeigt wird zu ändern) und schließlich die Assign-Methode überschrieben, damit TOurCollectionItem einen anderen TOurCollectionItem zugeordnet werden können. Wenn wir im letzten Schritt auslassen funktioniert die Methode zuweisen unser Auflistungsklasse nicht!











Verfahren TOurCollectionItem.Assign(Source: TPersistent);

beginnen

if Quelle ist TOurCollectionItem dann

SomeValue: = TOurCollectionItem(Source). SomeValue

sonst

geerbt; //Raises eine Ausnahme

end;



Funktion TOurCollectionItem.GetDisplayName: String;

beginnen

Ergebnis: = Format ('Item % d',[Index]);

end;





Die Umsetzung der TOurCollection ist sehr viel komplexer und verlangt von uns einiges an Arbeit zu tun.











TOurCollection = Klasse(TCollection)

private

FOwner: TComponent;

geschützt

Funktion GetOwner: TPersistent; außer Kraft setzen;

Funktion GetItem(Index: Integer): TOurCollectionItem;

Verfahren SetItem (Index: Integer; Wert:

TOurCollectionItem);

Verfahren Update(Item: TOurCollectionItem);

öffentliche

Konstruktor Create(AOwner: TComponent);



Funktion Hinzufügen: TOurCollectionItem;

Funktion Insert(Index: Integer): TOurCollectionItem;



Eigenschaft Elemente [Index: Integer]: TOurCollectionItem

Lesen Sie GetItem

Schreiben Sie SetItem;

end;





Es gibt eine Anzahl von Elementen zu bedecken, die anhand der oben stehenden Klassendeklaration, damit wir von oben beginnen soll und Abdeckung jedes wiederum.



GetOwner ist eine virtuelle Methode in TPersistent eingeführt. Dies muss überschrieben werden, da der Standard-Code für diese Methode gibt NULL zurück. In unserer Implementierung verändern wir den Konstruktor, um nur einen Parameter zu erhalten (AOwner: TComponent). Wir speichern diese Parameter in FOwner, die dann als Ergebnis GetOwner übergeben wird (TComponent absteigend von TPersistent, also ist daher ein gültiges Ergebnis-Typ).











Konstruktor TOurCollection.Create(AOwner: TComponent);

beginnen

vererbt Create(TOurCollectionItem);

FOwner: = AOwner;

end;



Funktion TOurCollection.GetOwner: TPersistent;

beginnen

Ergebnis: = FOwner;

end;





Nicht nur speichert Erstellen des Besitzers (die für den Objektinspektor ordnungsgemäß funktioniert erforderlich ist), es erzählt auch Delphi, welche Klasse unsere CollectionItem von 'aufrufenden geerbt Create(TOurCollectionItem) ist'.



GetItem / SetItem werden genauso deklariert, da sie in TCollection, aber statt der Arbeit an TCollectionItem sie auf unsere neue Klasse TOurCollectionItem Arbeit. Diese werden später in unserer 'Items'-Eigenschaft verwendet.



Update als oben ist geradlinig Ersatz des Originals, arbeiten an unserer neuen CollectionItem-Klasse statt.



Hinzufügen / Insert sind beide zuständig für das Hinzufügen von Elementen zur Liste, diese beiden ersetzt wurden um Objekte der entsprechenden Klasse zurückzugeben.



Schließlich ist eine Eigenschaft 'Items' eingeführt, um die ursprünglichen Items-Eigenschaft wieder zu ersetzen, so dass wir TOurCollectionItem statt TCollectionItem sparen wir das unnötige Problem der Typumwandlung des Ergebnis jedes Mal ein Ergebnis zurückgegeben werden.



Schließlich ein Beispiel für die Implantation dieser Eigenschaftentyp in einer Komponente unserer eigenen.











TCollectionComponent = Klasse(TComponent)

private

FOurCollection: TOurCollection;

Verfahren SetOurCollection (const Value:

TOurCollection);

öffentliche

Konstruktor Create(AOwner: TComponent); außer Kraft setzen;

Destruktor Zerstören; außer Kraft setzen;

veröffentlicht

Eigenschaft OurCollection: TOurCollection

FOurCollection lesen

Schreiben Sie SetOurCollection;

end;





Es ist so einfach. Nachdem unsere TCollection-Klasse geschrieben wurde all der harten Arbeit geschieht. Unser Konstruktor erstellt der Collection-Klasse, der Destruktor zerstört es und SetOurCollection tut dies.











Konstruktor TCollectionComponent.Create(AOwner: TComponent);

beginnen

geerbt;

FOurCollection: = TOurCollection.Create(Self);

end;



Destruktor TCollectionComponent.Destroy;

beginnen

FOurCollection.Free;

geerbt;

end;



Verfahren TCollectionComponent.SetOurCollection)

const Wert: TOurCollection);

beginnen

FOurCollection.Assign(Value);

end;





Wie bereits erwähnt, das (selbst) zu übergeben ist die TOurCollectionItem.Create in TOurCollections FOwner Variable gespeichert, die infolge des GetOwner übergeben wird. Ein Punkt hierbei ist, dass wir im SetOurCollection nicht FOurCollection festlegen: = Wert, wie Sie das Objekt ersetzen (Objekte sind einfach Zeiger), wir weisen unsere-Eigenschaft auf den Wert.



Neuere Versionen von Delphi machen es noch einfacher. Anstatt GetOwner in unserer Collection-Klasse außer Kraft zu setzen, können wir unsere Kollektion von TOwnedCollection jetzt stattdessen ableiten. TOwnedCollection ist ein Wrapper für TCollection mit dieser Arbeit für uns erledigt.



Untereigenschaften

Früher in diesem Artikel haben wir gesehen, wie war es möglich, eine erweiterbare Eigenschaft zu erstellen. Die Einschränkung der früheren Verfahrens ist, dass jeder Unterpunkt als boolesche Eigenschaft. In diesem nächsten Abschnitt wird gezeigt, wie erweiterbare Eigenschaften erstellen, die beliebige Eigenschaft enthalten kann.



Falls eine Komponente eine Eigenschaft eines Record-Typs ist, könnte dies ganz leicht durch Verfügbarmachen jeweils die Eigenschaften separat implementiert werden. Wenn unsere Komponente jedoch zwei oder mehr Eigenschaften des gleichen komplexen Typs einführen muss wird unseres Erachtens Objektinspektor plötzlich sehr kompliziert.



Die Antwort ist eine komplexe Struktur (gleichermaßen zu einem Datensatz oder Objekt) erstellen und diese Struktur als Eigenschaft wann immer erforderlich zu veröffentlichen. Das offensichtliche Problem ist, dass Delphi nicht weiß, wie diese Eigenschaft angezeigt, wenn wir es sagen. Erstellen einer voll ausgewachsenen Eigenschaft Editor (mit Dialogen usw.) wäre übertrieben, glücklicherweise Delphi hat eine Lösung zur Verfügung gestellt. Wie bereits erwähnt, basiert Delphi interne streaming um die TPersistent-Klasse. Der erste Schritt ist deshalb unsere komplexen Struktur von dieser Klasse abgeleitet werden.











Typ

TExpandingRecord = Klasse(TPersistent)

private

FIntegerProp: Integer;

FStringProp: String;

FCollectionProp: TOurCollection;

Verfahren SetCollectionProp (const Value:

TOurCollection);

öffentliche

Konstruktor Create(AOwner: TComponent);

Destruktor Zerstören; außer Kraft setzen;



Verfahren Assign(Source: TPersistent); außer Kraft setzen;

veröffentlicht

Eigenschaft IntegerProp: Integer

FIntegerProp lesen

Schreiben Sie FIntegerProp;

Eigenschaft StringProp: String

FStringProp lesen

Schreiben Sie FStringProp;

Eigenschaft CollectionProp: TOurCollection

FCollectionProp lesen

Schreiben Sie SetCollectionProp;

end;





In der obigen Struktur haben wir ein Nachkomme des TPersistent erstellt und es gegeben, drei Beispieleigenschaften, eine ganze Zahl, eine Zeichenfolge und die Sammlung, die wir weiter oben in diesem Artikel TOurCollection erstellt.



Der Konstruktor und Destruktor einfach kümmern erstellen und zerstören das CollectionProp-Objekt, und die SetCollectionProp wird implantiert, anzuhalten, dieser Objektverweis verloren gehen (wir Assign(value), anstatt FCollectionProp: = Value). Während die weisen implementiert wird, damit wir die Eigenschaften des TExpandableRecord ein weiteres TExpandableRecord zuweisen können (wieder, dies ist erforderlich, da müssen wir ihm zuweisen, wenn es schließlich als eine Eigenschaft einer Komponente implementiert ist).











Verfahren TExpandingRecord.Assign(Source: TPersistent);

beginnen

if Quelle ist TExpandingRecord dann

mit TExpandingRecord(Source) soll beginnen

Self.IntegerProp: = IntegerProp;

Self.StringProp: = StringProp;



Tatsächlich weist der //This

Self.CollectionProp: = CollectionProp;

Ende sonst

geerbt; //Raises eine Ausnahme

end;



Konstruktor TExpandingRecord.Create(AOwner: TComponent);

beginnen

vererbt Erstellen;

FCollectionProp: = TOurCollection.Create(AOwner);

end;



Destruktor TExpandingRecord.Destroy;

beginnen

FCollectionProp.Free;

geerbt;

end;



Verfahren TExpandingRecord.SetCollectionProp (const Value: TOurCollection);

beginnen

FCollectionProp.Assign(Value);

end;





Wenn dieser Code umgesetzt hat all die harte Arbeit geschieht. Um diese Klasse als Objekt zu implementieren ist jetzt ziemlich geradlinig.



(Siehe ExpandingComponent.pas)











TExpandingComponent = Klasse(TComponent)

private

FProperty1,

FProperty2,

FProperty3: TExpandingRecord;

geschützt

Verfahren SetProperty1 (const Value:

TExpandingRecord);

Verfahren SetProperty2 (const Value:

TExpandingRecord);

Verfahren SetProperty3 (const Value:

TExpandingRecord);

öffentliche

Konstruktor Create(AOwner: TComponent); außer Kraft setzen;

Destruktor Zerstören; außer Kraft setzen;

veröffentlicht

Eigenschaft 1 Eigenschaft: TExpandingRecord

Lesen FProperty1

Schreiben SetProperty1;

Eigenschaft 2 Eigenschaft: TExpandingRecord

Lesen FProperty2

Schreiben SetProperty2;

Eigenschaft Eigenschaft-3: TExpandingRecord

Lesen FProperty3

Schreiben SetProperty3;

end;





Der Konstruktor müssen die drei Objekte, die als Eigenschaften zu erstellen, der Destruktor benötigen offensichtlich zu zerstören. Die SetPropertyX-Verfahren werden die Assign(Value) in das richtige Objekt.



Kompilieren Sie diese Komponente in einem Paket und legen Sie eine TExpandingComponent auf das Formular. Suchen im Objektinspektor Sie wird Beachten Sie, dass unsere TExpandingRecord Properties alle ein [+] neben ihnen haben, dieser Button unser Eigentum zu zeigen, alle seine untergeordneten Eigenschaften erweitern wird.



Expanding component



Auf den ersten Blick sieht alles gut, unsere Immobilien sind mit einer erweiterbaren sub Eigenschaften gezeigt, aber alles ist nicht so perfekt, wie es zunächst scheinen. Klick auf die Schaltfläche [..] von unserem 'CollectionProp' wird nicht aufgerufen den standard TCollection-Editor, in der Tat, es tut nichts.



Könnten Sie sich Fragen 'Was haben wir getan falsch?', die Antwort ist 'Nichts!'. Der Fehler hier ist überhaupt nicht unsererseits, liegt der Fehler mit den Entwicklern von Delphi, Borland. Obwohl ich an die Entwickler von Delphi als unfehlbar denken gerne, sind sie nicht, sie machen manchmal Fehler und dies ist ein perfektes Beispiel eines.



Beim Registrieren eines Editors können Sie Komponentenebene beschränken, für die es gilt. Sie können angeben, dass es sollte nur auf bestimmte Eigenschaftennamen oder nur auf bestimmte Komponenten funktionieren, und das ist, was sie getan haben. Obwohl Delphi Architektur definiert, das unterste Objekt-Formular von streaming-fähig TPersistent, hier jemand bei Borland Eigenschaften-Editor funktioniert nur auf Objekte, die von TComponent abgeleitet. Ein versehen, ich bin sicher, aber das hilft nicht wirklich uns überhaupt.



Die Antwort auf dieses Problem besteht darin, unsere TExpandableRecord von TComponent statt TPersistent abstammen, dann wird der Editor aufgerufen werden. Das Problem ist, dass Delphi Eigenschaft der Standardeditor für Eigenschaften des Typs TComponent (und Nachkommen) zeigt eine Combobox, anstatt eine erweiterbare Ansicht von Untereigenschaften.



Die ganze Lösung dieses Dilemmas liegt im Eigenschaften-Editoren und im letzten Teil dieser Serie abgedeckt we







Komponente schreiben, Teil 2


Komponente schreiben, Teil 2 : Mehreren tausend Tipps, um Ihr Leben einfacher machen.


In diesem Artikel wird behandelt wie erweiterte Eigenschaften, schreiben Schreiben von benutzerdefinierten streaming für diese Eigenschaften und Untereigenschaften.




Dieser Artikel erschien ursprünglich in Delphi-Entwickler



Copyright Pinnacle Publishing, Inc. Alle Rechte vorbehalten.







Dieser Artikel ist Teil eines dreiteiligen Artikels auf Komponenten. Teil 1 behandelt die einfache Erstellung von Komponenten, Teil zwei behandelt wie erweiterte Eigenschaften, schreiben Schreiben von benutzerdefinierten streaming für diese Eigenschaften und Untereigenschaften. Der letzte Teil deckt Eigenschaft / Komponenten-Editoren, wie spezielle Editoren für die Komponente zu schreiben / -Eigenschaft, und wie Sie schreiben 'versteckt' Komponenten.



Oft ist es notwendig, Komponenten zu schreiben, die erweiterte Funktionen ausführen. Diese Komponenten müssen häufig entweder verweisen auf andere Komponenten, benutzerdefinierte Eigenschaft Datenformate, aufweisen oder eine Eigenschaft, die eine Liste der Werte, anstatt einen einzelnen Wert besitzt. In diesem Teil werden wir verschiedene Beispiele für diese Themen, beginnend mit der einfachsten erkunden.



Verweise auf Komponenten

Einige Komponenten müssen auf andere Komponenten zu verweisen. TLabel besitzt zum Beispiel eine 'FocusControl'-Eigenschaft. Beim aufnehmen ein kaufmännisches und-Zeichen in der Eigenschaft 'Beschriftung' unterstreicht es den nächsten Buchstaben (& Hallo wird Hello), drücken die Tastenkombination ALT-H auf Ihrer Tastatur löst ein Ereignis in Ihrer Bezeichnung. Wenn die 'FocusControl'-Eigenschaft festgelegt wurde, wird Fokus angegebene Steuerelement übergeben werden.



Solche Eigenschaft in Ihrem eigenen Komponente zu haben ist ganz einfach. Alles, was Sie zu tun ist, deklarieren Sie eine neue Eigenschaft, und legen den Eigenschaftentyp auf der niedrigsten Basisklasse, die es annehmen kann (TWinControl erlaubt jedem Nachfahre des TWinControl verwendet werden), aber es gibt folgen.











Typ

TSimpleExample = Klasse(TComponent)

private

FFocusControl: TWinControl;

geschützt

Verfahren SetFocusControl (const Value: TWinControl); virtuelle;

öffentliche

veröffentlicht

Eigenschaft FocusControl: TWinControl lesen FFocusControl schreiben SetFocusControl;

end;



Verfahren TSimpleExample.SetFocusControl (const Value: TWinControl);

beginnen

FFocusControl: = Value;

end;





Das obige Beispiel zu nehmen. Dies ist ein ganz einfaches Beispiel (also den Komponentennamen) wie man eine Komponente schreiben, die eine andere Komponente verweist. Haben Sie diese Eigenschaft in der Komponente zeigt Objektinspektor eine Combobox mit eine Liste der Komponenten, die den Kriterien (alle Komponenten stammen von TWinControl) entsprechen.



Unsere Komponente kann so etwas zu tun











Verfahren TSimpleExample.DoSomething;

beginnen

if (Assigned(FocusControl)) und

(FocusControl.Enabled) dann

FocusControl.Setfocus;

end;





Zunächst geprüft, ob die Eigenschaft zugewiesen wurde, wenn also wir den Fokus darauf setzen, aber es Situationen, gibt wenn die Eigenschaft nicht NULL ist noch die Komponente, auf die, der er verweist, nicht mehr gültig ist. Dies geschieht häufig, wenn eine Eigenschaft eine Komponente verweist, die zerstört worden ist.



Zum Glück bietet uns Delphi mit einer Lösung. Wenn eine Komponente zerstört wird, benachrichtigt er seines Besitzers (Kontaktformular), dass es zerstört wird. An dieser Stelle wird jede Komponente, die im Besitz der gleichen Formulars zu dieser Veranstaltung informiert. Wir müssen eine Standardmethode TComponent genannt 'Benachrichtigung' überschreiben, um dieses Ereignis zu überfüllen.











Typ

TSimpleExample = Klasse(TComponent)

private

FFocusControl: TWinControl;

geschützt

Verfahren Benachrichtigung (AComponent: TComponent;

Betrieb: TOperation); außer Kraft setzen;

Verfahren SetFocusControl (const Value: TWinControl); virtuelle;

öffentliche

veröffentlicht

Eigenschaft FocusControl: TWinControl lesen FFocusControl schreiben SetFocusControl;

end;



Verfahren TSimpleExample.SetFocusControl (const Value: TWinControl);

beginnen

FFocusControl: = Value;

end;



Verfahren TSimpleExample.Notification (AComponent: TComponent;

Betrieb: TOperation);

beginnen

erbte; //Never vergessen zu nennen

if (Operation = OpRemove) und

(AComponent = FocusControl) dann

FFocusControl: = null;

end;





Jetzt wird unsere referenzierte Komponente zerstört wir informiert werden, an welcher Stelle können wir unsere Referenz zu null festlegen. Beachten Sie jedoch, dass 'jede Komponente, die im Besitz der gleichen Formulars dieser Veranstaltung auch informiert wird' , ich sagte



Dies führt uns ein weiteres Problem. Wir erfahren nur, dass die Komponente zerstört wird, wenn es die gleiche Form gehört. Es ist möglich, unser Eigentum auf Komponenten über andere Formen (oder auch ohne ein Eigentümer überhaupt) zeigen zu lassen, und wenn diese Komponenten zerstört werden, werden wir nicht benachrichtigt. Wieder einmal gibt es eine Lösung.



TComponent stellt eine Methode namens 'FreeNotification'. Der Zweck des FreeNotification ist, sagen die Komponente (FocusControl) uns im Auge behalten, wenn es zerstört wird.



Eine Implementierung würde wie folgt aussehen.











Typ

TSimpleExample = Klasse(TComponent)

private

FFocusControl: TWinControl;

geschützt

Verfahren Benachrichtigung (AComponent: TComponent;

Betrieb: TOperation); außer Kraft setzen;

Verfahren SetFocusControl (const Value: TWinControl); virtuelle;

öffentliche

veröffentlicht

Eigenschaft FocusControl: TWinControl lesen FFocusControl schreiben SetFocusControl;

end;



Verfahren TSimpleExample.SetFocusControl (const Value: TWinControl);

beginnen

if Wert <> FFocusControl dann

beginnen

if Assigned(FFocusControl) dann

FFocusControl.RemoveFreeNotification(Self);



FFocusControl: = Value;



if Assigned(FFocusControl) dann

FFocusControl.FreeNotification(Self);

Ende;

end;



Verfahren TSimpleExample.Notification (AComponent: TComponent;

Betrieb: TOperation);

beginnen

if (Operation = OpRemove) und

(AComponent = FocusControl) dann

FFocusControl: = null;

end;





Beim Festlegen der Eigenschaft unserer FocusControl prüfen wir zuerst, wenn es bereits zu einer Komponente festgelegt ist. Wenn sie bereits festgelegt ist müssen wir die ursprüngliche Komponente mitteilen, die wir nicht mehr brauchen, zu wissen, wann es zerstört wird. Sobald unser Eigentum auf den neuen Wert festgelegt wurde, informieren wir der neuen Komponente fordern wir eine Benachrichtigung wenn es freigegeben wird. Der Rest unseres Codes bleibt gleich wie die referenzierte Komponente noch unsere Benachrichtigungsmethode nennt.



Sätze

Dieser Abschnitt ist wirklich ganz einfach und dauert nicht lang zu decken. Ich zweifle nicht, dass Sie bereits mit der Erstellung Ihrer eigenen ordinalen Typen vertraut sind.











Typ

TComponentOption = (CoDrawLines,

CoDrawSolid,

CoDrawBackground);





Eigenschaften dieses Typs werden eine Combobox mit einer Liste aller möglichen Werte zeigen, aber manchmal müssen Sie eine Kombination aus viele (oder alle) dieser Werte einstellen. Dies waren Sätze kommen in spielen











Typ

TComponentOption = (CoDrawLines,

CoDrawSolid,

CoDrawBackground);

TComponentOptions = set TComponentOption;





Veröffentlichen eine Eigenschaft vom Typ TComponentOptions würde dazu führen, dass ein [+] neben unseren Eigenschaftsnamen erscheinen. Wenn Sie klicken, um die Eigenschaft zu erweitern, sehen Sie eine Liste der Optionen. Für jedes Element im TComponentOption finden Sie eine boolesche Eigenschaft, Sie können einschließen / Elemente aus Ihrer Gruppe ausschließen, indem Sie dessen Wert auf True / False festlegen.



Es ist einfach zu überprüfen / ändern von Elementen in einer Menge von innerhalb unserer Komponente wie folgt.











Wenn CoDrawLines in OurComponentOptions

dann DrawTheLines;





oder











Verfahren TSomeComponent.SetOurComponentOptions (const Value: TComponentOptions);

beginnen

if (CoDrawSolid Wert) und

(CoDrawBackground Wert) dann

{eine Ausnahme auslösen}



FOurComponentOptions: = Value;

Ungültig wird;

end;





Binary-Eigenschaften

Manchmal ist es notwendig, Ihre eigenen streaming-Routinen zum Lesen und Schreiben von benutzerdefinierten Eigenschaftentypen (Dies ist wie Delphi liest / schreibt die Eigenschaften Top und Left für nicht sichtbare Komponenten ohne tatsächlich veröffentlichen diese Eigenschaften im Objektinspektor) zu schreiben.



Beispielsweise habe ich einmal eine Komponente, um ein Formular basierend auf ein Bitmap-Bild prägen. Mein Code zum Zeitpunkt einer Fenster-Region eine Bitmap konvertieren war extrem langsam und wäre möglicherweise keine Nutzung zur Laufzeit. Meine Lösung war, die Daten zur Entwurfszeit zu konvertieren und Streamen die binären Daten, die aus der Umwandlung geführt haben. Zum Erstellen von binären Eigenschaften ist ein Prozess in drei Schritten.



1. schreiben Sie eine Methode zum Schreiben der Daten.

2. schreiben Sie eine Methode zum Lesen der Daten.

3. sagen Sie Delphi, wir eine binäre Eigenschaft haben und pass unserer lesen / Methoden schreiben.











Typ

TBinaryComponent = Klasse(TComponent)

private

FBinaryData: Zeiger;

FBinaryDataSize: DWord;

Verfahren WriteData(S: TStream);

Verfahren ReadData(S: TStream);

geschützt

Verfahren DefineProperties(Filer: TFiler); außer Kraft setzen;

öffentliche

Konstruktor Create(AOwner: TComponent); außer Kraft setzen;

end;





DefineProperties wird von Delphi aufgerufen, wenn unsere Komponente übertragen werden muss. Alles, was wir tun müssen, ist diese Methode überschreiben, und fügen Sie eine Eigenschaft mit TFiler.DefineProperty oder TFiler.DefineBinaryProperty.











Verfahren TFiler.DefineBinaryProperty (const Name: String;

ReadData, WriteData: TStreamProc; HasData: Boolean);



Konstruktor TBinaryComponent.Create(AOwner: TComponent);

beginnen

geerbt;

FBinaryDataSize: = 0;

end;



Verfahren TBinaryComponent.DefineProperties(Filer: TFiler);

var

HasData: Boolean;

beginnen

geerbt;

HasData: = FBinaryDataSize <> 0;

Filer.DefineBinaryProperty ('BinaryData', ReadData,

WriteData, HasData);

end;



Verfahren TBinaryComponent.ReadData (S: TStream);

beginnen

S.Read (FBinaryDataSize, SizeOf(DWord));

if FBinaryDataSize > 0 dann beginnen

GetMem (FBinaryData, FBinaryDataSize);

S.Read (FBinaryData ^, FBinaryDataSize);

end;

end;



Verfahren TBinaryComponent.WriteData (S: TStream);

beginnen

//This wird nicht aufgerufen, wenn FBinaryDataSize = 0



S.Write (FBinaryDataSize, Sizeof(DWord));

S.Write (FBinaryData ^, FBinaryDataSize);

end;





Erstens überschreiben wir DefineProperties. Sobald wir dies getan haben, definieren wir eine binäre Eigenschaft mit den Werten-



-BinaryData: der unsichtbare Eigenschaftenname verwendet werden.

-ReadData: das Verfahren für das Lesen der Daten verantwortlich.

-WriteData: das Verfahren zum Schreiben der Daten verantwortlich.

-HasData: Wenn dies falsch ist, das WriteData-Verfahren wird nicht einmal aufgerufen.



Persistenz

Eine kurze Erklärung der Persistenz ist in Ordnung, als wir es in den folgenden Abschnitten beziehen soll. Persistenz ist macht es möglich, dass Delphi zum Lesen und Schreiben der Eigenschaftendes aller seiner Bestandteile. TComponent abgeleitet wird von einer Klasse namens TPersistent. TPersistent ist einfach eine Delphi-Klasse kann mit seinen Eigenschaften gelesen und geschrieben von Delphi, was bedeutet, dass alle Nachkommen von TPersistent auch diese dieselbe Funktion haben.



Sammlungen

Wenn wir in diesem Artikel weiterkommen decken wir Komponenteneigenschaften mehr Komplexität. Sammlungen sind eine der komplexesten 'standard' Delphi-Eigenschaftentypen. Wenn Sie einem TDBGrid auf ein Formular fallen und seine Eigenschaften im Objektinspektor betrachten, sehen Sie eine Eigenschaft namens 'Spalten'.



Spalten ist die Auflistungseigenschaft, wenn Sie klicken Sie auf [..] sehen Sie ein kleines Fenster pop-up. Dieses Fenster ist der standard-Eigenschaft-Editor für TCollection-Eigenschaften (und Nachkommen der TCollection).



columns editor



Wenn Sie klicken Sie auf 'Neu' sehen Sie ein neues Element hinzugefügt (ein TColumn-Element), klicken Sie auf dieses Element wählt es in Objektinspektor damit Sie seine Eigenschaften ändern können / Veranstaltungen. Wie geschieht dies?



Die Columns-Eigenschaft steigt von TCollection. TCollection ist ähnlich wie ein Array, welches eine Liste von TCollectionItem enthält. Da TCollection von TPersistent abstammt ist es ist in der Lage, diese Liste von Elementen zu streamen, ebenso TCollectionItem ist auch von TPersistent abstammen und seine Eigenschaften kann auch streamen. So ist haben wir ein Array-ähnliche Element von streaming aller seiner Elemente und deren Eigenschaften fähig.



Das erste, was man beim Erstellen eigener Struktur basierend auf TCollection / TCollectionItem ist unser CollectionItem zu definieren.



(Siehe OurCollection.pas)











Typ

TOurCollectionItem = Klasse(TCollectionItem)

private

FSomeValue: String;

geschützt

Funktion GetDisplayName: String; außer Kraft setzen;

öffentliche

Verfahren Assign(Source: TPersistent); außer Kraft setzen;

veröffentlicht

Eigenschaft SomeValue: String

FSomeValue lesen

Schreiben Sie FSomeValue;

end;





Was wir hier getan haben, ist ein Nachkomme des TCollectionItem erstellen. Wir haben eine token Eigenschaft mit dem Namen 'SomeValue' hinzugefügt, überschreiben die GetDisplayName-Funktion (um den Text, der im Standard-Editor angezeigt wird zu ändern) und schließlich die Assign-Methode überschrieben, damit TOurCollectionItem einen anderen TOurCollectionItem zugeordnet werden können. Wenn wir im letzten Schritt auslassen funktioniert die Methode zuweisen unser Auflistungsklasse nicht!











Verfahren TOurCollectionItem.Assign(Source: TPersistent);

beginnen

if Quelle ist TOurCollectionItem dann

SomeValue: = TOurCollectionItem(Source). SomeValue

sonst

geerbt; //Raises eine Ausnahme

end;



Funktion TOurCollectionItem.GetDisplayName: String;

beginnen

Ergebnis: = Format ('Item % d',[Index]);

end;





Die Umsetzung der TOurCollection ist sehr viel komplexer und verlangt von uns einiges an Arbeit zu tun.











TOurCollection = Klasse(TCollection)

private

FOwner: TComponent;

geschützt

Funktion GetOwner: TPersistent; außer Kraft setzen;

Funktion GetItem(Index: Integer): TOurCollectionItem;

Verfahren SetItem (Index: Integer; Wert:

TOurCollectionItem);

Verfahren Update(Item: TOurCollectionItem);

öffentliche

Konstruktor Create(AOwner: TComponent);



Funktion Hinzufügen: TOurCollectionItem;

Funktion Insert(Index: Integer): TOurCollectionItem;



Eigenschaft Elemente [Index: Integer]: TOurCollectionItem

Lesen Sie GetItem

Schreiben Sie SetItem;

end;





Es gibt eine Anzahl von Elementen zu bedecken, die anhand der oben stehenden Klassendeklaration, damit wir von oben beginnen soll und Abdeckung jedes wiederum.



GetOwner ist eine virtuelle Methode in TPersistent eingeführt. Dies muss überschrieben werden, da der Standard-Code für diese Methode gibt NULL zurück. In unserer Implementierung verändern wir den Konstruktor, um nur einen Parameter zu erhalten (AOwner: TComponent). Wir speichern diese Parameter in FOwner, die dann als Ergebnis GetOwner übergeben wird (TComponent absteigend von TPersistent, also ist daher ein gültiges Ergebnis-Typ).











Konstruktor TOurCollection.Create(AOwner: TComponent);

beginnen

vererbt Create(TOurCollectionItem);

FOwner: = AOwner;

end;



Funktion TOurCollection.GetOwner: TPersistent;

beginnen

Ergebnis: = FOwner;

end;





Nicht nur speichert Erstellen des Besitzers (die für den Objektinspektor ordnungsgemäß funktioniert erforderlich ist), es erzählt auch Delphi, welche Klasse unsere CollectionItem von 'aufrufenden geerbt Create(TOurCollectionItem) ist'.



GetItem / SetItem werden genauso deklariert, da sie in TCollection, aber statt der Arbeit an TCollectionItem sie auf unsere neue Klasse TOurCollectionItem Arbeit. Diese werden später in unserer 'Items'-Eigenschaft verwendet.



Update als oben ist geradlinig Ersatz des Originals, arbeiten an unserer neuen CollectionItem-Klasse statt.



Hinzufügen / Insert sind beide zuständig für das Hinzufügen von Elementen zur Liste, diese beiden ersetzt wurden um Objekte der entsprechenden Klasse zurückzugeben.



Schließlich ist eine Eigenschaft 'Items' eingeführt, um die ursprünglichen Items-Eigenschaft wieder zu ersetzen, so dass wir TOurCollectionItem statt TCollectionItem sparen wir das unnötige Problem der Typumwandlung des Ergebnis jedes Mal ein Ergebnis zurückgegeben werden.



Schließlich ein Beispiel für die Implantation dieser Eigenschaftentyp in einer Komponente unserer eigenen.











TCollectionComponent = Klasse(TComponent)

private

FOurCollection: TOurCollection;

Verfahren SetOurCollection (const Value:

TOurCollection);

öffentliche

Konstruktor Create(AOwner: TComponent); außer Kraft setzen;

Destruktor Zerstören; außer Kraft setzen;

veröffentlicht

Eigenschaft OurCollection: TOurCollection

FOurCollection lesen

Schreiben Sie SetOurCollection;

end;





Es ist so einfach. Nachdem unsere TCollection-Klasse geschrieben wurde all der harten Arbeit geschieht. Unser Konstruktor erstellt der Collection-Klasse, der Destruktor zerstört es und SetOurCollection tut dies.











Konstruktor TCollectionComponent.Create(AOwner: TComponent);

beginnen

geerbt;

FOurCollection: = TOurCollection.Create(Self);

end;



Destruktor TCollectionComponent.Destroy;

beginnen

FOurCollection.Free;

geerbt;

end;



Verfahren TCollectionComponent.SetOurCollection)

const Wert: TOurCollection);

beginnen

FOurCollection.Assign(Value);

end;





Wie bereits erwähnt, das (selbst) zu übergeben ist die TOurCollectionItem.Create in TOurCollections FOwner Variable gespeichert, die infolge des GetOwner übergeben wird. Ein Punkt hierbei ist, dass wir im SetOurCollection nicht FOurCollection festlegen: = Wert, wie Sie das Objekt ersetzen (Objekte sind einfach Zeiger), wir weisen unsere-Eigenschaft auf den Wert.



Neuere Versionen von Delphi machen es noch einfacher. Anstatt GetOwner in unserer Collection-Klasse außer Kraft zu setzen, können wir unsere Kollektion von TOwnedCollection jetzt stattdessen ableiten. TOwnedCollection ist ein Wrapper für TCollection mit dieser Arbeit für uns erledigt.



Untereigenschaften

Früher in diesem Artikel haben wir gesehen, wie war es möglich, eine erweiterbare Eigenschaft zu erstellen. Die Einschränkung der früheren Verfahrens ist, dass jeder Unterpunkt als boolesche Eigenschaft. In diesem nächsten Abschnitt wird gezeigt, wie erweiterbare Eigenschaften erstellen, die beliebige Eigenschaft enthalten kann.



Falls eine Komponente eine Eigenschaft eines Record-Typs ist, könnte dies ganz leicht durch Verfügbarmachen jeweils die Eigenschaften separat implementiert werden. Wenn unsere Komponente jedoch zwei oder mehr Eigenschaften des gleichen komplexen Typs einführen muss wird unseres Erachtens Objektinspektor plötzlich sehr kompliziert.



Die Antwort ist eine komplexe Struktur (gleichermaßen zu einem Datensatz oder Objekt) erstellen und diese Struktur als Eigenschaft wann immer erforderlich zu veröffentlichen. Das offensichtliche Problem ist, dass Delphi nicht weiß, wie diese Eigenschaft angezeigt, wenn wir es sagen. Erstellen einer voll ausgewachsenen Eigenschaft Editor (mit Dialogen usw.) wäre übertrieben, glücklicherweise Delphi hat eine Lösung zur Verfügung gestellt. Wie bereits erwähnt, basiert Delphi interne streaming um die TPersistent-Klasse. Der erste Schritt ist deshalb unsere komplexen Struktur von dieser Klasse abgeleitet werden.











Typ

TExpandingRecord = Klasse(TPersistent)

private

FIntegerProp: Integer;

FStringProp: String;

FCollectionProp: TOurCollection;

Verfahren SetCollectionProp (const Value:

TOurCollection);

öffentliche

Konstruktor Create(AOwner: TComponent);

Destruktor Zerstören; außer Kraft setzen;



Verfahren Assign(Source: TPersistent); außer Kraft setzen;

veröffentlicht

Eigenschaft IntegerProp: Integer

FIntegerProp lesen

Schreiben Sie FIntegerProp;

Eigenschaft StringProp: String

FStringProp lesen

Schreiben Sie FStringProp;

Eigenschaft CollectionProp: TOurCollection

FCollectionProp lesen

Schreiben Sie SetCollectionProp;

end;





In der obigen Struktur haben wir ein Nachkomme des TPersistent erstellt und es gegeben, drei Beispieleigenschaften, eine ganze Zahl, eine Zeichenfolge und die Sammlung, die wir weiter oben in diesem Artikel TOurCollection erstellt.



Der Konstruktor und Destruktor einfach kümmern erstellen und zerstören das CollectionProp-Objekt, und die SetCollectionProp wird implantiert, anzuhalten, dieser Objektverweis verloren gehen (wir Assign(value), anstatt FCollectionProp: = Value). Während die weisen implementiert wird, damit wir die Eigenschaften des TExpandableRecord ein weiteres TExpandableRecord zuweisen können (wieder, dies ist erforderlich, da müssen wir ihm zuweisen, wenn es schließlich als eine Eigenschaft einer Komponente implementiert ist).











Verfahren TExpandingRecord.Assign(Source: TPersistent);

beginnen

if Quelle ist TExpandingRecord dann

mit TExpandingRecord(Source) soll beginnen

Self.IntegerProp: = IntegerProp;

Self.StringProp: = StringProp;



Tatsächlich weist der //This

Self.CollectionProp: = CollectionProp;

Ende sonst

geerbt; //Raises eine Ausnahme

end;



Konstruktor TExpandingRecord.Create(AOwner: TComponent);

beginnen

vererbt Erstellen;

FCollectionProp: = TOurCollection.Create(AOwner);

end;



Destruktor TExpandingRecord.Destroy;

beginnen

FCollectionProp.Free;

geerbt;

end;



Verfahren TExpandingRecord.SetCollectionProp (const Value: TOurCollection);

beginnen

FCollectionProp.Assign(Value);

end;





Wenn dieser Code umgesetzt hat all die harte Arbeit geschieht. Um diese Klasse als Objekt zu implementieren ist jetzt ziemlich geradlinig.



(Siehe ExpandingComponent.pas)











TExpandingComponent = Klasse(TComponent)

private

FProperty1,

FProperty2,

FProperty3: TExpandingRecord;

geschützt

Verfahren SetProperty1 (const Value:

TExpandingRecord);

Verfahren SetProperty2 (const Value:

TExpandingRecord);

Verfahren SetProperty3 (const Value:

TExpandingRecord);

öffentliche

Konstruktor Create(AOwner: TComponent); außer Kraft setzen;

Destruktor Zerstören; außer Kraft setzen;

veröffentlicht

Eigenschaft 1 Eigenschaft: TExpandingRecord

Lesen FProperty1

Schreiben SetProperty1;

Eigenschaft 2 Eigenschaft: TExpandingRecord

Lesen FProperty2

Schreiben SetProperty2;

Eigenschaft Eigenschaft-3: TExpandingRecord

Lesen FProperty3

Schreiben SetProperty3;

end;





Der Konstruktor müssen die drei Objekte, die als Eigenschaften zu erstellen, der Destruktor benötigen offensichtlich zu zerstören. Die SetPropertyX-Verfahren werden die Assign(Value) in das richtige Objekt.



Kompilieren Sie diese Komponente in einem Paket und legen Sie eine TExpandingComponent auf das Formular. Suchen im Objektinspektor Sie wird Beachten Sie, dass unsere TExpandingRecord Properties alle ein [+] neben ihnen haben, dieser Button unser Eigentum zu zeigen, alle seine untergeordneten Eigenschaften erweitern wird.



Expanding component



Auf den ersten Blick sieht alles gut, unsere Immobilien sind mit einer erweiterbaren sub Eigenschaften gezeigt, aber alles ist nicht so perfekt, wie es zunächst scheinen. Klick auf die Schaltfläche [..] von unserem 'CollectionProp' wird nicht aufgerufen den standard TCollection-Editor, in der Tat, es tut nichts.



Könnten Sie sich Fragen 'Was haben wir getan falsch?', die Antwort ist 'Nichts!'. Der Fehler hier ist überhaupt nicht unsererseits, liegt der Fehler mit den Entwicklern von Delphi, Borland. Obwohl ich an die Entwickler von Delphi als unfehlbar denken gerne, sind sie nicht, sie machen manchmal Fehler und dies ist ein perfektes Beispiel eines.



Beim Registrieren eines Editors können Sie Komponentenebene beschränken, für die es gilt. Sie können angeben, dass es sollte nur auf bestimmte Eigenschaftennamen oder nur auf bestimmte Komponenten funktionieren, und das ist, was sie getan haben. Obwohl Delphi Architektur definiert, das unterste Objekt-Formular von streaming-fähig TPersistent, hier jemand bei Borland Eigenschaften-Editor funktioniert nur auf Objekte, die von TComponent abgeleitet. Ein versehen, ich bin sicher, aber das hilft nicht wirklich uns überhaupt.



Die Antwort auf dieses Problem besteht darin, unsere TExpandableRecord von TComponent statt TPersistent abstammen, dann wird der Editor aufgerufen werden. Das Problem ist, dass Delphi Eigenschaft der Standardeditor für Eigenschaften des Typs TComponent (und Nachkommen) zeigt eine Combobox, anstatt eine erweiterbare Ansicht von Untereigenschaften.



Die ganze Lösung dieses Dilemmas liegt im Eigenschaften-Editoren und im letzten Teil dieser Serie abgedeckt we

Komponente schreiben, Teil 2

Komponente schreiben, Teil 2 : Mehreren tausend Tipps, um Ihr Leben einfacher machen.
Komponente schreiben, Teil 2
Wiezutun
Freunden empfehlen
  • gplus
  • pinterest

Kommentar

Einen Kommentar hinterlassen

Wertung