Discussion:
Komponenten mit Record Properties
(zu alt für eine Antwort)
Hans-Peter Diettrich
vor 12 Jahren
Permalink
In D5 habe ich ein Control ohne Quelltext, das neu implementiert werden
soll. Dabei bin ich über eine published property gestolpert, die einem
TRect entspricht, nur mit Width und Height anstelle von Right und
Bottom. Soweit noch kein Problem, der entsprechende Record-Typ wurde
hinzugefügt, und dann funktioniert auch
Self.MyProp.Width := 100; //in D5 noch zulässig
im Code.

Daß die Property im OI nicht angezeigt wird könnte ich ja noch verstehen
(Editor fehlt), aber auch beim Öffnen der alten Formulare gibt es einen
Fehler "missing property" o.ä., und das ist übel :-(

Was läuft da schief?

DoDi
Björn Schreiber
vor 12 Jahren
Permalink
Hallo Hans-Peter.
Post by Hans-Peter Diettrich
Bottom. Soweit noch kein Problem, der entsprechende Record-Typ wurde
hinzugefügt, und dann funktioniert auch
Self.MyProp.Width := 100; //in D5 noch zulässig
im Code.
Sicher? Ich habe das gerade einmal mit TRect ausprobiert. Ein Zugriff
auf Rect.Left führt in meinem D5 wie erwartet zu der Fehlermeldung

"Der linken Seite kann nichts zugewiesen werden".

Ich denke, es handelt sich eher um eine weitere Klasse mit
entsprechenden Eigenschaften.


Grüße,
Björn
--
Björn Schreiber, DRIGUS GmbH
Hans-Peter Diettrich
vor 12 Jahren
Permalink
Post by Björn Schreiber
Hallo Hans-Peter.
Post by Hans-Peter Diettrich
Bottom. Soweit noch kein Problem, der entsprechende Record-Typ wurde
hinzugefügt, und dann funktioniert auch
Self.MyProp.Width := 100; //in D5 noch zulässig
im Code.
Sicher? Ich habe das gerade einmal mit TRect ausprobiert. Ein Zugriff
auf Rect.Left führt in meinem D5 wie erwartet zu der Fehlermeldung
"Der linken Seite kann nichts zugewiesen werden".
Hmm, da hätte ich das Programm wohl auch noch laufen lassen sollen.
Post by Björn Schreiber
Ich denke, es handelt sich eher um eine weitere Klasse mit
entsprechenden Eigenschaften.
Das wollte ich eigentlich vermeiden, falls möglich.

DoDi
Peter
vor 12 Jahren
Permalink
Post by Hans-Peter Diettrich
Post by Björn Schreiber
Ich denke, es handelt sich eher um eine weitere Klasse mit
entsprechenden Eigenschaften.
Das wollte ich eigentlich vermeiden, falls möglich.
Ist nicht möglich. Eine Property mit einem record-type ist zwar legal,
aber man kann ihr nur einen kompletten Record zuweisen, nicht ein
einzelnes Feld ändern. Eine Anweisung wie dein

Self.MyProp.Width := 100

tut nicht was Du erwartest. Self.MyProp gibt eine *Kopie* des Records
zurück, und die ändert der Rest des Statements, was natürlich unsinnig
ist. Neuere Delphi-Versionen weisen so ein statement daher beim
Compilieren ab.

Oh, und published geht nicht für solche Properties, da der Compiler
keine run-time type information für sie erzeugt.

Was Du brauchst ist ein TPersistent-Abkömmling mit den entsprechenden
Subproperties. Sieh Dir mal an, wie TControl.Font implementiert ist,
das ist das beste Modell in D5.
--
Peter Below
Hans-Peter Diettrich
vor 12 Jahren
Permalink
...
Das steht u.a. in der DFM Datei zu diesem Control:
...
//property foo: TRect
foo.Top = 1
foo.Bottom = 1
foo.Left = 1
foo.Right = 1
//property bar: ???
bar.Left = 0
bar.Top = 0
bar.Width = 0
bar.Height = 0
...

Bei TRect scheint das also zu funktionieren (kein Meckern) - gibt dazu
irgendwelche Magie?
Wenn hier "bar" ein Objekt wäre, müßte das dann nicht in "object...end"
eingeschlossen sein?

Könnte es sich um C++Builder Control handeln, das sich in Delphi so
garnicht nachbilden läßt?


Hmmm, bei Font wird die gleiche Syntax verwendet:
Font.Charset = DEFAULT_CHARSET
Font.Color = 10930928
Font.Height = -21
Font.Name = 'Times New Roman'
Font.Style = [fsBold]
und TFont ist eine Klasse. Wird so ein Objekt vom Lader automatisch
erzeugt, oder wie funktioniert das?

DoDi
Peter
vor 12 Jahren
Permalink
...
Bist Du sicher, das TRect hier wirklich Windows.TRect ist und nicht
eine Klasse mit dem gleichen Namen?
Post by Hans-Peter Diettrich
Bei TRect scheint das also zu funktionieren (kein Meckern) - gibt
dazu irgendwelche Magie? Wenn hier "bar" ein Objekt wäre, müßte das
dann nicht in "object...end" eingeschlossen sein?
Müßte es. Vielleicht ist das ein "custom property". The Delphi
Komponentenmodel erlaubt es eine Klasse, die von TPersistent abstammt,
sog. pseudoproperties zu deklarieren. Dazu muß man die
DefineProperties-Methode überschreiben und zwei Methoden bereitstellen,
die den Wert dieser Pseudoproperty in das DFM-File streamen bzw.
daraus lesen.
Post by Hans-Peter Diettrich
Könnte es sich um C++Builder Control handeln, das sich in Delphi so
garnicht nachbilden läßt?
Weis nicht, mit C++Builder kenne ich mich überhaupt nicht aus. Ich mag
C-basierte Sprachen nicht, die Syntax führt bei mir zu
Übelkeitsattacken ;-)
Post by Hans-Peter Diettrich
Font.Charset = DEFAULT_CHARSET
Font.Color = 10930928
Font.Height = -21
Font.Name = 'Times New Roman'
Font.Style = [fsBold]
und TFont ist eine Klasse. Wird so ein Objekt vom Lader automatisch
erzeugt, oder wie funktioniert das?
Nein. TFont ist keine TComponent-Abkömmling sondern nur TPersistent.
Das Streaming-System erwartet, das die gestreamte Komponente in ihrem
Constructor ein TFont-Objekt erzeugt und über die Font.Property
zugänglich macht. Der filer sagt diesem Objekt dann "Schreib deine
Properties" oder "Lade deine Properties". Ein TPersistent weis wie das
geht.
--
Peter Below
Hans-Peter Diettrich
vor 12 Jahren
Permalink
...
Zumindest komme ich dort raus, wenn ich zur Definition springe.
...
Sowas habe ich zu TRect schon gesucht, und bin nicht fündig geworden.
Sonst hätte sich meine Frage ja erübrigt ;-)
...
Mein Versuch, eine Klasse für obige Eigenschaft von TPersistent
abzuleiten (wie TFont), führte leider nicht zum gewünschten Erfolg, die
Fehlermeldung blieb gleich.

