I don't want to use the mouseup event because I want to allow the user to drag outside the control. I need to check the state via a timer. I vaguely remember doing this before at some point by using
If MouseButtons = MouseButtons.Left Then...
But now it says that MouseButtons is a type and cannot be used as an expression (which is true, its an enum type).
Maybe they changed things or maybe I just remember wrong.. either way, how would I check if the button is still down?
This is tried and tested. I created a new class: testmouseclass and created a shared function you can use anytime to determine if the LeftMouseButton is down. This is possible with the help of GetAsyncKeyState call.
Imports System.Runtime.InteropServices 'Need to import...
Public Class testmouseclass
<DllImport("user32.dll", CharSet:=CharSet.Auto, ExactSpelling:=True)> Public Shared Function GetAsyncKeyState(ByVal vkey As Long) As Long
End Function
Public Shared Function LeftMouseIsDown() As Boolean
Return GetAsyncKeyState(Keys.LButton) > 0 And &H8000
End Function
End Class
Example Usage
Private Sub Timer1_Tick(sender As Object, e As System.EventArgs) Handles Timer1.Tick
If testmouseclass.LeftMouseIsDown() Then MsgBox("It IS!")
End Sub
Depending on the timer tick's this can be a headache as well. Not sure how you are using this, but in my example I had the timer at 3 seconds and when I held the LeftMouseButton down a message popped up and when I clicked it again it popped up again because of the timer and the left mouse was down. Also this work's even outside of your application...
What you want is to test the Mouse.LeftButton property.
If System.Windows.Input.Mouse.LeftButton = Windows.Input.MouseButtonState.Pressed Then
Related
I am currently working with a program which is supposed to run in the background but also check if "mod + o" is pressed then do something. But I cannot figure out how a vb.net program can listen to key presses when the program is not Selected / Opened.
You can use P/Invocation to be able to use WinAPI's GetAsyncKeyState() function, then check that in a timer.
<DllImport("user32.dll")> _
Public Shared Function GetAsyncKeyState(ByVal vKey As System.Windows.Forms.Keys) As Short
End Function
Const KeyDownBit As Integer = &H8000
Private Sub Timer1_Tick(sender As Object, e As System.EventArgs) Handles Timer1.Tick
If (GetAsyncKeyState(Keys.LWin) And KeyDownBit) = KeyDownBit AndAlso (GetAsyncKeyState(Keys.O) And KeyDownBit) = KeyDownBit Then
'Do whatever you want when 'Mod + O' is held down.
End If
End Sub
EDIT:
To make the code only execute one time per key press, you can add a little While-loop to run until either of the buttons are released (add it inside your If-statement):
While GetAsyncKeyState(Keys.LWin) AndAlso GetAsyncKeyState(Keys.O)
End While
This will stop your code from executing more than once while you hold the keys down.
When using this in a Console Application just replace every System.Windows.Forms.Keys and Keys with ConsoleKey, and replace LWin with LeftWindows.
I try to run a timer from my winform application. For some reason the function that should run on the timer's tick (IsTimeOffsetValid) is not called nor stopped on break point, and basically nothing happens. I attached a code sample below.
I appreciate the help.
Module Module1
Sub main()
Dim OutputForm As New Form17
Application.Run(OutputForm)
End Sub
End Module
Public Class Form17
Private TimerServerOffset As New System.Timers.Timer
Private Sub Form17_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
AddHandler TimerServerOffset.Elapsed, New System.Timers.ElapsedEventHandler(AddressOf IsTimeOffsetValid)
TimerServerOffset.Interval = 1
TimerServerOffset.Start()
End Sub
Private Sub IsTimeOffsetValid()
MsgBox("IsTimeOffsetValid")
End Sub
End Class
Apart from errors in the code that you posted there are other issues with the design.
Read this question: System.Timers.Timer vs System.Threading.Timer
The callback is called on a worker thread (not the UI thread) so displaying a message box could be a big problem.
then switch to a more fitting timer. If all you want to do is validate the inputs every second, switch to the System.Windows.Forms.Timer. The tick handler runs on the UI thread so you can change the UI in the handler.
Then consider changing the interval a message box popping up every millisecond is not possible and not user friendly.
Finally, I would suggest NOT using a timer for this: just handle changes to the input fields and respond to changed inputs or use the standard validation events of the WinForms controls. This is much cheaper (on the CPU) and will not mess with the focus.
To start with I have a fairly unique situation in that I am dealing with large amounts of data - multiple series of about 500,000 points each. The typical plot time is about 1s which is perfectly adequate.
The chart is created 'WithEvents' in code and the plot time doesn't change.
However, when I add the sub with the handler for the click event ..
Private Sub Chart_Main_Click(ByVal sender As Object, _
ByVal e As MouseEventArgs) Handles Chart_Main.Click
Dim y As Integer = Chart_Main.ChartAreas(0).AxisX.PixelPositionToValue(e.X)
'MsgBox(y)
End Sub
the plot time blows out to 3min. Even having no code in the sub, the result is the same. There is no reference to the click event in any of the code so I am at a loss as to why this is occurring. I suspect it has something to do with the number of points being added but not knowing the cause is frustrating.
Is anyone able to explain what is going on?
Ok, i don't know if the explanation in the comments was sufficient, so here some example code...
Also i wanted to try this myself!
Essencially, what you do is take control on when you want Windows to check the events.
For that, i suggested two wrappers on AddHandler and RemoveHandler that can safely be called from worker threads.
So, what you have to do, is:
Initialize the Handler in the constructor
Call RemoveClickHandler on your control, each time you want it to be left alone by the EventHandler
But don't forget to reinitialize the handler afterwards via AddClickHandler
Also, your handler method should not have the 'Handles' keyword anymore...
Public Class MainForm
Public Sub New()
' This call is required by the designer.
InitializeComponent()
m_pPictureClickHandler = New MouseEventHandler(AddressOf hndPictureClick)
AddClickHandler(pbxFirst, m_pPictureClickHandler)
End Sub
' Have a persistent local instance of the delegate (for convinience)
Private m_pPictureClickHandler As MouseEventHandler
Public Sub AddClickHandler(obj As Control, target As [Delegate])
If Me.InvokeRequired Then
Me.Invoke(New Action(Of Control, [Delegate])(AddressOf AddClickHandler), obj, target)
Else
AddHandler obj.MouseClick, target
End If
End Sub
Public Sub RemoveClickHandler(obj As Control, target As [Delegate])
If Me.InvokeRequired Then
Me.Invoke(New Action(Of Control, [Delegate])(AddressOf RemoveClickHandler), obj, target)
Else
RemoveHandler obj.MouseClick, target
End If
End Sub
' Here your Plot is done
Public Sub LockedPlot()
RemoveClickHandler(pbxFirst, m_pPictureClickHandler)
' do something on your handler free control ...
AddClickHandler(pbxFirst, m_pPictureClickHandler)
End Sub
' This is your handler (note without a 'Handles' keyword)
Private Sub hndPictureClick(sender As Object, e As MouseEventArgs)
' do something with the click
MessageBox.Show(String.Format("Yeah! You clicked at: {0}x{1}", e.X.ToString(), e.Y.ToString()))
End Sub
End Class
I suppose an even better design would be to create a child class of your chart that has an LPC style method called, say 'SafePlot', with folowing features:
It accepts a pointer (delegate) to a procedure
It will remove all the event handler before invoking the procedure
Finally it would reinitialize the handlers on it's own after the job is done.
It may require a collection to all handler refering to it's events.
-> For that reason i'd let the class manage the handlers entiraly...
Alternativly you could put the 'SafePlot' idea in your main class. then you could manage the event handler there... but that is disputable
Well i can think of a few other ways to do this, but i'm cutting the brainstorming now!
If interested in one of these design solutions, give me a poke.
I am building a custom control and what i would like to do is have an event lets call this event OnMenuShow. Now what i would like to be able to do is handle this event inside my control to show one menu but allow the user implementing my custom control to handle the event in inside the parent form to show a different menu if they wish. so the users code would look something like this.
Public Sub Control_OnMenuShow(sender as Object, e as CustomEventArgs) handles Control.OnMenyShow
'DO some work
e.handled = true
end Sub
I'm just not sure on how to prevent the event from firing twice once for the code inside the control the other in the event. if someone could point me in the right direction that would be very helpful
-Nathan
In your control:
Public Event MenuShow As EventHandler
Public Overridable Sub OnMenuShow()
RaiseEvent MenuShow(New EventArgs)
End Sub
Now the consumer may override OnMenuShow which would bypass your raiseevent statement without you needing to "check" anything.
I found that Hans' answer reponse fit my question the best, and had he left it in an answer I would have marked his as answered. I will leave how I ended up coding this for future refrence.
Public Event MenuShow As EventHandler(Of MenuShowEventArgs)
Public Overridable Sub OnMenuShowEvent()
Dim args As New MenuShowEventArgs(False, "Control")
RaiseEvent MenuShow(Me, args)
if args.handled then return
'DO WORK
End Sub
I am working on a vb.net project and i have a "start" and "Pause" Buttons on the FormPost.exe
I am trying to schedule a batch process to run every day in the morning at 4:00 AM.
How can i run a command prompt to execuite FormPost.exe and them click on "start" button, all via command prompt?
Please let me know. Thanks
What you can do is this override the OnControlCreateMethod() as follows:
Public Class Form1
Public Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
End Sub
Protected Overrides Sub OnCreateControl()
MyBase.OnCreateControl()
If Environment.CommandLine.Contains("/clickme") Then
ClickMeButton.PerformClick()
// ... need to wait here until click event handler has finished, e.g.
// using synchronization objects
Close()
End If
End Sub
End Class
If you pass "/clickme" on the command line it will execute the click event and then close the form. If the form is the only one in the application it will terminate.
Be aware, though, that you will need to add some logic that waits for the click event handler to finish. Try avoid using polling or sleep. Instead try using synchronization objects.
If this is your application; you can modify the code so that it checks if you are running it from the command line / with appropriate arguments and fire the button click() itself. That'd be the easiest approach (I think John's answer shows this)
If it's not your application; you can still accomplish the same thing, but it's not as pretty. You can write code that will execute the winForm then activate it (to ensure it has focus)
Public Shared Sub ActivateWoW()
Dim myApp As Process = Process.GetProcessesByName("MyApp").First
AppActivate(myApp.Id)
End Sub
Then, you can use SendKeys() to simulate interaction with the form. Let's say the start button takes two 'tab' keys to be selected...
SendKeys.Send("{TAB}{TAB}")
Then a quick pause...
Thread.Sleep(25)
Then hit the enter key (which is almost always just as good as mouse click when the button is selected)
SendKeys.Send("{ENTER}")
If you want to get more involved than that; you need to start using WIN32 API calls. Here is some sample code for a Mouse click...
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr
Private Const WM_LBUTTONUP As Long = &H202
Private Const WM_RBUTTONUP As Long = &H205
Private Const WM_LBUTTONDOWN As Long = &H201
Private Const WM_RBUTTONDOWN As Long = &H204
Private Shared Function MakeDWord(ByVal LoWord As Integer, ByVal HiWord As Integer) As Long
Return (HiWord * &H10000) Or (LoWord And &HFFFF&)
End Function
Public Shared Sub SendMouseClick()
Dim Wow As Long = FindWindow("GxWindowClass", "MyWindow")
Dim dWord As Long = MakeDWord(LastX - LastRectX, LastY - LastRectY)
SendMessage(Wow, WM_RBUTTONDOWN, 1&, dWord)
Threading.Thread.Sleep(100)
SendMessage(Wow, WM_RBUTTONUP, 1&, dWord)
End Sub