【问题标题】:Property setter never seems to fire in Lazarus?财产二传手似乎永远不会在拉撒路开火?
【发布时间】:2015-11-29 19:31:08
【问题描述】:

概述

我有一个TCustomControl 我正在拉撒路工作,在这个类之外我有一个单独的TPersistent 类,它将用于某些属性。

TCustomControl 发布的TPersistent 类应该在对象检查器中显示为子属性,因为我不希望某些属性从顶层显示,基本上这是将一些属性放入它自己的TCustomControl 内的组。

这段代码的结构如下:

type
  TMyControlHeaderOptions = class(TPersistent)
  private
    FOnChange: TNotifyEvent;
    FHeight: Integer;
    FVisible: Boolean;
    procedure SetHeight(const Value: Integer);
    procedure SetVisible(const Value: Boolean);
  protected
    procedure Changed;
  public
    constructor Create(AOwner: TComponent); virtual;
    destructor Destroy; override;

    procedure Assign(Source: TPersistent); override;
    property OnChange: TNotifyEvent read FOnChange write FOnChange;
  published
    property Height: Integer read FHeight write SetHeight default 20;
    property Visible: Boolean read FVisible write SetVisible default True;
  end;

  TMyControl = class(TCustomControl)
  private
    FHeaderOptions: TMyControlHeaderOptions;
    procedure SetHeaderOptions(const Value: TMyControlHeaderOptions);
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  published
    property Align;
    property BorderStyle default bsSingle;
    property HeaderOptions: TMyControlHeaderOptions read FHeaderOptions write SetHeaderOptions;
  end; 

这是TMyControlHeaderOptions的代码:

constructor TMyControlHeaderOptions.Create(AOwner: TComponent);
begin
  FHeight   := 20;
  FVisible  := True;
end;

destructor TMyControlHeaderOptions.Destroy;
begin
  inherited Destroy;
end;

// this method never fires (see TMyControl.SetHeaderOptions)
procedure TMyControlHeaderOptions.Assign(Source: TPersistent);
begin
  if (Source is TMyControlHeaderOptions) then
  begin
    FHeight   := (Source as TMyControlHeaderOptions).Height;
    FVisible  := (Source as TMyControlHeaderOptions).Visible;
  end
  else
    inherited Assign(Source);
end;

procedure TMyControlHeaderOptions.Changed;
begin
  if Assigned(FOnChange) then
  begin
    FOnChange(Self);
  end;
end;

procedure TMyControlHeaderOptions.SetHeight(const Value: Integer);
begin
  if Value <> FHeight then
  begin
    FHeight := Value;
    Changed;
  end;
end;

procedure TMyControlHeaderOptions.SetVisible(const Value: Boolean);
begin
  if Value <> FVisible then
  begin
    FVisible := Value;
    Changed;
  end;
end;

还有TCustomControl 代码:

constructor TMyControl.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);

  FHeaderOptions    := TMyControlHeaderOptions.Create(Self);
  Self.ControlStyle := Self.ControlStyle + [csAcceptsControls];
  Self.BorderStyle  := bsSingle;
  Self.Height       := 200;
  Self.Width        := 250;
end;

destructor TMyControl.Destroy;
begin
  FHeaderOptions.Free;
  inherited Destroy;
end;

// this method never fires which is why TMyControlHeaderOptions.Assign
// never fires either. So the task is understanding and solving why this
// procedure never gets fired? 
procedure TMyControl.SetHeaderOptions(const Value: TMyControlHeaderOptions);
begin
  FHeaderOptions.Assign(Value);
end;

问题

HeaderOptions 属性永远不会在设计时或运行时触发或被触发,我无法理解或理解为什么不呢?从上面代码中包含的 cmets SetHeaderOptions 可以看出,它似乎根本没有做任何事情,它从不响应在设计时或运行时所做的更改。

我没有安装 Delphi 来进行比较或测试,但代码取自我之前一直在使用的自定义控件,我非常确定它应该可以工作,我似乎没有遗漏任何内容我可以看到。在这一点上我唯一的假设是 Lazarus 和 Delphi 的差异,所以问题可能出在 Lazarus 内部?

问题

所以我的问题是为什么属性设置器 HeaderOptions 永远不会被解雇,可以做些什么来确保它成功?

我感觉到一些简单或明显的东西,但我无法弄清楚它是什么。

【问题讨论】:

  • 当您设置该属性时会触发属性设置器,您永远不会为“HeaderOptions”分配任何内容。
  • @SertacAkyuz,TMyControl.SetHeaderOptions 过程中的FHeaderOptions.Assign(Value); 行应该调用TMyControlHeaderOptions 类中覆盖的Assign
  • 属性是HeaderOptions,不是FHeaderOptions。

