问题
Here's my component overall structure:
- My Component
- property Categories: TCollection (of TCategory)
- TCategory
- property Icon: TPicture read FIcon write SetIcon;
- property Example: Integer read FExample write SetExample;
- (Other category properties...)
- TCategory
- (Other properties...)
- property Categories: TCollection (of TCategory)
In the Object Inspector of the IDE, I choose a picture and it gets serialized successfully (I checked it in form view as text). I set a breakpoint on SetIcon
method and when I compile and run the application, it doesn't get called at all. At the same time, SetExample
gets called right as it should.
What's wrong with the TPicture
property?
P.S: Set method is called in IDE, but not at runtime.
Here's a MCVE of the code:
unit MCVE;
interface
uses
System.SysUtils, System.Classes, Vcl.Controls, Graphics, Dialogs;
type
TMyCollectionItem = class(TCollectionItem)
private
FIcon: TPicture;
procedure SetIcon(const Value: TPicture);
public
constructor Create(Collection: TCollection); override;
published
property Icon: TPicture read FIcon write SetIcon;
end;
TMyCollection = class(TCollection)
end;
TMCVE = class(TCustomControl)
private
FCollection: TMyCollection;
procedure SetCollection(const Value: TMyCollection);
public
constructor Create(AOwner: TComponent); override;
published
property MyCollection: TMyCollection read FCollection write SetCollection;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Samples', [TMCVE]);
end;
{ TMyCollectionItem }
constructor TMyCollectionItem.Create(Collection: TCollection);
begin
inherited;
FIcon := TPicture.Create;
end;
procedure TMyCollectionItem.SetIcon(const Value: TPicture);
begin
ShowMessage('SetIcon is called!');
FIcon.Assign(Value);
end;
{ TMCVE }
constructor TMCVE.Create(AOwner: TComponent);
begin
inherited;
FCollection := TMyCollection.Create(TMyCollectionItem);
end;
procedure TMCVE.SetCollection(const Value: TMyCollection);
begin
FCollection := Value;
end;
end.
回答1:
For simple types like Integer
, Boolean
, Double
etc., streaming operates the way you describe. The streaming framework reads the value, and calls the property setter.
For a more complex type like TPicture
, it does not happen that way. The streaming framework cannot call the property setter. In order to do so it would need to get hold of a fully formed TPicture
. And it doesn't know how to do that, a priori.
So what happens instead, is that the streaming framework calls the getter of the property to obtain the TPicture
instance that your component's constructor created. And then streams in the state of the TPicture
stored in the .dfm file.
As it happens, TPicture
has no published properties. The picture data is saved to the .dfm file under a property named Data
. So, where does that come from. The answer is in the overridden DefineProperties
method. The code looks like this:
procedure TPicture.DefineProperties(Filer: TFiler);
function DoWrite: Boolean;
begin
// .... code removed for brevity
end;
begin
Filer.DefineBinaryProperty('Data', ReadData, WriteData, DoWrite);
end;
The WriteData
and ReadData
methods do the streaming.
So, to recap. You asked why the setter for a TPicture
property was not called when streaming in the picture's properties. It is not called because the picture object that you created in your constructor streams in its properties.
来源:https://stackoverflow.com/questions/31858026/set-method-of-tpicture-property-doesnt-get-called-when-the-form-is-created