我想要一个按钮上方的弹出菜单:
Delphi 包装 Win32 菜单系统的方式似乎排除了底层 Win32 API 提供的所有模式或标志,这些模式或标志当时不在 VCL 作者的大脑中。一个这样的例子似乎是TPM_BOTTOMALIGN
可以传入的TrackPopupMenu
,但是,Delphi 包装器似乎使这不仅在股票 VCL 中不可能,而且通过不明智地使用私有和受保护的方法,是不可能的(至少在我看来是不可能的) 在运行时或通过覆盖准确地执行。VCL 组件 TPopupMenu 的设计也不是很好,因为它应该有一个被调用的虚方法PrepareForTrackPopupMenu
,除了调用TrackPopupMenu
或TrackPopupMenuEx
,然后允许某人重写实际调用该 Win32 方法的方法。但现在为时已晚。也许 Delphi XE5 将正确地完成 Win32 API 的基本覆盖。
我尝试过的方法:
方法 A:使用 Metrics 或字体:
准确确定弹出菜单的高度,以便在调用 popupmenu.Popup(x,y) 之前减去 Y 值。结果:必须处理 Windows 主题的所有变体,并做出我似乎无法确定的假设。在现实世界中似乎不太可能产生好的结果。这是基本字体度量方法的示例:
height := aPopupMenu.items.count * (abs(font.height) + 6) + 34;
您可以考虑隐藏的项目,并且对于具有单一主题模式设置的单一版本的窗口,您可能会像那样接近,但并不完全正确。
方法 B:让 Windows 来做:
尝试传入TPM_BOTTOMALIGN
最终到达 Win32 API 调用TrackPopupMenu
。
到目前为止,我想我可以做到,如果我修改 VCL menus.pas.. 我在这个项目中使用的是 Delphi 2007。不过,我对这个想法并不那么高兴。
这是我正在尝试的代码类型:
procedure TMyForm.ButtonClick(Sender: TObject);
var
pt:TPoint;
popupMenuHeightEstimate:Integer;
begin
// alas, how to do this accurately, what with themes, and the OnMeasureItem event
// changing things at runtime.
popupMenuHeightEstimate := PopupMenuHeight(BookingsPopupMenu);
pt.X := 0;
pt.Y := -1*popupMenuHeightEstimate;
pt := aButton.ClientToScreen(pt); // do the math for me.
aPopupMenu.popup( pt.X, pt.Y );
end;
或者我想这样做:
pt.X := 0;
pt.Y := 0;
pt := aButton.ClientToScreen(pt); // do the math for me.
aPopupMenu.popupEx( pt.X, pt.Y, TPM_BOTTOMALIGN);
当然,VCL 中没有 popupEx。也没有任何方法可以传递TrackPopupMenu
比 VCL 家伙可能在 1995 年在 1.0 版中添加的标志更多的标志。
注意:我认为在显示菜单之前估计高度的问题是不可能的,因此我们实际上应该通过TrackPopupMenu
不估计高度来解决问题。
更新:TrackPopupMenu
直接调用不起作用,因为 VCL 方法TPopupMenu.Popup(x,y)
中的其余步骤对于调用我的应用程序以绘制其菜单并使其看起来正确是必需的,但是如果没有邪恶的技巧就不可能调用它们,因为它们是私有方法. 修改 VCL 是一个地狱般的提议,我也不希望接受它。