Why DateTimePicker won't trigger keyDown and KeyPress events with the tab key? - vb.net

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

Related

how to create button shortcuts in vbnet

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.

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

Button of UserControl vanishes when program is debugged

My UserControl button disappears when I debug my program. I have checked the code including the designer.vb code countless times there's nothing that makes the button .enabled = false or .visible = false. Any ideas why this is happening?
On my UserControl:
Private Sub btn_Begin_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btn_Begin.Click
Start_Race()
End Sub
Public Sub Start_Race()
TimeNow(Past_Time)
TimeNow(Start_Time)
lbl_Start_Time_Driver.Text = Past_Time
btn_Begin.BackColor = Color.Green
btn_Begin.Text = "Started!"
End Sub
Public Property Active_bool As Boolean
Get
Return btn_Begin.Visible
End Get
Set(ByVal value As Boolean)
btn_Begin.Visible = value
End Set
End Property
On Form1:
Private Sub btn_Start_All_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btn_Start_All.Click
Dim allActiveUserControls = From uc_Index In Controls.OfType(Of LapTimerGUI)()
Where uc_Index.Active_bool
For Each User_Control In allActiveUserControls
User_Control.Start_Race()
Next
End Sub
I do Google my head off before I post my ridiculous questions here btw :)
This is strange. Does any MsgBoxes pop up if you add this code to your UserControl:
Private Sub UserControl_ControlRemoved(sender As Object, e As System.Windows.Forms.ControlEventArgs) Handles Me.ControlRemoved
MsgBox("Control Removed!")
End Sub
Private Sub Button2_EnabledChanged(sender As Object, e As System.EventArgs) Handles Button2.EnabledChanged
MsgBox("EnabledChanged!")
End Sub
If so, then you can add a breakpoint to these MsgBoxes and lock at the CallStack (CTRL+L) from where it triggers.
Btw: If the control is removed somehow, .PerformClick() still triggers (for me). Thus I bet, that the control is somehow disabled (Enabled = False).
Lastly, if any container of the button (such as your UserControl) is disabled, the button will be disabled too,
After lots of playing around I finally found the problem!
The value was set to =False in my properties. I'm so blonde! Thanks guys for the help ^_^/
Public Property Active_bool As Boolean
Get
Return btn_Begin.Visible
End Get
Set(ByVal value As Boolean)
btn_Begin.Visible = value
End Set
End Property
Although, Something sets the values to =False ever now and then. Very annoying :3
And I can not set the value to =True in the properties... Only in the hidden designer code...

NumericUpDown.value is not saved in the User Settings

I have a NumericUpDown Control on a form. In the Application Settings / Properties Binding, for the value parameter, i can't select my USER setting called : Heures (Integer / User).
I tried to save the value by this way :
Private Sub NumericUpDownHeures_Leave(sender As System.Object, e As System.EventArgs) Handles NumericUpDownHeures.Leave
My.Settings.Heures = NumericUpDownHeures.Value
My.Settings.Save()
End Sub
But it's not saved.
No problem for other settings (String / User). But i don't understand why the settings (Integer / User) are not saved.
Please help, Thanks.
As you are putting "NumericUpDown1.Value" you have to set the value at My.Settings.Heures to decimal.
In Form1_Load add:
NumericUpDownHeures.Value = My.Settings.Heures
and add to the event listener for your button or other widget:
My.Settings.Heures = NumericUpDownHeures.Value
I would guess the issue is that the Leave event is not being fired as you expect it to be, especially if the user just clicks the up/down arrows. I suspect that it is only fired when the user actually clicks into the value area, then leaves. You could verify this by debugging to see if your code is ever hit or by showing a simple msgbox from that event.
I think that you will have better luck if you hook the LostFocus or ValueChanged event.
I want to add to this as well for anyone looking at this in the future.
Save your settings as shown already by putting
My.Settings.Heures = NumericUpDownHeures.Value into your ValueChanged event, and then doing reverse in the form load event.
The problem is, this value changed event fires before the form load when you first initialize, so it will keep defaulting to whatever value you have set in the designer because you're overwriting the setting value with the designer value.
To get around this, you need a private/public boolean at the top of your code that is only set to true once your form has loaded (set to true at the bottom of your form_load event), then you can add the condition to the ValueChanged event checking if the form is loaded yet or not. If it is, then change the setting value, if not, then don't.
An example:
Private IsFormLoaded As Boolean = False
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
NumericUpDown1.Value = My.Settings.SavedNumValue
IsFormLoaded = True
End Sub
Private Sub NumericUpDown1_ValueChanged(sender As Object, e As EventArgs) Handles NumericUpDown1.ValueChanged
If IsFormLoaded = False Then Exit Sub
My.Settings.SavedNumValue = NumericUpDown1.Value
End Sub
OR
Private Sub NumericUpDown1_ValueChanged(sender As Object, e As EventArgs) Handles NumericUpDown1.ValueChanged
If IsFormLoaded Then
My.Settings.SavedNumValue = NumericUpDown1.Value
End If
End Sub

