Click here to Skip to main content
11,490,292 members (52,847 online)
Click here to Skip to main content

Extended NumericUpDown Control

, 5 May 2014 CPOL 106.2K 2.2K 104
Rate this:
Please Sign up or sign in to vote.
An extended NumericUpDown control with better focus and mouse wheel management.

Introduction

If you have ever written a data-entry application, there's a big chance you used the NumericUpDown control. This control is great to provide a field to enter numeric values, with advanced features like up-down buttons and accelerating auto-repeat.

The other side of the coin is that NumericUpDown is not really mouse-aware. I experienced some bugs and bad behaviors:

  • I need to select all the text when it gets focus (see below), but it misses some of the TextBox properties, like SelectedText, SelectionStart, SelectionLength (an AutoSelect property will be useful).
  • Some of the standard events are not working properly (see below): MouseEnter, MouseLeave.
  • Rotating the mouse wheel when the control is focused causes its value to change. A property to change this behavior, like InterceptArrowsKeys for up/down keys, will be useful.

That's why I decided to subclass it, fixing these points and adding missing features and properties.

Missing TextBox Properties

I needed some missing TextBox properties when I was asked to select all the text in the control when it got the focus.

Yes, NumericUpDown exposes a Select(int Start, int Length) method you can call to select all text. At first try, I attached to the GotFocus event to call Select(0, x) but, hey, wait a moment... what should I use for x? It seems that any value is accepted, even if greater than the text length. OK, let's say x=100 and proceed. This works well with the keyboard focus keys (like TAB), but it's completely useless with the mouse: a mouse click raises the GotFocus event (where I select all the text), but as soon as you release the button, a zero-selection is done, leaving the control with no selection. OK, I thought, let's add a SelectAll on the MouseUp event too, but this way, the user cannot perform a partial selection anymore; each time the mouse button is released, all the text is selected. I need to know if a partial selection exists; in a TextBox, I can test it with SelectionLength > 0, so I need to access the underlying TextBox control.

Now comes the tricky part: NumericUpDown is a composite control, a TextBox and a button box. Looking inside it through the Reflector, we can find the internal field which holds the textbox part:

Friend upDownEdit As UpDownEdit  ' UpDownEdit inherits from TextBox

We'll obtain a reference to this field using the underlying Controls() collection. Note that we should add some safety checks because future .NET Framework implementations could change things.

''' <summary>
''' object creator
''' </summary>
Public Sub New()
    MyBase.New()
    ' get a reference to the underlying UpDownButtons field
    ' Underlying private type is System.Windows.Forms.UpDownBase+UpDownButtons
    _upDownButtons = MyBase.Controls(0)
    If _upDownButtons Is Nothing _
           OrElse _upDownButtons.GetType().FullName <> _
           "System.Windows.Forms.UpDownBase+UpDownButtons" Then
        Throw New ArgumentNullException(Me.GetType.FullName & _
        ": Can't a reference to internal UpDown buttons field.")
    End If
    ' Get a reference to the underlying TextBox field.
    ' Underlying private type is System.Windows.Forms.UpDownBase+UpDownButtons
    _textbox = TryCast(MyBase.Controls(1), TextBox)
    If _textbox Is Nothing _
           OrElse _textbox.GetType().FullName <> _
           "System.Windows.Forms.UpDownBase+UpDownEdit" Then
        Throw New ArgumentNullException(Me.GetType.FullName & _
        ": Can't get a reference to internal TextBox field.")
    End If
End Sub 

Now that we have the underlying TextBox, it is possible to export some missing properties:

<Browsable(False)>
<DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)>
Public Property SelectionStart() As Integer
    Get
        Return _textbox.SelectionStart
    End Get
    Set(ByVal value As Integer)
        _textbox.SelectionStart = value
    End Set
End Property

And finally, we can have a perfectly working mouse management:

' MouseUp will kill the SelectAll made on GotFocus.
' Will restore it, but only if user have not made
' a partial text selection.
Protected Overrides Sub OnMouseUp(ByVal mevent As MouseEventArgs)
    If _autoSelect AndAlso _textbox.SelectionLength = 0 Then
        _textbox.SelectAll()
    End If
    MyBase.OnMouseUp(mevent)
End Sub

Mouse Events Not Raised Properly

The original MouseEnter and MouseLeave events are raised in couples: a MouseEnter immediately followed by a MouseLeave. Maybe that's why, to discourage their use, they are marked with a <Browsable(False)> attribute. Since I need the MouseEnter event to update my StatusBar caption, I investigated a little on this "bug".

As said above, NumericUpDown is a composite control (red rectangle in the following picture) containing a TextBox (left green rectangle) and some other controls:

The "control" area is the one between the red and the green rectangles; when you fly over it with the mouse, you'll receive the MouseEnter event while between the red and the green, then MouseLeave when inside the green rectangle. The same happens when you leave.

The better way to raise these events, now that we can access the underlying TextBox, is to re-raise the MouseEnter and MouseLeave events as raised from the TextBox itself; this is what NumericUpDownEx does.

MouseWheel Management

NumericUpDown's management of the mouse wheel is, sometimes, really annoying. Suppose you have an application which displays some kind of chart, with a topmost dialog (toolbox) to let the user change some parameters of the graph. In this dialog, the only controls which can keep the focus are NumericUpDown ones:

After your user puts the focus inside one of them, the mouse wheel is captured by the NumericUpDown. When the user wheels to, say, scroll the graph, the effect is that the focused field value is changed; this behavior is really annoying.

A fix could be to kill the WM_MOUSEWHEEL message for the control, but this will kill even "legal" wheelings.

The NumericUpDown has a property which allows WM_MOUSEWHEEL messages to pass only if the mouse pointer is over the control, making sure that the user is wheeling to change the control value.

This is done by keeping track of the mouse state in the MouseEnter-MouseLeave events, then killing WM_MOUSEWHEEL messages accordingly.

How to Use the Control

Simply include NumericUpDownEx.vb in your project and use the control like you'll do with the standard NumericUpDown. If you have a C# project, you could reference the CoolSoft.NumericUpDownEx.dll assembly or, better, try to convert the code to C# (it should not be so difficult). I could provide a C# version upon request.

Updates

v1.5 (28/Mar/2014)

  • Removed reflection code, now underlying controls are retrieved with managed code only (thanks to JWhattam for this suggestion)

v1.4 (17/Dec/2012)

  • New option to show up/down buttons when the control has focus (regardless of mouseover), thanks to Fred Kreppert for his suggestion

v1.3 (15/Mar/2010)

  • Added new WrapValue property: when set, if Maximum is reached during an increment, Value will restart from Minimum (and vice versa)
    (feature suggested by YosiHakel here)
  • Cleaned up the C# version

v1.2 (10/Feb/2010)

  • Added two new events BeforeValueDecrement and BeforeValueIncrement, as suggested by andrea@gmi. This will allow to give different increment/decrement depending on the current control value
  • Added a C# version of the control to the ZIP

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Claudio Nicora
Technical Lead CoolSoft
Italy Italy
I started programming in the early 1984, when I was 12, using each and every version of VB, from QuickBasic (1985) to VB.NET 11.0, but also C#, C++, PHP, JavaScript, Fortran, Pascal, Modula2, together with a lot of frameworks like Symfony, jQuery, Drupal.

My most popular project is VirtualMIDISynth, a software MIDI synthesizer (using .sf2 soundfont files) implemented as a virtual device driver. It works on all modern Windows OS from XP to 8, both 32 and 64 bit.

Another project of mine is DeCodEx (DEsigner CODe EXtractor), a free tool to split VisualStudio 2003 forms and controls source code into the new 2005/2008 partial classes format (*.vb and *.Designer.vb, or *.cs and *.Designer.cs).

I like writing tools to make my seveloper and SysAdmin life easier.

You can find them here: http://coolsoft.altervista.org.
Follow on   LinkedIn

Comments and Discussions

 
QuestionNumericUpDOwn control in c# Pin
Member 939044415-May-15 1:32
memberMember 939044415-May-15 1:32 
AnswerRe: NumericUpDOwn control in c# Pin
Claudio Nicora15-May-15 2:31
memberClaudio Nicora15-May-15 2:31 
GeneralRe: NumericUpDOwn control in c# Pin
Member 939044415-May-15 3:44
memberMember 939044415-May-15 3:44 
SuggestionEasier way to access contained controls Pin
JWhattam25-Mar-14 18:45
memberJWhattam25-Mar-14 18:45 
GeneralRe: Easier way to access contained controls Pin
Claudio Nicora28-Mar-14 0:21
memberClaudio Nicora28-Mar-14 0:21 
Questionx86 to x64 produces designer errors? Pin
codetowns23-Jun-13 1:36
membercodetowns23-Jun-13 1:36 
AnswerRe: x86 to x64 produces designer errors? Pin
Claudio Nicora23-Jun-13 21:55
memberClaudio Nicora23-Jun-13 21:55 
GeneralRe: x86 to x64 produces designer errors? Pin
codetowns23-Jun-13 22:24
membercodetowns23-Jun-13 22:24 
GeneralMouse wheel usage Pin
cvogt6145720-Mar-13 4:35
membercvogt6145720-Mar-13 4:35 
SuggestionAddtional feature - display updown buttons with focus Pin
Fred Kreppert15-Dec-12 9:14
memberFred Kreppert15-Dec-12 9:14 
Hi Claudio,

I love this control enhancement and will find many uses for it. Smile | :)

I found that there could be some confusion though if the buttons are hidden until the mouse is moved over the control. A user may not realize that it is an updown control unless they just happen to move the mouse over the control, and instead, use the keyboard to move to the field and change the value. They might not be aware that the arrow keys would be enabled to increase and decrease the value either, which would take away some of the functionality designed into it under those conditions.

To solve this dilema I modified your code to add a feature which allows the updown buttons to be displayed whenever the text box has the focus. I also realize the advantage of showing the updown buttons when the mouse is over the field as well, even when the text box doesn't have the focus, so having either the focus or the mouseover feature is yet another option.

To add this feature I added 2 additional modes to ShowUpDownButtonsMode as follows:
public enum ShowUpDownButtonsMode
{
    /// <summary>UpDownButtons are always visible (default behavior)</summary>
    Always,
    /// <summary>UpDownButtons are visble only when mouse is over the control</summary>
    WhenMouseOver,
    /// <summary>UpDownButtons are visible only when control has the focus</summary>
    WhenFocus,
    /// <summary>UpDownButtons are visible when control has focus or mouse is over the control</summary>
    WhenFocusOrMouseOver
}

Below the definition for _mouseOver I added a definition for _haveFocus as follows:
// flag to track mouse position
private bool _mouseOver = false;
// flag to track focus
private bool _haveFocus = false;

We now need to use this variable to track when the control has the focus. That is done by modifying the OnGotFocus override function and adding the equivalent OnLostFocus override function as follows: (Note that we also update the button visibility if one of the focus modes is being used.)
// select all the text on focus enter
protected override void OnGotFocus(EventArgs e)
{
    _haveFocus = true;
    if (_autoSelect)
    {
        _textbox.SelectAll();
    }
    // Update UpDownButtons visibility
    if (_showUpDownButtons == ShowUpDownButtonsMode.WhenFocus | _showUpDownButtons == ShowUpDownButtonsMode.WhenFocusOrMouseOver)
    {
        UpdateUpDownButtonsVisibility();
    }
    base.OnGotFocus(e);
}
 
// indicate that we have lost the focus
protected override void OnLostFocus(EventArgs e)
{
    _haveFocus = false;
    // Update UpDownButtons visibility
    if (_showUpDownButtons == ShowUpDownButtonsMode.WhenFocus | _showUpDownButtons == ShowUpDownButtonsMode.WhenFocusOrMouseOver)
    {
        UpdateUpDownButtonsVisibility();
    }
    base.OnLostFocus(e);
}

To save a few nanoseconds of CPU time we could also modify the MouseEnterLeave function to accommodate the WhenFocus mode since that mode is irrelevent of mouse movement, but it is not necessary to make it work properly. We do however, need to modify the UpdateUpDownButtonsVisibility function to accommodate our 2 new modes. That is done as follows:
public void UpdateUpDownButtonsVisibility()
{
    // test new state
    bool newVisible = false;
    switch (_showUpDownButtons)
    {
        case ShowUpDownButtonsMode.WhenMouseOver:
            newVisible = _mouseOver;
            break;
        case ShowUpDownButtonsMode.WhenFocus:
            newVisible = _haveFocus;
            break;
        case ShowUpDownButtonsMode.WhenFocusOrMouseOver:
            newVisible = _mouseOver | _haveFocus;
            break;
        default:
            newVisible = true;
            break;
    }
 
    // assign only if needed
    .....
}

That is all the code we need to modify. The control now has the ability to show the updown buttons when it has the focus, but not show them when it doesn't have the focus. It also has the ability to combine that with showing the updown buttons whenever the mouse is over the control as well. The designer can now choose any of 4 modes for when the updown buttons are displayed, depending upon the needs for its use.

I hope that someone is able to find this information useful. Big Grin | :-D

Fred
GeneralRe: Addtional feature - display updown buttons with focus Pin
Claudio Nicora17-Dec-12 2:33
memberClaudio Nicora17-Dec-12 2:33 
GeneralRe: Addtional feature - display updown buttons with focus Pin
Fred Kreppert17-Dec-12 6:08
memberFred Kreppert17-Dec-12 6:08 
QuestionIncrement value change at run-time Pin
Blubbo17-May-12 8:51
memberBlubbo17-May-12 8:51 
AnswerRe: Increment value change at run-time Pin
Claudio Nicora20-May-12 22:22
memberClaudio Nicora20-May-12 22:22 
QuestionGreat arrows Pin
silvio pontes9-Nov-11 23:16
membersilvio pontes9-Nov-11 23:16 
AnswerRe: Great arrows Pin
Claudio Nicora4-Dec-11 23:25
memberClaudio Nicora4-Dec-11 23:25 
GeneralBehavior when user clears the text Pin
MWBate14-Feb-11 9:26
memberMWBate14-Feb-11 9:26 
GeneralHiding the Up/Down arrows Pin
Orf17-Dec-10 12:29
memberOrf17-Dec-10 12:29 
QuestionNumericUpDown click-to-send feature C# missing ?? Pin
Sam Tal30-Sep-10 20:57
memberSam Tal30-Sep-10 20:57 
AnswerRe: NumericUpDown click-to-send feature C# missing ?? Pin
Claudio Nicora30-Sep-10 21:58
memberClaudio Nicora30-Sep-10 21:58 
GeneralLocation change problem Pin
pampasit25-May-10 18:15
memberpampasit25-May-10 18:15 
GeneralRe: Location change problem Pin
Claudio Nicora25-May-10 21:58
memberClaudio Nicora25-May-10 21:58 
GeneralRe: Location change problem Pin
pampasit25-May-10 23:05
memberpampasit25-May-10 23:05 
GeneralRe: Location change problem Pin
Claudio Nicora26-May-10 6:32
memberClaudio Nicora26-May-10 6:32 
GeneralRe: Location change problem Pin
pampasit26-May-10 17:09
memberpampasit26-May-10 17:09 
GeneralFeature suggestion - wrap min / max values Pin
YosiHakel13-Mar-10 6:33
memberYosiHakel13-Mar-10 6:33 
GeneralRe: Feature suggestion - wrap min / max values Pin
Claudio Nicora15-Mar-10 10:29
memberClaudio Nicora15-Mar-10 10:29 
GeneralRe: Feature suggestion - wrap min / max values Pin
YosiHakel15-Mar-10 12:10
memberYosiHakel15-Mar-10 12:10 
GeneralIn need of c# version Pin
Member 7795279-Feb-10 13:19
memberMember 7795279-Feb-10 13:19 
GeneralRe: In need of c# version Pin
Claudio Nicora9-Feb-10 22:27
memberClaudio Nicora9-Feb-10 22:27 
GeneralRe: In need of c# version Pin
Ray Cassick12-Feb-10 3:00
memberRay Cassick12-Feb-10 3:00 
GeneralRe: In need of c# version Pin
Claudio Nicora12-Feb-10 4:40
memberClaudio Nicora12-Feb-10 4:40 
GeneralRe: In need of c# version Pin
Johnny J.13-Mar-13 14:46
memberJohnny J.13-Mar-13 14:46 
GeneralRe: In need of c# version Pin
Claudio Nicora13-Mar-13 22:47
memberClaudio Nicora13-Mar-13 22:47 
GeneralRe: In need of c# version Pin
Eric Lapouge1-Jun-13 9:22
memberEric Lapouge1-Jun-13 9:22 
GeneralRe: In need of c# version Pin
Claudio Nicora2-Jun-13 1:01
memberClaudio Nicora2-Jun-13 1:01 
GeneralLittle improvement Pin
andrea@gmi28-May-09 0:39
memberandrea@gmi28-May-09 0:39 
GeneralNew version available Pin
Claudio Nicora21-Jan-09 5:52
memberClaudio Nicora21-Jan-09 5:52 
GeneralRe: New version available Pin
Berryl Hesh22-Jan-09 5:00
memberBerryl Hesh22-Jan-09 5:00 
GeneralRe: New version available Pin
Claudio Nicora22-Jan-09 7:20
memberClaudio Nicora22-Jan-09 7:20 
GeneralUpDownButtons and MouseLeave Pin
M McCracken9-Dec-08 11:24
memberM McCracken9-Dec-08 11:24 
GeneralRe: UpDownButtons and MouseLeave Pin
Claudio Nicora9-Dec-08 23:48
memberClaudio Nicora9-Dec-08 23:48 
GeneralRe: UpDownButtons and MouseLeave [modified] Pin
M McCracken10-Dec-08 3:20
memberM McCracken10-Dec-08 3:20 
GeneralRe: UpDownButtons and MouseLeave [modified] Pin
Claudio Nicora10-Dec-08 22:08
memberClaudio Nicora10-Dec-08 22:08 
GeneralRe: UpDownButtons and MouseLeave Pin
Berryl Hesh18-Jan-09 6:49
memberBerryl Hesh18-Jan-09 6:49 
GeneralRe: UpDownButtons and MouseLeave Pin
Claudio Nicora20-Jan-09 3:14
memberClaudio Nicora20-Jan-09 3:14 
GeneralRe: UpDownButtons and MouseLeave Pin
Berryl Hesh20-Jan-09 8:18
memberBerryl Hesh20-Jan-09 8:18 
GeneralFormatting the value Pin
doshe4-Dec-08 12:18
memberdoshe4-Dec-08 12:18 
GeneralRe: Formatting the value Pin
Claudio Nicora4-Dec-08 22:51
memberClaudio Nicora4-Dec-08 22:51 
GeneralGreat Pin
Member 468474525-Nov-08 7:38
memberMember 468474525-Nov-08 7:38 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.150520.1 | Last Updated 5 May 2014
Article Copyright 2008 by Claudio Nicora
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid