If you hold down the key in a KeyDown Sub, it repeats the code until it is released.
Is there any way to prevent the code from continuously running and keep it so that it only runs once? Thanks.
You need to handle more than one key events to do that. For example
Public Class Form1
Private keyHolding As Boolean = False
Private Sub Form1_KeyDown(sender As Object, e As KeyEventArgs) Handles Me.KeyDown
If Not keyHolding Then
Label1.Text &= "Keydown event detected "
keyHolding = True
'Place the code that you want to run only once in the key down event here...
Else
Label1.Text &= "User is holding the key down "
'Place the code that you want to run continuously in the key down event here...
End If
End Sub
Private Sub Form1_KeyUp(sender As Object, e As KeyEventArgs) Handles Me.KeyUp
Label1.Text &= "KeyUp event detected "
keyHolding = False
End Sub
End Class
Just keep in mind that this approach is good for standard windows forms applications. If you are developing a game, for example, then this approach will cause various problems and there are better solutions either via native API calls or some game developing framework.
Hope this helps.
Related
I hope this is going to be a real quick question:
I have a TreeView on a Windows form.
I run this code to open directories, displayed in a tree view:
Private Sub OpenFolder()
Try
System.Diagnostics.Process.Start(SelectedDir)
Catch ex As Exception
MessageBox.Show("Mappen " & SelectedDir & " kan ikke åbnes!")
End Try
End Sub
When I call OpenFolder() from the KeyDown event:
Private Sub TreeViewDir_KeyDown(sender As Object, e As KeyEventArgs) Handles TreeViewDir.KeyDown
If e.KeyCode = Keys.Enter Then
OpenFolder()
e.SuppressKeyPress = True
ElseIf e.KeyCode = Keys.Delete Then
DeleteFolder()
e.SuppressKeyPress = True
End If
End Sub
..I get a windows error sound. But no error message. What is driving me up the walls, is that this sub fires the function without any problems at all.
Private Sub TreeViewDir_DoubleClick(sender As Object, e As EventArgs) Handles TreeViewDir.DoubleClick
OpenFolder()
End Sub
The error sound plays when the folder opens, but again, only on KeyDown. Can someone tell me why this happens only on the KeyDown event and what I'm doing wrong here?
First, let me point out that your OpenFolder() method isn't responsible for that beep sound, the KeyPress event is. This is a standard behavior of Windows when a key is pressed where it has no job to do.
Now, setting SuppressKeyPress to true, should, in fact, prevent the KeyPress event from firing and therefore, no beep sound should be played. However, in some cases when you execute some code in the KeyDown event, it takes time for the keystroke to be suppressed and therefore causing the beep sound.
To get around this, you can simply subscribe to the KeyPress event and set e.Handled to true:
Private Sub TreeViewDir_KeyPress(sender As Object, e As KeyPressEventArgs) Handles TreeViewDir.KeyPress
If e.KeyChar = ChrW(Keys.Enter) Then e.Handled = True
End Sub
Another solution, if you don't want to use KeyDown, is to allow some time for the keystroke to be suppressed, by delaying the execution of your method:
Private Async Sub TreeViewDir_KeyDown(sender As Object, e As KeyEventArgs) Handles TreeViewDir.KeyDown
' ^^^^^ ⟸ Don't forget the Async keyword.
If e.KeyCode = Keys.Enter Then
e.SuppressKeyPress = True ' This is first
Await Task.Delay(100) ' Followed by a small delay
OpenFolder() ' Then call the method.
End If
End Sub
So I'm currently designing my A-level computing project, and I need to know if VB.NET can register multiple keypresses at the same time, e.g F&J, and be able to treat them as separate keypresses. I may need anywhere up to 4 keypresses at once so if VB.NET can't do it, my program will be limited (though only slightly).
If this is possible, do i just treat it as if they weren't pressed at the same time and check for both keys individually, or is there a special way of detecting this?
Thanks in advance.
You can keep track of the keys you have pressed, and released.
Create a new winforms project and add a Label. This should give you a good starting point.
Public Class Form1
Private pressedKeys As New List(Of System.Windows.Forms.Keys)()
Private Sub Form1_KeyDown(sender As Object, e As KeyEventArgs) Handles MyBase.KeyDown
If Not pressedKeys.Contains(e.KeyCode) Then pressedKeys.Add(e.KeyCode)
printCurrentKeys()
End Sub
Private Sub Form1_KeyUp(sender As Object, e As KeyEventArgs) Handles MyBase.KeyUp
If pressedKeys.Contains(e.KeyCode) Then pressedKeys.Remove(e.KeyCode)
printCurrentKeys()
End Sub
Private Sub printCurrentKeys()
If pressedKeys.Count > 0 Then
Me.Label1.Text = pressedKeys.
Select(Of String)(Function(k) Chr(k)).
Aggregate(Function(s1, s2) s1 & ", " & s2)
Else
Me.Label1.Text = ""
End If
End Sub
End Class
(The above 8 keys made possible by my anti-ghosting keyboard, the Sidewinder X4.)
Supposedly you can check in the registry
Dim KeyboardDataQueueSize = My.Computer.Registry.GetValue(
"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Kbdclass\Parameters",
"KeyboardDataQueueSize", Nothing)
https://www.google.com/search?q=keyboard+buffer+size
Part of a program I am modifying involves communicating through a serial port using a proprietary library. Unfortunately, this library does not have the same SerialPort.DataReceived event that the System.IO.Ports namespace contains. In fact, it has no events whatsoever, however it does have two functions that can probably be used similarly:
Port.WaitForData(int time)
This function waits the given amount of time to recieve some previously specified strings over the port. It returns 1 for yes, received string, or 0 for no, did not recieve string, timed out.
Port.IsReceiveBufferEmpty()
This function returns a boolean of yes, the receive buffer is empty or no, the receive buffer contains data.
It seems to me I will have to create some thread to be continuously looping whenever the port is opened and do one of these two things:
For every loop, call WaitForData(some big number) with the specified strings it is looking for set to "", or vbCrLf, or something else that I can confirm it will recieve everytime data is sent. If it finds smoething, read it and write to a textbox. If WaitForData doesn't find anything, loop again.
For every loop, call IsReceiveBufferEmpty(), and if it isn't, read it and write to a textbox.
What the best way to go about implementing this? The first options seems potentially more efficient to me, although I know next to nothing about how these method work under the hood. Obviously I want to keep my form responsive when doing this, so how should I go about continuously looping without freezing the form but being able to read any incoming data?
Thanks for your help.
Perhaps not the most elegant solution, but you could use a BackgroundWorker to do the IO. e.g. something like this pseudo-code:
Public Class MyForm
Private _port As ProprietaryIOLibrary
Private WithEvents Worker As System.ComponentModel.BackgroundWorker
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
_port = New ProprietaryIOLibrary()
Worker = New System.ComponentModel.BackgroundWorker()
Worker.WorkerReportsProgress = True
Worker.WorkerSupportsCancellation = True
Worker.RunWorkerAsync()
End Sub
Private Sub ButtonCancel_Click(sender As Object, e As EventArgs) Handles ButtonCancel.Click
Worker.CancelAsync()
End Sub
Private Sub Worker_DoWork(sender As Object, e As DoWorkEventArgs) Handles Worker.DoWork
Do
If _port.WaitForData(1000) Then ' Adjust timeout depending on cancel responsiveness?
Dim data As String = _port.ReadDataAsString() ' ?
' Trigger the ProgressChanged event, passing the data
Worker.ReportProgress(0, data)
End If
If Worker.CancellationPending Then
Exit Do
End If
Loop
End Sub
Private Sub Worker_ProgressChanged(sender As Object, e As ProgressChangedEventArgs) Handles Worker.ProgressChanged
' Update the UI with the data received
' ProgressChanged is called on the UI thread, so no need to Invoke
Dim data As String = DirectCast(e.UserState, String)
TextBox1.Text &= data & vbCrLf
End Sub
Private Sub Worker_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles Worker.RunWorkerCompleted
TextBox1.Text &= "Complete!"
End Sub
End Class
My problem:
I need to detect what event (e.g. TextChanged) is currently being handled from within the handling Sub that's handling multiple objects/events. I cannot find examples online (though this may be a terminological issue). I already know how to detect the object but I do not know how to detect the event.
To reproduce:
In Visual Studio, in design mode, drag two text boxes onto a fresh form.
Use the following as is:
Code:
Private Sub TextBox_CombinedEvents(sender As Object, e As EventArgs) Handles TextBox1.Enter,
TextBox2.Enter, TextBox1.TextChanged, TextBox2.TextChanged,
TextBox1.Leave, TextBox2.Leave
Dim detectedEvent as String = "?"
If detectedEvent = "Enter" Then
MessageBox.Show("Text has been entered.")
Elseif detectedEvent = "TextChanged" Then
MessageBox.Show("Text has been changed.")
Elseif detectedEvent = "Leave" Then
MessageBox.Show("Text box has been left.")
Else
MessageBox.Show("Event not detected.")
End If
End Sub
I'm venturing into making my VB.NET application a little better to use by making some of the forms modeless.
I think I've figured out how to use dlg.Show() and dlg.Hide() instead of calling dlg.ShowDialog(). I have an instance of my modeless dialog in my main application form:
Public theModelessDialog As New dlgModeless
To fire up the modeless dialog I call
theModelessDialog.Show()
and within the OK and Cancel button handlers in dlgModeless I have
Private Sub OK_Button_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles OK_Button.Click
Me.DialogResult = System.Windows.Forms.DialogResult.OK
Me.Hide()
End Sub
Private Sub Cancel_Button_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Cancel_Button.Click
Me.DialogResult = System.Windows.Forms.DialogResult.Cancel
Me.Hide()
End Sub
and that seems to work fine.
The "X" button in the upper right is getting me, though. When I close the form with that button, then try to reopen the form, I get
ObjectDisposedException was unhandled. Cannot access a disposed object.
I feel like I'm most of the way there but I can't figure out how to do either of the following:
Hide that "X" button
Catch the event so I don't dispose of the object (just treat it like I hit Cancel)
Any ideas?
The class of this dialog is System.Windows.Forms.Form.
Catch the FormClosing event and, if the reason is UserClosing, set Cancel on the event to true.
Something like the following:
Private Sub Form1_FormClosing(sender as Object, e as FormClosingEventArgs) _
Handles Form1.FormClosing
if e.CloseReason = CloseReason.UserClosing then
e.Cancel = true
Me.Hide()
end if
End Sub
Use Me.Close() to hide the form. To open it, use the following snippet:
If theModelessDialog.IsDisposed Then
theModelessDialog = New dlgModeless
End If
dlgModeless.Show()
If this is saving data, then you'll need to figure some way of storing it (perhaps in a static variable/s in the form). This is the proper way to do what you are trying to achieve though.
You'll also have to forgive me if my VB is off, it's been a while.
the formclosing event allows me to do a managed exit of the form so I have included a question to confirm to exit. I also have a form flag bterminate to force the cancel where i want it to and therefore not ask the question. Thanks your suggestion helped me as well :)
Dim msgboxresponse As MsgBoxResult
If e.CloseReason = CloseReason.UserClosing Then
If Not Me.bTerminate Then
msgboxresponse = MsgBox("Are you sure you want to cancel adding?", _
MsgBoxStyle.Question + MsgBoxStyle.YesNo, Me.Text)
If msgboxresponse <> MsgBoxResult.Yes Then
e.Cancel = True
Return
End If
End If
End If
#John was Hiding the form in his code and the answers above provide a solution to that case. Often, though, you are not planning to use the form again, so you really do want the form to be Disposed. All Close related activities will be in one place if you Handle the FormClosing event using Me.FormClosing by adding it to anyCancel/Close/Exit code that you already have. e.g. in #John's case:
Private Sub Cancel_Button_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles Cancel_Button.Click, Me.FormClosing
....More code
Me.Dispose
End Sub
Note the use of the Me.Dispose instead of any existing Me.Close. If you leave the Me.Close you'll create an infinite loop. See this for the subtle differences between Close and Dispose.
Agree with handling the FormClosing event. Or change the properties on the form to hide the system X control.
I've tried everything and it didn't work
if you just want to close, without showing an messagebox, you will just need:
Private Sub FORM1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles Me.FormClosing
>e.Cancel = False
>FORM2.Show() (if you want to show another form)
End Sub
Hope this helps you...!