Search code examples
.netvb.netwinformscontrolsnumericupdown

NumericUpDown control for Percentages?


How can I set a NumericUpDown control to display values as percentages?


Solution

  • You'll have to derive your own custom control and override the UpdateEditText() method. While we're at it, let's override the default Minimum, Maximum, and Increment property values to be a little more percentage-friendly.

    We'll also need to override the base ParseEditText() method to interpret the user-generated input as a percentage (dividing by 100), because the user will expect to enter 80 to represent 80% (and the Decimal parser needs to ignore the percentage sign).

    Public Class PercentUpDown
        Inherits NumericUpDown
    
        Private Shared ReadOnly DefaultValue As New [Decimal](0.0)      ' 0%
        Private Shared ReadOnly DefaultMinimum As New [Decimal](0.0)    ' 0%
        Private Shared ReadOnly DefaultMaximum As New [Decimal](1.0)    ' 100%
        Private Shared ReadOnly DefaultIncrement As New [Decimal](0.01) ' 1%
    
        Public Sub New()
            Value = DefaultValue
            Minimum = DefaultMinimum
            Maximum = DefaultMaximum
            Increment = DefaultIncrement
        End Sub
    
        Protected Overrides Sub UpdateEditText()
            If UserEdit Then
                ParseEditText()
            End If
    
            Text = Value.ToString(String.Format("p{0}", DecimalPlaces))
        End Sub
    
        Protected Shadows Sub ParseEditText()
            Debug.Assert(UserEdit = True, "ParseEditText() - UserEdit == false")
    
            Try
                If Not String.IsNullOrWhiteSpace(Text) AndAlso _
                   Not (Text.Length = 1 AndAlso Text.Equals("-")) Then
    
                    Value = Constrain(Decimal.Parse(Text.Replace("%", String.Empty), NumberStyles.Any, CultureInfo.CurrentCulture) / 100)
    
                End If
            Catch ex As Exception
                ' Leave value as it is
            Finally
                UserEdit = False
            End Try
        End Sub
    
        Private Function Constrain(origValue As [Decimal]) As [Decimal]
            Debug.Assert(Minimum <= Maximum, "minimum > maximum")
    
            If origValue < Minimum Then Return Minimum
            If origValue > Maximum Then Return Maximum
    
            Return origValue
        End Function
    
    End Class
    

    We could expand the scope of the class a little by adding a TextFormat property where we could set the numeric display format we'd like to use at design time, so that we can support displaying the value as a Currency, for example.

    The above code, though, is nice and compact and specifically targets Percentages, taking advantage of the existing DecimalPlaces property. The Value property is stored as the mathematical representation of the percentage (0.5 for 50%, for example), so it's simple to plug into a formula without worrying about dividing by 100.