Firemonkey:如何定义一个包含另一个组件的组件?

Posted

技术标签:

【中文标题】Firemonkey:如何定义一个包含另一个组件的组件?【英文标题】:Firemonkey: How to define a component that contain another component? 【发布时间】:2016-12-23 10:18:26 【问题描述】:

在 Delphi 下,我想创建一个新的 firemonkey 控件,该控件将包含另一个 firemonkey 控件。这不是一个真正的问题,因为我可以这样做:

constructor TMyComponent.Create(AOwner: TComponent);
begin
  inherited;
  FBtn := Trectangle.create(self);
  FBtn.parent := self;
  FBtn.stored := false;
end;

但现在我想允许最终用户也可以在 Object Inspector 中修改 FBtn 的属性!我不知道该怎么做:(

如果我删除 FBtn.stored := False 那么我将在结构资源管理器中拥有一些名称如 的组件,并且每次我都会将表单作为文本查看并返回以查看表单结构浏览器中将出现一个新组件:(

【问题讨论】:

尝试在 TMyComponent.Create 中添加 FBtn.SetSubcomponent(true)。在 vcl 中工作正常,如果他们不是很疯狂,也应该在 firemonkey 中工作...... 不是什么都没有改变,如果我这样做 FBtn.SetSubcomponent‌​(true) 我仍然会在结构资源管理器中有奇怪的组件名称 :( 在某种程度上 FBtn.SetSubcomponent‌​(true) 像 FBtn 一样工作。存储... 【参考方案1】:

您想在 Objct Inspector 中公开的任何内容都必须在 published 属性中声明,例如:

type
  TMyComponent = class(TComponent)
  private
    FBtn: TRectangle;
    procedure SetBtn(Value: TRectangle);
  published
    property Btn: TRectangle read FBtn write SetBtn;
  end;

constructor TMyComponent.Create(AOwner: TComponent);
begin
  inherited;
  FBtn := TRectangle.create(Self);
  FBtn.Parent := Self;
  FBtn.Stored := False;
  FBtn.SetSubComponent(True);
end;

procedure TMyComponent.SetBtn(Value: TRectangle);
begin
  FBtn.Assign(Value);
end;

【讨论】:

感谢它的工作!对象检查器中只有一个 remak 我的名称类似于 MyComponent1. ... 是否可以使用类似 MyComponent1.button1 的名称来代替示例?无论如何,这并不重要 @lok​​i 你所描述的没有意义。请澄清。 remy,我知道我想说什么,用你的方法(看起来正是我想要的),如果我进入事件选项卡并尝试向子组件添加事件,我会收到“不能为未命名的组件创建方法” :( 当然不是,因为子组件的Name属性还没有赋值。您真的不应该将子组件的事件暴露给用户。主组件应该为子组件的事件分配自己的处理程序,然后主组件可以向用户公开自己的事件。【参考方案2】:

虽然 Remy 的回答在技术上可行,但我认为它不是合适的方法。这是因为当您将任何TComponent 后代发布为属性时,这假定您将在外部 创建这样的控件,然后在对象检查器中分配它。您肯定不想让这种情况发生,因为这个控件是在内部创建的。您也可能只想允许用户更改某些属性,而不是所有属性。

相反,我建议使用TPersistent,它只公开您真正想要提供访问权限的控件的那些属性。

unit MyComponentUnit;

interface

uses
  System.Classes, System.SysUtils,
  Fmx.Controls, FMX.StdCtrls, FMX.Objects;

type
  TMyComponentButton = class(TPersistent)
  private
    FBtn: TRectangle;
    function GetXRadius: Single;
    function GetYRadius: Single;
    procedure SetXRadius(const Value: Single);
    procedure SetYRadius(const Value: Single);
    //And any other property getters/setters you wish to wrap
  public
    constructor Create(ABtn: TRectangle);
    destructor Destroy; override;
    procedure Assign(Source: TPersistent); override;
  published
    property XRadius: Single read GetXRadius write SetXRadius;
    property YRadius: Single read GetYRadius write SetYRadius;
    //And any other properties you wish to wrap
  end;

  TMyComponent = class(TControl)
  private
    FBtn: TRectangle;
    FMyComponentButton: TMyComponentButton;
    procedure SetMyComponentButton(const Value: TMyComponentButton);
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  published
    property MyComponentButton: TMyComponentButton
      read FMyComponentButton write SetMyComponentButton;
  end;

implementation

 TMyComponentButton 

constructor TMyComponentButton.Create(ABtn: TRectangle);
begin
  inherited Create;
  FBtn:= ABtn;
end;

destructor TMyComponentButton.Destroy;
begin

  inherited;
end;

procedure TMyComponentButton.Assign(Source: TPersistent);
begin
  if Source is TMyComponentButton then begin
    XRadius:= (Source as TMyComponentButton).XRadius;
    YRadius:= (Source as TMyComponentButton).YRadius;
    //And everything else you need to assign
  end else
    inherited;
end;

function TMyComponentButton.GetXRadius: Single;
begin
  Result:= FBtn.XRadius;
end;

function TMyComponentButton.GetYRadius: Single;
begin
  Result:= FBtn.YRadius;
end;

procedure TMyComponentButton.SetXRadius(const Value: Single);
begin
  FBtn.XRadius:= Value;
end;

procedure TMyComponentButton.SetYRadius(const Value: Single);
begin
  FBtn.YRadius:= Value;
end;

 TMyComponent 

constructor TMyComponent.Create(AOwner: TComponent);
begin
  inherited;
  FBtn:= TRectangle.Create(Self);
  FBtn.Parent:= Self;
  FBtn.Stored:= False;
  FMyComponentButton:= TMyComponentButton.Create(FBtn);
end;

destructor TMyComponent.Destroy;
begin
  FreeAndNil(FMyComponentButton);
  FreeAndNil(FBtn);
  inherited;
end;

procedure TMyComponent.SetMyComponentButton(const Value: TMyComponentButton);
begin
  FMyComponentButton.Assign(Value);
end;

end.

【讨论】:

您的TMyComponentButton 类没有实现Assign() @remy 是的,这取决于 op 来决定如何实施。只是奠定了基本概念。 您至少可以展示如何为您创建的属性实现它。 属性不必公开外部类。想想 TFont 等。 是否可以(通过属性)公开组件的子类,在该子类中您取消发布(移动到公共或受保护)您不想公开的属性?

以上是关于Firemonkey:如何定义一个包含另一个组件的组件?的主要内容,如果未能解决你的问题,请参考以下文章

如何托管 FireMonkey 表单客户端在另一个内部对齐?

如何在 Firemonkey 的 TTabItem 中插入另一个控件

在 Firemonkey 组件中旋转时如何避免重复图像?

Firemonkey 组件节省设计时间大小

如何在Delphi FireMonkey中填充TStringGrid?

Firemonkey:如何从我的应用程序中打开本机 IOS 地图?