tags:

views:

766

answers:

7

What is the most elegant way of restricting the input of a TextBox control (or anything else that comes standard with .NET 3.5) to floating point numbers?

Currently, I'm doing all the heavy lifting myself by inheriting from TextBox and overriding OnKeyPress. However, I can't help but wonder if I'm reinventing the wheel.

+2  A: 

Take a look at the MaskedTextBox control. It also inherits from TextBoxBase and probably has the functionality you're building into yours.

Mike at KBS
Thanks for the quick response, Mike. I did in fact research the MaskedTextBox control prior to resorting to manual labor, however, I couldn't get it to behave like I wanted out of the box with floating-point numbers. Perhaps I did something foolish, though...
I don't think that MaskedTextBox could do anything good here, as it doesn't seem to allow an infinite (user defined) number of character.
VirtualBlackFox
A: 

I don't think the wheel's been invented yet (at least in the .NET framework). I'm sure there's something on CodeProject or the like, doing similar to what you are though, so it may be worth a Google.

The heavy lifting shouldn't be too incredibly bad though. There is a little bit more to it than at first glance.

The overly simplified example is you can handle OnKeyPress, do a Float.TryParse with the new character appended in. If true, keep the keypress; if false, cancel it (e.Handled = true).

The hard part is what if they delete, cut, or paste a selection. The other thing is when they're just starting out (you might want to accept "-" as partial valid input)...

lc
http://www.codeproject.com/KB/edit/RegexText.aspx seem to do it.
VirtualBlackFox
Thanks for your suggestion, but it's not that simple. What if the user changes the carets position? As far as I can see, you can't blindly append.
Thanks for the link VirualBlackFox. I'll definitely take a look.
Good point about that, but can't you get the current selection with SelectionStart and SelectionLength?
lc
Btw i don't think limiting only input is great, the copy paste case is a problem in itself as some user expect to be able to paste a number at the start of the input and remove (manually) the old text to make it valid...
VirtualBlackFox
lc, the more I think about your suggestion, the more attractive it becomes... The thing is, I don't care about the order of the floating-point number when I check it lexically. Therefore the position of the newly added character is irrelevant. Try-Parse should work, if I didn't miss anything.
My current implementation to avoid confusing the user is to check for valid (number and '.') input, filter this on copy paste or other change and color the background and invalidate ok buttons if the number isn't one or isn't in the range I expect.
VirtualBlackFox
@VirtualBlackFox agreed on both counts, sometimes limiting input can work against you. you're probably better off doing exactly what you suggested in your last comment...
lc
A: 

Or check for match using

void TextBox_KeyUp(object sender, KeyEventArgs e)
{
  if(..) // your condition
  {
    e.SuppressKeyPress = false;
  }
  else
  {
    e.SuppressKeyPress = true;
  }
}

or

void TextBox_KeyUp(object sender, KeyEventArgs e)
{
   e.SuppressKeyPress = CheckInput(e.KeyValue); // where CheckInput is boolean method
}
abatishchev
+1  A: 

What about using a NumericUpDown?

Jay Riggs
+9  A: 

Don't forget the following issues/corner-cases that you will be facing if you go down your proposed route:

  • Users can use Ctrl-V or Shift-Insert to paste "invalid" values in (That second one is a little trickier to catch) ... but users probably should be allowed to paste legal values into the control
  • Users can right click, paste invalid values in using the default context-menu
  • Even if you've tried to fix the previous issue by giving the textbox its own context menu, users can right-click outside your control, hold down the right button, drag over your textbox and let go to access the default context-menu (and paste in invalid values)
  • Whatever key processing you do shouldn't disable key combinations like Alt - F4, etc. (And yes, you will break this if you set SuppressKeyPress for everything that isn't a valid digit)
  • Users should probably be able to enter partial values (e.g. "-.", as they begin to type "-.23") without your textbox punishing them
  • Numbers like "-.1e-2" could be considered legal
  • A user could feasibly enter a value which contains only digits, but which would overflow a float
  • The worst one of all: Some other mysterious corner case that your users will find after you've shipped (!!)

The moral? It's can be very tricky to do what you're suggesting.

You probably either want to do a combination of the following:

  • Use a control that someone that knows all the corner cases built (like microsoft)
  • Do basic validation in the TextChanged event (and doing something passive, like changing the textbox background color)
  • Save the validation until you actually try to use the value they've typed in
  • Use the system libraries to Parse the user's value for you
Daniel LeCheminant
Gah, so many edge cases to take care of. I hate users... ;)
@ecarF: No kidding; that's why it's such a pain to really build a robust input control like the OP is asking for.
Daniel LeCheminant
Yup, wait for the user to finish what theyre doing (press OK), then hit the value up with a regex and either continue or throw a MessageBox. Much nicer than overriding OnKeyPress.
Karl
Thanks for putting in the effort. ;)
@Karl: MessageBox is evil and should just die. Disabling ok and providing the reason in some reserved area or around the invalid textbox is a lot easier on the poor user.
VirtualBlackFox
+1 VirtualBlackFox. I'll probably just flash the background of the input control and use a tooltip.
@VirtualBlackFox: Agreed. MessageBoxes for invalid input are sure to drive user's crazy! (Note how my solution says "do something PASSIVE!")
Daniel LeCheminant
@VirtualBlackFox: Screw the user! They're the ones creating the problems in the first place. 11JYN isn't a valid zip code dumba**!!!! :-p
BFree
A: 

I discovered the ValidatingType property of a maskedTextBox:

maskedTextBox1.ValidatingType = typeof(System.Double);

It does indeed tell you whether it's valid or not. Unfortunately, it only seems to validate when focus changes (and even then it doesn't actually do anything); but perhaps there's some way of using this.

Smashery
A: 

Is it necessary to do the validation during data entry, or can you afford to check the final value entered once focus is lost?

If the latter, you can also use an ErrorProvider control to help restrict functionality until the input is resolved.

Dillie-O
Thanks, I guess I should have explained better. I'm validating user-input during data entry.