DoDi
Peter
vor 12 Jahren
Permalink
Post by Hans-Peter Diettrich
Mein Versuch, eine Klasse für obige Eigenschaft von TPersistent
abzuleiten (wie TFont), führte leider nicht zum gewünschten Erfolg,
die Fehlermeldung blieb gleich.
Wenn Du ein Bißchen Geduld hast kann ich am Wochenende ein Beispiel
produzieren. Momentan reicht die Zeit nicht, sorry.
--
Peter Below
Hans-Peter Diettrich
vor 12 Jahren
Permalink
Post by Peter
Post by Hans-Peter Diettrich
Mein Versuch, eine Klasse für obige Eigenschaft von TPersistent
abzuleiten (wie TFont), führte leider nicht zum gewünschten Erfolg,
die Fehlermeldung blieb gleich.
Wenn Du ein Bißchen Geduld hast kann ich am Wochenende ein Beispiel
produzieren.
Das wäre nett :-)
Post by Peter
Momentan reicht die Zeit nicht, sorry.
Ich kann auch noch etwas länger warten...

DoDi
Peter
vor 12 Jahren
Permalink
Post by Hans-Peter Diettrich
Post by Peter
Wenn Du ein Bißchen Geduld hast kann ich am Wochenende ein Beispiel
produzieren.
Das wäre nett :-)
OK, hier ist es. Pack's in eine Designtime-Package und wirf ein
TDemoPanel auf ein Form. Ich habe das mit XE2 entwickelt, aber die Unit
sollte auch mit älteren Delphi-Versionen funktionieren.