标签: delphi lazarus


【解决方案1】:

当您更改此 TPersistent 中的属性时,它会触发该特定属性的属性设置器。它不应该调用 TPersistent 本身的设置器。这只发生在两种情况下:a) 当 DFM 在创建时流式传输时,或 b) 当您手动为实际 TPersistent 分配新值时。如果您想在任何属性更改时捕获,您需要单独捕获每个属性,可能会触发反馈给其所有者的OnChange 通知事件。这实际上就是 TFontTStrings 之类的工作方式。

查看一些内置类,例如 TFontTStrings - 它们使用名为 OnChangeTNotifyEvent 来处理此类更改。

【讨论】:

  • 我想知道 OP 是否真的在询问类似这样的情况:stackoverflow.com/questions/31858026/…
  • 感谢您的反馈,我会看看是否有其他方法来捕获更改。这有点奇怪,我在 Delphi 中知道我已经以这种方式完成了它,并且它在例如 TMyControl.SetHeaderOptions 会触发但在 Lazarus 中它似乎没有做任何事情。
  • @Craig 在 Delphi 中也是如此。实际上,我从来没有使用过 Lazarus,但肯定是一样的。
  • 这就是我难过的原因,我知道从技术上讲 Lazarus 并不是 Delphi 的完全克隆,因此应该被视为它自己的,但它仍然使用几乎相同的 Pascal OOP 语法。因此,如果不是 Lazarus,那么它一定是我的代码,但我很肯定我所做的与我通常在 Delphi 中所做的没有什么不同。
  • @Craig 这两种情况都行不通。实际的TPersistent 本身并不关心它的成员做什么。由您决定如何将其反馈给它的所有者。您过去的经验一定有一些您忘记的额外代码。如果TPersistent 的属性设置器在其成员之一更改时被触发,我个人会讨厌。
【解决方案2】:

我仍然对为什么这在 Lazarus 中不起作用感到困惑,因为我几乎可以肯定它在 Delphi 中起作用。

与此同时,我设法想出了一个解决方法:

TMyControl = class(TCustomControl)
  private
    FHeaderOptions: TMyControlHeaderOptions;
    procedure HeaderOptionsChanged(Sender: TObject); // added this line
    procedure SetHeaderOptions(const Value: TMyControlHeaderOptions); // removed this procedure
  published
    property Align;
    property BorderStyle default bsSingle;
    property HeaderOptions: TMyControlHeaderOptions read FHeaderOptions write FHeaderOptions; // changed this 
  end;

然后在构造函数中添加这个:

constructor TMyControl.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);

  FHeaderOptions          := TMyControlHeaderOptions.Create(Self);
  FHeaderOptions.OnChange := @HeaderOptionsChanged; // added this line

  Self.ControlStyle := Self.ControlStyle + [csAcceptsControls];
  Self.BorderStyle  := bsSingle;
  Self.Height       := 200;
  Self.Width        := 250;
end;

新的HeaderOptionsChanged 过程的代码:

procedure TMyControl.HeaderOptionsChanged(Sender: TObject);
begin
  // header options changed
  Invalidate;
end;

【讨论】:

  • 这不是解决方法,这就是它的设计方式,正如我在回答中所解释的那样。但是,您引入了两个新问题。 a) 你为什么使用@?那是不必要的。 b) 你不应该摆脱属性设置器,你应该保留它并使用FHeaderOptions.Assign(Value);。诸如TFontTStrings 之类的东西正是出于这样的原因。
  • @JerryDodge 在 Lazarus/FPC 中您需要使用“@”符号运算符;)我删除了 FHeaderOptions.Assign(Value);,因为它仍然不会触发。
  • 我明白了。我对使用Assign() 的观点是,使用直接分配给FHeaderOptions 是将指针复制到可能不同的实例,而不是实际值。在某些情况下,这可能会导致内存泄漏。
  • 我几乎可以肯定它在 Delphi 中确实有效。 不。你错了。此外,此处概述的更改是完全错误的。您现在泄漏了TMyControlHeaderOptions 的实例。 @Jerry 说得对,你应该接受他的回答。 -1
  • @DavidHeffernan 我在哪里泄露TmyControlHeaderOptions 的实例?查看原始代码,这只是一个修改版本,析构函数在那里发布FHeaderOptions
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-05
  • 1970-01-01
  • 2015-04-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多