Creating a component with named sub-components?

前端 未结 3 1950
长情又很酷
长情又很酷 2020-11-30 07:17

I need to know the basics behind making a component produce and manage sub-components. I originally tried this by creating a TCollection, and tried to put a nam

相关标签:
3条回答
  • 2020-11-30 07:30

    This Thread helped me creating something as we discussed yesterday. I took the package posted there and modified it a bit. Here is the source:

    TestComponents.pas

    unit TestComponents;
    
    interface
    
    uses
      Classes;
    
    type
      TParentComponent = class;
    
      TChildComponent = class(TComponent)
      private
        FParent: TParentComponent;
        procedure SetParent(const Value: TParentComponent);
      protected
        procedure SetParentComponent(AParent: TComponent); override;
      public
        destructor Destroy; override;
        function GetParentComponent: TComponent; override;
        function HasParent: Boolean; override;
        property Parent: TParentComponent read FParent write SetParent;
      end;
    
      TParentComponent = class(TComponent)
      private
        FChilds: TList;
      protected
        procedure GetChildren(Proc: TGetChildProc; Root: TComponent); override;
      public
        constructor Create(AOwner: TComponent); override;
        destructor Destroy; override;
        property Childs: TList read FChilds;
      end;
    
    implementation
    
    { TChildComponent }
    
    destructor TChildComponent.Destroy;
    begin
      Parent := nil;
      inherited;
    end;
    
    function TChildComponent.GetParentComponent: TComponent;
    begin
      Result := FParent;
    end;
    
    function TChildComponent.HasParent: Boolean;
    begin
      Result := Assigned(FParent);
    end;
    
    procedure TChildComponent.SetParent(const Value: TParentComponent);
    begin
      if FParent <> Value then
      begin
        if Assigned(FParent) then
          FParent.FChilds.Remove(Self);
        FParent := Value;
        if Assigned(FParent) then
          FParent.FChilds.Add(Self);
      end;
    end;
    
    procedure TChildComponent.SetParentComponent(AParent: TComponent);
    begin
      if AParent is TParentComponent then
        SetParent(AParent as TParentComponent);
    end;
    
    { TParentComponent }
    
    constructor TParentComponent.Create(AOwner: TComponent);
    begin
      inherited;
      FChilds := TList.Create;
    end;
    
    destructor TParentComponent.Destroy;
    var
      I: Integer;
    begin
      for I := 0 to FChilds.Count - 1 do
        FChilds[0].Free;
      FChilds.Free;
      inherited;
    end;
    
    procedure TParentComponent.GetChildren(Proc: TGetChildProc; Root: TComponent);
    var
      i: Integer;
    begin
      for i := 0 to FChilds.Count - 1 do
        Proc(TComponent(FChilds[i]));
    end;
    
    end.
    

    TestComponentsReg.pas

    unit TestComponentsReg;
    
    interface
    
    uses
      Classes,
      DesignEditors,
      DesignIntf,
      TestComponents;
    
    type
      TParentComponentEditor = class(TComponentEditor)
        procedure ExecuteVerb(Index: Integer); override;
        function GetVerb(Index: Integer): string; override;
        function GetVerbCount: Integer; override;
      end;
    
    procedure Register;
    
    implementation
    
    uses
      ColnEdit;
    
    type
      TChildComponentCollectionItem = class(TCollectionItem)
      private
        FChildComponent: TChildComponent;
        function GetName: string;
        procedure SetName(const Value: string);
      protected
        property ChildComponent: TChildComponent read FChildComponent write FChildComponent;
        function GetDisplayName: string; override;
      public
        constructor Create(Collection: TCollection); override;
        destructor Destroy; override;
      published
        property Name: string read GetName write SetName;
      end;
    
      TChildComponentCollection = class(TOwnedCollection)
      private
        FDesigner: IDesigner;
      public
        property Designer: IDesigner read FDesigner write FDesigner;
      end;
    
    procedure Register;
    begin
      RegisterClass(TChildComponent);
      RegisterNoIcon([TChildComponent]);
      RegisterComponents('Test', [TParentComponent]);
      RegisterComponentEditor(TParentComponent, TParentComponentEditor);
    end;
    
    { TParentComponentEditor }
    
    procedure TParentComponentEditor.ExecuteVerb(Index: Integer);
    var
      LCollection: TChildComponentCollection;
      i: Integer;
    begin
      LCollection := TChildComponentCollection.Create(Component, TChildComponentCollectionItem);
      LCollection.Designer := Designer;
      for i := 0 to TParentComponent(Component).Childs.Count - 1 do
        with TChildComponentCollectionItem.Create(nil) do
        begin
          ChildComponent := TChildComponent(TParentComponent(Component).Childs[i]);
          Collection := LCollection;
        end;
      ShowCollectionEditorClass(Designer, TCollectionEditor, Component, LCollection, 'Childs');
    end;
    
    function TParentComponentEditor.GetVerb(Index: Integer): string;
    begin
      Result := 'Edit Childs...';
    end;
    
    function TParentComponentEditor.GetVerbCount: Integer;
    begin
      Result := 1;
    end;
    
    { TChildComponentCollectionItem }
    
    constructor TChildComponentCollectionItem.Create(Collection: TCollection);
    begin
      inherited;
      if Assigned(Collection) then
      begin
        FChildComponent := TChildComponent.Create(TComponent(TOwnedCollection(Collection).Owner).Owner);
        FChildComponent.Name := TChildComponentCollection(Collection).Designer.UniqueName(TChildComponent.ClassName);
        FChildComponent.Parent := TParentComponent(TComponent(TOwnedCollection(Collection).Owner));
      end;
    end;
    
    destructor TChildComponentCollectionItem.Destroy;
    begin
      FChildComponent.Free;
      inherited;
    end;
    
    function TChildComponentCollectionItem.GetDisplayName: string;
    begin
      Result := FChildComponent.Name;
    end;
    
    function TChildComponentCollectionItem.GetName: string;
    begin
      Result := FChildComponent.Name;
    end;
    
    procedure TChildComponentCollectionItem.SetName(const Value: string);
    begin
      FChildComponent.Name := Value;
    end;
    
    end.
    

    The most important thing is the RegisterNoIcon which prevents showing the component on the form when you create it. The overridden methods in TChildComponent are causing them to be nested inside the TParentComponent.

    Edit: I added a temporary collection to edit the items in the built-in TCollectionEditor instead of having to write an own one. The only disadvantage is that the TChildComponentCollectionItem has to publish every property that TChildComponent has published to be able to edit them inside the OI.

    0 讨论(0)
  • 2020-11-30 07:32

    Implement TCollectionItem.GetDisplayName to "name" the collection items.

    And concerning the collection: when this is a published property, the collection will automatically be named as the property name.

    Be careful to implement GetOwner when you create properties of TPersistent.

    0 讨论(0)
  • 2020-11-30 07:40

    Use the TComponent.SetSubComponent routine:

    type
      TComponent1 = class(TComponent)
      private
        FSubComponent: TComponent;
        procedure SetSubComponent(Value: TComponent);
      public
        constructor Create(AOwner: TComponent); override;
      published
        property SubComponent: TComponent read FSubComponent write SetSubComponent;
      end;
    
    procedure Register;
    
    implementation
    
    procedure Register;
    begin
      RegisterComponents('Samples', [TComponent1]);
    end;
    
    { TComponent1 }
    
    constructor TComponent1.Create(AOwner: TComponent);
    begin
      inherited Create(AOwner);
      FSubComponent := TComponent.Create(Self);  // Nót AOwner as owner here !!
      FSubComponent.Name := 'MyName';
      FSubComponent.SetSubComponent(True);
    end;
    
    procedure TComponent1.SetSubComponent(Value: TComponent);
    begin
      FSubComponent.Assign(Value);
    end;
    

    I now understand this sub component would be part of a collection item. In that case: no difference, use this method.

    0 讨论(0)
提交回复
热议问题