【问题标题】:Property is not being written to, only read from in Delphi XE2属性没有被写入,只能从 Delphi XE2 中读取
【发布时间】:2014-12-07 08:02:04
【问题描述】:

我创建了自己的组件,其中包含一个 TFont 以及 适当的属性。

组件在 Public 声明中具有以下属性

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 过程。

这是怎么回事?

【问题讨论】:

    标签: delphi properties call


    【解决方案1】:

    在为子属性赋值时不会调用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;
    

    【讨论】:

    • 我期待的只是帽子。谢谢你。即使在写入子属性时,是否可以让它以调用 SetCaptionFont 的方式运行?
    • 没有。 TFont 是它自己的独立对象。您必须使用它的OnChange 事件来知道它的任何值何时被更改。
    • 为什么不只创建两个属性?您正在访问的字体的每个子属性一个。
    • 如果您想要完整的TFont 功能,那就太过分了。 TFont 有 9 个属性,你要复制 TMyComponent 中的那些吗?为使其可行,您必须删除 CaptionFont 属性,以便用户无法直接访问 TFont,绕过您的重复属性。如果组件有多个 Font 属性怎么办?你也要复制那 9 倍吗?
    【解决方案2】:

    理解这一点的关键是赋值运算符的位置。

    当您对属性使用赋值运算符时,会调用属性设置器。让属性设置器为您的属性执行的唯一方法是编写这种形式的代码:

    CaptionFont := ...;
    

    你不这样做。相反,你写:

    CaptionFont.Name := ...;
    

    这里使用CaptionFont 的getter,然后使用Name 的setter。

    这就解释了为什么不调用 setter。

    除此之外,您错误地实现了 setter。您的实现可能会泄漏您在构造函数中创建的对象,该对象的引用保存在FCaptionFont 中。当您分配给FCaptionFont 时,您将失去对该对象的跟踪。

    类属性的约定是将设置属性的操作转换为对Assign的调用。这使得属性具有值语义而不是引用。属性值被复制。因此,setter 看起来像这样:

    procedure TMyComponent.SetCaptionFont(Value: TFont);
    begin
      FCaptionFont.Assign(Value);
    end;
    

    【讨论】:

    • 好的,谢谢大卫。所以如果我理解正确,我需要做的就是把 FCaptionFont.Assign(Value);在 SetCaptionFont 过程中,当改变 TFont 的子属性的值时,该过程将被调用?还是雷米的答案仍然适用?
    • 我的那部分答案与雷米的相同。我一点也不反对他。我添加这个答案的原因是第一部分。我试图解释,也许比雷米解释得更清楚,为什么不叫二传手。 Remy 确实解释了它,但我不确定你是否一定完全理解它,我真的很想强调你需要将赋值运算符应用于属性以让 setter 触发。就是这样。
    • 我明白了。所以我不能通过设置器应用任何子属性仍然是真的吗?遇到问题
    • 好吧,设置没有执行是吗?这是问题的基本点。
    • 是的,没错。提出一个新问题是否违反 StackOverflow 规则,我询问如何使代码按我预期的方式工作?正如您所说,这个问题从根本上要求回答为什么代码会以这种方式运行,而不是如何使其以其他方式运行。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-12-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-20
    相关资源
    最近更新 更多