![]() |
Reihenfolge beim Laden von Properties verändern
Hallo DP'ler
ich bin auf ein Problem beim Entwickeln von Komponenten gestoßen. Ich möchte die Reihenfolge ändern in der die Properties in einer von TComponent abgeleitenen Komponente vom ObjektInstpektor gespeichert bzw geladen werden. Ich hoffe Ihr könnt mir helfen. Also ein Beispiel:
Delphi-Quellcode:
Ok soweit ist das mit der Zuweisung auch kein Problem, aber was ist wenn ich den Wert von FProp2 bei der Zuweisung von Prop1 benötige,
type
MyComonent = class(TComponent) private FProp1 : Integer; FProp2: Integer; procedure SetProp1(Value: Integer); procedure SetProp2(Value: Integer); published property Prop1 : integer read FProp1 write SetProp1; property Prop2: integer read FProp2 write SetProp2; end; procedure MyComponent.SetProp1(Value: Integer); begin FProp1 := Value; end; procedure MyComponent.SetProp2(Value; Integer); begin FProp2 := Value; end; sagen wir z.B. um zu FProp1 einen Fixwert zu addieren. Ein Beispiel für den Setter von FPop1
Delphi-Quellcode:
Da WriteComponentRes und das Read-Gegenstück die Arbeit iterativ vornehmen, habe ich mal die Reihenfolge der Property-Definitionen inerhalb der MyComponent-Klasse geändert und Prop2 vor Prop1 gesetzt also so als Beispielcode:
procedure MyComponent.SetProp1(Value:Integer);
begin FProp1 := Value + FProp2; end;
Delphi-Quellcode:
und gehofft das die Serialisierung von prop2 vor prop1 stattfindet, dazu habe ich auch mal in der entsprechenden
property Prop2 : integer
read FProp2 write SetProp2; property Prop1: integer read FProp1 write SetProp1; *.dfm Datei nachgesehen und dort hat sich die Reihenfolge tatsächlich geändert, aber beim Laden wird immer der Setter von Pop1 zuerst aufgerufen :gruebel: :gruebel: Ich bin mit meinem Latein erstmal am Ende, oder ich sehe das Offensichtliche nicht? :wall: Und an alle die es noch nicht wissen -> :dp: :cheers: |
Re: Reihenfolge beim Laden von Properties verändern
Vieleicht könnte es sein, daß die Properties in sortierter alpabetischer Reihenfolge geladen werden.
Benenne die Properties doch mal um Prop1 -> bProp1 Prop1 -> aProp2 Damit kannst du zumindest testen, ob meine Vermutung richtig ist. |
Re: Reihenfolge beim Laden von Properties verändern
Eigentlich ist die Reihenfolge in der DFM entscheidend. Hast du sicher gestellt, dass die neu erstellte dfm auch neu eingebunden wurde?
|
Re: Reihenfolge beim Laden von Properties verändern
Die Reihenfolge ist nicht steuerbar. Wenn du abhängige Eigenschaften hast, dann führe die Prüfung in den einzelnen Settern nur durch, wenn nicht csLoading im ComponentState ist. Dadurch werden sie geprüft beim Setzen und haben somit immer einen gültigen Wert beim Speichern. Somit ist alles was aus der DFM geladen wird gültig und muss nicht geprüft werden.
Falls du aber paranoid bist und auch den Inhalt der DFM prüfen willst, dann überschreibe die Methode Loaded und prüfe dort die beiden Properties nochmal entsprechend. |
Re: Reihenfolge beim Laden von Properties verändern
Hi,
solche Beispiele sind hervorragend geeignet, irgendwann mal bei der Fehlersuche den Entwickler in die Verzweifelung zu treiben. Wenn gegenseitige Abhängigkeiten entstehen, sollte man andere Wege gehen (anstatt Fields virtuelle Getter für Properties oder gleich Methoden). Aber um eine konkrete Lösung zu bringen, braucht man auch ein konkretes Problem. Cu, Frank |
Re: Reihenfolge beim Laden von Properties verändern
Hallo an alle,
whow - eine Menge Antworten :hi: - vielen Dank an alle die sich die Mühe gemacht haben, es war noch lang gestern Nacht ,d.h. etwas verspätet meine Antwort. Also der Reihe nach: @bernau Die alphabetische Reihenfolge habe ich mehrfach getestet und es hat keinen Einfluss auf die Reihenfolge der Speicherung, aber mal ganz ehrlich, wer will denn schon in der JVCL eine Komponente benutzen die dem Benutzer im Objektmanager ein aPop1:Bool und bProp2:Bool anbietet. :-D @sirius Ich habe neue Test-Poperties erstellt und hinzugefügt und wieder gelöscht, den Properties keine default Eigenschaft mitgegeben, sowie die Eigenschaften im Objektinspektor geändert,das Formular gespeichert, den Inhalt mit einem Editor überprüft und Debug-BrakePoints gesetzt die auch Angesprungen wurden und die erwarteten Werte hatten, das sollte das Einbinden absolut sicherstellen. -> Ergebnis die Reihenfolge beim Speichern in der *.dfm entspricht genau! der Reihenfolge der Klassendefinition im published Abschnitt. @Roachford Zitat:
csLoading ist Flag das anzeigt das die Komponente geladen wird, entweder aus einer Datei oder einem Stream, aber genau das ist der Zeitpunkt an dem der entsprechende Wert eingetragen werden soll. Zu der Load Methode die ausgeführt wird, NACHDEM alle Properties geladen und csLoading aus dem ComponentState entfernt werden wird, konnte ich mich noch nicht durchringen, aber Danke für Deinen Hinweis. :wink: @dataspider Ja du hast Recht das Beispiel war sehr einfach, hier jetzt ein Praxisnahes: Problemstellung: Ich habe eine CommandoProzessor Komponente geschrieben die Befehle vom Benutzer entgegen nehmen soll, direkt und ohne Aufruf von cmd.exe /C... , diese Komponente funktioniert auch einwandfrei, jetzt bin ich auf den Gedanken gekommen das man einen TCustomMemo Nachfahren also TJvMemo und TMemo der Komponente zuweisen kann. Die Prozessor-Komponente übernimmt das Einstellen des Oberflächen-Designs des Memos als auch das Senden des PipeOutput's an das Memo, und hier kommt es auf die Reihenfolge an. Wenn die CommandProzessor Komponente auf der Oberfläche der IDE platziert wird soll der CommandProzessor natürlich noch nicht gestartet werden. Das soll nur möglich sein wenn eine TCustomMemo der Komponente zugewiesen ist. Dafür habe ich einen Property-Schalter AutoRunShell eingefügt , bei dem der Benutzer entscheiden kann, ob der CommandProzessor gleich nach dem Start des Programms gestartet wird und PipeOutput-Daten an das Memo gesendet werden. Das Problem ist das der PipeOutput in einem Dispatcher-Thread untergebracht werden muss, da das OutputPipe Read-Ereignis solange blockiert wird bis Daten im Pipe vorliegen und dies erfordert die Übergabe der Memo-Komponente in der Create-Anweisung des TThread-Nachfahren, aber dies muss aber auch direkt nachdem der CommandProzessor gestartet ist geschehen, damit die InputPipe (evtl.) nicht blockiert oder Daten verloren gehen. So also wenn jetzt das Programm startet wird im Setter der AutoRunShell geprüft ob eine TCustomMemo Komponente zugewiesen ist, und eine entsprechende Run() prozedur aufgerufen, die die Interna steuert, das ist alles. Ein stark vereinfachtes Code-Beispiel des Modells:
Delphi-Quellcode:
So was ich noch rausgefunden habe:
type
TCommandShell = class TComponent private FShellMemo: TCusomMemo; FAutoRunShell: Boolean; procedure SetAutoRunShell(Value: Boolean); procedure SetShellMemo(Value: TCustomMemo); protected procedure Run(); // macht die Arbeit -> Prozess starten, Thread starten usw. published // die ShellMemo, extra VOR der AutoRunShell platziert property ShellMemo: TCustomMemo read FShellMemo write SetShellMemo // ruft bei Bedarf die Run property auf property AutoRunShell:Boolean read FAutoRunShell write SetAutoRunShell; end procedure TCommandShell.SetShellMemo(Value: TCustomMemo); // dieser Setter sollte zuerst aufgerufen werden // er wird aber immer NACH allen einfachen Datentypen aufgerufen begin FShellMemo := Value; end; procedure TCommandShell.AutoRunShell(Value: Boolean); // dieser Setter (und alle anderen einfachen Properties) // wird IMMER vorher aufgerufen, // egal wie ich es in der Klassendefnition angebe begin FAutoRunShell := Value; if ( Assigned( FShellMemo ) AND FAutoShellRun ) then // <- Hier ist das Problem... ,FShellMemo wurde noch nicht zugewiesen Run(); // die Procedure die die Arbeit machen sollte end; Was doch funktioniert ist wenn man die Reihenfolge in der Klassedefinition ändert UND wenn es sich dabei um einfache (int,bool,float) und selbstdefnierte Datentypen (type pocedure TEventHandler of Object;) u.ä. handelt, dann ist die Reihenfolge steuerbar. Also mein Beispiel (von Post#1) Prop2 vor Prop1 zu definieren funktioniert, dies wie gesagt nur mit einfachen und selbstdefinierten Typen. Sobald etwas vom Typ Object und im speziellen TComponent ist funktioniert es nicht. :gruebel: Dank an alle die sich die Zeit nehmen und genommen haben. |
Re: Reihenfolge beim Laden von Properties verändern
ok, nochmal zum mitschreiben.
Folgende Gedankenkette: 1. Wir sorgen dafür, dass die Properties immer gültige Kombinationen haben. 2. Wenn 1. zutrifft, dann werden auch nur gültige Kombinationen gestreamt. 3. Wenn 2. zutrifft, dann werden nur gültige Werte gelesen. 4. Wenn 3. zutrifft, dann kann man sich die Überprüfung bei 3. sparen. 5. Wenn 4. erfüllt ist, dann kann man in 1. die Überprüfung begrenzen, dass diese immer nur dann ausgeführt wird, wenn nicht 3. aktuell ist. Ergo: Wenn csLoading in ComponentState gesetzt ist, dann keine Gültigkeitsprüfungen. Dadurch kommen immer gültige Kombinationen in die Properties bzw. in die DFM. Damit kann man sich die Überprüfung der Eigenschaft beim Laden des Formulars sparen.
Delphi-Quellcode:
Da die Werte zur Designzeit überprüft werden, brauchen sie nicht mehr beim Laden überprüft werden.
type
TDependancy = class(TComponent) private fValueMin: integer; fValueMax: integer; procedure SetValueMin(const AValue: integer); procedure SetValueMax(const AValue: integer); public constructor Create(AOwner: TComponent); override; published property ValueMin: integer read fValueMin write SetValueMin default 0; property ValueMax: integer read fValueMax write SetValueMax default 10; end; ... constructor TDependancy.Create(AOwner: TComponent); begin inherited; fValueMax := 10; end; procedure TDependancy.SetValueMin(const AValue: integer); begin if fValueMin <> AValue then begin if ( AValue >= fValueMax ) and not ( csLoading in ComponentState ) then raise Exception.CreateFmt('minimum value has to be below the maximum value (%d)!', [fValueMax]); fValueMin := AValue; end; end; procedure TDependany.SetValueMax(const AValue: integer); begin if fValueMax <> AValue then begin if ( AValue <= fValueMin ) and not ( csLoading in ComponentState ) then raise Exception.CreateFmt('maximum value has to be above the minimum value (%d)!', [fValueMin]); fValueMax := AValue; end; end; Wenn du eine Aktion machst, welche mit/bei dem Laden ausgeführt werden muss und gültige Werte in beiden Properties voraussetzt, dann füge diese in die überschriebene Loaded; Methode ein. Wenn es nur um die Überprüfung der Abhängigkeiten geht, dann wie im Beispiel gezeigt. /EDIT: Dein Problem mit den Memos: Properties mit Instanzen werden aus der DFM gestreamt, aber deren Instanzen werden erst nach dem Streamen aller Elemente aktualisiert von der VCL. Somit kannst du definitiv nichts machen, wenn die AutoRunShell Property mit true aus dem Formular geladen wird. Dein Problem ist genau das von mir zuletzt beschriebene: Starte den Thread etc. in der überschriebenen Loaded Methode. Dort hast du die zugewiesene Memo Instanz samt Inhalt und dort kannst du auch schauen, ob AutoRunShell gesetzt ist. |
Re: Reihenfolge beim Laden von Properties verändern
Ich fasse das jetzt mal zusammen.
Zitat:
Das ist in der Tat gut, nur hatte ich ja nicht vor die Eigenschaft zu püfen wenn ohnehin fest steht das gültige Werte vorhanden sind. Auch hilft mir die Abfrage von csLoading nicht, denn dann würde ich ja den Code beim Laden einfach überspringen, so als wenn ich ihn erst gar nicht geschrieben hätte. Und das hilft mir bei meinem Problem ja nicht weiter, der Code sollte ja direkt beim Laden ausgeführt werden. Zur Reihenfolge der Properties: Es so wie du es gesagt hast, es existieren zwei Listen, eine für Datentypen und eine für Objekte. Die Reihenfolge der Properties in jeder Liste ist absolut zu der Reihenfolge der Klasse in der sie deklariert worden sind, mit der Ausnahme das sie nach Einfachen und Objekt-Typen getrennt werden. Und Danke nochmal für den Hinweis auf Loaded (das wäre wohl meine nächste Überlegung gewesen), ich habe meine Komponente nun so angepasst:
Delphi-Quellcode:
Also damit steht fest, die Reihenfolge ist nur für Einfache und Objekt-Typen getrennt steuerbar, und auf dieses Verhalten sollte man sich nicht verlassen. Dh. wenn alle Properties zur Verfügung stehen müssen ist die virtuelle procedure TComponent.Loaded zu verwenden.
procedure TCommandShell.Loaded; // override von TComponent
begin inherited; if (Assigned( FShellMemo) AND FAutoRun AND ( NOT csDesigning in ComponentState) ) ) then Run(); end; Dank an alle. :zwinker: [CLOSED] :dp: |
Re: Reihenfolge beim Laden von Properties verändern
Zitat:
Wenn die Klasse der TComponent-Property aber fix ist, kannst du es auch als SubComponent deklarieren. Dann bist du zwar selber für die Erzeugung der Instanz im Create verantworlich, die Properties werden aber in der normalen Reihenfolge in die DFM geschrieben. Das kann man ganz einfach auspropieren: - erzeuge ein neues TForm - ändere im Font des Forms die Eigenschaft Size - speichere das Form ab In der DFM stehen die Eigenschaften des Fonts in der Reihenfolge, wie das Font-Property im TForm deklariert ist, obwohl TFont eine Klasse ist. Ein Beispiel für ein TComponent-Property gibt es auch: - setze einfach ein TLabeledEdit in das Form In der DFM sind nun die Eigenschaften des Labels in der korrekten Reihenfolge der Properties des TLabeledEdit. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 02:49 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz