问题
Please consider such scenerio:
I have component called TMenuItemSelector
which has two published properties: PopupMenu
- allows to pick an instance of TPopupMenu
from the form and MenuItem
which allows to pick any instance of TMenuItem
from the form.
I would like to modify property editor for MenuItem
property in a way that when PopupMenu
is assigned then only menu items from this PopupMenu
are visible in a drop down list.
I know that I need to write my own descendant of TComponentProperty
and override GetValues
method. The problem is that I do not know how to access the form on which TMenuItemSelector
is lying.
Original TComponentProperty
is using this method to iterate all available instances:
procedure TComponentProperty.GetValues(Proc: TGetStrProc);
begin
Designer.GetComponentNames(GetTypeData(GetPropType), Proc);
end;
However, Designer
seems to be precompiled so I have no idea how GetComponentNames
works.
This is what I have so far, I guess only thing which I am missing is the implementation of 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.
Anyone could help?
Thanks.
回答1:
When TMenuItemProp.GetValues()
is called, you need to look at the TMenuItemSelector
object whose MenuItem
property is currently being edited, see if that object has a PopupMenu
assigned, and if so then loop through its items as neded, eg:
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;
BTW, you need to implement TMenuItemSelector
and TMenuItemProp
in separate packages. With the exception of the RegisterComponents()
function, (which is implemented in a runtime package), design-time code is not allowed to be compiled into run-time executables. It is against the EULA, and Embarcadero's design-time pacakges are not allowed to be distributed. You need to implement TMenuItemSelector
in a runtime-only package, and then implement TMenuItemProp
and Register()
in a designtime-only package that Requires
the runtime-only package and uses
the unit that TMenuItemSelector
is declared in, eg:
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.
来源:https://stackoverflow.com/questions/9983606/how-to-modify-tcomponentproperty-to-show-only-particular-items-on-drop-down-list