Best way to handle invalid input data in WinForms?

Viewed 46

I've put together a form in which a user can dynamically generate a customer order with one or more order positions. For each position, there are several attributes like amount, product name, price, discount etc.

My main problem is: What is the best way to deal with invalid values for the input fields? For example if a user types "X" into the amount field instead of 1, 2 or whatever. The basic idea was to let the user enter everything they want - but the order can only be saved once every input field contains valid data. If not, all invalid fields will be highlighted so the user knows what he did wrong.

So far, this seems to work just fine but my idea was to also have a Customer_Order object which would be updated everytime the user changes the value of an input field. Obviously I could not do that if I want to allow the user to enter Strings like "X" into Integer or Decimal fields... so it seems to me that I have 2 options:

A: Either restrict the input fields and programatically turn invalid values into zeros (For example: User enters "abc" into price field -> String will be converted to 0,00) OR B: keep my original plan with not so strict input regulations and NOT have a Customer_Order object that is always kept up to date. I would instead create the object from scratch and fill it with all the data from the input fields when the user finishes the order.

My problem with A is that I would like to keep the input fields as non-strict as possible. If a user types in something invalid, they should SEE what they typed in instead of the program changing the value. And my problem with B is that having an always up-to-date object of the customer order makes it easier to calulate prices on the fly. If I don't have that object, I would have to read out and parse all the necessary input fields every time I want to calculate something.

I'm not that experienced with GUIs so I really don't know if I'm missing something here... what would be the most elegant way to handle this? Is it generally a bad idea to have an always up-to-date object in the background at all times?

1 Answers

One option is to only allow valid keys. This can be done by utilizing the KeyDown event handler.

Create a new Windows Forms App (.NET Framework) project

Add a TextBox to the form (name: textBoxAmount)

Open Solution Explorer

  • In VS menu, click View
  • Select Solution Explorer

Open Properties Window

  • In VS menu, click View
  • Select Properties Window

Add TextBox KeyDown event handler

  • In Properties Window, select textBoxAmount from the drop-down
  • Click enter image description here
  • Double-click KeyDown

Add module (name: HelperInput.vb)

  • Click Project
  • Select Add Module... (name: HelperInput.vb)
  • Click OK

HelperInput.vb:

Imports System.Globalization

Module HelperInput
    Public Sub TBKeyDownMonetaryValue(sender As Object, e As System.Windows.Forms.KeyEventArgs)
        Dim tb As Control = DirectCast(sender, Control) 'TextBox

        Dim isKeyAllowed As Boolean = False

        Dim nfInfo As NumberFormatInfo = CultureInfo.CurrentUICulture.NumberFormat
        Debug.WriteLine($"currency symbol: {nfInfo.CurrencySymbol} decimal separator: {nfInfo.CurrencyDecimalSeparator} number group separator: {nfInfo.NumberGroupSeparator} currency group separator: {nfInfo.CurrencyGroupSeparator}")

        If Not Control.ModifierKeys = Keys.Shift Then
            Select Case e.KeyCode
                Case Keys.Enter
                    isKeyAllowed = True
                Case Keys.Back
                    isKeyAllowed = True
                Case Keys.Delete
                    isKeyAllowed = True
                Case Keys.NumPad0
                    isKeyAllowed = True
                Case Keys.NumPad1
                    isKeyAllowed = True
                Case Keys.NumPad2
                    isKeyAllowed = True
                Case Keys.NumPad3
                    isKeyAllowed = True
                Case Keys.NumPad4
                    isKeyAllowed = True
                Case Keys.NumPad5
                    isKeyAllowed = True
                Case Keys.NumPad6
                    isKeyAllowed = True
                Case Keys.NumPad7
                    isKeyAllowed = True
                Case Keys.NumPad8
                    isKeyAllowed = True
                Case Keys.NumPad9
                    isKeyAllowed = True
                Case Keys.D0
                    isKeyAllowed = True
                Case Keys.D1
                    isKeyAllowed = True
                Case Keys.D2
                    isKeyAllowed = True
                Case Keys.D3
                    isKeyAllowed = True
                Case Keys.D4
                    isKeyAllowed = True
                Case Keys.D5
                    isKeyAllowed = True
                Case Keys.D6
                    isKeyAllowed = True
                Case Keys.D7
                    isKeyAllowed = True
                Case Keys.D8
                    isKeyAllowed = True
                Case Keys.D9
                    isKeyAllowed = True
                Case Else
                    isKeyAllowed = False
            End Select
        End If

        'only allow one currency decimal separator
        If e.KeyCode = Keys.Oemcomma AndAlso nfInfo.CurrencyDecimalSeparator = "," AndAlso (String.IsNullOrEmpty(tb.Text) OrElse Not tb.Text.Contains(nfInfo.CurrencyDecimalSeparator)) Then
            isKeyAllowed = True
        ElseIf e.KeyCode = Keys.OemPeriod AndAlso nfInfo.CurrencyDecimalSeparator = "." AndAlso (String.IsNullOrEmpty(tb.Text) OrElse Not tb.Text.Contains(nfInfo.CurrencyDecimalSeparator)) Then
            isKeyAllowed = True
        End If

        If Not isKeyAllowed Then
            e.Handled = True
            e.SuppressKeyPress = True
        End If
    End Sub
End Module

Form1.vb:

Public Class Form1
    Private Sub TextBoxAmount_KeyDown(sender As Object, e As KeyEventArgs) Handles TextBoxAmount.KeyDown
        HelperInput.TBKeyDownMonetaryValue(sender, e)
    End Sub
End Class

Resources:

Related