在化合物(?)组件中命名子组件

发布于 2025-01-24 05:56:59 字数 6521 浏览 1 评论 0原文

我基本上要创建的是从tscrollbox继承的组件。该组件具有tgroupbox,其中一个tflowpanel。我需要的是双击此组件时,tcollection类似于编辑器出现在其中我可以添加组件(tfiltros),该组件将是该tflowpanel < /代码>。问题是我希望这些组件被命名,以便我可以通过代码直接访问它们,就像tclientDataSet,您添加字段并将其显示在您的代码中。

我设法通过覆盖getChildren并将其返回tflofpanel的孩子来使其几乎起作用。这也要求我使tfiltros的所有者是他们所处的形式。它在结构面板中显示为儿童(甚至不是直接的孩子),并且也将其保存在DFM中,但是,当我关闭表单并再次打开表单时,它无法从DFM加载数据,从而违反了访问。我不知道如何覆盖加载以正确设置孩子。

在我如何解决这个问题的任何帮助下,甚至不同的想法都会非常好。我是创建Delphi组件的新手。

我当前的代码在这个问题

unit uFiltros;

interface

uses
  System.SysUtils, System.Classes, Vcl.Controls, Vcl.ExtCtrls, Forms, StdCtrls, 
  ClRelatorio, Math, DesignEditors, DesignIntf, System.Generics.Collections;

type
  TFiltrosEditor = class(TComponentEditor)
    procedure ExecuteVerb(Index: Integer); override;
    function GetVerb(Index: Integer): String; override;
    function GetVerbCount: Integer; override;
  end;

  TFiltros = class(TScrollingWinControl)
  private
    FChilds: TList<TComponent>;
    FGroupBox: TGroupBox;
    FFlowPanel: TFlowPanel;
    FWidth: Integer;
    procedure OnFlowPanelResize(Sender: TObject);
    procedure SetWidth(AWidth: Integer);
  public
    procedure GetChildren(Proc: TGetChildProc; Root: TComponent); override;
    function GetChildOwner: TComponent; override;
    constructor Create(AOwner: TComponent); override;
    property Childs: TList<TComponent> read FChilds;
  published
    property Width: Integer read FWidth write SetWidth;
  end;

  TClFiltro = class(TFiltro)
  private
    FFiltros: TFiltros;
  protected
    procedure SetParent(AParent: TWinControl); override;
  public
    constructor Create(AOwner: TComponent; AFiltros: TFiltros); reintroduce;
    function GetParentComponent: TComponent; override;
    function HasParent: Boolean; override;
    property Parent: TWinControl write SetParent;
  end;

  TFiltroItem = class(TCollectionItem)
  private
    FFiltro: TClFiltro;
  protected
    function GetDisplayName: String; override;
  public
    constructor Create(Collection: TCollection); override;
    destructor Destroy; override;
  published
    property Filtro: TClFiltro read FFiltro write FFiltro;
  end;

  TFiltrosCollection = class(TOwnedCollection)
  private
    FDesigner: IDesigner;
  public
    property Designer: IDesigner read FDesigner write FDesigner;
  end;

procedure Register;

implementation

uses Dialogs, ClFuncoesBase, Vcl.Graphics, ColnEdit;

procedure Register;
begin
  RegisterClass(TClFiltro);
  RegisterNoIcon([TClFiltro]);
  RegisterComponents('Cl', [TFiltros]);
  RegisterComponentEditor(TFiltros, TFiltrosEditor);
end;

{ TFiltroItem }

constructor TFiltroItem.Create(Collection: TCollection);
begin
   inherited;
   if Assigned(Collection) then
   begin
      FFiltro        := TClFiltro.Create(TFiltros(Collection.Owner).Owner, TFiltros(Collection.Owner));
      FFiltro.Name   := TFiltrosCollection(Collection).Designer.UniqueName(TClFiltro.ClassName);
      FFiltro.Parent := TFiltros(Collection.Owner).FFlowPanel;
      FFiltro.Margins.Top      := 1;
      FFiltro.Margins.Bottom   := 1;
      FFiltro.AlignWithMargins := True;
      //FFiltro.SetSubComponent(True);
   end;
end;

destructor TFiltroItem.Destroy;
begin
   FFiltro.Free;
   inherited;
end;

function TFiltroItem.GetDisplayName: String;
begin
   Result := FFiltro.Name;
end;

{ TFiltros }

constructor TFiltros.Create(AOwner: TComponent);
begin
   inherited;
   FChilds := TList<TComponent>.Create;
   // Configurações ScrollBox
   Align      := TAlign.alRight;
   AutoScroll := False;
   AutoSize   := True;

   //Configurações GroupBox
   FGroupBox := TGroupBox.Create(Self);
   FGroupBox.Parent     := Self;
   FGroupBox.Caption    := ' Fil&tros ';
   FGroupBox.Font.Style := [fsBold];

   //Configurações FlowPanel
   FFlowPanel := TFlowPanel.Create(FGroupBox);
   FFlowPanel.Parent     := FGroupBox;
   FFlowPanel.Top        := 15;
   FFlowPanel.Left       := 2;
   FFlowPanel.AutoSize   := True;
   FFlowPanel.FlowStyle  := TFlowStyle.fsRightLeftTopBottom;
   FFlowPanel.Caption    := '';
   FFlowPanel.OnResize   := OnFlowPanelResize;
   FFlowPanel.BevelOuter := TBevelCut.bvNone;
end;

function TFiltros.GetChildOwner: TComponent;
begin
   Result := FFlowPanel;
end;

procedure TFiltros.GetChildren(Proc: TGetChildProc; Root: TComponent);
var I: Integer;
begin
//  inherited;
  for I := 0 to FChilds.Count - 1 do
    Proc(TComponent(FChilds[I]));
end;

procedure TFiltros.OnFlowPanelResize(Sender: TObject);
begin
   FGroupBox.Width     := FFlowPanel.Width + 4;
   FGroupBox.Height    := Max(FFlowPanel.Height + 17, Height);
   VertScrollBar.Range := FGroupBox.Height;
   FWidth := FFlowPanel.Width;
end;

procedure TFiltros.SetWidth(AWidth: Integer);
begin
   FFlowPanel.Width := AWidth;
   FWidth := FFlowPanel.Width;
   OnFlowPanelResize(Self);
end;

{ TFiltrosEditor }

procedure TFiltrosEditor.ExecuteVerb(Index: Integer);
var LCollection: TFiltrosCollection;
    I: Integer;
begin
  LCollection := TFiltrosCollection.Create(Component, TFiltroItem);
  LCollection.Designer := Designer;
  for I := 0 to TFiltros(Component).Childs.Count - 1 do
    with TFiltroItem.Create(nil) do
    begin
      FFiltro    := TClFiltro(TFiltros(Component).Childs[I]);
      Collection := LCollection;
    end;
  ShowCollectionEditorClass(Designer, TCollectionEditor, Component, LCollection, 'Filtros');
end;

function TFiltrosEditor.GetVerb(Index: Integer): String;
begin
   Result := 'Editar filtros...';
end;

function TFiltrosEditor.GetVerbCount: Integer;
begin
   Result := 1;
end;

{ TClFiltro }

constructor TClFiltro.Create(AOwner: TComponent; AFiltros: TFiltros);
begin
   inherited Create(AOwner);
   FFiltros := AFiltros;
end;

function TClFiltro.GetParentComponent: TComponent;
begin
   Result := FFiltros;
end;

function TClFiltro.HasParent: Boolean;
begin
   Result := Assigned(FFiltros);
end;

procedure TClFiltro.SetParent(AParent: TWinControl);
begin
   if Assigned(AParent) then
      FFiltros.FChilds.Add(Self)
   else
      FFiltros.FChilds.Remove(Self);
   inherited;
end;

end.

What I'm basically trying to create is a component that inherits from TScrollBox. That component has a TGroupBox and inside it a TFlowPanel. What I need is when I double click this component, a TCollection-like editor appears where I can add components (TFiltros) that will be children of that TFlowPanel. The problem is that I want those components to be named, such that I can directly access them via code, kinda like a TClientDataSet, where you add fields and they appear in your code.

I've managed to make it almost work by overriding GetChildren and making it return the children of the TFlowPanel. That also required me to make TFiltros's owner be the Form which they are in. It shows in the Structure panel as children (even tho they are not direct children) and also saves it in the DFM, but when I close the form and open it again, it fails to load the data back from the DFM, throwing an Access Violation. I have no idea how to override the loading to properly set the children.

Structure view

Any help in how I can fix that, or even different ideas would be really nice. I'm new to creating Delphi components.

My current code which is heavily inspired in this question:

unit uFiltros;

interface

