如何知道 TToolButton 的 MenuItem 是否被下拉?
在用于承载菜单项的所有者绘制工具栏(设置了 MenuItem 和 Grouped 属性的 TToolButtons)的上下文中,我想知道是否删除了相应的菜单项。问题是 OnAdvancedCustomDrawButton 中的 State 属性不反映该信息。
单击工具按钮时,其 Down 属性为 true,但在上面的特定情况下(MenuItem 设置且 Grouped=True),在菜单被删除后,另一个 OnAdvancedCustomDrawButton 被触发,但这次 Down 设置为 false。
这意味着我最终会绘制处于“未按下”状态的按钮。
查看 VCL 的源代码,似乎有关哪个工具按钮被删除的信息存储在 TToolBar 的 FMenuButton private 字段中,并且 Windows 通过 Perform(TB_SETHOTITEM) 通知热状态,但是两者都没有其中提供读取访问...
此外,VCL 通过私有 FTempMenu 执行下拉菜单,因此其句柄不可访问。
PS: FWIW如果使用hacky解决方案,唯一可用的私有字段似乎是FButtonMenu,它必须与CustomDraw中的Button.MenuItem进行比较,其他私有字段要么设置得不够早(如 FMenuButton)或具有可变位置的私有变量(如 MenuButtonIndex)。不过还是不太满意。
In the context of an owner-draw toolbar used to host menu entries (TToolButtons with their MenuItem and Grouped properties set), I want to know if the corresponding menuitem is dropped. The problem is that the State property in the OnAdvancedCustomDrawButton doesn't reflect that information.
When the toolbutton is clicked, its Down property is true, but in the particular case above (MenuItem set and Grouped=True), just after the menu is dropped, another OnAdvancedCustomDrawButton is fired but this time with Down set to false.
This means that I end up drawing the button with a NOT down state.
Looking at the source of the VCL, it seems the information about which toolbutton is dropped is stored in the TToolBar's FMenuButton private field, and Windows is notified of the hot state by a Perform(TB_SETHOTITEM), however neither of these provide read-access...
Also the VCL performs the dropdown via a private FTempMenu, whose handle is thus not accessible.
PS: FWIW if using the hacky solution, the only private field usable seems to be FButtonMenu which will have to be compared against your Button.MenuItem in the CustomDraw, the others private fiels are either not set early enough (like FMenuButton) or are private variables (like MenuButtonIndex) with a variable location. Still not too satisfying though.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
获取菜单下拉状态是有问题的,弹出菜单的代码非常复杂,使用了一些消息挂钩。它通常不是您想要触及的代码。幸运的是,工具栏本身使用
FMenuDropped
变量来跟踪下拉菜单状态。不幸的是,该变量是私有,您无法从外部访问它,“黑客”技巧不起作用。由于是私有的,它也不提供 RTTI!有两种可能的解决方案:
修改 VCL 并添加一个属性,使 FMenuDropped 从外部可用
转到 ComCtrls.pas,找到
TToolBar = class(TToolWindow)
声明,转到公共部分并添加此内容:从您的代码中,您将能够检查工具栏是否有下拉菜单。不幸的是,它需要对 VCL 进行修改。从来都不是一个好主意,很难在多个程序员之间同步。
使用 hack 直接访问 FMenuDropped 字段,无需更改 VCL
为此,您需要获取
FMenuDropped
字段的偏移量。一旦您明白了,您可以编写如下内容:865
实际上是 Delphi 2010 的正确常量!这是获取常量的非常快速方法。程序 TToolButton.Paint
,在其中放置一个制动点。Inspect
编辑器,该编辑器允许您检查任何内容。输入整数(FToolbar)
。将结果记在纸上。Integer(@FToolBar.FMenuDropped)
。请注意第二个数字。当然也有一些可能的问题。首先,这取决于您使用的确切 Delphi 版本。如果代码需要在不同版本的Delphi编译器上编译,则需要使用巧妙的
$IFDEF
。尽管如此,这是可行的。(编辑):您可以使用相同的技术来访问任何类的任何 Private 字段。但在执行此操作之前,您需要多次考虑,因为私有字段设为私有是有原因的。
Getting the menu-dropped-down status is problematic, the code that makes the menu pop up is pretty convoluted, makes use of some message hooks. It's generally not the code you'd want to touch. Fortunately the Toolbar itself keeps track of the drop-down menu status, using the
FMenuDropped
variable. Unfortunately that variable is private, you can't access it from outside, the "hacked" trick doesn't work. Being private it also doesn't offer RTTI!There are two possible solutions:
Modify the VCL and add a property that makes FMenuDropped available from outside
Go to ComCtrls.pas, find the
TToolBar = class(TToolWindow)
declaration, go to the public section and add this:From your code you'll then be able to check the toolbar if it has a dropped down menu or not. The unfortunate part of this is that it requires modifications to the VCL. Never a good idea, difficult to synchronize amongst several programmers.
Use a hack to access the FMenuDropped field directly, without changing the VCL
To do this you need to get the offset of the
FMenuDropped
field. Once you get that you can write something like this:The
865
is actually the correct constant for Delphi 2010! Here's a very quick way of getting the constant.procedure TToolButton.Paint
, place a brakepoint in there.Inspect
editor that allows you to inspect anything. EnterInteger(FToolbar)
. Note the result on the piece of paper.Integer(@FToolBar.FMenuDropped)
. Note this second number.There are of course some possible problems. First of all this depends on the exact Delphi version you're using. If the code needs to be compiled on different versions of the Delphi compiler, clever
$IFDEF
need to be used. None the less this is workable.(Edit): You can use this same technique to access any Private field of any class. But you'll need to think many times before doing this, because private fields are made private for a reason.
使用班级助手。
例如。
现在,无论您在哪里使用 TToolBar,都可以访问名为 MenuDropped 的新属性。
Use a class helper.
For example.
Now, anywhere you use a TToolBar, you can now access new property called MenuDropped.
单击下拉按钮时,表单将发送
TBN_DROPDOWN
通知。这可用于跟踪启动菜单的按钮:请注意,“OnAdvancedCustomDrawButton”中的“DroppedDown”变量与按钮的“向下”状态不同步,它仅反映按钮的“向下”状态下拉箭头。
我相信,这就是这个问题中问题的原因:当工具栏具有
TBSTYLE_EX_DRAWDDARROWS
扩展样式及其按钮没有BTNS_WHOLEDROPDOWN
样式,菜单打开时仅按下按钮的下拉箭头部分推出。事实上,该按钮不是“按下”的。 AFAIU,即使如此,您仍想绘制按下的按钮。不幸的是,VCL 没有公开任何属性来让按钮“wholedropdown”。可以在按钮上设置此样式:
然后下拉部分将不会与按钮分开运行,或者更正确地说,不会有单独的下拉部分,因此按钮的按下/按下状态将每当启动其菜单时即可设置。
但由于 VCL 不知道按钮的状态,因此会带来一个问题;每当 VCL 更新按钮时,就需要重新设置样式。
When a dropdown button is clicked, the form is sent a
TBN_DROPDOWN
notification. This can be used to track the button that launched a menu:Note that the 'DroppedDown' variable in 'OnAdvancedCustomDrawButton' is not synchronous with the 'Down' state of the button, it only reflects the 'down' state of the dropdown-arrow.
This, I believe, is the cause of the problem in this question: when a toolbar has the
TBSTYLE_EX_DRAWDDARROWS
extended style and its buttons do not have theBTNS_WHOLEDROPDOWN
style, only the dropdown-arrow part of the button is depressed when its menu is launched. The button, in fact, is not 'down'. AFAIU, you want to draw the button pressed even so. Unfortunately the VCL does not expose any property to have the buttons 'wholedropdown'.It is possible to set this style on the buttons:
Then the dropdown part will not act separately from the button, or more correctly, there won't be a separate dropdown part, hence the down/pressed state of a button will be set whenever its menu is launched.
But due to the VCL being unaware of the state of the buttons will pose one problem; whenever the VCL updates the buttons, re-setting the styles would be necessary.