Delphi主窗体启用自定义样式后MenuItem的OnClick事件不触发问题
问题原因
这是VCL自定义样式系统的已知历史缺陷,仅在「先加载应用自定义样式、后动态生成嵌套菜单」的场景下触发:
- VCL样式的菜单项初始化逻辑会错误将子菜单的首个可点击菜单项标记为无交互的标题类菜单项,跳过其点击事件的命中检测,因此你绑定的
onClick事件不会被触发 - 菜单项的禁用状态、外观渲染逻辑不受这个标记影响,所以你从外观上看不到任何异常
- 仅第一个子项会被误标记,后续菜单项的初始化逻辑正常,所以第二个语言选项可以正常触发事件
解决方案
你可以任选以下任意一种方法修复:
方案1:调整样式加载和菜单生成的顺序
将菜单生成逻辑提前到样式加载之前执行,避开样式初始化的缺陷,调整后的代码示例:
procedure TForm1.FormCreate(Sender: TObject); begin // 先生成菜单 if not assigned(menu) then menu := newMenu(self, 'Meny',[createSettingsMenu]); // 再加载应用样式 TStyleManager.LoadFromFile('mystyle.vsf'); TStyleManager.SetStyle('My Style'); end; // 可以删掉FormShow里的菜单生成逻辑
这个方案改动最小,不需要修改原有菜单生成的业务逻辑。
方案2:添加不可见的占位菜单项
在所有子菜单的首个位置插入一个隐藏的占位菜单项,让你的业务菜单项从第二个位置开始,避开误标记逻辑,以语言菜单为例:
function TForm1.createLanguageMenu: TMenuItem; var alt: array[0..2] of TMenuItem; begin // 插入隐藏的占位项 alt[0] := TMenuItem.Create(menu); alt[0].Visible := False; alt[1] := TMenuItem.Create(menu); alt[1].Caption := 'English'; alt[1].OnClick := langClick; alt[2] := TMenuItem.Create(menu); alt[2].Caption := 'Swedish'; alt[2].OnClick := langClick; result := newSubMenu('Language', 0, 'Language', alt); end;
这个方案适合无法调整样式加载顺序的场景。
方案3:升级Delphi版本
该缺陷已在Delphi 10.4 Sydney及后续版本中修复,如果你的项目允许升级开发环境,升级后即可直接解决该问题。
内容的提问来源于stack exchange,提问作者Sara
相关产品推荐
相关产品推荐

