如何修改TComponentProperty以仅显示下拉列表中的特定项目?

Wod*_*dzu 4 delphi propertyeditor delphi-2009

请考虑这样的场景:

我有一个被调用的组件TMenuItemSelector,它有两个已发布的属性:PopupMenu- 允许TPopupMenu从表单中选择一个实例,并MenuItem允许从表单中选择任何实例TMenuItem.

我想以MenuItem一种方式修改属性的属性编辑器,在PopupMenu分配时,只有菜单项PopupMenu才能在下拉列表中看到.

我知道我需要编写自己的后代TComponentProperty和覆盖GetValues方法.问题是我不知道如何访问TMenuItemSelector正在撒谎的表单.

Original TComponentProperty使用此方法迭代所有可用实例:

procedure TComponentProperty.GetValues(Proc: TGetStrProc);
begin
  Designer.GetComponentNames(GetTypeData(GetPropType), Proc);
end;
Run Code Online (Sandbox Code Playgroud)

但是,Designer似乎是预编译的,所以我不知道它是如何GetComponentNames工作的.

这就是我到目前为止所做的,我想我唯一缺少的是实现GetValues:

unit uMenuItemSelector;

interface

uses
  Classes, Menus, DesignIntf, DesignEditors;

type
  TMenuItemSelector = class(TComponent)
  private
    FPopupMenu: TPopUpMenu;
    FMenuItem: TMenuItem;
    procedure SetPopupMenu(const Value: TPopUpMenu);
    procedure SetMenuItem(const Value: TMenuItem);
  published
    property PopupMenu: TPopUpMenu read FPopupMenu write SetPopupMenu;
    property MenuItem: TMenuItem read FMenuItem write SetMenuItem;
  end;

type
  TMenuItemProp = class(TComponentProperty)
  public
    function GetAttributes: TPropertyAttributes; override;
    procedure GetValues(Proc: TGetStrProc); override;
  end;

procedure Register;

implementation

procedure Register;
begin
  RegisterPropertyEditor(TypeInfo(TMenuItem), TMenuItemSelector, 'MenuItem', TMenuItemProp);
  RegisterComponents('Test', [TMenuItemSelector]);
end;

{ TMenuItemSelector }

procedure TMenuItemSelector.SetMenuItem(const Value: TMenuItem);
begin
  FMenuItem := Value;
end;

procedure TMenuItemSelector.SetPopupMenu(const Value: TPopUpMenu);
begin
  FPopupMenu := Value;
end;

{ TMenuItemProperty }

function TMenuItemProp.GetAttributes: TPropertyAttributes;
begin
  Result := inherited GetAttributes + [paValueList, paSortList];
end;

procedure TMenuItemProp.GetValues(Proc: TGetStrProc);
begin
  //How to filter MenuItems from the form in a way that only
  //MenuItems which belong to TMenuItemSelector.PopupMenu are displayed? \
  //And how to get to that form?
  //inherited;

end;

end.
Run Code Online (Sandbox Code Playgroud)

有人可以帮忙吗?

谢谢.

Rem*_*eau 7

TMenuItemProp.GetValues()调用时,您需要查看当前正在编辑TMenuItemSelectorMenuItem属性的对象,查看该对象是否已PopupMenu分配,如果是,则将其项目循环为neded,例如:

procedure TMenuItemProp.GetValues(Proc: TGetStrProc); 
var
  Selector: TMenuItemSelector;
  I: Integer;
begin 
  Selector := GetComponent(0) as TMenuItemSelector;
  if Selector.PopupMenu <> nil then
  begin
    with Selector.PopupMenu.Items do
    begin
      for I := 0 to Count-1 do
        Proc(Designer.GetComponentName(Items[I]));
    end;
  end else
    inherited GetValues(Proc);
end; 
Run Code Online (Sandbox Code Playgroud)

