6

我正在创建一个从 TCustomControl 派生的自定义控件,例如:

type
  TMyCustomControl = class(TCustomControl)
private
  FText: string;
  procedure SetText(const Value: string);
protected
  procedure Paint; override;
public
  constructor Create(AOwner: TComponent); override;
  destructor Destroy; override;
published
  property Text: string read FText write SetText;
end;

请注意,为了保持示例的简短和简单,以上内容是不完整的。

无论如何,在我的控制中,我有一个 Paint 事件,它FText使用 Canvas.TextOut 显示文本(来自字段)。

当我的组件被添加到 Delphi 表单设计器时(在用户可以对组件进行任何更改之前),我希望 TextOut 显示组件的名称 - TButton、TCheckBox、TPanel 等是带有标题属性的示例。

如果我尝试在构造函数中将组件的名称分配给 FText,它会返回空,例如''

constructor TMyCustomControl.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FText  := Name; //< empty string
  ShowMessage(Name); //< empty message box too
end;

如果我更改FText := NameFText := 'Name';确实将文本输出到我的组件,所以我知道这在实际代码中不是问题,但显然这输出“名称”而不是实际的组件名称,如 MyCustomControl1、MyCustomControl2 等。

所以我的问题是,如何从它的构造函数事件中获取组件的名称?

4

4 回答 4

6

构造函数运行时,该Name属性尚未分配。Name在设计时,IDE 会在组件被拖放到设计器上之后,在控件的构造函数退出之后为属性分配一个值。在运行时,该Name属性由 DFM 流系统设置,它也在构造函数退出后调用。

无论哪种方式,TControl.SetName()属性设置器都会验证新值,然后将新值设置为控件的Text属性,以匹配当前Text值与旧Name值匹配并且控件的ControlStyle属性包含csSetCaption标志(默认情况下)。当Text属性因任何原因发生更改时,控件会自动向自身发送CM_TEXTCHANGED通知。您可以让您的控件捕获该消息并调用Invalidate()自身以触发新的重绘。在您的Paint()处理程序内部,只需按原样绘制当前Name值,无论它碰巧是什么值。如果它是空白的,那就这样吧。不要强求Name,让VCL正常为你处理。

于 2013-01-28T17:46:54.353 回答
5

我相信处理这个问题的正确方法是使用继承的TextCaption属性TCustomControl,并确保csSetCaption ControlStyle设置了。

于 2013-01-28T17:40:10.483 回答
0

要应用名称,您可以覆盖TComponent.Loaded方法。

但我认为您不应该将名称复制到文本。这些是语义上独立的属性,向它们添加意想不到的绑定有一天会伤害你。

而是 WMPaint 方法应该检查 Text 是否为空,然后渲染 Name,但是 Text 的属性不应该改变。

procedure TMyComponent.WMPaint; message WM_Paint; var InternalCaption: string;
begin
....
   InternalCaption := Self.Text;
   If InternalCaption = '' then InternalCaption := Self.Name;
   If InternalCaption = '' then InternalCaption := Self.ClassName;
....
   Self.Canvas.OutText(InternalCaption);

Name := 'AAA'; Name := 'BBB';如果有的话 - 您应该将属性分开,只是出于不应该使文本和名称不同步的简单原因。并且使用您的方法,第一个语句将解决文本,第二个语句将使旧名称在实际名称更改后仍然显示。

于 2013-01-29T08:15:04.673 回答
0

一个简单的方法是覆盖方法 SetName:

TMyCaptionString = type of WideString;

TMyLabel = class(TCustomControl)
private
  FCaption: TMyCaptionString;
  FCaptionAsName: Boolean;
  procedure SetCaption(Value: TMyCaptionString);
protected
  procedure SetName(const NewName: TComponentName); override;
public
  constructor Create(AOwner: TComponent); override;
  property Caption: TMyCaptionString read FCaption write SetCaption;
end;

implementation

constructor TMyLabel.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  ControlStyle := ControlStyle + [csOpaque, csReplicatable,csSetCaption];
  FCaptionAsName := (csDesigning in ComponentState) and not (csReadingState in ControlState);
  ...
end;

procedure TMyLabel.SetName(const NewName: TComponentName);
begin
  if FCaptionAsName then
  begin
    FCaptionAsName := FCaption = Name;
    FCaption := NewName;
    invalidate;
  end;
  inherited SetName(NewName);
end;

procedure TMyLabel.SetCaption(Value: TMyCaptionString);
begin
  if FCaption <> Value then
  begin
    FCaption := Value;
    Invalidate;
    FCaptionAsName := False;
  end;
end;

我需要自己的 Caption poreprty 变量,因为我想使用宽字符串而不是 unicode 并编写自定义属性编辑器。抱歉,我写的是旧主题,但我希望这会有所帮助。

于 2015-09-28T10:32:41.307 回答