unit demoPanelU;

interface

uses
Windows, SysUtils, Classes, Controls, ExtCtrls;

type
TMyRect = class(TPersistent)
private
FOnChange: TNotifyEvent;
FRect: TRect;
protected
procedure Change; virtual;
function GetHeight: Integer;
function GetWidth: Integer;
procedure SetAsTRect(const Value: TRect);
procedure SetHeight(const Value: Integer);
procedure SetLeft(const Value: Integer);
procedure SetTop(const Value: Integer);
procedure SetWidth(const Value: Integer);
public
constructor Create; virtual;
procedure Assign(Source: TPersistent); override;
property AsTRect: TRect read FRect write SetAsTRect;
property OnChange: TNotifyEvent read FOnChange write FOnChange;
published
property Left: Integer read FRect.Left write SetLeft;
property Height: Integer read GetHeight write SetHeight;
property Top: Integer read FRect.Top write SetTop;
property Width: Integer read GetWidth write SetWidth;
end;

TDemoPanel = class(TPanel)
private
FRect: TMyRect;
procedure SetRect(const Value: TMyRect);
protected
procedure RectChanged(Sender: TObject);
procedure Paint; override;
public
constructor Create(aOwner: TComponent); override;
destructor Destroy; override;
published
property Rect: TMyRect read FRect write SetRect;
end;

procedure Register;

implementation

uses
Graphics;

procedure Register;
begin
RegisterComponents('Samples', [TDemoPanel]);
end;

constructor TDemoPanel.Create(aOwner: TComponent);
begin
inherited Create(aOwner);
FRect := TMyRect.Create;
FRect.OnChange := RectChanged;
end;

destructor TDemoPanel.Destroy;
begin
FRect.Free;
inherited Destroy;
end;

procedure TDemoPanel.Paint;
begin
inherited;
Canvas.Brush.Color := clYellow;
Canvas.FillRect(Rect.AsTRect);
end;

procedure TDemoPanel.RectChanged(Sender: TObject);
begin
Invalidate;
end;

procedure TDemoPanel.SetRect(const Value: TMyRect);
begin
FRect.Assign(Value);
end;

// Initialize FRect to some sensible default
constructor TMyRect.Create;
begin
inherited;
FRect := Rect(5, 5, 25, 25);
end;

procedure TMyRect.Assign(Source: TPersistent);
begin
if Source is TMyRect then begin
FRect := TMyRect(Source).FRect;
Change;
end
else
inherited;
end;

procedure TMyRect.Change;
begin
if Assigned(FOnChange) then FOnChange(Self);
end;

function TMyRect.GetHeight: Integer;
begin
Result := FRect.Bottom - FRect.Top
end;

function TMyRect.GetWidth: Integer;
begin
Result := FRect.Right - FRect.Left;
end;

procedure TMyRect.SetAsTRect(const Value: TRect);
begin
FRect := Value; // should validate left < Top etc.
Change;
end;

procedure TMyRect.SetHeight(const Value: Integer);
begin
if Value < 0 then
FRect.Top := FRect.Bottom + Value
else
FRect.Bottom := FRect.Top + Value;
Change;
end;

procedure TMyRect.SetLeft(const Value: Integer);
begin
OffsetRect(FRect, Value - Frect.Left, 0);
Change;
end;

procedure TMyRect.SetTop(const Value: Integer);
begin
OffsetRect(FRect, 0, Value - FRect.Top);
Change;
end;

procedure TMyRect.SetWidth(const Value: Integer);
begin
if Value < 0 then
FRect.Left := FRect.Right + Value
else
FRect.Right := FRect.Left + Value;
Change;
end;

end.
--
Peter Below
Hans-Peter Diettrich
vor 12 Jahren
Permalink
Post by Peter
Post by Hans-Peter Diettrich
Post by Peter
Wenn Du ein Bißchen Geduld hast kann ich am Wochenende ein Beispiel
produzieren.
Das wäre nett :-)
OK, hier ist es. Pack's in eine Designtime-Package und wirf ein
TDemoPanel auf ein Form. Ich habe das mit XE2 entwickelt, aber die Unit
sollte auch mit älteren Delphi-Versionen funktionieren.
[...]

Danke :-)

Wesentlich war wohl die Erzeugung einer Instanz im Konstruktor. Danach
erschien die Property im OI.

DoDi

Loading...