How to: Convert Numeric User Input in Web Controls to Numbers

Because a Web page can be displayed anywhere in the world, users can input numeric data into a TextBox control in an almost unlimited number of formats. As a result, it is very important to determine the locale and culture of the Web page's user. When you parse user input, you can then apply the formatting conventions defined by the user's locale and culture.

To convert numeric input from a Web TextBox control to a number

  1. Determine whether the string array returned by the HttpRequest.UserLanguages property is populated. If it is not, continue to step 6.

  2. If the string array returned by the UserLanguages property is populated, retrieve its first element. The first element indicates the user's default or preferred language and region.

  3. Instantiate a CultureInfo object that represents the user's preferred culture by calling the CultureInfo.CultureInfo(String, Boolean) constructor.

  4. Call either the TryParse or the Parse method of the numeric type that you want to convert the user's input to. Use an overload of the TryParse or the Parse method with a provider parameter, and pass it either of the following:

  5. If the conversion fails, repeat steps 2 through 4 for each remaining element in the string array returned by the UserLanguages property.

  6. If the conversion still fails or if the string array returned by the UserLanguages property is empty, parse the string by using the invariant culture, which is returned by the CultureInfo.InvariantCulture property.

Example

The following example is the complete code-behind page for a Web form that asks the user to enter a numeric value in a TextBox control and converts it to a number. That number is then doubled and displayed by using the same formatting rules as the original input.

Imports System.Globalization

Partial Class NumericUserInput
   Inherits System.Web.UI.Page

   Protected Sub OKButton_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles OKButton.Click
      Dim locale As String
      Dim culture As CultureInfo = Nothing
      Dim number As Double
      Dim result As Boolean

      ' Exit if input is absent.
      If String.IsNullOrEmpty(Me.NumericString.Text) Then Exit Sub

      ' Hide form elements.
      Me.NumericInput.Visible = False

      ' Get user culture/region
      If Not (Request.UserLanguages.Length = 0 OrElse String.IsNullOrEmpty(Request.UserLanguages(0))) Then
         Try
            locale = Request.UserLanguages(0)
            culture = New CultureInfo(locale, False)

            ' Parse input using user culture.
            result = Double.TryParse(Me.NumericString.Text, NumberStyles.Any, culture.NumberFormat, number)
         Catch
         End Try
         ' If parse fails, parse input using any additional languages.
         If Not result Then
            If Request.UserLanguages.Length > 1 Then
               For ctr As Integer = 1 To Request.UserLanguages.Length - 1
                  Try
                     locale = Request.UserLanguages(ctr)
                     ' Remove quality specifier, if present.
                     locale = Left(locale, InStr(locale, ";") - 1)
                     culture = New CultureInfo(Request.UserLanguages(ctr), False)
                     result = Double.TryParse(Me.NumericString.Text, NumberStyles.Any, culture.NumberFormat, number)
                     If result Then Exit For
                  Catch
                  End Try
               Next
            End If
         End If
      End If
      ' If parse operation fails, use invariant culture.
      If Not result Then
         result = Double.TryParse(Me.NumericString.Text, NumberStyles.Any, CultureInfo.InvariantCulture, number)
      End If
      ' Double result
      number *= 2

      ' Display result to user.
      If result Then
         Response.Write("<P />")
         Response.Write(Server.HtmlEncode(Me.NumericString.Text) + " * 2 = " + number.ToString("N", culture) + "<BR />")
      Else
         ' Unhide form.
         Me.NumericInput.Visible = True

         Response.Write("<P />")
         Response.Write("Unable to recognize " + Server.HtmlEncode(Me.NumericString.Text))
      End If
   End Sub   
End Class
using System;
using System.Globalization;

