delphi 如何在双击TCollectionItem时调用属性编辑器?

gab6jxml  于 11个月前  发布在  其他
关注(0)|答案(1)|浏览(157)

我有一个自定义控件。它有一个TOwnedCollection类型的published属性,其中当然包含几个TCollectionItem的示例(继承到我自己的实现)。让我们称它们为TMyComponentTMyCollectionTMyCollectionItem
TMyCollectionItem中,我有一个TPersistent属性(我们称之为MyProp: TMyProp),它也有一个安装在IDE中的相应的设计时编辑器。所有这些都很好,所以当你编辑集合时,选择一个集合项,你可以单击对象检查器中的小按钮来调用属性编辑器。所有这些都很好。
我想添加的是在集合编辑器中双击项目以调用相同的属性编辑器的功能。然而,我找不到如何做到这一点的适当信息。ChatGPT给了我无法编译的代码,可能是因为它适用于旧版本的 Delphi 。
如何实现双击TMyCollectionItem以调用TMyCollectionItem.MyProp的属性编辑器的功能?
注意事项:查看ColnEdit.pas(用于此集合编辑器)的源代码,我看到TListView实现了OnClick,但没有实现OnDblClick,因此我怀疑是否有可能在不推出自己的集合编辑器的情况下做我想做的事情。

px9o7tmv

px9o7tmv1#

IDE的 default 集合编辑器本身不支持您要查找的功能。如您所述:
注意事项:查看ColnEdit.pas的源代码(对于这个集合编辑器),我看到TListView实现了OnClick,但没有实现OnDblClick,因此我怀疑是否有可能在不推出自己的集合编辑器的情况下做我想做的事情。
当双击TListView项时,默认的集合编辑器根本不实现任何逻辑。
(BTW,更新对象检查器的不是TListVew.OnClick事件,而是TListVew.OnChange事件。这是在选择ListView项目时触发的事件。)
然而,一切都没有失去!有一种方法可以访问默认集合编辑器的TListView,并为其分配自己的OnDblClick事件处理程序:

  • ColnEdit.TCollectionEditor派生一个新类,并让它为继承的TListView分配一个OnDblClick处理程序。
  • ColnEdit.TCollectionProperty派生一个新类并重写其虚GetEditorClass()方法以返回从TCollectionEditor派生的类类型。
  • DesignIntf.RegisterPropertyEditor()注册TCollectionProperty派生类,以覆盖TMyCollection类的默认集合编辑器。

然后,派生的集合编辑器可以在需要时使用DesignEditors.GetComponentProperties()函数调用TMyProp属性编辑器:

  • 使用DesignIntf.CreateSelectionList()创建一个列表,并将相应的TMyCollectionItem对象Add()添加到列表中。
  • 将该列表传递给GetComponentProperties(),同时沿着一个回调,该回调将为TMyCollectionItem对象的每个属性接收一个IPropertyEditor
  • 当回调函数看到TMyProp属性编辑器时,它可以调用编辑器的Edit()方法。

举例来说:

uses
  ..., Vcl.ComCtrls, DesignIntf, DesignEditors, System.TypInfo, ColnEdit;

type
  ...

  TMyCollectionEditor = class(TCollectionEditor)
  private
    procedure ListViewDblClick(Sender: TObject);
    procedure InvokeMyPropEditor(const Prop: IProperty);
  public
    constructor Create(AOwner: TComponent); override;
  end;

  TMyCollectionProperty = class(TCollectionProperty)
  public
    function GetEditorClass: TCollectionEditorClass; override;
  end;

procedure Register;

...

constructor TMyCollectionEditor.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  ListView1.OnDblClick := ListViewDblClick;
end;

procedure TMyCollectionEditor.ListViewDblClick(Sender: TObject);
var
  Item: TListItem;
  Components: IDesignerSelections;
begin
  Item := ListView1.Selected;
  if Item = nil then Exit;
  Components := CreateSelectionList();
  Components.Add(Collection.Items[Item.Index]);
  GetComponentProperties(Components, [tkClass], Designer, InvokeMyPropEditor);
end;

