【问题标题】:Why the composite component fails to parent controls?为什么复合组件无法父控件?
【发布时间】:2011-02-09 19:52:18
【问题描述】:

我创建了自己的组件: TPage ,其中包含子组件 TPaper (TPanel)。 问题是,当我将 TMemo 或 TButton 等控件放在 TPaper 上(几乎填满整个区域)时,这些控件根本不会加载。见下面的例子

TPaper = class(TPanel)
  protected
      constructor Create(AOwner: TComponent);override;
      destructor Destroy;override;
  public
      procedure Paint; override;
  end;





TPage = class(TCustomControl)
   private
      FPaper:TPaper;
   protected
      procedure CreateParams(var Params:TCreateParams); override;
   public
      constructor Create(AOwner: TComponent);override;
      destructor Destroy;override;

   published
      property Paper: TPaper read FPaper write FPaper;
   end;




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

  PaperOrientation:=poPortrait;
  PaperSize:=psA4;
  PaperBrush:=TBrush.Create;
  PaperBrush.Color:=clWhite;
  PDFDocument:=Nil;
  FPaper:=TPaper.Create(Self);
  FPaper.Parent:=Self;
  FPaper.SetSubComponent(True);
  end;

... Memo1 在设计时是 TPaper (TPanel) 的父级,但在 按“运行”它不存在。

procedure TForm1.btn1Click(Sender: TObject);
begin
if not Assigned(Memo1) then ShowMessage('I do not exist');   //Memo1 is nil
end;

你知道出了什么问题吗?

非常感谢

PS 德尔福 7

当我将 TMemo 放入 TPaper 并保存单元 (Unit1) 后,在检查关联的 dfm 文件后,没有 TMemo 组件的痕迹。 (这就是它无法加载到应用程序的原因。)

