Home
Tuesday, 17 October 2017
Main Menu
Home
Blog
News
Software
Forums
Music
Email
Web Links
Blog Links
Musical Links
Nerdery Links
Other Neato Links
Syndicate
 
Fixing what annoys me about the NumericUpDown control...
User Rating: / 4
PoorBest 
Written by WATYF on Monday, 22 August 2005 (17765 hits)
Category: .NET Programming

One of the things that bothers me the most about .NET controls (or... I should say... most any platform's stock controls) is that there's always that one behavior in the control that just totally screws up what you're trying to do with it. I remember this problem haunting me all the way back in my early programming days, as a young pup, working with VBA. I'd be building some groovy new interface in Access that was just gonna "wow" all my coworkers... I'd add a control to my form to perform what I considered to be a perfectly simple, menial task... and I'd throw some code behind it and test out the app, only to find out that there was just one... stupid... little... quirk about the control that grated on me like nails on a chalkboard. And sometimes it wasn't just annoying... it was even conflicting with the way I had coded the form, or causing me to have to add additional code just to account for it. Sometimes, piddly little crap like this would have me hunting for hours (sometimes even days ) just to find a suitable workaround.

There are way too many of these experiences for me to recount right now (not to mention that I've repressed most of those memories by this point), but one such example just happened to me the other day, involving the NumericUpDown control and the wonderful way in which it doesn't bother to validate user input to make sure it stays within the Minimum and Maximum range... so I thought I'd share with you how I got around it.

Like I said, the problem I'm having with the NumericUpDown control is that it doesn't force the validation of the "Minimum" and Maximum" values if you don't set the ReadOnly property to "True". Let me explain a little better... The NumericUpDown control (hereon referred to as the "NUD") is a quaint little control that allows you to give the user a way to select a number between x and y. To control the values that the user can select, you set a Minimum (the lowest number that the NUD will scroll to) and a Maximum (the highest number it will scroll to). But unfortunately, the user can also type directly into the control, and it's possible to type a value that's higher than the Maximum, or lower than the Minimum.

At this point, you're probably asking, "So what's the point of the Minimum and Maximum values?" Well, I'm glad you asked that... because the point is.... abso-freaking-lutely nothing!! The only way to stop them from typing in a value that isn't outside of the desired range is to set the NUD's ReadOnly property to True. This does, not one, but two annoying things. It grays out the edit box of the control, so that it looks like the control is disabled... so now, the user may pass by the control, not even knowing that it's available for them to edit (thanks a lot MS). And it also stops the user from being able to type anything in the NUD, even if it's in the range of the Minimum and Maximum values. In other words, the only way to change the numbers if ReadOnly is on, is to use the scroll arrows. Well, if the numeric range is pretty wide (say, 1 to 1000), then it's gonna take the user an absurdly long time to scroll through all those values.

What they should have done  is created a property similar to the "Limit To List" property found in some MS ComboBox controls. What that does, is allow the user to type in the control, but if what they type doesn't exist in the "list" (or in the case of the NUD, the "range") then it won't let the user exit the control, until they correct it. This way, the user can directly type the value OR use the scroll bars, and you (the lazy programmer) don't have to worry about writing a bunch of code to validate every NUD that you have on your forms, just to make sure they didn't type something outside of your set range.

So... how do we get rid of this annoying behavior? We use a handy-dandy feature in .NET called inheritance. Why make your own custom NUD control from scratch, when you can just inherit the existing NUD and change the things about it that suck?  What we're gonna do is inherit the NUD, and add a property called LimitToRange. Then, we'll catch the Validating event, and force the displayed value to be within the range, if the user typed something outside of the set range (and if our LimitToRange property is set to True, of course). This takes a surprisingly small amount of code, and is pretty darn easy to implement. Here's the code we'll use:

using System.Windows.Forms;
using System.ComponentModel;
 
namespace CustomControls
{
            public class NumericUpDwn : NumericUpDown
            {
                        public NumericUpDwn()
                        {
                                    this.TextAlign = HorizontalAlignment.Center;
                        }
 
                        private bool limitRange = true;
                        [Description("Forces typed values to stay within the Minimum and Maximum range."), Category("Behavior"), DefaultValue(true)]
                        public bool LimitToRange
                        {
                                    get
                                    {
                                                return limitRange;
                                    }
                                    set
                                    {
                                                limitRange = value;
                                    }
                        }
 
                        protected override void OnValidating(CancelEventArgs e)
                        {
                                    if (LimitToRange) {this.Text= this.Value.ToString();}
                                    base.OnValidating(e);
                        }
            }

}

Add that to a Class Library project, compile it to a dll, reference the dll in your Project, and then add the Control to the ToolBox, and you can now use a NUD control in your forms that doesn't cause you all kinds of indegestion.

A few things to note:

I set the name of the custom control to "NumericUpDwn". Some people like to call their custom controls something much different (like "MyCustomUpDown" or whatever), so set it to whatever custom name you like.

I always want my text to be centered in NUD's, so I added a line to force the textalign to center when the control is initialized (this.TextAlign = HorizontalAlignment.Center;). You can get rid of that if you don't like this behavior.

I set the default of the LimitToRange property to True (because I'm always going to want it on), so you can change that as well, if it doesn't suit you.

Most importantly, the primary effect of this control happens in the Validating event, when we do this: if (LimitToRange) {this.Text= this.Value.ToString();} What's happening here is we're catching the Validating event, setting the Text of the control to equal the Value of the control, and letting it continue on with the Validation. Now... you may be wondering why I'm doing this, and more specifically, why I'm not checking the Minimum and Maximum values and forcing the value to stay within that range, if it falls outside of it. Well, the reason is, because the NUD control already does that for you. See... what happens behind the scenes is, if the user enters something outside of the set range, the Text property is set to whatever the user entered, but the Value property is set to the closest value within the set range. So, if the range is 50-100, and they enter 150, the Text property will be set to 150, but the Value property will be set to 100 (the closest value to what they entered that's within the set range). Likewise, if they entered 25, the Text will be 25, but the Value will be 50. It does this because the Value of the control can't be outside of the set range, but the Text can. So, since the NUD already does this validation for us (making sure the Value is always within the set range) all we have to do is make sure that the Text always equals whatever the Value is.

Now... this points out the most annoying behavior about the standard NUD control... the fact that the Text and the Value can be two totally different values. If you don't use some kind of custom validation, and if you aren't aware of this (not very obvious) behavior, then you can get stuck checking the wrong property. Let's say you add a NUD to your form, set the range to 1-50, and don't bother validating. Later on in your code, you check the value of the NUD by using "myNUD.Value". Well, your user enters 75 into the control. The Text is set to 75... the Value (behind the scenes) is set to 50, and when you check the value later on, you'll see "50".... but the user entered "75". So we have a complete disconnect between what your user entered, and what your code thinks they entered. This is never a good thing. Obviously, you can avoid this disconnect by referencing the "Text" property instead, but then the value might be outside of your set range, which is why we have to use a custom control like the one above.

 

WATYF

 
< Prev   Next >

Comments

You must javascript enabled to use this form

By the way, if you set ReadOnly property to true you still can change the background of the control by calling BackColor = Color.White (for example) to override the default 'greyed' background in this mode.

Posted by vic, on 01/28/2007 at 16:15

Vic's comment is corerct plus you could also set the BackColor property of the control in Visual Studio form designer. I know it seems annoying to not validate the Minimum and Maximum values when the user enters a value. But wouldn't it be more annoying if say your minimum value is 100 and maximum value is 500 and as soom as the user starts to type 1, to enter 150, they get hit with an invalid range error. Plus, how is the control supposed to know when the user is finished entering the data? They could be attempting to enter 150 correctly or 15 incorrrectly.

Posted by Anthony, on 03/22/2007 at 12:52

Changing the backcolor makes it look better, but it doesn't solve the problem. The 'look' of the control was just a minor issue... the major issue was that they couldn't type in the control if its ReadOnly was set to true. And you can tell when the user is done because they exit the control. That's when you validate. They don't get hit with an invalid range error while they're typing, because validation doesn't happen until they're done and exit the control (usually by hitting 'OK'). The way it's set up now, there are no downsides, and it addresses every issue I had with the control. WATYF

Posted by WATYF, on 03/22/2007 at 13:52

Cheers for posting this. I thought I was going batty in seeing the braindead behaviour of the standard control, and was struggling to find any mention of the problem at all until I came across your post.

This is a pretty fundamental oversight in my view. You can tell it is an oversight, because if (at anytime?) you access myNUD.Value, the value of the text updates as it should. That means that believe it or not, your validating method could look like this:

decimal holyCrap = this.Value;
base.OnValidating(e);

I found it hard to justify a subclass to fix such a trivial bug, but now I'm worried accessing .Value wont pick up a valid user entered value either...

Might be time for a FixedNumericUpDown.

Posted by Heath Raftery, whose homepage is here on 05/23/2007 at 01:46

 1 
Page 1 of 1 ( 4 Comments )
©2007 MosCom

 

© 2017 Musical Nerdery
Joomla! is Free Software released under the GNU/GPL License.