procedure TMyCollectionEditor.InvokeMyPropEditor(const Prop: IProperty);
begin
  if Prop.GetPropType = typeinfo(TMyProp) then
    Prop.Edit;
end;

function TMyCollectionProperty.GetEditorClass: TCollectionEditorClass;
begin
  Result := TMyCollectionEditor;
end;

procedure Register;
begin
  RegisterComponents('...', [TMyComponent]);
  RegisterPropertyEditor(typeinfo(TMyCollection), TMyComponent, '', TMyCollectionProperty);
  ...
end;

字符串
尽管如此,我确实测试了这种方法,虽然它在功能上确实有效,但有一个 * 轻微 * 的视觉副作用-在10.2.1及更高版本中,IDE的主题化功能将不再应用于显示的集合编辑器窗口。因此,例如,如果您在IDE中使用深色主题,集合编辑器将不再显示为深色。
要解决此问题,您需要使用OpenTools API中的IOTAIDEThemingServices接口注册TCollectionEditor派生类,以便IDE将其主题化:
Using IDE Styles in Third-Party Plugins
大卫霍伊尔也写了一篇关于这个主题的文章:
Theming OTA Forms
举例来说:

uses
  ..., ToolsAPI;

type
  ...

  TMyCollectionEditor = class(TCollectionEditor)
  private
    FThemingService: IOTAIDEThemingServices;
    FThemingNotifier: Integer;
    ...
    procedure ThemeChanged;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  end;

  TThemeChangedProc = procedure of object;

  TMyThemingNotifier = class(TInterfacedObject, IOTANotifier, INTAIDEThemingServicesNotifier)
  public
    OnThemeChanged: TThemeChangedProc;
    constructor Create(AProc: TThemeChangedProc);
    procedure AfterSave;
    procedure BeforeSave;
    procedure Destroyed;
    procedure Modified;
    procedure ChangingTheme;
    procedure ChangedTheme;
  end;

...

constructor TMyCollectionEditor.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  ...

  if Supports(BorlandIDEServices, IOTAIDEThemingServices, FThemingService) then
  begin
    ThemeChanged;
    FThemingNotifier := FThemingService.AddNotifier(TMyThemingNotifier.Create(ThemeChanged) as INTAIDEThemingServicesNotifier);
  end;
end;

destructor TMyCollectionEditor.Destroy;
begin
  if Assigned(FThemingService) then
    FThemingService.RemoveNotifier(FThemingNotifier);
  inherited Destroy;
end;

procedure TMyCollectionEditor.ThemeChanged;
begin
  if FThemingService.IDEThemingEnabled then
  begin
    FThemingService.RegisterFormClass(TMyCollectionEditor);
    FThemingService.ApplyTheme(Self);
  end;
end;

constructor TMyThemingNotifier.Create(AProc: TThemeChangedProc);
begin
  inherited Create;
  OnThemeChanged := AProc;
end;

procedure TMyThemingNotifier.AfterSave;
begin
end;

procedure TMyThemingNotifier.BeforeSave;
begin
end;

procedure TMyThemingNotifier.Destroyed;
begin
end;

procedure TMyThemingNotifier.Modified;
begin
end;

procedure TMyThemingNotifier.ChangingTheme;
begin
end;

procedure TMyThemingNotifier.ChangedTheme;
begin
  if Assigned(OnThemeChanged) then
    OnThemeChanged();
end;


尽管Embarcadero的文件说以下内容(重点是我的):
在插件初始化或表单构造函数中,使用表单的类(即表单类型)调用IOTAIDEThemingServices.RegisterFormClass。**您只需要执行一次此操作即可将样式钩子应用到表单。**您不需要注销它。
我发现如果在主题更改后RegisterFormClass()没有在ApplyStyle()之前再次调用RegisterFormClass(),那么ApplyStyle()不会正确应用新主题!就像IDE在主题更改期间丢失了以前的Form类注册一样(IDE bug?我已经报告了它:RSP-43972)。在每次调用ApplyStyle()之前调用RegisterFormClass()都可以工作。
我还注意到,默认的TCollectionEditor根本不会对IDE主题的更改做出React(绝对是一个IDE bug,我已经报告过了:RSP-43971)。

相关问题