【问题讨论】:

    标签: delphi


    【解决方案1】:

    Serge 是对的。 Delphi 只流式传输它们所在的表单所拥有的组件。为了避免在读取表单文件期间发生的 EClassNotfound 异常(您现在至少应该在 dfm 文件中看到一个 Tpaper 组件),您必须注册该类通过使用 RegisterClass 函数(在单元类中)。一个很好的地方是在你的单元的初始化部分。

    如果无法将 Tpaper 的所有者设置为 Form,那么您仍然可以通过覆盖 Getchildren 和 GetChildOwner 方法并应用 TCustomForm 使用的逻辑来让 Delphi 流式传输您的子组件:

    TPage = class
     ...
    public
      procedure GetChildren(Proc: TGetChildProc; Root: TComponent); override;
      function GetChildOwner:TComponent; override;
    end;
    
    
    
    procedure TPage.GetChildren(Proc: TGetChildProc; Root: TComponent);  // this is copied
    var                                                                  // from 
      I: Integer;                                                        // TCustomForm
      OwnedComponent: TComponent;
    begin
      inherited GetChildren(Proc, Root);
      if Root = Self then
        for I := 0 to ComponentCount - 1 do
        begin
          OwnedComponent := Components[I];
          if not OwnedComponent.HasParent then Proc(OwnedComponent);
        end;
    end;
    
    function TPage.GetChildOwner: TComponent;
    begin
      inherited;
      Result:=Self;
    end;
    

    【讨论】:

    • 感谢您的回答。我试图在我的代码中实现这个 sn-p,但它的工作方式与我预期的不同。 TPaper 中插入的组件根本不会出现在 dfm 文件中。我在帮助中找到了 TComponentStyle:csSubComponent 该组件是组件的子组件,它是其 Owner 属性的值。与顶级组件不同,子组件不与它们所在的表单或数据模块一起保存。相反,子组件显示为其所有者的已发布属性的值,并且其已发布的属性和事件与所属组件一起保存在表单文件中。
    • 我想,我不能指望任何插入到 TPaper 中的 TWinCOntrol 都会保存在 dfm 文件中。
    • 如何将备忘录插入 Tpaper?如果您这样做: AMemo:=Tmemo.create(FPaper) 那么很明显,它不会流入dfm。试试 AMemo:=TMemo.create(MyPage),看看是否可行。
    • 在设计时。我把 TPAge 放在表格上。它里面也有TPPaper。没事。然后我将任何 VCL 控件(TMemo 等)放在子组件 TPaper 中。它看起来也很好。但是当我按下运行按钮时,应用程序运行,但 TMemo 消失了。所以我关闭了应用程序,我看到我宝贵的 TMemo 存在于 TPaper 中。所以我做了这个技巧:我将 Unit1 保存在某个地方,然后我打开 Unit1.dfm 并且我没有看到任何 TMemo 对象“描述”。难怪我没有加载。这很可能是因为我之前写的论点。我不想在 programmaticaly 内创建 TMemo。我敢打赌它会这样工作。
    【解决方案2】:

    这个问题是 5 年前的问题,但是因为我遇到了同样的问题并且在网络中找不到可行的解决方案,所以决定分享我在经过多次测试后找到的解决方案。

    TClientPanel = class(TCustomControl)
    private
      procedure WMNCHitTest(var Message: TWMNCHitTest); message WM_NCHITTEST;
    public
      constructor Create(AOwner: TComponent); override;
    end;
    
    TMainPanel = class(TCustomControl)
    private
      FClient: TClientPanel;
    protected
      function GetChildOwner: TComponent; override;
      procedure GetChildren(Proc: TGetChildProc; Root: TComponent); override;
      procedure ReadState(Reader: TReader); override;
      procedure CreateComponentEvent(Reader: TReader; ComponentClass: TComponentClass; var Component: TComponent);
    public
      constructor Create(AOwner: TComponent); override;
      destructor Destroy; override;
    ...
    end;
    
    constructor TClientPanel.Create(AOwner: TComponent);
    begin
      inherited Create(AOwner);
      ControlStyle := ControlStyle + [csAcceptsControls, csNoDesignVisible];
    end;
    
    procedure TClientPanel.WMNCHitTest(var Message: TWMNCHitTest);
    begin
      if not (csDesigning in ComponentState) then
        Message.Result := HTTRANSPARENT
      else
        inherited;
    end;
    
    var
      TClientPanel_Registered: Boolean = False;
    
    constructor TMainPanel.Create(AOwner: TComponent);
    begin
      inherited Create(AOwner);
      FClient := TClientPanel.Create(Self);
      FClient.Parent := Self;
      FClient.Align := alClient;
      Exclude(FComponentStyle, csInheritable);
      if not TClientPanel_Registered then
      begin
        RegisterClasses([TClientPanel]);
        TClientPanel_Registered := True;
      end;
    end;
    
    destructor TMainPanel.Destroy;
    begin
      FClient.Free;
      inherited Destroy;
    end;
    
    function TMainPanel.GetChildOwner: TComponent;
    begin
      Result := Self;
    end;
    
    procedure TMainPanel.GetChildren(Proc: TGetChildProc; Root: TComponent);
    begin
      Proc(TControl(FClient));
    end;
    
    procedure TMainPanel.CreateComponentEvent(Reader: TReader; ComponentClass: TComponentClass; var Component: TComponent);
    begin
      if ComponentClass.ClassName = 'TClientPanel' then Component := FClient;
    end;
    
    procedure TMainPanel.ReadState(Reader: TReader);
    begin
      Reader.OnCreateComponent := CreateComponentEvent;
      inherited ReadState(Reader);
      Reader.OnCreateComponent := nil;
    end;
    

    不是很专业,但我希望它会有所帮助:^)

    附:刚刚做了一个快速测试(XE5),但基本上可以工作。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-04-14
      • 2014-02-13
      • 2016-02-01
      • 2015-08-28
      • 1970-01-01
      • 2018-11-17
      • 2020-04-16
      相关资源
      最近更新 更多