how to handle multiple KeyPress events - vb.net

I need my win form - vb.net, to detect if Control + P is being pressed as well as Control + Shift + P as well as just the letter P being pressed.
i have ready how this should be done, and then wrote it into my application, however i can't get it to work so i assume im doing something fundamentally wrong.
my code
Private Sub Form1_KeyUp(sender As Object, e As KeyEventArgs) Handles DataGridView1.KeyUp, MyBase.KeyDown
If e.KeyCode = Keys.F9 Then
System.Diagnostics.Process.Start("calc.exe")
End If
If e.KeyCode = (Keys.P AndAlso Keys.ControlKey AndAlso Keys.ShiftKey) Then
If PrintBatchStickersToolStripMenuItem.Enabled = False Then Exit Sub
If DataGridView1.Rows.Count = 0 Then Exit Sub
Dim rowIndex As Integer = 0
rowIndex = DataGridView1.CurrentRow.Index
PrintAllMatchingProductCodeToolStripMenuItem_Click(sender, e)
ElseIf e.KeyCode = (Keys.P AndAlso Keys.ControlKey) Then
If PrintBatchStickersToolStripMenuItem.Enabled = False Then Exit Sub
If DataGridView1.Rows.Count = 0 Then Exit Sub
Dim rowIndex As Integer = 0
rowIndex = DataGridView1.CurrentRow.Index
PrintBatchQTYToolStripMenuItem_Click(sender, e)
ElseIf e.KeyCode = Keys.P Then
If PrintBatchStickersToolStripMenuItem.Enabled = False Then Exit Sub
If DataGridView1.Rows.Count = 0 Then Exit Sub
Dim rowIndex As Integer = 0
rowIndex = DataGridView1.CurrentRow.Index
PrintSingleStickerToolStripMenuItem_Click(sender, e)
End If
End Sub
if i remove the brackets i can get it to detect the P key being pressed, but never the Control and the Shift or a combination of them two.
i added this to the KeyUp event as from my testing if i did this on keydown, and a user held down the keys, the code would loop over and over printing multiple copies of the sticker. and i need the code to only execute once.
keypress from my understanding wont handle the control and shift keys from what i could understand.
am i going wrong with the keyup, because the keys could be getting released at separate times? and if i cant use keyup how to do i handle not printing multiple times on keydown?

You need to use KeyData rather than KeyCode and you need to combine your Keys values properly:
Select Case e.KeyData
Case Key.P
'P was pressed without modifiers.
Case Keys.Control Or Key.P
'Ctrl+P was pressed without other modifiers.
Case Keys.Control Or Keys.Shift Or Keys.P
'Ctrl+Shift+P was pressed without other modifiers.
End Select
It may seem odd to use Or rather than And but this is a bitwise operation, not a Boolean operation. If you understand how bitwise logic works, which you should, then it's obvious why Or is used.
As an alternative:
If e.KeyCode = Keys.P AndAlso Not e.Alt Then
If e.Control Then
If e.Shift Then
'Ctrl+Shift+P was pressed without other modifiers.
Else
'Ctrl+P was pressed without other modifiers.
End If
Else
'P was pressed without modifiers.
End If
End If

Related

Visual Studio ComboBox won't cycle through items with up/down arrows once users types

