0

I wish to make this WinForm control (NumericUpDown) enforce whole numbers conditionally depending on the type (whole vs floating) of the thing that I am working with.

If I set the DecimalPlaces = 0, it displays 0, and then increments/decrements that by 1 when I click up/down. However, if I enter 0.6, then it will display as 1, but will remain as 0.6. If I subsequently increment it, the underlying but not displayed value will be 1.6.

I am looking for a simple, idiomatic way to enforce what I want (hopefully it is clear what I want). If I have to intercept some sort of event, then I will, but I hope to just rely on some flag/setting that the NumericUpDown class already provides.

If your solution involves sub-classing NumericUpDown, then I will have to think about that. At this stage in the release I would prefer a well-documented hack to a clean change that might cause bugs elsewhere. I would like to have the option of not sub-classing the NumericUpDown.

Let me know if you have questions, thanks.

Hamish Grubijan
  • 10,562
  • 23
  • 99
  • 147

2 Answers2

1

The underlying value is of type decimal. The DecimalPlaces only affects the number of digits displayed in the control. The easiest way to achieve what you want is to round the NumericUpDown.Value to an int.

Andrei Pana
  • 4,484
  • 1
  • 26
  • 27
  • Andrei, could you be more specific on HOW to do that? More precisely - which event should I intercept? I do not want users to even think that they can enter non-integer values. I *could* round the value, but I also do not want them to be able to enter 0.7 - I want it to fail validation. – Hamish Grubijan Dec 03 '10 at 22:57
  • You should round the value everywhere you are using it. Or if you want to intercept an event (and add a little hack to it), you can intercept ValueChanged event and here modify the Value to the Round part of it (only if it is not already rounded - to avoid an infinite loop!) – Andrei Pana Dec 03 '10 at 23:01
  • Hm ... is it better than hacking the TextChanged event? I do not want to correct the value silently - I want the user to know somehow that their input will not be accepted. – Hamish Grubijan Dec 03 '10 at 23:14
  • I think it's better - but depends on how you want the user to know that their input will not be accepted. If you intercept ValueChanged, if the users enters 1.6 when he presses Enter, you will change the value in the ValueChanged event handler to be 2. On the other hand, intercepting TextChanged is really not a good idea because this event supports the .NET Framework infrastructure and is not intended to be used directly from your code plus its event handler will be called on which character used for introducing 1.6 and it will most probably be a mess. – Andrei Pana Dec 04 '10 at 00:19
  • @Andrei, do you know which rounding algorithm the `NumericUpDown` control employs for displaying text? It is not the same as `Math.Round(decimal value, 0)`. Since I am returning a different value than the one that is stored internally, I at least want it to be equal to what will be displayed. For instance, when I enter the value `6.5`, it displays it as a 7, but the round mentioned above gives me a 6. So weird! – Hamish Grubijan Dec 05 '10 at 19:17
  • 1
    Yeah, that's a trick .Net plays to beginners. Take a look at the answer here: http://stackoverflow.com/questions/977796/in-c-math-round2-5-result-is-2-instead-of-3-are-you-kidding-me and see it's not that weird afterall, at least, it's documented! :) – Andrei Pana Dec 06 '10 at 08:40
0

Here's an imperfect solution that we're using. First, prevent the user from typing a decimal (everything else the user might type seems to be handled by the control itself just fine):

Private Sub HandleKeyPress(sender As Object, e As Windows.Forms.KeyPressEventArgs) Handles MyNumericUpDown.KeyPress
   If e.KeyChar = "."c Then e.Handled = True
End Sub

Second, prevent the user from pasting anything other than digits. Note that the approach I've taken hard-codes a couple of specific key combinations (ctrl-v and shift-insert). It doesn't handle other ways the user might paste, such as using the up-down control's context menu.

Protected Overrides Function ProcessCmdKey(ByRef msg As System.Windows.Forms.Message, 
                                           ByVal keyData As System.Windows.Forms.Keys) As Boolean
  If keyData = (Keys.Shift Or Keys.Insert) OrElse keyData = (Keys.Control Or Keys.V) Then
     Dim data As IDataObject = Clipboard.GetDataObject
     If data Is Nothing Then
        Return MyBase.ProcessCmdKey(msg, keyData)
     Else
        Dim text As String = CStr(data.GetData(DataFormats.StringFormat, True))
        If text = String.Empty Then
           Return MyBase.ProcessCmdKey(msg, keyData)
        Else
           For Each ch As Char In text
              If Not Char.IsNumber(ch) Then
                 Return True
              End If
           Next
           Return MyBase.ProcessCmdKey(msg, keyData)
        End If
     End If
  Else
     Return MyBase.ProcessCmdKey(msg, keyData)
  End If
End Function

This isn't the perfect solution, but it is close enough to the intended behavior for our needs.

Darryl
  • 5,907
  • 1
  • 25
  • 36