我正试图让管理员通过Ctrl+单击来启用/禁用应用程序主菜单中的菜单项。为了做到这一点,我在主表单中注入了一个自定义版本的TMenuItem类,并重写了Click虚拟方法,如下所示:
uses
Forms, Menus;
type
TMenuItem = class(Menus.TMenuItem)
public
ControlActivationState: Boolean;
procedure Click; override;
end;
TMyMainForm = class(TForm)
...
procedure TMenuItem.Click;
begin
if ControlActivationState and IsKeyPressed(VK_CONTROL) then
Self.Enabled := not Self.Enabled
else
inherited;
end;
它有效,但仅适用于顶级菜单。为什么顶级菜单项即使被禁用也会接收OnClick事件,而其他菜单项则不会?有没有办法让子菜单项也接收这些事件?
顶级OnClick
事件由收到WM_INITMENUPOPUP
消息触发。即使禁用了顶级项目,也会发送该消息。我不知道为什么会在这种情况下发送,但确实如此。对于有子项的子项也是如此。
但是,对于没有子项的子项,OnClick
由WM_COMMAND
消息触发。但是,如果菜单项被禁用,系统甚至从不发送消息。
你想做的事不可能轻易完成。我能看到你这样做的唯一方法就是处理原始的鼠标和键盘事件。就我个人而言,我不会考虑这么做。
TMenuItem是一个TComponent,即它不是一个窗口控件,也没有经典事件。相反,发生在实际窗口控件上的单击事件被委派给TMenuItem实例。我不知道哪个窗口控件是事件的真正宿主,但即使我知道了,我也认为很难确定哪个TMenuItem对应于实际的点击点。
我的建议是用一个树控件创建一个用于菜单编辑的专用窗口,该控件在运行时根据实际菜单布局来填充其项,然后为反映相应菜单项的树节点提供启用/禁用功能。然后,您可以保存/加载菜单项列表等。这应该更干净、更容易,然后深入到VCL的黑暗深处,弄清楚(并覆盖)事件是如何从"真实"控件传播到称为TComponent
s…
事实上,你正试图用艰难的方式
解决问题的简单方法是覆盖TMenuItem
的OnDrawItem()
方法,使其显示为禁用状态,并交替处理OnClick
事件。
(不要忘记设置菜单的.OwnerDraw
属性以使此解决方案工作。)
编辑:
根据Delphi的帮助,使用OnAdvancedDrawItem
事件更容易,因为它提供了关于菜单项本身的信息。