I have a userform with a dropdown combobox that is filled with items when the form is opened. If the box has focus, the up/down arrow keys will cycle through the items (as it should), or the user can select from the drop down list and still be able to use the up/down arrows from there. If the user starts typing, the box is set to append from the list items, but that disables the up/down arrow keys from cycling through ALL the items. At that point, it only cycles through the items that start with whatever the user typed. I would like it to erase the typed data and cycle through all the combobox items (starting at the index was whatever was appended) just like it does in the VBA version.
Even if the user types something, then selects a completely different item from the dropdown, the next time up/down keys are pressed, it only goes back to whatever was typed.
I attempted to handle this in the arrow up/down keydown (or keyup) event as follows:
Private Sub SeriesBox_KeyDown(sender As Object, e As KeyEventArgs) Handles SeriesBox.KeyDown
Dim CurIndex As Integer = SeriesBox.FindStringExact(SeriesBox.Text)
Select Case e.KeyCode
Case Keys.Down
SeriesBox.Text = String.Empty
SeriesBox.SelectedIndex = CurIndex + 1
Case Keys.Up
SeriesBox.Text = String.Empty
SeriesBox.SelectedIndex = CurIndex - 1
End Select
End Sub
The arrow keys don't actually enter this sub, so I tried adding:
Protected Overrides Function ProcessCmdKey(ByRef msg As Message, ByVal keydata As Keys) As Boolean
If keydata = Keys.Right Or keydata = Keys.Left Or keydata = Keys.Up Or keydata = Keys.Down Then
OnKeyDown(New KeyEventArgs(keydata))
ProcessCmdKey = True
Else
ProcessCmdKey = MyBase.ProcessCmdKey(msg, keydata)
End If
End Function
But this didn't seem to make a difference. Is there something I am missing?
This may work:
First set you combobox's AutoCompleteMode and AutoCompleteSouce to None
Then paste this to your code:
Private Sub SeriesBox_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles SeriesBox.TextChanged
For Each it As String In SeriesBox.Items
If Not SeriesBox.Text = it Then
If it.Contains(SeriesBox.Text) Then
Dim length As Integer = SeriesBox.Text.Length
SeriesBox.Text = ""
SeriesBox.Text = it
SeriesBox.Select(length, SeriesBox.Text.Length - length)
SeriesBox.SelectedItem = SeriesBox.Text
End If
End If
Next
End Sub

VB.net Key Combinations doesn't work

The following code works for Ctrl + D, F2, and F7, but does not work for Ctrl + A. Anyone know what the issue could be?
Private Sub AgentSetupForm_KeyDown(sender As Object, e As System.Windows.Forms.KeyEventArgs) Handles MyBase.KeyDown
Select Case e.KeyCode
' Ctrl + A
Case Keys.A And (e.Control And Not e.Shift And Not e.Alt)
Dim f As New InvestorAccountsForm
f.MdiParent = TraderoomMDI
f.Show()
' Ctrl + D
Case Keys.D And (e.Control And Not e.Shift And Not e.Alt)
Dim f As New InvestorAddressForm
f.MdiParent = TraderoomMDI
f.Show()
' F2, F7
Case Keys.F2 'And (e.Shift And Not e.Control And Not e.Alt)
Me.Text = "F2 Test"
Case Keys.F7
stlFormMode.Text = "Edit"
End Select
End Sub
The accepted answer is totally incorrect.
VB.NET is not C#. So if And is used in a context with Booleans (on the both sides) VB.NET automatically switches to logical And as opposed to bitwise And. So this is not a problem at all. I just run the OP code and it ran fine - Ctrl + A combination was captured successfully. Furthermore, if a key combination means something for the OS (or some other component) it doesn't mean it won't be captured in our application.
The difference between And and AndAlso is that the first evaluates the Boolean expressions from left to right all along to the end, even though some of the expressions evaluates to False. The later evaluates expressions UNTIL it reaches False or to the end - that is short-circuiting.
To improve the readability of your code you can test for a particular key at the Case statement. Then insert an If block within the particular case to check the condition of control keys:
Select Case e.KeyCode
Case Keys.A
If e.Control AndAlso Not (e.Shift OrElse e.Alt)
Dim f As New InvestorAccountsForm
f.MdiParent = TraderoomMDI
f.Show()
End If
'
'
End Select
Or you can factor out the If block to embrace the Select Case for all the cases concerning the condition e.Control AndAlso ....
Unfortunately I cannot answer your question as it seems your issue cannot be (easily) reproduced. Based on the comments, it is more likely to be hardware or driver related problem than the Case statements in your code.
EDIT:
As it seems you have an issue with the CTRL + A combination, you could try overriding ProcessCmdKey and see if that works. The method should be called before the keys are handled and/or translated by the .NET environment.
Protected Overrides Function ProcessCmdKey(ByRef msg As System.Windows.Forms.Message, keyData As System.Windows.Forms.Keys) As Boolean
If keyData = (Keys.Control Or Keys.A) Then
...your code here...
Return True
End If
Return MyBase.ProcessCmdKey(msg, keyData)
End Function
If the other combinations work you can keep them in the KeyDown event.
EDIT 2:
#BozhidarStoinev is right about what he says; I was wrong saying that you're currently using the bitwise And.
Apparently it works using And or AndAlso in Select Case-statements due to how boolean values are converted in VB.NET. In this case as long as the first operator is just And you'll be fine. The following AndAlsos below are used because they perform shortcircuiting, which is faster due to that AndAlso only evaluates the right side if the left side is True.
The following:
Keys.A And (e.Control AndAlso Not e.Shift AndAlso Not e.Alt)
converts to either:
(65 And True) = (65 And -1) = 65 'I added parentheses for improved readability
or:
(65 And False) = (65 And 0) = 0
It is not certain that multi-statement cases will work every time however. It might work for keys, but as <anything> And False equals to zero there is a chance for unexpected results if you are checking for 0 in another Case.
I know this question is old
But it works easily
Protected Overrides Function ProcessCmdKey(ByRef msg As Message, keyData As Keys) As Boolean
Select Case keyData
Case (Keys.Control + Keys.Shift + Keys.Alt + Keys.B)
MsgBox("Control + Shift + Alt + B")
Return True
Case Keys.Control + Keys.Alt + Keys.C
MsgBox("Control + Alt + C")
Return True
End Select
Return MyBase.ProcessCmdKey(msg, keyData)
End Function