partial class NumericUserInput : System.Web.UI.Page
{
   protected void OKButton_Click(object sender, EventArgs e)
   {
      string locale;
      CultureInfo culture = null;
      double number = 0;
      bool result = false;

      // Exit if input is absent.
      if (String.IsNullOrEmpty(this.NumericString.Text)) return;

      // Hide form elements.
      this.NumericInput.Visible = false;

      // Get user culture/region
      if (!(Request.UserLanguages.Length == 0 || String.IsNullOrEmpty(Request.UserLanguages[0])))
      {
         try
         {
            locale = Request.UserLanguages[0];
            culture = new CultureInfo(locale, false);

            // Parse input using user culture.
            result = Double.TryParse(this.NumericString.Text, NumberStyles.Any,
                                     culture.NumberFormat, out number);
         }
         catch { }
         // If parse fails, parse input using any additional languages.
         if (!result)
         {
            if (Request.UserLanguages.Length > 1)
            {
               for (int ctr = 1; ctr <= Request.UserLanguages.Length - 1; ctr++)
               {
                  try
                  {
                     locale = Request.UserLanguages[ctr];
                     // Remove quality specifier, if present.
                     locale = locale.Substring(1, locale.IndexOf(';') - 1);
                     culture = new CultureInfo(Request.UserLanguages[ctr], false);
                     result = Double.TryParse(this.NumericString.Text, NumberStyles.Any, culture.NumberFormat, out number);
                     if (result) break;
                  }
                  catch { }
               }
            }
         }
      }
      // If parse operation fails, use invariant culture.
      if (!result)
         result = Double.TryParse(this.NumericString.Text, NumberStyles.Any, CultureInfo.InvariantCulture, out number);

      // Double result.
      number *= 2;

      // Display result to user.
      if (result)
      {
         Response.Write("<P />");
         Response.Write(Server.HtmlEncode(this.NumericString.Text) + " * 2 = " + number.ToString("N", culture) + "<BR />");
      }
      else
      {
         // Unhide form.
         this.NumericInput.Visible = true;

         Response.Write("<P />");
         Response.Write("Unable to recognize " + Server.HtmlEncode(this.NumericString.Text));
      }
   }
}

The HttpRequest.UserLanguages property is populated from the culture names that are contained in Accept-Language headers included in an HTTP request. However, not all browsers include Accept-Language headers in their requests, and users can also suppress the headers completely. This makes it important to have a fallback culture when parsing user input. Typically, the fallback culture is the invariant culture returned by CultureInfo.InvariantCulture. Users can also provide Internet Explorer with culture names that they input in a text box, which creates the possibility that the culture names may not be valid. This makes it important to use exception handling when instantiating a CultureInfo object.

When retrieved from an HTTP request submitted by Internet Explorer, the HttpRequest.UserLanguages array is populated in order of user preference. The first element in the array contains the name of the user's primary culture/region. If the array contains any additional items, Internet Explorer arbitrarily assigns them a quality specifier, which is delimited from the culture name by a semicolon. For example, an entry for the fr-FR culture might take the form fr-FR;q=0.7.

The example calls the CultureInfo constructor with its useUserOverride parameter set to false to create a new CultureInfo object. This ensures that, if the culture name is the default culture name on the server, the new CultureInfo object created by the class constructor contains a culture's default settings and does not reflect any settings overridden by using the server's Regional and Language Options application. The values from any overridden settings on the server are unlikely to exist on the user's system or to be reflected in the user's input.

Your code can call either the Parse or the TryParse method of the numeric type that the user's input will be converted to. Repeated calls to a parse method may be required for a single parsing operation. As a result, the TryParse method is better, because it returns false if a parse operation fails. In contrast, handling the repeated exceptions that may be thrown by the Parse method can be a very expensive proposition in a Web application.

Compiling the Code

To compile the code, copy it into an ASP.NET code-behind page so that it replaces all the existing code. The ASP.NET Web page should contain the following controls:

  • A Label control, which is not referenced in code. Set its Text property to "Enter a Number:".

  • A TextBox control named NumericString.

  • A Button control named OKButton. Set its Text property to "OK".

Change the name of the class from NumericUserInput to the name of the class that is defined by the Inherits attribute of the ASP.NET page's Page directive. Change the name of the NumericInput object reference to the name defined by the id attribute of the ASP.NET page's form tag.

Security

To prevent a user from injecting script into the HTML stream, user input should never be directly echoed back in the server response. Instead, it should be encoded by using the HttpServerUtility.HtmlEncode method.

See Also

Concepts

Performing Formatting Operations

Parsing Numeric Strings