Click here to Skip to main content
13,766,769 members
Rate this:
Please Sign up or sign in to vote.
See more:
Hey :)

I need some help with just a small bit of code, I've been struggling for a while now with this and would love it if someone could help me out. So, when a user (myself, as I'm not distributing) right clicks or left clicks, either "10000" or "10001" respectively, are added to a listbox. However, as you can see why in the code below, the integers indicating a right or a left click (10000/10001) are listed multiple times because, each time the timer ticks, the r/l click is recorded if the buttons are pressed down. Even when I'm clicking my mouse normally, it will list "10000" 6 times, for example, because the timer interval is so fast (10ms).

So basically I need to ensure that only one of these integers are added (i.e. only the actual event of pressing down on r/l mouse buttons is listed, rather than recording if it is pressed down every 10ms.
Here is the code I've got so far:
<pre>    Private Declare Function GetAsyncKeyState Lib "user32" (ByVal vkey As Long) As Integer
    Declare Sub mouse_event Lib "user32" Alias "mouse_event" (ByVal dwFlags As Integer, ByVal dx As Integer, ByVal dy As Integer, ByVal cButtons As Integer, ByVal dwExtraInfo As Integer)
    Public Const MOUSEEVENTF_LEFTUP = &H4
    Public Const MOUSEEVENTF_RIGHTUP = &H10
    Dim Mouse_LeftClick As Boolean
    Dim Mouse_RightClick As Boolean

Private Sub Timer_Tick(sender As Object, e As EventArgs) Handles Timer.Tick

        Mouse_LeftClick = GetAsyncKeyState(Keys.LButton)

        If Mouse_LeftClick Then



        End If

        Mouse_RightClick = GetAsyncKeyState(Keys.RButton)

        If Mouse_RightClick Then



        End If

    End Sub

That about sums it up, I hope you understand my problem and thanks so much in advance! :) Also, I'm trying to get better at this language, if you notice anything wrong with how I've formatted the code, please tell me (e.g. is there too much white-space, is it easy to understand)!


What I have tried:

I've tried making it so that in the first 10 or so items in the listbox, only 1 click can exist, but this simply is not practical, especially considering the speed of the timer. :|
Posted 6-Nov-17 12:07pm
Updated 9-Nov-17 1:51am
GKP1992 6-Nov-17 23:16pm
Why are you using the Timer.Tick event, and not the mouse click event?
[no name] 7-Nov-17 2:57am
Hello :)

Thanks for your suggestion! However, I don't know what element of my form should have the mouse_click event as my form's quite complex in that it has several child windows (wordpad, excel) attached to the form. If I click into any of those child windows, I'm not pressing down onto any element and, thus, the mouse_click won't work (I don't think it will anyways). The timer_tick, on the other hand, does list clicks on the child windows.

You're probably right, but I tried your suggestion and I couldn't get it working, thank you though!
Rate this: bad
Please Sign up or sign in to vote.

Solution 1

If you are stuck with using the timer tick, then you need to remember the mouse buttons from one tick to the next. Then you only add the "mouse event" if it is new. i.e. button pressed now, but wasn't last time.


static oldstate = impossible_value

  newstate = getmousestate()
  if ((newstate != oldstate) && (oldstate != impossible_value))
    process_the_new_state()  <<<< do your thing here
  oldstate = newstate  <<<< remember for next time
[no name] 7-Nov-17 13:17pm
I've probably misinterpreted your comment, but multiple mouse clicks must be recorded, I'm not sure how to make this work. Rated regardless, for your time. :)
Peter_in_2780 7-Nov-17 18:03pm
I've updated my solution with pseudocode. Give that a try.
Rate this: bad
Please Sign up or sign in to vote.

Solution 2

What you can do is (based on the nSuggestion of GKP1992) :
- override the OnControlAdded-method of your Form
- catch the MouseClick-Event from each Control of the Form (each added Control)
- inside the MouseClick-Eventhandler add the Information to your Listbox

Public Class Form1

    Protected Overrides Sub OnControlAdded(e As ControlEventArgs)
        Dim myControl As Control = e.Control
        AddHandler myControl.MouseClick, AddressOf MouseClickHandler
    End Sub

    Private Sub MouseClickHandler(sender As Object, e As MouseEventArgs) Handles Me.MouseClick
        If e.Button = MouseButtons.Left Then ListBox1.Items.Add("Left")
        If e.Button = MouseButtons.Right Then ListBox1.Items.Add("Right")
     End Sub

end class
[no name] 7-Nov-17 13:12pm
Hi Ralf :)

This works really well for any form objects, and is much simpler than my previous code (thanks for the knowledge), however, since I need the mouse clicks to be recorded when clicking on an external process (which is set as a child on my parent window), this unfortunately does not work. My previous code does work on the child form, however, as I explained, it records multiple clicks, instead of just one as it's in a timer function. Do you have any ideas? Rated, either way.

Thanks so much for your help and your time,
Ralf Meier 7-Nov-17 14:42pm
Hi Helllboy,
thanks for your vote.
Perhaps I could help you again but in the moment I don't have any idea with what you mean with "an external process (which is set as a child on my parent window". Please explain that or better give me an example ...
[no name] 7-Nov-17 15:32pm
Hey Ralf, thanks for your patience. By this I mean an application, different to my actual form, is embedded inside my form space. This is acheived through this code:

<dllimport("user32.dll")> Public Shared Function SetParent(ByVal hWndChild As IntPtr, ByVal hWndNewParent As IntPtr) As IntPtr
End Function
<dllimport("user32.dll")> Public Shared Function SendMessage(ByVal hWnd As IntPtr, ByVal Msg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer
End Function
Private Const WM_SYSCOMMAND As Integer = &H112
Private Const SC_MAXIMIZE As Integer = &HF030

Private Sub SetChildTimer_Tick(sender As Object, e As EventArgs) Handles SetChildTimer.Tick
Dim ClientProcess As Process = Process.GetProcessesByName("notepad").FirstOrDefault

If ClientProcess IsNot Nothing Then

SetParent(ClientProcess.MainWindowHandle, ClientContainer.Handle)
SendMessage(ClientProcess.MainWindowHandle, WM_SYSCOMMAND, SC_MAXIMIZE, 0)

End If

SetChildTimer.Enabled = False
End Sub


This will set notepad as the child of the form once the timer has ticked.

This is a sample picture:
(from MSDN- not malicious)
So, although the child form is embedded and has become part of the parent application, it still remains an external process. It confines to the forms borders, but does not become an actual element of the form and, therefore, the code that you kindly posted will not work.


PS: If you need to know the name of the process when helping me with the code, it's handle is "WPS Spreadsheets (32 bit)" and it's process name is "et.exe"
Ralf Meier 7-Nov-17 16:05pm
OK ... I understand what you ... and I'm sorry ... for this I don't know a Solution because (as you wrote) Notepad does not really belong to your form and so I can't catch the Events from it.
But you should realize also that this application perhaps hooks the Events itself and so you will not get the right information "outside". So, if I'm right, your Timer-method will fail also ...

But tell me please : for what do you need this really ?
[no name] 7-Nov-17 19:10pm
Hello, sorry for my late reply, I don't expect you to reply to this promptly either xD. The timer method does capture the mouse click. If you look at the code, it captures all mouse clicks, not only the ones that occur inside my form, but also outside of the form. The problem is, each time the timer ticks (every 50ms or so), the mouse click is recorded - so it records the actual time in which to click is held down, I don't want it to do that, I want it to record the actual event of clicking, that way only a single "10000" is returned to the listbox. Some, in capturing the mouse clicks [essentially] the method works, it just needs honing - it must only capture the action of clicking once.
Ralf Meier 8-Nov-17 14:11pm
OK ... so why do you compare the actual state of the MouseButtons with the last state of your MouseButtons (with your method). If there is a difference (in direction 'coming up') you store the result in your Listbox (and only then).
Ralf Meier 9-Nov-17 6:52am
See my new Solution ...
Rate this: bad
Please Sign up or sign in to vote.

Solution 3

Based on the Suggestion I made in Solution 2 a new Code-Snippet :
Private Declare Function GetAsyncKeyState Lib "user32" (ByVal vkey As Long) As Integer

Dim Mouse_LeftClick As Boolean
Dim Mouse_RightClick As Boolean
Dim Mouse_LeftClick_Last As Boolean
Dim Mouse_RightClick_Last As Boolean

Private Sub Timer_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick

    Mouse_LeftClick = GetAsyncKeyState(Keys.LButton)
    If Mouse_LeftClick And Not Mouse_LeftClick_Last Then
    End If
    Mouse_LeftClick_Last = Mouse_LeftClick

    Mouse_RightClick = GetAsyncKeyState(Keys.RButton)
    If Mouse_RightClick And Not Mouse_RightClick_Last Then
    End If
    Mouse_RightClick_Last = Mouse_RightClick

End Sub

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

  Print Answers RSS
Top Experts
Last 24hrsThis month

Advertise | Privacy | Cookies | Terms of Service
Web01-2016 | 2.8.181114.1 | Last Updated 9 Nov 2017
Copyright © CodeProject, 1999-2018
All Rights Reserved.
Layout: fixed | fluid

CodeProject, 503-250 Ferrand Drive Toronto Ontario, M3C 3G8 Canada +1 416-849-8900 x 100