Results 1 to 17 of 17

Thread: [RESOLVED] How to make a TextBox accept only numbers, decimal separator and minus sign?

  1. #1

    Thread Starter
    Hyperactive Member Spybot's Avatar
    Join Date
    Jan 2019
    Posts
    329

    Resolved [RESOLVED] How to make a TextBox accept only numbers, decimal separator and minus sign?

    Hello!

    I'm trying to make a textBox with the following acceptance requirements:
    - positive numbers.
    - negative numbers.
    - decimal separator (occurring only once)
    - minus sign (occurring only once)
    - Don't allow the minus sign after a number eg. 7.5-2

    Right now, I almost got it with following code under the KeyPress event of the textBox, but the minus sign still can be entered more than once, and it can also be entered after a number.

    VB.NET Code:
    1. Dim DecimalSeparator As String = Application.CurrentCulture.NumberFormat.NumberDecimalSeparator
    2.    If Not IsNumeric(e.KeyChar) AndAlso Convert.ToByte(e.KeyChar) <> 8 AndAlso Convert.ToByte(e.KeyChar) <> 45 Then
    3.       e.Handled = Not (e.KeyChar = DecimalSeparator And DirectCast(sender, TextBox).Text.IndexOf(DecimalSeparator) = -1)
    4.    End If

    I need some help here, please.

  2. #2
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,350

    Re: How to make a TextBox accept only numbers, decimal separator and minus sign?

    The first option would be to use a NumericUpDown instead of a TextBox, although that may not be appropriate in all circumstances. Beyond that, you can't really do this properly with a standard TextBox and handling events. If nothing else, you can't really prevent invalid data being pasted in unless you also prevent valid data being pasted in.

    The solution is to use a custom control. That allows you to build in functionality like you're now putting in the form but it also allows code that you can't easily put in the form. Many people have created such custom TextBoxes. Here's mine:

    https://www.vbforums.com/showthread....meric-Text-Box

    If you look through that code, you'll see some of the reasons that a rigorous implementation is hard to impossible using a standard control.

  3. #3
    eXtreme Programmer .paul.'s Avatar
    Join Date
    May 2007
    Location
    Chelmsford UK
    Posts
    25,481

    Re: How to make a TextBox accept only numbers, decimal separator and minus sign?

    Quote Originally Posted by jmcilhinney View Post
    …If nothing else, you can't really prevent invalid data being pasted in unless you also prevent valid data being pasted in.
    ??? Does tend to be so

  4. #4
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,350

    Re: How to make a TextBox accept only numbers, decimal separator and minus sign?

    Quote Originally Posted by .paul. View Post
    ??? Does tend to be so
    I've reread that a couple of times and it looks OK to me. Did you misread it? Note that I say "invalid data" the first time and "valid data" the second time. Basically, it's either let all data be pasted or none.

  5. #5
    eXtreme Programmer .paul.'s Avatar
    Join Date
    May 2007
    Location
    Chelmsford UK
    Posts
    25,481

    Re: How to make a TextBox accept only numbers, decimal separator and minus sign?

    Try this… (it handles pasting only valid numbers)

    Code:
    Public Class numericTextbox
     
        Inherits TextBox
     
        Const WM_PASTE As Integer = &H302
     
        Protected Overrides Sub OnKeyPress(ByVal e As System.Windows.Forms.KeyPressEventArgs)
            Dim strText As String = Me.Text
            strText = strText.Remove(Me.SelectionStart, Me.SelectionLength)
            strText = strText.Insert(Me.SelectionStart, e.KeyChar)
            e.Handled = CBool(strText.LastIndexOf("-") > 0) Or Not (Char.IsControl(e.KeyChar) OrElse Char.IsDigit(e.KeyChar) OrElse (e.KeyChar = "."c And Not Me.Text.Contains(".") Or e.KeyChar = "."c And Me.SelectedText.Contains(".")) OrElse (e.KeyChar = "-"c And Me.SelectionStart = 0))
            MyBase.OnKeyPress(e)
        End Sub
     
        Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
            If m.Msg = WM_PASTE Then
                Dim strText As String = Me.Text
                strText = strText.Remove(Me.SelectionStart, Me.SelectionLength)
                strText = strText.Insert(Me.SelectionStart, Clipboard.GetText)
                Dim result As Decimal = 0
                If Not Decimal.TryParse(strText, result) Then
                    Return
                End If
            End If
            MyBase.WndProc(m)
        End Sub
     
    End Class

  6. #6
    eXtreme Programmer .paul.'s Avatar
    Join Date
    May 2007
    Location
    Chelmsford UK
    Posts
    25,481

    Re: How to make a TextBox accept only numbers, decimal separator and minus sign?

    Quote Originally Posted by jmcilhinney View Post
    I've reread that a couple of times and it looks OK to me. Did you misread it? Note that I say "invalid data" the first time and "valid data" the second time. Basically, it's either let all data be pasted or none.
    Yeah I just re-read it. You’re right, I didn’t catch you with a horrendous blooper, but it is possible to restrict pasting…

  7. #7
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,350

    Re: How to make a TextBox accept only numbers, decimal separator and minus sign?

    Quote Originally Posted by .paul. View Post
    it is possible to restrict pasting…
    With a standard TextBox? I know that it can be done with a custom control but that was my point: it's better to use a custom control because there's no easy way to use a standard TextBox and do a proper job.

  8. #8
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,350

    Re: How to make a TextBox accept only numbers, decimal separator and minus sign?

    For the record, here's my SimpleNumberBox class from the CodeBank:
    vb.net Code:
    1. Imports System.Globalization
    2.  
    3. Public Class SimpleNumberBox
    4.     Inherits System.Windows.Forms.TextBox
    5.  
    6.     Private Const WM_PASTE As Integer = &H302
    7.  
    8.     Protected Overrides Sub OnKeyPress(ByVal e As System.Windows.Forms.KeyPressEventArgs)
    9.         Dim keyChar = e.KeyChar
    10.         Dim formatInfo = NumberFormatInfo.CurrentInfo
    11.  
    12.         If Char.IsControl(keyChar) OrElse _
    13.            Char.IsDigit(keyChar) OrElse _
    14.            ((keyChar = formatInfo.NegativeSign OrElse _
    15.              keyChar = formatInfo.NumberDecimalSeparator) AndAlso _
    16.             Me.ValidateText(keyChar)) Then
    17.             MyBase.OnKeyPress(e)
    18.         Else
    19.             e.Handled = True
    20.         End If
    21.     End Sub
    22.  
    23.     Protected Overrides Sub OnValidated(ByVal e As System.EventArgs)
    24.         If Not Decimal.TryParse(Me.Text, New Decimal) Then
    25.             Me.Clear()
    26.         End If
    27.  
    28.         MyBase.OnValidated(e)
    29.     End Sub
    30.  
    31.     Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
    32.         If m.Msg <> WM_PASTE OrElse _
    33.            Not Clipboard.ContainsText() OrElse _
    34.            Me.ValidateText(Clipboard.GetText()) Then
    35.             MyBase.WndProc(m)
    36.         End If
    37.     End Sub
    38.  
    39.     Private Function ValidateText(ByVal newText As String) As Boolean
    40.         Dim isValid = True
    41.         Dim currentText = Me.Text
    42.         Dim selectionStart = Me.SelectionStart
    43.         Dim proposedText = currentText.Substring(0, selectionStart) & _
    44.                            newText & _
    45.                            currentText.Substring(selectionStart + Me.SelectionLength)
    46.  
    47.         If Not Decimal.TryParse(proposedText, _
    48.                                 NumberStyles.AllowLeadingSign Or NumberStyles.AllowDecimalPoint, _
    49.                                 Nothing, _
    50.                                 New Decimal) Then
    51.             Dim formatInfo = NumberFormatInfo.CurrentInfo
    52.             Dim negativeSign = formatInfo.NegativeSign
    53.             Dim decimalSeparator = formatInfo.NumberDecimalSeparator
    54.  
    55.             isValid = (proposedText = negativeSign) OrElse _
    56.                       (proposedText = decimalSeparator) OrElse _
    57.                       (proposedText = negativeSign & decimalSeparator)
    58.         End If
    59.  
    60.         Return isValid
    61.     End Function
    62.  
    63. End Class
    Fairly rigorous but not as fancy as the more fully-featured NumberBox class.

  9. #9
    PowerPoster ChrisE's Avatar
    Join Date
    Jun 2017
    Location
    Frankfurt
    Posts
    3,048

    Re: How to make a TextBox accept only numbers, decimal separator and minus sign?

    Quote Originally Posted by jmcilhinney View Post
    With a standard TextBox? I know that it can be done with a custom control but that was my point: it's better to use a custom control because there's no easy way to use a standard TextBox and do a proper job.

    to restrict paste I use ShortcutsEnabled ..
    Code:
     Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
            Me.TextBox1.ShortcutsEnabled = False
        End Sub
    
     Private Sub TextBox1_KeyPress(sender As Object, e As System.Windows.Forms.KeyPressEventArgs) Handles TextBox1.KeyPress
            If e.KeyChar = "."c Then
                'only one allowed
                e.Handled = (CType(sender, TextBox).Text.IndexOf("."c) <> -1)
            ElseIf e.KeyChar = "-"c Then
                'only one allowed
                e.Handled = (CType(sender, TextBox).Text.IndexOf("-"c) <> -1)
                'Backspace 
            ElseIf e.KeyChar <> ControlChars.Back Then
                e.Handled = ("0123456789".IndexOf(e.KeyChar) = -1)  '<--allowed
            End If
        End Sub
    to hunt a species to extinction is not logical !
    since 2010 the number of Tigers are rising again in 2016 - 3900 were counted. with Baby Callas it's 3901, my wife and I had 2-3 months the privilege of raising a Baby Tiger.

  10. #10
    eXtreme Programmer .paul.'s Avatar
    Join Date
    May 2007
    Location
    Chelmsford UK
    Posts
    25,481

    Re: How to make a TextBox accept only numbers, decimal separator and minus sign?

    Quote Originally Posted by ChrisE View Post
    to restrict paste I use ShortcutsEnabled ..
    Code:
     Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
            Me.TextBox1.ShortcutsEnabled = False
        End Sub
    
     Private Sub TextBox1_KeyPress(sender As Object, e As System.Windows.Forms.KeyPressEventArgs) Handles TextBox1.KeyPress
            If e.KeyChar = "."c Then
                'only one allowed
                e.Handled = (CType(sender, TextBox).Text.IndexOf("."c) <> -1)
            ElseIf e.KeyChar = "-"c Then
                'only one allowed
                e.Handled = (CType(sender, TextBox).Text.IndexOf("-"c) <> -1)
                'Backspace 
            ElseIf e.KeyChar <> ControlChars.Back Then
                e.Handled = ("0123456789".IndexOf(e.KeyChar) = -1)  '<--allowed
            End If
        End Sub
    The point is, with a custom control, you can allow valid pasting instead of disallowing all pasting…

  11. #11

    Thread Starter
    Hyperactive Member Spybot's Avatar
    Join Date
    Jan 2019
    Posts
    329

    Re: How to make a TextBox accept only numbers, decimal separator and minus sign?

    In my case disallowing all pasting is perfectly fine, since I don't want the user to paste anything into the TextBox. this redices many problems.

  12. #12
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,350

    Re: How to make a TextBox accept only numbers, decimal separator and minus sign?

    Quote Originally Posted by Spybot View Post
    In my case disallowing all pasting is perfectly fine, since I don't want the user to paste anything into the TextBox. this redices many problems.
    It's your app but, I have to say, it really annoys me if I can't paste valid data into a text field.

  13. #13
    Smooth Moperator techgnome's Avatar
    Join Date
    May 2002
    Posts
    34,543

    Re: How to make a TextBox accept only numbers, decimal separator and minus sign?

    Quote Originally Posted by jmcilhinney View Post
    It's your app but, I have to say, it really annoys me if I can't paste valid data into a text field.
    Well, then it's good thing it isn't a text field but a numeric field. :P

    Honestly IO've gotten to the point where I no longer care about what a user types or pastes in, and only do checks either on exit of the field, or when submit button is clicked. There are more important things in life where I don't need (or care) to try to come up with all the different ways a user might type or paste something in.


    -tg
    * I don't respond to private (PM) requests for help. It's not conducive to the general learning of others.*
    * I also don't respond to friend requests. Save a few bits and don't bother. I'll just end up rejecting anyways.*
    * How to get EFFECTIVE help: The Hitchhiker's Guide to Getting Help at VBF - Removing eels from your hovercraft *
    * How to Use Parameters * Create Disconnected ADO Recordset Clones * Set your VB6 ActiveX Compatibility * Get rid of those pesky VB Line Numbers * I swear I saved my data, where'd it run off to??? *

  14. #14

    Thread Starter
    Hyperactive Member Spybot's Avatar
    Join Date
    Jan 2019
    Posts
    329

    Re: How to make a TextBox accept only numbers, decimal separator and minus sign?

    As for the negative sign between numbers. I used this code:
    VB.NET Code:
    1. If TextBox8.Text.IndexOf("-") <> -1 Then
    2.             Dim TxtBoxLength As Integer = TextBox1.Text.Length
    3.             Dim dIndex As Integer = TextBox1.Text.IndexOf("-")
    4.             If dIndex <> 0 And TxtBoxLength >= 2 Then
    5.                 'Error sound
    6.                 Console.Beep(2500, 120)
    7.                 Console.Beep(2000, 120)
    8.             Else
    9.                 'Proceed...
    10.             End If
    11.         End If

    I appreciate the time you spend to answer my question.
    I got many ideas to solve this problem from you.

    Thank you all!

  15. #15
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    39,047

    Re: [RESOLVED] How to make a TextBox accept only numbers, decimal separator and minus

    You could have written that far more compactly such that it would run more efficiently. It also seems to have a significant bug in it. You check TextBox8 in the first If statement, then work with TextBox1 for everything else. If there's a reason for that, such that TextBox8 SHOULD be checked before checking TextBox1, then some of the other points I'd make are irrelevant, but that seems like a bug.

    1) Using the default names for the controls is a path to madness. Madness is not a destination most people are seeking.

    2) You end up checking that IndexOf twice, assuming the name of the textbox is a bug. That's a costly waste, but since computers are so fast, you'll never see that cost. Calling Contains might be faster, but even if it isn't, you should...maybe...retain the result of the first IndexOf so that you can use it later. After all, it's a short line of code, but what has to happen is that each character is examined to see if it is the one you are looking for, so that short line of code hides a loop.

    3) You should be aware of, and making use of, AndAlso rather than And. AndAlso shortcircuits evaluation, so you could do this:

    Code:
    If TextBox1.Text.Length>=2 AndAlso TextBox1.Text.IndexOf("-") > 0 Then
    The second part won't even happen if the Length is shorter than 2. If the length is shorter than 2, then the overall condition MUST evaluate to False, so checking the second half (which is the costly IndexOf) will be skipped entirely.

    4) That code seems like it will fail for a string like: -123-4, and will also fail for --1234. IndexOf will return 0, because it returns the first instance of the sought character. So, if the first character is -, it returns 0, which will pass your test. The fact that there are other dashes in the string will be ignored, and yet they are there, so the string is invalid.

    5) All the numeric types (and several others) have a TryParse method which will do the job for you, do it correctly, and do it more safely. In this case, you'd want something like Decimal.TryParse or Double.TryParse. The code would look like this:

    Code:
    Dim d As Double 'You probably don't have a use for this, but it has to be there for the function.
    
    If Double.TryParse(TextBox1.Text,d) Then
     'It IS a double, and that double is found in d.
    Else
     'It is NOT double, so this is where you can beep at the user.
    End If
    That's all that's needed. No need for IndexOf at all. No need to check the length of the string, either, because this will work for an empty string, for strings with a minus sign in the right place, and strings with dashes in incorrect places. This will also work for multiple decimal points, as well.
    My usual boring signature: Nothing

  16. #16

    Thread Starter
    Hyperactive Member Spybot's Avatar
    Join Date
    Jan 2019
    Posts
    329

    Re: [RESOLVED] How to make a TextBox accept only numbers, decimal separator and minus

    You are completely right.
    I didn't see those coming, but this -123-4, and this --1234, will not happen because of the code on post# 9.
    Anyway... the tryParse method seems to be cleaner than mine, thank you for your suggestion.

  17. #17
    eXtreme Programmer .paul.'s Avatar
    Join Date
    May 2007
    Location
    Chelmsford UK
    Posts
    25,481

    Re: How to make a TextBox accept only numbers, decimal separator and minus sign?

    Quote Originally Posted by jmcilhinney View Post
    With a standard TextBox? I know that it can be done with a custom control but that was my point: it's better to use a custom control because there's no easy way to use a standard TextBox and do a proper job.
    You could hook the clipboard and allow/disallow pasting depending on the clipboard contents…

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width