delphi – “无法为未命名的组件创建方法”

前端之家收集整理的这篇文章主要介绍了delphi – “无法为未命名的组件创建方法”前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
以下代码(在包中注册时)为我们提供了一个在托盘Test中注册的名为TParentComponent的组件.

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

奇怪的是Child对象确实有一个名字.

这是来源:

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.

上述来源改编自another answer here on StackOverflow.

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

提前致谢!

解决方法

设计时间要求摘要

>您想要或需要一个能够容纳多个子组件的自定义组件.
>这些子组件将由该自定义组件创建.
>子组件需要能够在代码中通过其名称引用为设计时的任何正常组件;因此不是Form.CustomComponent.Children [0],而是Form.Child1.
>因此,子组件需要声明 – 并因此添加到 – 模块的源文件(Form,Frame或DataModule).
>子组件将由默认IDE集合编辑器管理.
>因此,孩子需要完全包装到TCollectionItem中.

评估当前代码

你已经很顺利了,但除了你的问题,代码还有一些需要改进的地方:

>您创建的集合永远不会被释放.
>每次显示集合编辑器时都会创建一个新集合.
>如果从TreeView中删除子项,则旧的相应CollectionItem将保留,从而生成AV.
>设计时间和运行时代码不分割.

以下是代码的重写工作版本,其中包含以下更改:

>特殊组件称为Master,因为Parent与Delphi的Parent混淆太多(已经有两种类型).因此,一个孩子被称为奴隶.
> Slave被保存在TComponentList(单元Contnrs)中,以便在单个从站销毁时自动更新列表. ComponentList拥有从属.
>对于每个Master,只创建一个Collection.这些Master-Collection组合保存在单独的TStockItems ObjectList中.列表拥有库存项目,并在“完成”部分中释放列表.
>实现GetNamePath,以便在Object Inspector中将slave显示为Slave1,而不是SlaveWrappers(0).
>为TSlaveWrapper类的事件添加了额外的属性编辑器.不知何故,默认TMethodProperty的GetFormMethodName会导致您获得的错误.原因将在Designer.GetObjectName中,但我不确切知道原因.现在GetFormMethodName被覆盖,这解决了您的问题中的问题.

备注

按集合中项目的顺序(使用集合编辑器的箭头按钮)所做的更改尚未保留.试着让自己实现.

在TreeView中,每个Slave现在都是Master的直接子节点,而不是Slaves属性的子节点,正如通常在集合中看到的那样:

为了实现这一点,我认为TSlaves应该来自TPersistent,并且ComponentList将被包含在其中.这肯定是另一个不错的尝试.

组件代码

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.

编辑代码

unit MasterSlaveEdit;

interface

uses
  Classes,SysUtils,MasterSlave,Contnrs,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,'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.

注册

unit MasterSlaveReg;

interface

uses
  Classes,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.

包裹代码

requires
  rtl,DesignIDE;

contains
  MasterSlave in 'MasterSlave.pas',MasterSlaveEdit in 'MasterSlaveEdit.pas',MasterSlaveReg in 'MasterSlaveReg.pas';
原文链接:https://www.f2er.com/delphi/102207.html

猜你在找的Delphi相关文章