ComboBox SelectChangeCommited fires off with first keyboard input

I have a ComboBox. My issue is when I click on element 1, then start typing the name of a element 2, the first letter of my element 2 gets added. This only happens on the key down after a click event.
Process:
Click on ComboBox
Type in a name
Click on the elements name - element gets added to my grid
Type in another name - ISSUE - first element that matches the first letter I input gets added to the grid
Clicking and the enter key on a single selected element works fine.
My AutoCompleteMode is set to Append and AutoCompleteSource is ListItems
The solution I thought to control a click is in my SelectionChangeComitted function. If I can control the flag isCLick to only be set true when we know that a element from the combo box is clicked by the mouse. MouseClick will return true if the user clicks on the overall ComboBox therefore will always be true if we're dealing with this particular ComboBox. Since the parameter is EventArgs I cannot directly cast it to MouseEventArgs, would be great if I could.
To sum up what my problem is: I need to replace isClick boolean in my SelectionChangeCommitted to validate if the SelectionChangeCommitted was triggered by a click or not.
Is that possible in this case? Would there be any alternatives to bypass this issue?
I have event handler code below, the sequence of the call is: MouseClick -> PreviewKeyDown -> SelectionChangeCommitted -> SelectedValueChanged
http://pastebin.com/9zWGHbWE
AddHandler CType(ctl, ComboBox).PreviewKeyDown, Sub(sender As Object, e As PreviewKeyDownEventArgs)
If e.KeyCode = Keys.Down OrElse e.KeyCode = Keys.Up OrElse e.KeyCode = Keys.Right OrElse e.KeyCode = Keys.Left Then
e.IsInputKey = False
AddToGrid = False
isArrowKey = True
isClick = False
ElseIf e.KeyCode = Keys.Enter OrElse e.KeyCode = Keys.Tab Then
AddToGrid = True
isArrowKey = False
isClick = False
'Check if row already exists before insert a new one.
If Not RowExists(DirectCast(ctl, Control).Tag, DirectCast(ctl, ComboBox).Text.ToUpper()) Then AppendGrid(sender)
Else
AddToGrid = False
isArrowKey = False
isClick = True
End If
End Sub
AddHandler CType(ctl, ComboBox).SelectedValueChanged, Sub(sender As Object, e As EventArgs)
'MessageBox.Show("SelectionValueChanged")
If CType(sender, ComboBox).SelectedValue Is Nothing Then Return
If Not AddToGrid Then Return
'Check if row already exists before insert a new one.
If Not RowExists(DirectCast(ctl, Control).Tag, DirectCast(ctl, ComboBox).SelectedValue.ToString().ToUpper()) Then
AppendGrid(sender)
End If
End Sub
AddHandler CType(ctl, ComboBox).SelectionChangeCommitted, Sub(sender As Object, e As EventArgs)
'MessageBox.Show("SelectionChangedCommitted")
If CType(sender, ComboBox).SelectedValue Is Nothing Then Return
If isArrowKey Then Return 'if an arrow was used, then don't add it to the grid
If isClick Then
AddToGrid = True
End If
'Check if row already exists before insert a new one.
If Not RowExists(DirectCast(ctl, Control).Tag, DirectCast(ctl, ComboBox).SelectedValue.ToString().ToUpper()) AndAlso AddToGrid Then
AppendGrid(sender)
End If
End Sub
AddHandler CType(ctl, ComboBox).MouseClick, Sub(sender As Object, e As MouseEventArgs)
'MessageBox.Show("MouseClick")
isClick = True
AddToGrid = True
End Sub
It seems like everything here works fine for you except when a click is executed. I would recommend you to reset the click after you use it. Try the code below in your SelectionChangeCommitted.
If isClick Then
AddToGrid = True
isClick = false
End If

