我创建了自己的组件,里面有一个TFont 适当的属性。
该组件在公共声明
中具有以下属性property CaptionFont: TFont read fCaptionFont write SetCaptionFont;
程序SetCaptionFont看起来像这样
procedure TMyComponent.SetCaptionFont(value: TFont);
begin
fCaptionFont := value;
end;
我正在尝试使用以下代码将字体名称和字体大小分配给我的组件:
MyComponent.CaptionFont.Name := fGlobalStandardFontName;
MyComponent.CaptionFont.Size := fGlobalStandardFontSize;
但是,在行
处设置断点时MyComponent.CaptionFont.Name := fGlobalStandardFontName;
然后点击" Trace Into(F7)"调试按钮,代码跳转到TFont代码和 完全忽略了SetCaptionFont过程。
我期待调用SetCaptionFont过程。
这里发生了什么?
答案 0 :(得分:6)
SetCaptionFont()
,因为您没有为CaptionFont
属性本身分配任何内容。
即使被调用,您的SetCaptionFont()
也无法正确实施。您正在获取来源TFont
的所有权并泄露原始TFont
。您需要使用Assign()
将源TFont
值复制到现有的TFont
。
要检测子属性值更改(包括来自Assign()
),您需要将OnChange
事件处理程序分配给fCaptionFont
对象,例如:
constructor TMyComponent.Create(AOwner: TComponent);
begin
inherited;
fCaptionFont := TFont.Create;
fCaptionFont.OnChange := CaptionFontChanged;
end;
procedure TMyComponent.SetCaptionFont(value: TFont);
begin
fCaptionFont.Assign(value);
end;
procedure TMyComponent.CaptionFontChanged(Sender: TObject);
begin
Invalidate;
end;
procedure TMyComponent.Paint;
begin
// use fCaptionFont as needed...
end;
答案 1 :(得分:1)
理解这一点的关键是赋值运算符的放置。
在属性上使用赋值运算符时,将调用属性设置器。使属性setter为您的属性执行的唯一方法是编写以下形式的代码:
CaptionFont := ...;
你不这样做。相反,你写:
CaptionFont.Name := ...;
此处使用CaptionFont
的getter,然后使用Name
的setter。
这解释了为什么不调用setter。
除此之外,你错误地实现了setter。您的实现可能会泄漏您在构造函数中创建的对象,该对象的引用位于FCaptionFont
中。当您分配到FCaptionFont
时,您将失去对该对象的跟踪。
属性属性的约定是将设置属性的操作转换为对Assign
的调用。这使得属性具有值语义而不是引用。属性值被复制。因此,setter看起来像这样:
procedure TMyComponent.SetCaptionFont(Value: TFont);
begin
FCaptionFont.Assign(Value);
end;