Question

OK I have a series of textboxes that will only accept numeric values from 1 to 49 - with a unique number in each textbox. When validating, if there is a duplicate number in one of the boxes, I want the focus to be set to the offending textbox with it's text highlighted, so that i can just start typing and the selection will disappear. Problem (I think) is that the new keypress event is being added to the text before the selection can be removed. in searching Google for a solution, I was pointed to a post on this site which ultimately came up with this code posted by Robert Barnes

Private Sub txtScreen_KeyPress(sender As Object, e As KeyPressEventArgs) Handles txtScreen.KeyPress
    If txtScreen.SelectionStart < txtScreen.TextLength AndAlso Not [Char].IsControl(e.KeyChar) Then
        Dim SaveSelectionStart As Integer = txtScreen.SelectionStart
        Dim sb As New StringBuilder(txtScreen.Text)
        sb(txtScreen.SelectionStart) = e.KeyChar
        'Add the pressed key at the right position
        txtScreen.Text = sb.ToString()
        'SelectionStart is reset after setting the text, so restore it
        'Advance to the next char
        txtScreen.SelectionStart = SaveSelectionStart + 1
        e.Handled = True
    End If
End Sub

This works, but allows you to overtype the selection one character at a time instead of wiping (replacing) the whole thing with one keypress. I've never seen this kind of behaviour in a windows control before, nor have I seen a need for such a process, but it is an interesting feature. In any case upon realising what was happening within the If statement, I figured out I could acheive what I wanted with one line of code:

If tb.SelectionStart < tb.TextLength AndAlso Not [Char].IsControl(e.KeyChar) Then
        tb.SelectedText = ""
End If

This simply discards the selection and makes room for the new input, giving a more traditional Microsoft behaviour. I didn't need to set e.Handled in this case as I placed the code at the beginning of my KeyPress Event, allowing the keypress to be handled in subsequent code

so as per jmcilhinney I made some alterations... what I had (which worked but was a little sloppy)

Private Sub txtPick_KeyPress(sender As Object, e As System.Windows.Forms.KeyPressEventArgs) Handles txtNum1.KeyPress
    Dim tb As TextBox = CType(sender, TextBox)

    If tb.SelectionStart < tb.TextLength AndAlso Not Char.IsControl(e.KeyChar) Then
        tb.SelectedText = ""
    End If
    'exclude all keypresses that aren't digits
    If Not Char.IsDigit(e.KeyChar) Then e.Handled = True

    Dim num As Integer = Val(String.Concat(tb.Text, e.KeyChar))
    'make sure keypress keeps number in range
    If num > 49 Or num < 1 Then e.Handled = True
    'allow backspace, delete
    If e.KeyChar = vbBack Or e.KeyChar = ChrW(Keys.Delete) Then e.Handled = False
End Sub

and now I have:

Private Sub txtPick_KeyPress(sender As Object, e As System.Windows.Forms.KeyPressEventArgs) Handles txtNum1.KeyPress
    Dim tb As TextBox = CType(sender, TextBox)

    'exclude all keypresses that aren't digits
    If Not Char.IsDigit(e.KeyChar) Then e.Handled = True
    'allow backspace, delete
    If e.KeyChar = vbBack Or e.KeyChar = ChrW(Keys.Delete) Then e.Handled = False
End Sub

Private Sub txtNum_Validating(sender As Object, e As System.ComponentModel.CancelEventArgs) Handles txtNum1.Validating
    Dim tb As TextBox = CType(sender, TextBox)
    Dim num As Integer = Val(tb.Text)

    If IsNumeric(tb.Text) Then
        Select Case Val(tb.Text)
            Case Is < 1, Is > 49
                e.Cancel = True
        End Select
    End If
End Sub

Cleaned up quite nice and worked perfect, thanks jmchilinney!

Was it helpful?

Solution

KeyPress is the wrong event. You should handle the Validating event, which occurs when the user tries to leave the control. At that point, you can check that the input is a number and that it's unique. If it's not, you call SelectAll on the TextBox and set e.Cancel to True to prevent the control losing focus.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top