Disabling buttons one at a time

My partner and I are trying to figure out how to disable a button one at a time. We're making a program in Visual Studio Express 2012 that will disable a button once it is typed in a textbox. For example, we have five letters placed seperately on five different buttons If we were to put the letter "D" on the textbox, the button that contains that specific letter will be disabled. We're using the code
If e.KeyCode = Keys.D Then
Button1.Enabled = False
End If
Now that works, BUT if there were two or more buttons that has the same letters, all of them disables because then the code will be :
If e.KeyCode = Keys.D Then
Button1.Enabled = False
End If
If e.KeyCode = Keys.D Then
Button2.Enabled = False
End If
My problem would be in what way could I distinguish those buttons that has the same letter from one another so that when I type the letter on a textbox, only one button disables and when I type it in again, another button containing the same letter disables. Thanks!
Assuming all of the buttons are not in child panels:
If e.KeyCode = Keys.D Then
For Each b As Button In Me.Controls.OfType(Of Button)()
If b.Text.Contains("D") AndAlso b.Enabled Then
b.Enabled = False
Exit For
End If
Next
End If
This will recursively iterate all controls on the form looking for buttons and disable them based on the characters and number of characters entered into the textbox:
Private Sub textBox1_TextChanged(sender As Object, e As System.EventArgs)
Dim text As String = TryCast(sender, TextBox).Text.ToLower()
For Each b As Button In GetAllButtons(Me)
b.Enabled = True
Next
For Each c As Char In text
Dim count As Integer = text.Count(Function(cc) cc = c)
For i As Integer = 0 To count - 1
For Each b As Button In GetAllButtons(Me).Where(Function(x) x.Text.ToLower().Contains(c.ToString())).Take(count).ToList()
b.Enabled = False
Next
Next
Next
End Sub
Private Function GetAllButtons(control As Control) As List(Of Button)
Dim allButtons As New List(Of Button)()
If control.HasChildren Then
For Each c As Control In control.Controls
allButtons.AddRange(GetAllButtons(c))
Next
ElseIf TypeOf control Is Button Then
allButtons.Add(TryCast(control, Button))
End If
Return allButtons
End Function

Call a Function although the form is Minimized or Unselected on KeyDown in VB.NET

Well I have this code:
Public Sub FuncKeysModule(ByVal value As Keys)
'Check what function key is in a pressed state, and then perform the corresponding action.
Select Case value
Case Keys.F8
spam.Enabled = True
TabControl1.SelectedIndex = 0
state = "Activo"
Case Keys.F9
spam.Enabled = False
TabControl1.SelectedIndex = 1
state = "Parado"
End Select
End Sub
Private Sub frmMain_KeyDown(sender As Object, e As KeyEventArgs) Handles MyBase.KeyDown
If e.KeyValue = Keys.F1 Or Keys.F2 Or Keys.F3 Or Keys.F4 Or Keys.F5 Or Keys.F6 Or Keys.F7 Or Keys.F8 Or Keys.F9 Or Keys.F10 Or Keys.F11 Or Keys.F12 Then
FuncKeysModule(e.KeyValue)
e.Handled = True
End If
End Sub
When I press F8 or F9 the Form call the function that you can see. But I have a dude, how can I do call it if the Form is Minimized or Unselected (if I have TopMost on True)?
Thanks. ;)
I have this code:
http://pastebin.com/Vr8UjVnF
But everytime that I run my project an Excepcion occur, Could not set keyboard hook
What can I do? :S
It's posible, you have tu uncheck the "Enable Visual Studio hosting process" on the Properties -> Debug table of the project, and use this code:
http://pastebin.com/Vr8UjVnF
It's works :)