【问题标题】:How to modify TComponentProperty to show only particular items on drop down list?如何修改 TComponentProperty 以仅显示下拉列表中的特定项目?
【发布时间】:2012-04-16 12:53:18
【问题描述】:

请考虑这样的场景:

我有一个名为 TMenuItemSelector 的组件,它有两个已发布的属性:PopupMenu - 允许从表单中选择 TPopupMenu 的实例,MenuItem 允许选择 any 的实例TMenuItem 来自表单。

我想修改MenuItem 属性的属性编辑器,当分配PopupMenu 时,下拉列表中只有来自该PopupMenu 的菜单项可见。

我知道我需要编写自己的TComponentProperty 后代并覆盖GetValues 方法。问题是我不知道如何访问 TMenuItemSelector 所在的表单。

原来的TComponentProperty是用这个方法迭代所有可用的实例:

procedure TComponentProperty.GetValues(Proc: TGetStrProc);
begin
  Designer.GetComponentNames(GetTypeData(GetPropType), Proc);
end;

但是,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.

有人可以帮忙吗?

谢谢。

【问题讨论】:

  • 我认为使用Designer.Root 来获取表单。

标签: delphi delphi-2009 propertyeditor


【解决方案1】:

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

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; 

顺便说一句,您需要在单独的包中实现 TMenuItemSelectorTMenuItemProp。除了RegisterComponents() 函数(在运行时包中实现)之外,不允许将设计时代码编译成运行时可执行文件。它违反了 EULA,不允许分发 Embarcadero 的设计时包。你需要在一个runtime-only包中实现TMenuItemSelector,然后在一个designtime-only包中实现TMenuItemPropRegister()Requires是runtime-only包,usesTMenuItemSelector的单位声明在,例如:

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.

.

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.   

【讨论】:

  • 最后一段假设运行时包。如果你不使用这些,那么它会更简单一些,但你仍然会受到重新分配的限制。
  • 即使运行时包在使用该组件的应用程序中被禁用,您仍然必须(并且应该)将运行时代码和设计时代码拆分为单独的包。 IDE 将加载设计时包(因此它应该尽可能小),并将决定应用程序是静态链接到运行时代码还是动态链接到运行时代码。如果设计时代码在运行时包中,并且该运行时包被静态链接,那么设计时代码也将被处理并可能被链接,这是不允许的。自 D6 以来,运行时/设计时分离已被强制执行。
  • 谢谢雷米。还有一个问题,GetComponent(0) 是如何工作的。我正在阅读文档,它说该组件是从剪贴板中获取的,但是它首先是如何到达剪贴板的?
  • @Wodzu GetComponent 给出调用属性的组件。只要paMultiSelect 未添加到GetAttributes 中,索引0 就是您正在处理的唯一组件。选择多个MenuItemSelector 组件将禁用属性编辑器中的MenuItem 属性。剪贴板未使用。
  • @NGLN 感谢您的解释。
猜你喜欢
  • 2019-06-15
  • 2021-04-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多