uses
  System.SysUtils, System.Classes, Vcl.Controls, Vcl.ExtCtrls, Forms, StdCtrls, 
  ClRelatorio, Math, DesignEditors, DesignIntf, System.Generics.Collections;

type
  TFiltrosEditor = class(TComponentEditor)
    procedure ExecuteVerb(Index: Integer); override;
    function GetVerb(Index: Integer): String; override;
    function GetVerbCount: Integer; override;
  end;

  TFiltros = class(TScrollingWinControl)
  private
    FChilds: TList<TComponent>;
    FGroupBox: TGroupBox;
    FFlowPanel: TFlowPanel;
    FWidth: Integer;
    procedure OnFlowPanelResize(Sender: TObject);
    procedure SetWidth(AWidth: Integer);
  public
    procedure GetChildren(Proc: TGetChildProc; Root: TComponent); override;
    function GetChildOwner: TComponent; override;
    constructor Create(AOwner: TComponent); override;
    property Childs: TList<TComponent> read FChilds;
  published
    property Width: Integer read FWidth write SetWidth;
  end;

  TClFiltro = class(TFiltro)
  private
    FFiltros: TFiltros;
  protected
    procedure SetParent(AParent: TWinControl); override;
  public
    constructor Create(AOwner: TComponent; AFiltros: TFiltros); reintroduce;
    function GetParentComponent: TComponent; override;
    function HasParent: Boolean; override;
    property Parent: TWinControl write SetParent;
  end;

  TFiltroItem = class(TCollectionItem)
  private
    FFiltro: TClFiltro;
  protected
    function GetDisplayName: String; override;
  public
    constructor Create(Collection: TCollection); override;
    destructor Destroy; override;
  published
    property Filtro: TClFiltro read FFiltro write FFiltro;
  end;

  TFiltrosCollection = class(TOwnedCollection)
  private
    FDesigner: IDesigner;
  public
    property Designer: IDesigner read FDesigner write FDesigner;
  end;

procedure Register;

implementation

uses Dialogs, ClFuncoesBase, Vcl.Graphics, ColnEdit;

procedure Register;
begin
  RegisterClass(TClFiltro);
  RegisterNoIcon([TClFiltro]);
  RegisterComponents('Cl', [TFiltros]);
  RegisterComponentEditor(TFiltros, TFiltrosEditor);
end;

{ TFiltroItem }

constructor TFiltroItem.Create(Collection: TCollection);
begin
   inherited;
   if Assigned(Collection) then
   begin
      FFiltro        := TClFiltro.Create(TFiltros(Collection.Owner).Owner, TFiltros(Collection.Owner));
      FFiltro.Name   := TFiltrosCollection(Collection).Designer.UniqueName(TClFiltro.ClassName);
      FFiltro.Parent := TFiltros(Collection.Owner).FFlowPanel;
      FFiltro.Margins.Top      := 1;
      FFiltro.Margins.Bottom   := 1;
      FFiltro.AlignWithMargins := True;
      //FFiltro.SetSubComponent(True);
   end;
end;

destructor TFiltroItem.Destroy;
begin
   FFiltro.Free;
   inherited;
end;

function TFiltroItem.GetDisplayName: String;
begin
   Result := FFiltro.Name;
end;

{ TFiltros }

constructor TFiltros.Create(AOwner: TComponent);
begin
   inherited;
   FChilds := TList<TComponent>.Create;
   // Configurações ScrollBox
   Align      := TAlign.alRight;
   AutoScroll := False;
   AutoSize   := True;

   //Configurações GroupBox
   FGroupBox := TGroupBox.Create(Self);
   FGroupBox.Parent     := Self;
   FGroupBox.Caption    := ' Fil&tros ';
   FGroupBox.Font.Style := [fsBold];

   //Configurações FlowPanel
   FFlowPanel := TFlowPanel.Create(FGroupBox);
   FFlowPanel.Parent     := FGroupBox;
   FFlowPanel.Top        := 15;
   FFlowPanel.Left       := 2;
   FFlowPanel.AutoSize   := True;
   FFlowPanel.FlowStyle  := TFlowStyle.fsRightLeftTopBottom;
   FFlowPanel.Caption    := '';
   FFlowPanel.OnResize   := OnFlowPanelResize;
   FFlowPanel.BevelOuter := TBevelCut.bvNone;
end;

function TFiltros.GetChildOwner: TComponent;
begin
   Result := FFlowPanel;
end;

