首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >“无法为未命名组件创建方法”

“无法为未命名组件创建方法”
EN

Stack Overflow用户
提问于 2013-05-05 17:54:56
回答 2查看 1.5K关注 0票数 8

下面的代码(在包中注册时)提供了一个名为TParentComponent的组件,它在托盘Test中注册。

但是,当您使用属性编辑器(在同一代码中提供)创建子对象时,IDE将显示错误消息无法为未命名的组件创建方法.

奇怪的是,Child对象确实有一个名称。

这是消息来源:

代码语言:javascript
复制
unit TestEditorUnit;

interface

uses
  Classes, DesignEditors, DesignIntf;

type  
  TParentComponent = class;

  TChildComponent = class(TComponent)
  private
    FParent: TParentComponent;
    FOnTest: TNotifyEvent;
    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;
  published
    property OnTest: TNotifyEvent read FOnTest write FOnTest;
  end;

  TParentComponent = class(TComponent)
  private
    FChilds: TList;
  protected
    procedure GetChildren(Proc: TGetChildProc; Root: TComponent); override;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  published
    property Childs: TList read FChilds;
  end;

  TParentPropertyEditor = class(TPropertyEditor)
  public
    function GetAttributes: TPropertyAttributes; override;
    function GetValue: string; override;
    procedure Edit; override;
  end;

procedure Register;

implementation

uses
  ColnEdit;

type
  TChildComponentCollectionItem = class(TCollectionItem)
  private
    FChildComponent: TChildComponent;
    function GetName: string;
    function GetOnTest: TNotifyEvent;
    procedure SetName(const Value: string);
    procedure SetOnTest(const Value: TNotifyEvent);
  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;
    property OnTest: TNotifyEvent read GetOnTest write SetOnTest;
  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]);
  RegisterPropertyEditor(TypeInfo(TList), TParentComponent, 'Childs', TParentPropertyEditor);
end;

