Delphi如何检测表单上任何地方的点击事件,包括其他组件
Posted
技术标签:
【中文标题】Delphi如何检测表单上任何地方的点击事件,包括其他组件【英文标题】:Delphi How can I detect a click event anywhere on a form including other components 【发布时间】:2020-07-20 07:13:16 【问题描述】:我在 Delphi VCL 表单应用程序中有一个 TEdit
(如果重要,包含在 TFrame
实例中)。在用户指示他们完成编辑后,通过单击表单上的其他位置,插入符号和焦点保持在该控件上,直到我单击另一个控件,然后该控件获得焦点。但是,我希望 TEdit
无论用户单击何处都可以放松焦点。我希望我可以使用ActiveControl := nil
来结束对所选控件的关注,但我不确定在哪里调用它。
我想要的是焦点离开选定的控件而不必将其转移到另一个控件。我可以在表单的OnClick
事件中结束焦点,但是如果用户在我的表单上选择任何其他控件(也包含在框架中),这将不起作用,因为不会触发表单的OnClick
事件。为表单上的每个附加项目提供单独的 OnClick
事件似乎既不优雅又乏味。
实现这种行为的全局解决方案是什么?
【问题讨论】:
你为什么要这个?它只会让用户体验变得更糟,因为它是非标准的。 @DavidHeffernan 该程序提供了一个游戏板,并且编辑器主要由用户在初始设置期间使用。一旦游戏开始,用户就会给出输入,包括键盘以及由独立于这些编辑器的程序管理和显示的输入。在用户看来,焦点只是简单地转移到了其他组件,这些组件由包含图像的框架和 tlabel 文本显示组成。 【参考方案1】:尝试使用TApplication(Events).OnMessage
事件查找WM_LBUTTONDOWN
消息。
您可以使用 VCL 的 FindVCLWindow()
或 FindDragTarget()
函数(都在 Vcl.Controls
单元中)查看点击坐标处是否有 TWinControl
。或者更简单,您可以使用 VCL 的 FindControl()
函数(也在 Vcl.Controls
单元中)直接从消息的目标 HWND
获取 TWinControl
。
如果鼠标下不存在控件,或者控件不可聚焦(其CanFocus()
方法返回False
),则设置ActiveControl=nil
。否则,什么都不做,让被点击的控件在处理消息时自行获得焦点。
【讨论】:
@AshlarTApplication(Events).OnMessage
看到来自主线程的消息队列的每条消息,并由 VCL 消息循环(TApplication.Run()
、TForm.ShowModal()
等)检索。鼠标消息被发布到队列中,所以OnMessage
应该在正常条件下看到它们。如果您没有看到这些消息,那么可能是在您的代码中某处运行的辅助消息循环没有将消息传递给OnMessage
。使用OnMessage
的替代方法是使用SetWindowsHookEx()
在UI 线程中安装线程特定的鼠标挂钩。
谢谢。我发现了问题,并且 onMessage 正在对 WM_LButtonDown 做出反应。但是,当我单击 tedit 时,FindVCLWindow (mouse.curpos) 返回 nil。它似乎与 mouse.curpos 有关,所以单独的问题来了
@Ashlar 为什么要使用Mouse.CursorPos
,而不是使用WM_LBUTTONDOWN
本身提供的坐标? LParam
包含相对于消息目标HWND
的客户端坐标。从WM_LBUTTONDOWN
消息排队到可以检索到它之前,鼠标可能已经移动。就此而言,为什么不直接使用消息的目标HWND
本身呢?您可以使用 VCL 的 FindControl()
函数从 HWND
获取 TWinControl
。
您在此处的评论以及this related question 上的其他类似 cmets 让我按照您的建议更正我的代码。我只是在学习正确使用消息,因此您在 LParam
上的信息对我来说是新信息并且非常有帮助。我还有更多的研究要做!以上是关于Delphi如何检测表单上任何地方的点击事件,包括其他组件的主要内容,如果未能解决你的问题,请参考以下文章
如何在 UIScrollView 上检测“Drag Enter”
在 KineticJS 中检测舞台上的点击而不是形状上的点击