Errorprovider shows error on using windows close button(X)

Is there any way to turn the damned error provider off when i try to close the form using the windows close button(X). It fires the validation and the user has to fill all the fields before he can close the form..this will be a usability issue because many tend to close the form using the (X) button.
i have placed a button for cancel with causes validation to false and it also fires a validation.
i found someone saying that if you use Form.Close() function validations are run...
how can i get past this annoying feature.
i have a MDI sturucture and show the form using
CreateExam.MdiParent = Me
CreateExam.Show()
on the mdi parent's menuitem click
and have this as set validation
Private Sub TextBox1_Validating(ByVal sender As System.Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles TextBox1.Validating
If String.IsNullOrEmpty(TextBox1.Text) Then
Err.SetError(TextBox1, "required")
e.Cancel = True
End If
If TextBox1.Text.Contains("'") Then
Err.SetError(TextBox1, "Invalid Char")
e.Cancel = True
End If
End Sub
Any help is much appreciated.
googling only showed results where users were having problem using a command button as close button and that too is causing problem in my case
The ValidateChildren() method prevents the form from closing. Paste this code in your form to fix that:
protected override void OnFormClosing(FormClosingEventArgs e) {
e.Cancel = false;
}
This is quite simple fix, in your Form's Closing Event, set a flag to indicate leaving the form, for example blnLeave, when the Form gets loaded, set the flag to False, when the Closing event gets triggered, set that to True within that event handler, then the change as follows would be
Private Sub TextBox1_Validating(ByVal sender As System.Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles TextBox1.Validating
If (blnLeave) Then
e.Cancel = False;
Return
End If
If String.IsNullOrEmpty(TextBox1.Text) Then
Err.SetError(TextBox1, "required")
e.Cancel = True
End If
If TextBox1.Text.Contains("'") Then
Err.SetError(TextBox1, "Invalid Char")
e.Cancel = True
End If
End Sub
Edit: Amended this answer for inclusion as per OP's comments. My suggestion is to handle the Form's Closed Event as shown
Private Sub Form1_FormClosed(ByVal sender As System.Object, ByVal e As System.Windows.Forms.FormClosedEventArgs) Handles MyBase.FormClosed
blnLeave = True
End Sub
And handle it here in the Form's window procedure override as shown here....
Private Const SC_CLOSE As Integer = &HF060
Private Const WM_MENUSELECT As Integer = &H11F
Private Function LoWord(ByVal Num As Integer) As Integer
LoWord = Num & &HFFFF
End Function
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
If m.Msg = WM_MENUSELECT Then
If LoWord(m.WParam.ToInt32()) = SC_CLOSE Then
' Handle the closing via system Menu
blnLeave = True
End If
End If
MyBase.WndProc(m)
End Sub