IComponentEditor = interface ['{ECACBA34-DCDF-4BE2-A645-E4404BC06106}'] procedure Edit; 双击元件时触发 procedure ExecuteVerb(Index: Integer); 执行一个自定义动作 function GetVerb(Index: Integer): string; 获取自定义动作的字符串名称 function GetVerbCount: Integer; 获取自定义动作的数量 procedure PrepareItem(Index: Integer; const AItem: IMenuItem); 自定义动作的菜单项 procedure Copy; 此方法在元件信息被拷贝到剪贴板之后被调用 (* 试验结果:好像不会被调用?) function IsInInlined: Boolean; 返回元件的 Owner 是否是 csInline 状态 function GetComponent: TComponent; 获取当前选中的元件 function GetDesigner: IDesigner; 获取当前 IDE Designer end;TComponentEditor.Edit 方法在元件被双击时调用,重载此方法可以实现自定义的属性编辑器。缺省的操作是调用 TComponentEditor 的第一个动作。 { TComponentEditor } procedure TComponentEditor.Edit; virtual; begin if GetVerbCount > 0 then ExecuteVerb(0); // 缺省调用第一个动作 end;注意:如果在 Edit 方法中修改了元件属性,必须调用 Designer.Modified 方法通知 IDE 刷新 Object Inspector。===============================================================================⊙ TComponentEditor.PrepareItem 方法===============================================================================PrepareItem 在 GetVerb 之后被调用,用于建立更复杂的动作菜单项。IDE 传入的 Index 参数是指菜单序号,AItem 参数是新建菜单项的接口句柄。 procedure PrepareItem(Index: Integer; const AItem: IMenuItem); virtual;IMenuItems 接口在 DesignMenus.pas 中定义,可以被元件设计者使用的主要方法有: IMenuItem = interface(IMenuItems) ['{DAF029E1-9592-4B07-A450-A10056A2B9B5}'] // 新增子菜单 function AddItem(const ACaption: WideString; AShortCut: TShortCut; AChecked, AEnabled: Boolean; AonClick: TNotifyEvent = nil; hCtx: THelpContext = 0; const AName: string = ''): IMenuItem; overload; function AddItem(AAction: TBasicAction; const AName: string = ''): IMenuItem; overload; // 新增菜单分隔符 function AddLine(const AName: string = ''): IMenuItem; // 设置菜单项是否被 Checked property Checked: Boolean read GetChecked write SetChecked; // 设置菜单项是否 Enabled property Enabled: Boolean read GetEnabled write SetEnabled; // 设置菜单项是否 Visible property Visible: Boolean read GetVisible write SetVisible; end;===============================================================================⊙ TComponentEditor.Edit 方法===============================================================================TComponentEditor.Edit 方法在元件被双击时调用,重载此方法可以实现自定义的属性编辑器。缺省的操作是调用 TComponentEditor 的第一个动作。 { TComponentEditor } procedure TComponentEditor.Edit; virtual; begin if GetVerbCount > 0 then ExecuteVerb(0); // 缺省调用第一个动作 end;注意:如果在 Edit 方法中修改了元件属性,必须调用 Designer.Modified 方法通知 IDE 刷新 Object Inspector。===============================================================================⊙ TComponentEditor.IsInInlined 方法===============================================================================IsInInlined 判断当前元件是否处于内嵌的 top-class 对象中。例如,如果当前元件位于嵌入在 Form 中的 Frame 中,则返回 True,否则返回 False。IsInInlied 方法不是虚方法,不能被重载。在 TComponentEditor 中它判断 Owner 是否包含 csInline 状态。 { TComponentEditor } function TComponentEditor.IsInInlined: Boolean; begin Result := csInline in Component.Owner.ComponentState; end;(* 这个方法可用在何处?估计是被 IDE 使用。缺省不能在嵌入表单中的 Frame 中插入元件。)===============================================================================⊙ TDefaultEditor class===============================================================================TDefaultEditor 是 IDE 缺省的元件编辑器,提供对元件双击事件的增强处理。它重载了 Edit 方法实现在双击元件时生成元件的缺省事件代码。TDefaultEditor 继承自 TComponentEditor 和 IDefaultEditor: { DesignEditors.pas } TDefaultEditor = class(TComponentEditor, IDefaultEditor) { DesignIntf.pas } IDefaultEditor = interface(IComponentEditor) ['{5484FAE1-5C60-11D1-9FB6-0020AF3D82DA}'] end;IDefaultEditor 没有声明任何方法。(我猜想) 这是为了标识当前的元件编辑器是从 TDefaultEditor 继承下来的,但是为什么不直接使用 TObject.InheritsFrom 来识别呢?TDefaultEditor 在 Edit 方法中调用 GetComponentProperties 函数获取属性列表。GetComponentProperties 调用 TDefaultEditor.CheckEdit 过滤合适的方法: { TDefaultEditor.pas } procedure CheckEdit(const Prop: IProperty);CheckEdit 方法将从 GetComponentProperties 传进来的 IProperty 参数传递给 TDefaultEditor.EditProperty 虚方法: { TDefaultEditor } procedure EditProperty(const Prop: IProperty; var Continue: Boolean); virtual;EditProperty 使用 IProperty 判断属性的名称是否是:ONCREATE、ONCHANGE、ONCHANGE、onCLICK 之一,如果是则将该 IProperty 句柄保存在私有成员 FFirst、FBest 之中。EditProperty 还传入一个 Continue 参数,如果设置为 False,则 Edit 方法不会执行任何操作。否则 Edit 方法调用 IProperty 的 Edit 方法,生成事件句柄的代码。* 可以从 TDefaultEditor 继承以实现特殊的元件编辑操作。===============================================================================⊙ TSelectionEditor class===============================================================================TSelectionEditor 与 TComponentEditor 类似,它实现 ISelectionEditor 接口,可以为 IDE 中选中的一批不同类型的元件生成动作菜单项。比如,如果选中表单上若干个从 TControl 类派生的不同类型的元件,TSelectionEditor 会被创建。 { DesignEditors.pas } TSelectionEditor = class(TBaseSelectionEditor, ISelectionEditor)TSelectionEditor 的 GetVerb、GetVerbCount、PrepareItem 方法与 TComponentEditor 类似,只是 ExecuteVerb 方法有些不同。因为 TSelectionEditor 实现对一批元件的操作,所以 ExecuteVerb 中多传递了一个元件列表接口句柄: { TSelectionEditor } procedure ExecuteVerb(Index: Integer; const List: IDesignerSelections);IDesignerSelections 代表当前被选中的一批元件,可被使用的主要属性是 Count 和 Items 数组。Count 表示选中的元件数量,Items 数组表示选中的元件。 { DesignIntf.pas } IDesignerSelections = interface ['{7ED7BF30-E349-11D3-AB4A-00C04FB17A72}'] function Add(const Item: TPersistent): Integer; function Equals(const List: IDesignerSelections): Boolean; function Get(Index: Integer): TPersistent; function GetCount: Integer; property Count: Integer read GetCount; property Items[Index: Integer]: TPersistent read Get; default; end;* 为什么 Items 声明为 TPerisistent 而不是 TComponent ?TSelectionEditor 还有一个 RequiresUnits 虚方法,需要重载以返回可能使用到的元件所在的单元名称,不太清楚这个方法如何使用,如果只使用标准的 VCL 元件应该不用重载,至少我设计 TControl 的 TSelectionEditor 没有出错。 { TSelectionEditor } procedure RequiresUnits(Proc: TGetStrProc); virtual;TSelectionEditor 需要用 RegisterSelectionEditor 注册后才能使用: { DesignIntf.pas } procedure RegisterSelectionEditor(AClass: TClass; AEditor: TSelectionEditorClass);