{ 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
    TComponent(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;

{ 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;

function TChildComponentCollectionItem.GetOnTest: TNotifyEvent;
begin
  Result := FChildComponent.OnTest;
end;

procedure TChildComponentCollectionItem.SetName(const Value: string);
begin
  FChildComponent.Name := Value;
end;

procedure TChildComponentCollectionItem.SetOnTest(const Value: TNotifyEvent);
begin
  FChildComponent.OnTest := Value;
end;

{ TParentPropertyEditor }

procedure TParentPropertyEditor.Edit;
var
  LCollection: TChildComponentCollection;
  i: Integer;
begin
  LCollection := TChildComponentCollection.Create(GetComponent(0), TChildComponentCollectionItem);
  LCollection.Designer := Designer;
  for i := 0 to TParentComponent(GetComponent(0)).Childs.Count - 1 do
    with TChildComponentCollectionItem.Create(nil) do
    begin
      ChildComponent := TChildComponent(TParentComponent(GetComponent(0)).Childs[i]);
      Collection := LCollection;
    end;
  ShowCollectionEditorClass(Designer, TCollectionEditor, TComponent(GetComponent(0)), LCollection, 'Childs');
end;

function TParentPropertyEditor.GetAttributes: TPropertyAttributes;
begin
  Result := [paDialog];
end;

function TParentPropertyEditor.GetValue: string;
begin
  Result := 'Childs';
end;

end.

上述来源是从StackOverflow上的另一个答案改编而来的。

我为什么不能为OnTest创建一个方法

提前感谢!

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2013-05-10 02:24:42

设计时间要求摘要

  • 您需要或需要一个能够容纳多个子组件的自定义组件。
  • 这些子组件将由该自定义组件创建。
  • 子组件需要能够在代码中以它们的名称作为任何正常的组件被引用,这样就不是Form.CustomComponent.Children[0],而是Form.Child1
  • 因此,需要在模块的源文件(表单、框架或DataModule)中声明子组件,从而将其添加到源文件中。
  • 子组件将由默认IDE集合编辑器管理。
  • 因此,子程序需要完全封装到TCollectionItem中。

当前代码的评估

您已经做得很好了,但是除了您的问题,代码还有几点需要改进:

  • 您创建的集合从未被释放。
  • 每次显示集合编辑器时都会创建一个新集合。
  • 如果您从TreeView中删除了一个子对象,那么旧的相应的CollectionItem就会保留,从而导致AV。
  • 设计时和运行时代码不分离。

解决方案

下面是您的代码的重写、工作版本,并进行了以下更改:

  • 这个特殊的组件叫做Master,因为父组件与Delphi的Parent混淆太多了(已经有两种了)。因此,孩子被称为Slave
  • TComponentList (单元Contnrs)中保存从站以自动更新列表,以防个别从站被破坏。ComponentList拥有奴隶。
  • 对于每一个主,只创建一个集合。这些主收藏组合分别保存在一个独立的TStockItems ObjectList中。列表拥有库存项,列表在终结部分中被释放。
  • GetNamePath被实现,以便在对象检查器中显示为Slave1而不是SlaveWrappers(0)
  • 为TSlaveWrapper类的事件添加了额外的属性编辑器。在某种程度上,默认GetFormMethodNameTMethodProperty会导致所得到的错误。原因将在Designer.GetObjectName,但我不知道确切的原因。现在,GetFormMethodName已经过时了,它解决了问题中的问题。

备注

按集合中项的顺序所做的更改(使用集合编辑器的箭头按钮)尚未保留。试着去实现这一点。

在TreeView中,每个奴隶现在都是主的直接子级,而不是Slaves属性的子级,就像通常在集合中看到的那样:

为了实现这一点,我认为TSlaves应该从TPersistent下降,而ComponentList将被封装在其中。那肯定是又一次很棒的试演。

组件代码

代码语言:javascript
复制
unit MasterSlave;

interface

uses
  Classes, Contnrs;

type
  TMaster = class;

  TSlave = class(TComponent)
  private
    FMaster: TMaster;
    FOnTest: TNotifyEvent;
    procedure SetMaster(Value: TMaster);
  protected
    procedure SetParentComponent(AParent: TComponent); override;
  public
    function GetParentComponent: TComponent; override;
    function HasParent: Boolean; override;
    property Master: TMaster read FMaster write SetMaster;
  published
    property OnTest: TNotifyEvent read FOnTest write FOnTest;
  end;

  TSlaves = class(TComponentList)
  private
    function GetItem(Index: Integer): TSlave;
    procedure SetItem(Index: Integer; Value: TSlave);
  public
    property Items[Index: Integer]: TSlave read GetItem write SetItem; default;
  end;

  TMaster = class(TComponent)
  private
    FSlaves: TSlaves;
  protected
    procedure GetChildren(Proc: TGetChildProc; Root: TComponent); override;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  published
    property Slaves: TSlaves read FSlaves;
  end;

implementation

{ TSlave }

function TSlave.GetParentComponent: TComponent;
begin
  Result := FMaster;
end;

function TSlave.HasParent: Boolean;
begin
  Result := FMaster <> nil;
end;

procedure TSlave.SetMaster(Value: TMaster);
begin
  if FMaster <> Value then
  begin
    if FMaster <> nil then
      FMaster.FSlaves.Remove(Self);
    FMaster := Value;
    if FMaster <> nil then
      FMaster.FSlaves.Add(Self);
  end;
end;

procedure TSlave.SetParentComponent(AParent: TComponent);
begin
  if AParent is TMaster then
    SetMaster(TMaster(AParent));
end;

{ TSlaves }

function TSlaves.GetItem(Index: Integer): TSlave;
begin
  Result := TSlave(inherited Items[Index]);
end;

procedure TSlaves.SetItem(Index: Integer; Value: TSlave);
begin
  inherited Items[Index] := Value;
end;

{ TMaster }

constructor TMaster.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FSlaves := TSlaves.Create(True);
end;

destructor TMaster.Destroy;
begin
  FSlaves.Free;
  inherited Destroy;
end;

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

end.

编辑代码

代码语言:javascript
复制
unit MasterSlaveEdit;

interface

uses
  Classes, SysUtils, MasterSlave, Contnrs, DesignEditors, DesignIntf, ColnEdit;

type
  TMasterEditor = class(TComponentEditor)
  private
    function Master: TMaster;
  public
    procedure ExecuteVerb(Index: Integer); override;
    function GetVerb(Index: Integer): String; override;
    function GetVerbCount: Integer; override;
  end;

  TMasterProperty = class(TPropertyEditor)
  private
    function Master: TMaster;
  public
    procedure Edit; override;
    function GetAttributes: TPropertyAttributes; override;
    function GetValue: String; override;
  end;

  TOnTestProperty = class(TMethodProperty)
  private
    function Slave: TSlave;
  public
    function GetFormMethodName: String; override;
  end;

  TSlaveWrapper = class(TCollectionItem)
  private
    FSlave: TSlave;
    function GetName: String;
    function GetOnTest: TNotifyEvent;
    procedure SetName(const Value: String);
    procedure SetOnTest(Value: TNotifyEvent);
  protected
    function GetDisplayName: String; override;
  public
    constructor Create(Collection: TCollection); override;
    constructor CreateSlave(Collection: TCollection; ASlave: TSlave);
    destructor Destroy; override;
    function GetNamePath: String; override;
  published
    property Name: String read GetName write SetName;
    property OnTest: TNotifyEvent read GetOnTest write SetOnTest;
  end;

  TSlaveWrappers = class(TOwnedCollection)
  private
    function GetItem(Index: Integer): TSlaveWrapper;
  public
    property Items[Index: Integer]: TSlaveWrapper read GetItem; default;
  end;

implementation

type
  TStockItem = class(TComponent)
  protected
    Collection: TSlaveWrappers;
    Designer: IDesigner;
    Master: TMaster;
    procedure Notification(AComponent: TComponent; Operation: TOperation);
      override;
  public
    destructor Destroy; override;
  end;

  TStockItems = class(TObjectList)
  private
    function GetItem(Index: Integer): TStockItem;
  protected
    function CollectionOf(AMaster: TMaster; Designer: IDesigner): TCollection;
    function Find(ACollection: TCollection): TStockItem;
    property Items[Index: Integer]: TStockItem read GetItem;
      default;
  end;

var
  FStock: TStockItems = nil;

function Stock: TStockItems;
begin
  if FStock = nil then
    FStock := TStockItems.Create(True);
  Result := FStock;
end;

{ TStockItem }

destructor TStockItem.Destroy;
begin
  Collection.Free;
  inherited Destroy;
end;

procedure TStockItem.Notification(AComponent: TComponent;
  Operation: TOperation);
var
  I: Integer;
begin
  inherited Notification(AComponent, Operation);
  if Operation = opRemove then
    for I := 0 to Collection.Count - 1 do
      if Collection[I].FSlave = AComponent then
      begin
        Collection[I].FSlave := nil;
        Collection.Delete(I);
        Break;
      end;
end;

{ TStockItems }

function TStockItems.CollectionOf(AMaster: TMaster;
  Designer: IDesigner): TCollection;
var
  I: Integer;
  Item: TStockItem;
begin
  Result := nil;
  for I := 0 to Count - 1 do
    if Items[I].Master = AMaster then
    begin
      Result := Items[I].Collection;
      Break;
    end;
  if Result = nil then
  begin
    Item := TStockItem.Create(nil);
    Item.Master := AMaster;
    Item.Designer := Designer;
    Item.Collection := TSlaveWrappers.Create(AMaster, TSlaveWrapper);
    for I := 0 to AMaster.Slaves.Count - 1 do
    begin
      TSlaveWrapper.CreateSlave(Item.Collection, AMaster.Slaves[I]);
      Item.FreeNotification(AMaster.Slaves[I]);
    end;
    Add(Item);
    Result := Item.Collection;
  end;
end;

function TStockItems.GetItem(Index: Integer): TStockItem;
begin
  Result := TStockItem(inherited Items[Index]);
end;

function TStockItems.Find(ACollection: TCollection): TStockItem;
var
  I: Integer;
begin
  Result := nil;
  for I := 0 to Count - 1 do
    if Items[I].Collection = ACollection then
    begin
      Result := Items[I];
      Break;
    end;
end;

{ TMasterEditor }

procedure TMasterEditor.ExecuteVerb(Index: Integer);
begin
  case Index of
    0: ShowCollectionEditor(Designer, Master,
      Stock.CollectionOf(Master, Designer), 'Slaves');
  end;
end;

function TMasterEditor.GetVerb(Index: Integer): String;
begin
  case Index of
    0: Result := 'Edit slaves...';
  else
    Result := '';
  end;
end;

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

function TMasterEditor.Master: TMaster;
begin
  Result := TMaster(Component);
end;

{ TMasterProperty }

procedure TMasterProperty.Edit;
begin
  ShowCollectionEditor(Designer, Master,
    Stock.CollectionOf(Master, Designer), 'Slaves');
end;

function TMasterProperty.GetAttributes: TPropertyAttributes;
begin
  Result := [paDialog];
end;

function TMasterProperty.GetValue: String;
begin
  Result := Format('(%s)', [Master.Slaves.ClassName]);
end;

function TMasterProperty.Master: TMaster;
begin
  Result := TMaster(GetComponent(0));
end;

{ TOnTestProperty }

function TOnTestProperty.GetFormMethodName: String;
begin
  Result := Slave.Name + GetTrimmedEventName;
end;

function TOnTestProperty.Slave: TSlave;
begin
  Result := TSlaveWrapper(GetComponent(0)).FSlave;
end;

{ TSlaveWrapper }

constructor TSlaveWrapper.Create(Collection: TCollection);
begin
  CreateSlave(Collection, nil);
end;

constructor TSlaveWrapper.CreateSlave(Collection: TCollection; ASlave: TSlave);
var
  Item: TStockItem;
begin
  inherited Create(Collection);
  if ASlave = nil then
  begin
    Item := Stock.Find(Collection);
    FSlave := TSlave.Create(Item.Master.Owner);
    FSlave.Name := Item.Designer.UniqueName(TSlave.ClassName);
    FSlave.Master := Item.Master;
    FSlave.FreeNotification(Item);
  end
  else
    FSlave := ASlave;
end;

destructor TSlaveWrapper.Destroy;
begin
  FSlave.Free;
  inherited Destroy;
end;

function TSlaveWrapper.GetDisplayName: String;
begin
  Result := Name;
end;

function TSlaveWrapper.GetName: String;
begin
  Result := FSlave.Name;
end;

function TSlaveWrapper.GetNamePath: String;
begin
  Result := FSlave.GetNamePath;
end;

function TSlaveWrapper.GetOnTest: TNotifyEvent;
begin
  Result := FSlave.OnTest;
end;

procedure TSlaveWrapper.SetName(const Value: String);
begin
  FSlave.Name := Value;
end;

procedure TSlaveWrapper.SetOnTest(Value: TNotifyEvent);
begin
  FSlave.OnTest := Value;
end;

{ TSlaveWrappers }

function TSlaveWrappers.GetItem(Index: Integer): TSlaveWrapper;
begin
  Result := TSlaveWrapper(inherited Items[Index]);
end;

initialization

finalization
  FStock.Free;

end.

登记码

代码语言:javascript
复制
unit MasterSlaveReg;

interface

uses
  Classes, MasterSlave, MasterSlaveEdit, DesignIntf;

procedure Register;

implementation

procedure Register;
begin
  RegisterClass(TSlave);
  RegisterNoIcon([TSlave]);
  RegisterComponents('Samples', [TMaster]);
  RegisterComponentEditor(TMaster, TMasterEditor);
  RegisterPropertyEditor(TypeInfo(TSlaves), TMaster, 'Slaves',
    TMasterProperty);
  RegisterPropertyEditor(TypeInfo(TNotifyEvent), TSlaveWrapper, 'OnTest',
    TOnTestProperty);
end;

end.

包码

代码语言:javascript
复制
requires
  rtl,
  DesignIDE;

contains
  MasterSlave in 'MasterSlave.pas',
  MasterSlaveEdit in 'MasterSlaveEdit.pas',
  MasterSlaveReg in 'MasterSlaveReg.pas';
票数 6
EN

Stack Overflow用户

发布于 2013-05-06 21:23:04

在About.com的创建自定义Delphi组件,第2部分,第4页文章中找到了一个足够的“解决办法”。

完整的示例源代码在他们的文章中,并且(似乎)与Delphi的所有版本一起工作。

但是,需要注意的是,此解决方案并不完美,因为它不允许您将集合编辑器与父组件和子组件分离开来(这意味着您必须为两个组件生成源代码,以使集合编辑器能够工作,并将其放在运行时包中)。

为了我现在的需要,这个可以.但是,如果有人能够直接根据我问题中的示例代码找到更好的解决方案,那就太好了(如果有人提供的话,我会把这个答案标记为正确的)。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/16387367

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档