AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein GUI-Design mit VCL / FireMonkey / Common Controls Delphi Reihenfolge beim Laden von Properties verändern
Thema durchsuchen
Ansicht
Themen-Optionen

Reihenfolge beim Laden von Properties verändern

Ein Thema von DeepCopy · begonnen am 24. Okt 2008 · letzter Beitrag vom 25. Okt 2008
Antwort Antwort
Benutzerbild von DeepCopy
DeepCopy

Registriert seit: 5. Aug 2008
Ort: Gießen
21 Beiträge
 
Lazarus
 
#1

Reihenfolge beim Laden von Properties verändern

  Alt 24. Okt 2008, 02:06
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:
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;
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,
sagen wir z.B. um zu FProp1 einen Fixwert zu addieren.

Ein Beispiel für den Setter von FPop1
Delphi-Quellcode:
procedure MyComponent.SetProp1(Value:Integer);
begin
 FProp1 := Value + FProp2;
end;
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:
Delphi-Quellcode:
 property Prop2 : integer
                     read FProp2
                     write SetProp2;

 property Prop1: integer
                    read FProp1
                    write SetProp1;
und gehofft das die Serialisierung von prop2 vor prop1 stattfindet, dazu habe ich auch mal in der entsprechenden
*.dfm Datei nachgesehen und dort hat sich die Reihenfolge tatsächlich geändert, aber beim Laden wird immer der Setter von Pop1 zuerst
aufgerufen


Ich bin mit meinem Latein erstmal am Ende, oder ich sehe das Offensichtliche nicht?

Und an alle die es noch nicht wissen ->
Andreas
Gruß Andreas
---------------------------------------------------
Sic Luceat Lux (so lasse das Licht erstrahlen)
- ich kam sah und wusste nichts -
  Mit Zitat antworten Zitat
Benutzerbild von bernau
bernau

Registriert seit: 1. Dez 2004
Ort: Köln
1.295 Beiträge
 
Delphi 12 Athens
 
#2

Re: Reihenfolge beim Laden von Properties verändern

  Alt 24. Okt 2008, 09:45
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.
Gerd
Kölner Delphi Usergroup: http://wiki.delphitreff.de
  Mit Zitat antworten Zitat
Benutzerbild von sirius
sirius

Registriert seit: 3. Jan 2007
Ort: Dresden
3.443 Beiträge
 
Delphi 7 Enterprise
 
#3

Re: Reihenfolge beim Laden von Properties verändern

  Alt 24. Okt 2008, 09:45
Eigentlich ist die Reihenfolge in der DFM entscheidend. Hast du sicher gestellt, dass die neu erstellte dfm auch neu eingebunden wurde?
Dieser Beitrag ist für Jugendliche unter 18 Jahren nicht geeignet.
  Mit Zitat antworten Zitat
Roachford
(Gast)

n/a Beiträge
 
#4

Re: Reihenfolge beim Laden von Properties verändern

  Alt 24. Okt 2008, 10:56
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.
  Mit Zitat antworten Zitat
Benutzerbild von dataspider
dataspider

Registriert seit: 9. Nov 2003
Ort: 04539 Groitzsch
1.351 Beiträge
 
Delphi 11 Alexandria
 
#5

Re: Reihenfolge beim Laden von Properties verändern

  Alt 24. Okt 2008, 12:02
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
Frank Reim
  Mit Zitat antworten Zitat
Benutzerbild von DeepCopy
DeepCopy

Registriert seit: 5. Aug 2008
Ort: Gießen
21 Beiträge
 
Lazarus
 
#6

Re: Reihenfolge beim Laden von Properties verändern

  Alt 24. Okt 2008, 18:47
Hallo an alle,

whow - eine Menge Antworten - 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.

@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 von Roachford:
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.
Sorry das verstehe ich nicht!
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.

@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:
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;
So was ich noch rausgefunden habe:
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.

Dank an alle die sich die Zeit nehmen und genommen haben.
Andreas
Gruß Andreas
---------------------------------------------------
Sic Luceat Lux (so lasse das Licht erstrahlen)
- ich kam sah und wusste nichts -
  Mit Zitat antworten Zitat
Roachford
(Gast)

n/a Beiträge
 
#7

Re: Reihenfolge beim Laden von Properties verändern

  Alt 24. Okt 2008, 19:21
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:
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;
Da die Werte zur Designzeit überprüft werden, brauchen sie nicht mehr beim Laden überprüft werden.

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.
  Mit Zitat antworten Zitat
Benutzerbild von DeepCopy
DeepCopy

Registriert seit: 5. Aug 2008
Ort: Gießen
21 Beiträge
 
Lazarus
 
#8

Re: Reihenfolge beim Laden von Properties verändern

  Alt 25. Okt 2008, 11:00
Ich fasse das jetzt mal zusammen.

Zitat von Roachford:
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.
Zu 1. und 2. gehe ich davon aus das du gültige Wertebereiche mit gültigen Kombinationen meinst, richtig?

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:
procedure TCommandShell.Loaded; // override von TComponent
begin
 inherited;
 if (Assigned( FShellMemo)
     AND FAutoRun
     AND ( NOT csDesigning in ComponentState) ) ) then
  Run();
end;
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.

Dank an alle. [CLOSED]
Andreas
Gruß Andreas
---------------------------------------------------
Sic Luceat Lux (so lasse das Licht erstrahlen)
- ich kam sah und wusste nichts -
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
11.452 Beiträge
 
Delphi 12 Athens
 
#9

Re: Reihenfolge beim Laden von Properties verändern

  Alt 25. Okt 2008, 11:59
Zitat von DeepCopy:
Sobald etwas vom Typ Object und im speziellen TComponent ist funktioniert es nicht.
Vermutlich ist das TComponent-Property als Child-Komponente instanziiert worden. Dann erfolgt das Streaming oim Anschluß an die anderen Properties wenn die "Components" in den Stream geschrieben werden.

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.
Uwe Raabe
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

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