Why does windows play a beep sound on KeyDown, but not on DoubleClick? - vb.net

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

Related

Prevent Repetitive KeyDown Code From Running (vb.net)

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.

Why DateTimePicker won't trigger keyDown and KeyPress events with the tab key?

Fellows, I am having this problem - the DateTimePicker won't trigger KeyDown and KeyPress events with the tab key (other keys are working fine, and the keyUp event as well, although it triggers after "arriving" at the DateTimePicker after pressing tab at the previous control focused). I'm using .net 4.6.1, Visual Basic and VS 2017.
What I'm trying to do -> Go to month and year directly on DateTimePicker in C# (Go to month and year directly on DateTimePicker)
Code I'm using:
Private Sub DateTimePicker1_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles DateTimePicker1.KeyDown
If e.KeyCode = Keys.Tab Then
e.Handled = True
MsgBox("TAB DOWN")
End If
End Sub
Private Sub DateTimePicker1_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles DateTimePicker1.KeyPress
e.Handled = True
MsgBox("tab press")
End Sub
Private Sub DateTimePicker1_KeyUp(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles DateTimePicker1.KeyUp
If e.KeyCode = Keys.Tab Then
MsgBox("TAB UP")
e.Handled = True
End If
End Sub
Any clues?
The Tab key is used for navigation. Moving the focus from one control to another. So your KeyDown event handler can never see it, the keystroke is intercepted and used before that. You could subscribe the PreviewKeyDown event and set the e.IsInputKey = true as a workaround, check the MSDN sample code in the linked article for code.
But it is the wrong event to use anyway, you'd still want this to work when the user changes focus with the mouse instead of the keyboard. So use the Enter event instead.
Do beware that both approaches have the same problem, the focus might already be on the month part from previous usage of the control so now your code will incorrectly move it to the year part. And you can't find out what part has the focus, that is up a creek without a good paddle. A very ugly workaround for that is to change the Format property, and back, that forces the control to re-create the control window and that always resets the focus. Use BeginInvoke() to run that code. Perhaps more constructively, consider to just not display the day if you are only interested in month+year, CustomFormat property.
Sample code that implements the focus hack:
Private Sub DateTimePicker1_Enter(sender As Object, e As EventArgs) Handles DateTimePicker1.Enter
Me.BeginInvoke(
New Action(Sub()
'' Hack to reset focus
DateTimePicker1.Format = DateTimePickerFormat.Long
DateTimePicker1.Format = DateTimePickerFormat.Short
DateTimePicker1.Focus()
SendKeys.Send("{Right}")
End Sub))
End Sub
It's not the right answer to this question, although it helps as well. If you want to just make the tab behave as the right key when inside a DateTimePicker, a good (sketchy) way to do is:
Private i = 2
Protected Overrides Function ProcessTabKey(ByVal forward As Boolean) As Boolean
Dim ctl As Control = Me.ActiveControl
If ctl IsNot Nothing AndAlso TypeOf ctl Is DateTimePicker And i <> 0 Then
SendKeys.Send("{Right}")
i -= 1
Return True
End If
i = 2
Return MyBase.ProcessTabKey(forward)
End Function
You need to override ProcessCmdKey function
Private isTab As Boolean = False
Private isShiftTab As Boolean = False
Protected Overrides Function ProcessCmdKey(ByRef msg As Message, ByVal keyData As Keys) As Boolean
If keyData = Keys.Tab Then
isTab = True
'Do something with it.
Else
isTab = False
End If
Return MyBase.ProcessCmdKey(msg, keyData)
End Function

Vb.net button still allows click while disabled

My button is responding to clicks while disabled.
Private Sub btnGenerate_Click(sender As Object, e As EventArgs) Handles btnGenerate.Click
btnGenerate.Enabled = False
Me.Cursor = Cursors.WaitCursor
'Do a bunch of operations
Me.Cursor = Cursors.Default
btnGenerate.Enabled = True
End Sub
It takes about 5-10 seconds to process the stuff I'm doing in the background. During that 5-10 seconds the button is greyed out, but if I click it a second time, then it performs the operational stuff a second time after finishing the first.
I'm missing something here. How can I prevent button from allowing interaction until operations are finished?
Dim Working as boolean=false
Private Sub btnGenerate_Click(sender As Object, e As EventArgs) Handles btnGenerate.Click
if Working=true then exit sub
' Your Work Process
Work()
End Sub
sub Work()
Working=True
' Work code
Working=False
end sub
this should prevent the double click
With VS2012, Async Work is very easy to use (compared to previous versions...).
The problem is the UI thread is not letting go. Without seeing what 'work' is actually going on, I can not explain why. I am hoping nothing that re enables the button...
However, Async will allow release of the UI thread and the enabled = false should take effect. Try something like this:
Private Async Sub btnGenerate_Click(sender As Object, e As EventArgs) Handles btnGenerate.Click
btnGenerate.Enabled = False
Dim t As New Task(Sub() MyWorkLoad())
t.Start()
Await t
btnGenerate.Enabled = True
End Sub
Private Sub MyWorkLoad()
'do your work here
'for testing
Dim time As Date = Now
Do While True
If DateAdd(DateInterval.Second, -5, Now) > time Then Exit Do
Loop
End Sub
This did work for me...

Why does MessageBox has an affect on SuppressKeyPress?

Here is a peculiar situation in vb. I was messing the with SuppressKeyPress property and I found out something strange.
The Situation
Lets say I have a text box called txtName, and I want the name to be without any numbers, and when a number is inserted, a MessageBox will pop out and report an error.
Private Sub TextBox1_KeyDown(sender As Object, e As System.Windows.Forms.KeyEventArgs) Handles txtName.KeyDown
If e.KeyCode >= Keys.D0 And e.KeyCode <= Keys.D9 And _
e.Modifiers <> Keys.Shift Then
e.SuppressKeyPress = True
MsgBox("Error - A Number has been pressed")
'The number appeared in the text box.
End If
End Sub
In this case, for some strange reason, if I type a number, it will be written in the text box, although I suppressed the keypress.
What I found out is that if I remove the MsgBox line, the number will not appear in the text box.
Private Sub TextBox1_KeyDown(sender As Object, e As System.Windows.Forms.KeyEventArgs) Handles txtName.KeyDown
If e.KeyCode >= Keys.D0 And e.KeyCode <= Keys.D9 And _
e.Modifiers <> Keys.Shift Then
e.SuppressKeyPress = True
'The number did not appear in the text box.
End If
End Sub
Question
What is going on? Why the MsgBox "allows" the key to be pressed? Why it has any effect on the SuppressKeyPress property?
This is a pretty typical side-effect of using MessageBox, it can cause lots of tricky to diagnose problems. The SuppressKeyPress property is implemented by searching the message queue for any keypress events and removing them. But that can happen only after your event handler completes.
Trouble is, it isn't completing any time soon. Your MsgBox() call is taking over and it starts pumping a message loop by itself. Like dialogs do, the equivalent of calling the infamous DoEvents() method. And it will readily dispatch the pending messages in the message queue, including those keypress messages that were supposed to be suppressed.
A band-aid for such a problem is to display the message box later, after the event handling is completed and Winforms had a chance to purge the keypress messages. Elegantly done by using the Control.BeginInvoke() method:
Private Sub TextBox1_KeyDown(sender As Object, e As KeyEventArgs) Handles TextBox1.KeyDown
If e.KeyCode >= Keys.D0 And e.KeyCode <= Keys.D9 And e.Modifiers <> Keys.Shift Then
e.SuppressKeyPress = True
Me.BeginInvoke(New Action(Sub() MsgBox("Error - A Number has been pressed")))
End If
End Sub
But the real fix is to use the correct event. You should always use the KeyPress event instead for this kind of filtering. That also avoids the very painful dependency on the user's active keyboard layout that's always present when you use KeyDown. Fix:
Private Sub TextBox1_KeyPress(sender As Object, e As KeyPressEventArgs) Handles TextBox1.KeyPress
If e.KeyChar >= "0"c AndAlso e.KeyChar <= "9"c Then
e.Handled = True
MsgBox("I don't like digits")
End If
End Sub
But then again, don't use message boxes to slap the user for making a simple mistake.
I found a little 'hack' for those who dont want to mess with Invoking methods, starting new threads manually etc.
My (old) code that didnt work was
Private Sub textEditKeyPress(ByVal sender As Object, ByVal e As KeyEventArgs)
If e.KeyValue = 220 Then
e.SuppressKeyPress = True
MessageBox.Show("\ not allowed. Use / instead.")
End If
End Sub
By changing the code to
Private Async Sub tEditDropBoxFolderName_EditValueChanged(ByVal sender As Object, ByVal e As KeyEventArgs)
If e.KeyValue = 220 Then
e.SuppressKeyPress = True
Await Task.Delay(100)
MessageBox.Show("\ not allowed. Use / instead.")
End If
End Sub
Everything works fine, and i havent found any side effects by using this.
(Code was translated by c#, maybe it needs some modifications for vb.)
So I tested your code and I can reproduce this behavior. I think the reason as to why this is happening is because the MsgBox (a modal dialog) will call Application.DoEvents resulting in the message being processed.
By replacing
MsgBox("Error - A Number has been pressed")
with
Application.DoEvents
you'll get the same result.
You should read Hans Passant's answer in the following SO post. He provides a good explanation about the relationship between ShowDialog (ref. MsgBox) and DoEvents.
Use of Application.DoEvents()

KeyUp event works in first winform, but not in second, why?

I have a winform program where I am trying to include PF functionality in tandem with button/mouse-click functionality. On the first screen that opens in the application, the keyup event works. I changed the KeyPreview property to True and wrote the following code:
Private Sub Vehicle_KeyUp(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles Me.KeyUp
If e.KeyCode = Keys.F1 Then
CarrierOn = True
Call Display_CarrierDetail_Screen()
ElseIf e.KeyCode = Keys.F9 Then
Call Display_History_Screen()
ElseIf e.KeyCode = Keys.F6 Then
Call Display_County_Screen()
ElseIf e.KeyCode = Keys.F5 Then
Call Instant_Observation("Vehicle")
End If
End Sub
The above works.
However, on opening the second form, I wrote very similar code, also changed that form's keypreview property to True, and tested the PF key - nothing happens.
Why would it work on the first form, but not on the second? The second does have the focus when I am pressing the PF key.
Thank you, laurie