how to create button shortcuts in vbnet - vb.net

I tried creating keyboard shortcuts for my buttons.
Here is my code
Private Sub form_main_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles Me.KeyPress
If Keys.ControlKey + Keys.N Then
'btn_add.PerformClick()
addentry()
ElseIf Keys.ControlKey + Keys.E Then
'btn_edit.PerformClick()
editentry()
End If
End Sub
The problem is even when I press other buttons the function is still called. I also tried using form keydown property but the result is still the same.
additional info:
the functions addentry and editentry will just call the form_addedit
btn_add will call for addentry
btn_edit will call for editentry

First of all Keys.*** is just an enumeration. Every entry in it is just a number representing a key code. So you are currently just adding numbers together.
Keys.ControlKey is 17 and Keys.N is 78, so you're literally writing:
If 17 + 78 Then
Which will always return True because it's greater than 0.
To do what you ask you must check which key was pressed by checking the event arguments (EventArgs) passed to the event.
But since you are using the KeyPress event you cannot get the key enumeration out of the event args, so I recommend you to use the KeyDown event instead.
Private Sub form_main_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles Me.KeyDown
If e.Control = True AndAlso e.KeyCode = Keys.N Then
addentry()
ElseIf e.Control = True AndAlso e.KeyCode = Keys.E Then
editentry()
End If
End Sub

If you put an ampersand in the .Text property of the button, Alt+key will fire the button, for example B&utton1 will fire with Alt+u.

Related

KeyPress handler being applied to wrong control

I have a .KeyPress event handler which is supposed to limit/control the keys which can be entered in a specific TextBox (more precisely, any of the textboxes in a specific DataGridViewTextBoxColumn)
Private Sub dgv_EditingControlShowing(sender As Object, e As DataGridViewEditingControlShowingEventArgs) Handles dgv.EditingControlShowing
If dgv.CurrentCell.ColumnIndex = myColumn.Index And Not e.Control Is Nothing Then
DirectCast(e.Control, TextBox).CharacterCasing = CharacterCasing.Upper
DirectCast(e.Control, TextBox).MaxLength = 10
AddHandler DirectCast(e.Control, TextBox).KeyPress, AddressOf controlKeyPress
End If
End Sub
Private Sub controlKeyPress(ByVal sender As System.Object, ByVal e As System.Windows.Forms.KeyPressEventArgs)
Dim charEnum As Integer = CUInt(Microsoft.VisualBasic.Asc(e.KeyChar))
Dim tb As TextBox = DirectCast(sender, TextBox)
Select Case charEnum
Case 8
' Always permit the keying of backspace (no suppression)
Case 42
' Permit the keying of asterisk (42) but only if it is the first character (otherwise, suppress the key press)
If Not tb.SelectionStart = 0 Then e.KeyChar = ""
Case 46
' Permit the keying of period (46) but only if it is not the first character and the first character is not an asterisk (otherwise, suppress the key press)
If tb.SelectionStart = 0 OrElse tb.Text.FirstOrDefault = "*" Then e.KeyChar = ""
Case 65 To 90, 97 To 122
' Permit the keying of upper-case alpha (65-90) and lower-case alpha (97-122) as long as the first character is not an asterisk (otherwise, suppress the key press)
If tb.Text.FirstOrDefault = "*" Then e.KeyChar = ""
Case Else
' All other characters, suppress the key press (set the KeyChar to nothing)
e.KeyChar = ""
End Select
End Sub
What's weird is, the same handler seems to be getting applied to other TextBox controls in the DataGridView, but in a different column (i.e. not in myColumn) Which is strange because I have a specific condition in the EditingControlShowing event that specifies that the handler should only be applied if the .ColumnIndex of the control matches that of the column to which it should apply (i.e. If dgv.CurrentCell.ColumnIndex = myColumn.Index) So I'm not sure why the same handler is being applied to a TextBox that's not in myColumn?
Also, it doesn't appear to be consistent - when I initially load the DGV, the other textboxes have no restrictions on them (as expected); when I go to edit a row, and the handler is applied to myColumn (as expected), the same handler also seems to be applied immediately to any other textboxes in the same row (but in debugging, I can't seem to trap where this happens, I can only trap the application of the event handler to the correct control)
I'm not sure if I should have a RemoveHandler call somewhere - and if so, where, because I can't find the point at which the handler is being applied erroneously in the first place?
I tried this but it doesn't seem to have any effect (again, while debugging, when I click in a TextBox in myOtherColumn, it does hit that line, but the restriction is still imposed anyway?)
Private Sub dgv_EditingControlShowing(sender As Object, e As DataGridViewEditingControlShowingEventArgs) Handles dgv.EditingControlShowing
If dgv.CurrentCell.ColumnIndex = myColumn.Index Then
DirectCast(e.Control, TextBox).CharacterCasing = CharacterCasing.Upper
DirectCast(e.Control, TextBox).MaxLength = 10
AddHandler DirectCast(e.Control, TextBox).KeyPress, AddressOf controlKeyPress
ElseIf dgv.CurrentCell.ColumnIndex = myOtherColumn.Index Then
RemoveHandler DirectCast(e.Control, TextBox).KeyPress, AddressOf controlKeyPress
End If
End Sub
All suggestions welcome!
The DataGridView control will reuse an editing control if it can to improve performance. You should keep a reference to the editing control from the EditingControlShowing event handler and use a RemoveHandler statement in the CellEndEdit event handler.
Actually, you may not need to keep the reference. You may be able to use the EditingControl property of the grid. Try that first.
EDIT:
I have just tested for myself and the EditingControl property of the grid is Nothing when the CellEndEdit event is raised, so my second suggestion above is out. That means that you need to retain a reference to the editing control from the EditingControlShowing event handler. If you're going to do that though, you may as well not use AddHandler and RemoveHandler. It's simpler to declare the field WithEvents and then use a Handles clause on the event handler, e.g.
Private WithEvents editingControl As TextBox
Private Sub DataGridView1_EditingControlShowing(sender As Object, e As DataGridViewEditingControlShowingEventArgs) Handles DataGridView1.EditingControlShowing
If DataGridView1.CurrentCell.ColumnIndex = 0 Then
editingControl = DirectCast(e.Control, TextBox)
End If
End Sub
Private Sub DataGridView1_CellEndEdit(sender As Object, e As DataGridViewCellEventArgs) Handles DataGridView1.CellEndEdit
editingControl = Nothing
End Sub
Private Sub EditingControl_KeyPress(sender As Object, e As KeyPressEventArgs) Handles editingControl.KeyPress
Console.WriteLine(e.KeyChar)
End Sub
That code will assign the editing control to the field if and only if the cell being edited is in the first column. Any control assigned to that field will have its events handled and the field is always reset when an editing session ends.

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