顺便说一句,你需要实现TMenuItemSelectorTMenuItemProp分开包装.除了RegisterComponents()函数(在运行时包中实现)之外,不允许将设计时代码编译为运行时可执行文件.这是针对EULA的,Embarcadero的设计时间版本不允许分发.您需要TMenuItemSelector在仅运行时的包中实现,然后在仅限设计时的包中实现TMenuItemProp并且Register()在仅Requires运行时包和声明uses的单元TMenuItemSelector中,例如:

unit uMenuItemSelector;

interface

uses
  Classes, Menus;

type
  TMenuItemSelector = class(TComponent)
  private
    FPopupMenu: TPopUpMenu;
    FMenuItem: TMenuItem;
    procedure SetPopupMenu(const Value: TPopUpMenu);
    procedure SetMenuItem(const Value: TMenuItem);
  protected
    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  published
    property PopupMenu: TPopUpMenu read FPopupMenu write SetPopupMenu;
    property MenuItem: TMenuItem read FMenuItem write SetMenuItem;
  end;

implementation

{ TMenuItemSelector }

procedure TMenuItemSelector.Notification(AComponent: TComponent; Operation: TOperation);
begin
  inherited;
  if Operation = opRemove then
  begin
    if AComponent = FPopupMenu then
    begin
      FPopupMenu := nil;
      FMenuItem := nil;
    end
    else if AComponent = FMenuItem then
    begin
      FMenuItem := nil;
    end;
  end;
end;

procedure TMenuItemSelector.SetMenuItem(const Value: TMenuItem);
begin
  if FMenuItem <> Value then
  begin
    if FMenuItem <> nil then FMenuItem.RemoveFreeNotification(Self);
    FMenuItem := Value;
    if FMenuItem <> nil then FMenuItem.FreeNotification(Self);
  end;
end;

procedure TMenuItemSelector.SetPopupMenu(const Value: TPopUpMenu);
begin
  if FPopupMenu <> Value then
  begin
    if FPopupMenu <> nil then FPopupMenu.RemoveFreeNotification(Self);
    FPopupMenu := Value;
    if FPopupMenu <> nil then FPopupMenu.FreeNotification(Self);
    SetMenuItem(nil);
  end;
end;

end.
Run Code Online (Sandbox Code Playgroud)

.

unit uMenuItemSelectorEditor;

interface

uses
  Classes, DesignIntf, DesignEditors;

type
  TMenuItemSelectorMenuItemProp = class(TComponentProperty)
  public
    function GetAttributes: TPropertyAttributes; override;
    procedure GetValues(Proc: TGetStrProc); override;
  end;       

procedure Register;

implementation

uses
  Menus, uMenuItemSelector;

procedure Register;
begin
  RegisterComponents('Test', [TMenuItemSelector]);
  RegisterPropertyEditor(TypeInfo(TMenuItem), TMenuItemSelector, 'MenuItem', TMenuItemSelectorMenuItemProp);
end;

{ TMenuItemSelectorMenuItemProp }

function TMenuItemSelectorMenuItemProp.GetAttributes: TPropertyAttributes;
begin
  Result := inherited GetAttributes + [paValueList, paSortList] - [paMultiSelect];
end;

procedure TMenuItemSelectorMenuItemProp.GetValues(Proc: TGetStrProc);
var
  Selector: TMenuItemSelector;
  I: Integer;
begin
  Selector := GetComponent(0) as TMenuItemSelector;
  if Selector.PopupMenu <> nil then
  begin
    with Selector.PopupMenu.Items do
    begin
      for I := 0 to Count-1 do
        Proc(Designer.GetComponentName(Items[I]));
    end;
  end else
    inherited GetValues(Proc);
end; 

end.   
Run Code Online (Sandbox Code Playgroud)

  • @Wodzu`GigComponent`给出了调用该属性的组件.只要在`GetAttributes`中没有添加`paMultiSelect`,索引0就是你工作的唯一组件.选择多个`MenuItemSelector`组件将禁用属性编辑器中的`MenuItem`属性.剪贴板未使用. (2认同)