procedure TFiltros.GetChildren(Proc: TGetChildProc; Root: TComponent);
var I: Integer;
begin
//  inherited;
  for I := 0 to FChilds.Count - 1 do
    Proc(TComponent(FChilds[I]));
end;

procedure TFiltros.OnFlowPanelResize(Sender: TObject);
begin
   FGroupBox.Width     := FFlowPanel.Width + 4;
   FGroupBox.Height    := Max(FFlowPanel.Height + 17, Height);
   VertScrollBar.Range := FGroupBox.Height;
   FWidth := FFlowPanel.Width;
end;

procedure TFiltros.SetWidth(AWidth: Integer);
begin
   FFlowPanel.Width := AWidth;
   FWidth := FFlowPanel.Width;
   OnFlowPanelResize(Self);
end;

{ TFiltrosEditor }

procedure TFiltrosEditor.ExecuteVerb(Index: Integer);
var LCollection: TFiltrosCollection;
    I: Integer;
begin
  LCollection := TFiltrosCollection.Create(Component, TFiltroItem);
  LCollection.Designer := Designer;
  for I := 0 to TFiltros(Component).Childs.Count - 1 do
    with TFiltroItem.Create(nil) do
    begin
      FFiltro    := TClFiltro(TFiltros(Component).Childs[I]);
      Collection := LCollection;
    end;
  ShowCollectionEditorClass(Designer, TCollectionEditor, Component, LCollection, 'Filtros');
end;

function TFiltrosEditor.GetVerb(Index: Integer): String;
begin
   Result := 'Editar filtros...';
end;

function TFiltrosEditor.GetVerbCount: Integer;
begin
   Result := 1;
end;

{ TClFiltro }

constructor TClFiltro.Create(AOwner: TComponent; AFiltros: TFiltros);
begin
   inherited Create(AOwner);
   FFiltros := AFiltros;
end;

function TClFiltro.GetParentComponent: TComponent;
begin
   Result := FFiltros;
end;

function TClFiltro.HasParent: Boolean;
begin
   Result := Assigned(FFiltros);
end;

procedure TClFiltro.SetParent(AParent: TWinControl);
begin
   if Assigned(AParent) then
      FFiltros.FChilds.Add(Self)
   else
      FFiltros.FChilds.Remove(Self);
   inherited;
end;

end.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

亽野灬性zι浪 2025-01-31 05:56:59

我终于设法做到了。它需要组合和覆盖 >和

基本上我学到的东西(如果我错了,您可以纠正我),如下:

要在“结构”选项卡中显示一个组件,该组件的lander必须成为形式。因此,第一件事是与该所有者创建tfiltro

getParentComponent定义了组件要居住的结构树中的位置,它不一定是实际的父母。因此,第二件事是使tfiltrogetParentComponent返回tscrollbox,但将实际父母设置为tflowpanel

现在,作为tfiltro的父不再是表单,它不会将其保存到DFM,因为tflowpanel是实际的父母,但并未定义为子组件。 tscrollbox中的覆盖<代码> getChildren ,使其返回每个tfiltro 解决此问题,现在它作为一个小时候保存在DFM中。

但是现在,要使tfiltro从DFM正确读取并相应地设置,必须是TownedCollection中的项目中发布的值,它本身是tscrollbox中的已发布值。然后,使tcollectionItem已发布的Value set函数定义tfiltro的父级为tflowpanel

The article which helped me the most in achieving this is available in the

I've finally managed to do it. It required a combination of TOwnedCollection and overriding GetChildren and GetParentComponent.

Basically what I've learned (and you can correct me if I'm wrong), is the following:

For a component to be shown in the Structure tab at all, the Owner of that component has to be the form. So the first thing was to create TFiltro with that owner.

GetParentComponent defines where in the Structure tree the component is going to reside in, it doesn't necessarily have to be the actual parent. So the second thing was to make GetParentComponent of the TFiltro return the TScrollBox but set the actual parent to be the TFlowPanel.

Now, as the parent of TFiltro no longer is the form, it won't save it to the DFM, because TFlowPanel is the actual parent but is not defined as a subcomponent. Overriding GetChildren in the TScrollBox and making it return every TFiltro solves this, and it is now saved in the DFM as a child.

But now, for the TFiltro to be properly read back from the DFM and be set again accordingly, it has to be a published value in an item inside the TOwnedCollection, which itself is a published value in the TScrollBox. Then, make the TCollectionItem published value's set function define the parent of the TFiltro to be the TFlowPanel.

The article which helped me the most in achieving this is available in the WayBack machine.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文