Tabindex ignored when Enabled set to true in a validating event handler

I have three text boxes.
txtBox1 - Enabled=True - TabIndex=1
txtBox2 - Enabled=False - TabIndex=2
txtBox3 - Enabled=True - TabIndex=3
I have an event handler that sets Enabled=True for txtBox2 when txtBox1 is validating. My problem is that the cursor doesn't go to txtBox2 after leaving txtBox1, it jumps to txtBox3.
Private Sub Example(ByVal sender As Object, _
ByVal e As System.ComponentModel.CancelEventArgs) Handles txtBox1.Validating
txtBox2.Enabled = True
End Sub
Is there a way to have the TabIndex respected after a field is enabled in a validating event handler? I can use Select() but that breaks reverse tabbing through the fields (SHIFT+TAB).
Thank you!
It is a simple chicken-and-egg problem. The Validating event fires because the textbox lost the focus. Which happened because you tabbed to the next control. Which was of course txtBox3, enabling txtBox2 does not change that.
Easy to fix, the ActiveControl property tells you which control is active. So write it like this:
txtBox2.Enabled = True
If Me.ActiveControl Is txtBox3 Then txtBox2.Focus()
Back-tabbing works properly as long as you have more than 3 controls that can receive the focus.
Ok so I posted the wrong solution so here is the correct one. Hans got me thinking and this is what I came up with that works for my needs:
Public Class Form1
' First you have to declare a variable
Dim m_TabForward As Boolean
' Capture the keystrokes to check for direction.
Private Sub GetTabDirection(ByVal sender As Object, _
ByVal e As PreviewKeyDownEventArgs) Handles _
txtBox1.PreviewKeyDown
If (e.KeyCode = Keys.Tab AndAlso e.Modifiers = Keys.Shift) Then
m_TabForward = False
ElseIf e.KeyCode = Keys.Tab Then
m_TabForward = True
End If
End Sub
' Enable the next textbox and select it. Because SelectNextControl
' takes a direction argument you don't have a problem with reversing order
' like you would using txtBox2.Select()
Private Sub ValidateFields(ByVal Sender As Object, _
ByVal e As System.ComponentModel.CancelEventArgs) _
Handles txtBox1.Validating
Dim ctl As Control = Sender
If Sender Is txtBox1 Then
txtBox2.Enabled = True
End If
SelectNextControl(ctl, m_TabForward, True, True, True)
End Sub
End Class

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