-
Feb 21st, 2019, 09:55 AM
#1
Thread Starter
New Member
[RESOLVED] Get object by mouse position on click
Hi guys!!
There's another VB6 special challenge! lol!
My application has many objects in many parents and z-orders... Someones showing disabled for editing (not clickable)... All the objects interact with the screen size and resizing, so the position is always kind of relative...
I'm trying to implement an tool to edit some properties of these objects out of the source code.
Question:
Is there anyway to get the object (at least its name) by mouse positioning on the screen? Thinking about the moment that the user clicks on the object and work around its name to show on my tool the properties that he could edit.
I found some ways to get the mouse position or implement some event on the object to get its position, but nothing so general... I don't wanna implement an event for all the objects... that's a lot!
Thanks!!
Last edited by fernandozap; Feb 21st, 2019 at 01:17 PM.
-
Feb 21st, 2019, 11:07 AM
#2
Re: Get object by mouse position on click
Well, I don't know a lot of windows api options that might make it simpler, so my approach, based on the little I know, would be to keep the bounds for all the controls in some structure so that they would be easy to look up, and then use the GetAsyncKeyState function to monitor for a button press in my window, then look up the control, or controls whose bounds include that point.
In the cases where I've used "objects" that I created and placed, so I know their bounds and z-order, I can just search the list I maintain in z-order and the first hit I get I know is the visible object that was clicked on, rather than some object that was under it in the z-order.
-
Feb 21st, 2019, 12:05 PM
#3
Thread Starter
New Member
Re: Get object by mouse position on click
Thanks Passel...
I could do this if my objects doesn't change their position with an resize, but it does! So I need something more accurate, if is it possible...
-
Feb 21st, 2019, 12:19 PM
#4
Addicted Member
Re: Get object by mouse position on click
This is just an idea for you to think.
You may implement a low-level mouse hook with your tool for this.
Based on that, you can get low-level mouse data for x, y coordinates when the mouse moves/clicks on controls in your targeting application.
Then, use WindowFromPoint API to get the hWnd of the controls based on the x, y coordinates (but excluding disabled or windowless controls)
-
Feb 21st, 2019, 01:11 PM
#5
Thread Starter
New Member
Re: Get object by mouse position on click
Last edited by fernandozap; Feb 21st, 2019 at 01:14 PM.
-
Feb 21st, 2019, 01:12 PM
#6
Thread Starter
New Member
Re: Get object by mouse position on click
Originally Posted by PGBSoft
This is just an idea for you to think.
You may implement a low-level mouse hook with your tool for this.
Based on that, you can get low-level mouse data for x, y coordinates when the mouse moves/clicks on controls in your targeting application.
Then, use WindowFromPoint API to get the hWnd of the controls based on the x, y coordinates (but excluding disabled or windowless controls)
Hey PGB... thanks for the tip!! I wrote a code with this:
Code:
(...)
lngHwnd = ChildWindowFromPoint(Me.hwnd, 10, 10)
GetClassName(lngHwnd, classname, 50)
(...)
the GetClassName API returns some name, but not the exactly object name. I'm searching for an API that returns that. Do you know anyone?
Thanks!
-
Feb 21st, 2019, 01:37 PM
#7
Re: Get object by mouse position on click
Once you've got the hWnd, couldn't you just iterate all your child controls, looking for you hWnd?
-
Feb 21st, 2019, 01:48 PM
#8
Addicted Member
Re: Get object by mouse position on click
Perhaps, this may help you...
-
Feb 21st, 2019, 04:11 PM
#9
Hyperactive Member
Re: Get object by mouse position on click
this code listens for mouseclick events and then uses a basic axis-aligned bounding box formula to see if the mouse position has collided with any control. The built in "me.controls" collection seems to return controls in topmost zorder so there is no need to look at the zorder
put a timer on a form, paste this code, and add overlapping controls to see it in action:
Code:
Option Explicit
Private Type POINTAPI
X As Long
Y As Long
End Type
Private Type RECT
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
Private Declare Function GetCursorPos Lib "user32" (lpPoint As POINTAPI) As Long
Private Declare Function GetAsyncKeyState Lib "user32" (ByVal vKey As Long) As Integer
Private Declare Function GetActiveWindow Lib "user32" () As Long
Private Declare Function GetWindowRect Lib "user32.dll" (ByVal hwnd As Long, ByRef lpRect As RECT) As Long
Private Sub Form_Load()
Timer1.Interval = 250
Timer1.Enabled = True
End Sub
Private Sub Timer1_Timer()
Dim pt As POINTAPI
Dim c As Control
If GetAsyncKeyState(1) <> 0 Then
GetCursorPos pt
If GetActiveWindow = Me.hwnd Then
For Each c In Me.Controls
If controlAtPoint(c, pt.X, pt.Y) Then
Debug.Print c.Name & " was clicked"
Exit For
End If
Next
End If
End If
End Sub
Private Function controlAtPoint(c As Control, mx As Long, my As Long) As Boolean
Dim a As RECT
Dim b As RECT
On Error Resume Next
GetWindowRect c.hwnd, a
b.Top = my
b.Left = mx
b.Bottom = my - 1
b.Right = mx + 1
controlAtPoint = ((a.Left < b.Left + (b.Right - b.Left)) And (a.Left + (a.Right - a.Left) > b.Left) And (a.Top < b.Top + (b.Top - b.Bottom)) And (a.Top + (a.Bottom - a.Top) > b.Top))
On Error GoTo 0
End Function
-
Feb 21st, 2019, 04:20 PM
#10
Re: Get object by mouse position on click
Do note that API solutions that rely on a hWnd will not find windowless controls (images, shapes, labels, etc) if that applies to you.
DllHell's solution might be modified to address the problem. Instead of using GetWindowRect to retrieve the dimensions, use the control's Width/Height properties. There are some gotchas there. For example, not all controls have a Width/Height property: Lines, menus, and probably a couple others that would be returned from the form's Controls() collection.
Another gotcha is that you may need to scale a control's dimensions to pixels (for comparison) as those dimensions are in the control's container scalemode, be it twips or whatever. More effort obviously, but may be reasonable to trap for windowless controls. But a solution like this would require additional checks: you don't want to return a hidden image control (.Visible = False)
Last edited by LaVolpe; Feb 21st, 2019 at 04:40 PM.
-
Feb 21st, 2019, 06:49 PM
#11
Re: Get object by mouse position on click
Hello, this code is similar to the one proposed above but it will work with all the forms and all the controls that have dimensions:
Code:
Private Type POINTAPI
X As Long
Y As Long
End Type
Private Declare Function GetCursorPos Lib "user32" (lpPoint As POINTAPI) As Long
Private Declare Function GetAsyncKeyState Lib "user32" (ByVal vKey As Long) As Integer
Private Declare Function ScreenToClient Lib "user32" (ByVal hWnd As Long, lpPoint As POINTAPI) As Long
Private Const VK_LBUTTON As Long = &H1
Private Sub Form_Load()
Timer1.Interval = 10
End Sub
Private Sub Timer1_Timer()
Dim iPt As POINTAPI
Dim iPt_Scaled As POINTAPI
Static sMousePressed As Boolean
Dim iFrm As Form
Dim iTheForm As Form
Dim iCtl As Control
Dim iTheControl As Control
Dim iVisible As Boolean
Dim iLeft As Single
Dim iTop As Single
Dim iWidth As Single
Dim iHeight As Single
If GetAsyncKeyState(VK_LBUTTON) <> 0 Then
GetCursorPos iPt
If Not sMousePressed Then
sMousePressed = True
iPt_Scaled.X = iPt.X * Screen.TwipsPerPixelX
iPt_Scaled.Y = iPt.Y * Screen.TwipsPerPixelX
For Each iFrm In Forms
If iFrm.Visible Then
If iFrm.WindowState <> vbMinimized Then
If iPt_Scaled.X >= iFrm.Left Then
If iPt_Scaled.X <= (iFrm.Left + iFrm.Width) Then
If iPt_Scaled.Y >= iFrm.Top Then
If iPt_Scaled.Y <= (iFrm.Top + iFrm.Height) Then
Set iTheForm = iFrm
Exit For
End If
End If
End If
End If
End If
End If
Next
If Not iTheForm Is Nothing Then
ScreenToClient iTheForm.hWnd, iPt
iPt_Scaled.X = iTheForm.ScaleX(iPt.X, vbPixels, iTheForm.ScaleMode)
iPt_Scaled.Y = iTheForm.ScaleY(iPt.Y, vbPixels, iTheForm.ScaleMode)
For Each iCtl In iTheForm.Controls
On Error Resume Next
iVisible = False
iVisible = iCtl.Visible
iLeft = 0
iLeft = iCtl.Left
iTop = 0
iTop = iCtl.Top
iWidth = 0
iWidth = iCtl.Width
iHeight = 0
iHeight = iCtl.Height
If iVisible then
If (iWidth = 0) Or (iHeight = 0) Then iVisible = False
End If
On Error GoTo 0
If iVisible Then
If iPt_Scaled.X >= iLeft Then
If iPt_Scaled.X <= (iLeft + iWidth) Then
If iPt_Scaled.Y >= iTop Then
If iPt_Scaled.Y <= (iTop + iHeight) Then
Set iTheControl = iCtl
Exit For
End If
End If
End If
End If
End If
Next
If Not iTheControl Is Nothing Then
Debug.Print "Clicked " & iTheControl.Name & " in " & iTheForm.Name
End If
End If
End If
Else
sMousePressed = False
End If
End Sub
Last edited by Eduardo-; Feb 22nd, 2019 at 08:15 AM.
-
Feb 22nd, 2019, 06:53 AM
#12
Re: Get object by mouse position on click
@Eduardo. Should the mouse be clicked exactly at 0,0 in the client area, your logic could return a false positive on a control that doesn't have a left,top,width or height property when no control is at 0,0 on the form. For example, a visible Line elsewhere on the form would return a false positive in that case. Since lines don't have those properties, your routine defaults to 0x0 @ 0,0. Well, iPt_Scaled.X >= 0 and iPt_Scaled.X <= 0 and iPt_Scaled.Y >= 0 and iPt_Scaled.Y <= 0 when mouse down at 0,0. Hopefully, I'm reading your code correctly, did not run it. If so, suggest maybe changing your check to following or toggle iVisible to False when width or height are zero.
If iVisible = True And iWidth > 0 And iHeight > 0
-
Feb 22nd, 2019, 08:16 AM
#13
Re: Get object by mouse position on click
Originally Posted by LaVolpe
@Eduardo. Should the mouse be clicked exactly at 0,0 in the client area, your logic could return a false positive on a control that doesn't have a left,top,width or height property when no control is at 0,0 on the form. For example, a visible Line elsewhere on the form would return a false positive in that case. Since lines don't have those properties, your routine defaults to 0x0 @ 0,0. Well, iPt_Scaled.X >= 0 and iPt_Scaled.X <= 0 and iPt_Scaled.Y >= 0 and iPt_Scaled.Y <= 0 when mouse down at 0,0. Hopefully, I'm reading your code correctly, did not run it. If so, suggest maybe changing your check to following or toggle iVisible to False when width or height are zero.
If iVisible = True And iWidth > 0 And iHeight > 0
Hi LaVolpe. Good idea, changed!
-
Feb 22nd, 2019, 07:08 PM
#14
Fanatic Member
Re: Get object by mouse position on click
Try Debug.Print Screen.ActiveControl.Name. See also ActiveForm.
-
Feb 22nd, 2019, 10:11 PM
#15
Re: Get object by mouse position on click
Originally Posted by qvb6
Try Debug.Print Screen.ActiveControl.Name. See also ActiveForm.
Won't work for all types of controls, i.e., shape controls or controls that can't get focus. Also won't work for disabled controls which the OP seems to want included.
-
Feb 26th, 2019, 08:56 AM
#16
Thread Starter
New Member
Re: Get object by mouse position on click
Really thanks for ALL!!
I think doing something that DllHell and Eduardo suggested will resolve! Because I wasn't figuring out how to do with accessibility API....
The priorities on my project has been changed, and I'll do this in the future (not just now), but I'll tell you when finished!
You're awesome guys!! thanks a lot!!
✌
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|