Click here to Skip to main content
Click here to Skip to main content

Extended NumericUpDown Control

By , 13 Mar 2013
 

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 Reflection; this is done in the control creator.

''' <summary>
''' object creator
''' </summary>
Public Sub New()
    MyBase.New()
    ' extract a reference to the underlying TextBox field
    _textbox = GetPrivateField(Me)
    If _textbox Is Nothing Then
        Throw New ArgumentNullException(Me.GetType.FullName _
                & ": Can't find internal TextBox field.")
    End If
    ' ...
End Sub


''' <summary>
''' Extracts a reference to the private underlying textbox field
''' </summary>
Private Shared Function GetPrivateField _
                    (ByVal ctrl As NumericUpDownEx) As TextBox
    ' find internal TextBox
    Dim textFieldInfo As Reflection.FieldInfo _
        = GetType(NumericUpDown).GetField("upDownEdit", _
                    Reflection.BindingFlags.FlattenHierarchy _
                    Or Reflection.BindingFlags.NonPublic _
                    Or Reflection.BindingFlags.Instance)
    ' take some caution... they could change field name
    ' in the future!
    If textFieldInfo Is Nothing Then
        Return Nothing
    Else
        Return TryCast(textFieldInfo.GetValue(ctrl), TextBox)
    End If
End Function

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.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)

About the Author

Claudio Nicora
Systems / Hardware Administrator CoolSoft
Italy Italy
Member
I started programming in the early 1984, when I was 12, using each and every version of VB, from QuickBasic (1985) to VB.NET 9.0, but also Fortran, Pascal, Modula2, C++, C#, PHP.
 
I'm the author of 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 Developer and SysAdmin life easier.
 
You can find them here: http://coolsoft.altervista.org.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
Hint: For improved responsiveness ensure Javascript is enabled and choose 'Normal' from the Layout dropdown and hit 'Update'.
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMouse wheel usagemembercvogt6145720 Mar '13 - 3:35 
SuggestionAddtional feature - display updown buttons with focusmemberFred Kreppert15 Dec '12 - 8:14 
GeneralRe: Addtional feature - display updown buttons with focusmemberClaudio Nicora17 Dec '12 - 1:33 
GeneralRe: Addtional feature - display updown buttons with focusmemberFred Kreppert17 Dec '12 - 5:08 
QuestionIncrement value change at run-timememberBlubbo17 May '12 - 7:51 
AnswerRe: Increment value change at run-timememberClaudio Nicora20 May '12 - 21:22 
QuestionGreat arrowsmembersilvio pontes9 Nov '11 - 22:16 
AnswerRe: Great arrowsmemberClaudio Nicora4 Dec '11 - 22:25 
GeneralBehavior when user clears the textmemberMWBate14 Feb '11 - 8:26 
GeneralHiding the Up/Down arrowsmemberOrf17 Dec '10 - 11:29 
QuestionNumericUpDown click-to-send feature C# missing ??memberSam Tal30 Sep '10 - 19:57 
AnswerRe: NumericUpDown click-to-send feature C# missing ??memberClaudio Nicora30 Sep '10 - 20:58 
GeneralLocation change problemmemberpampasit25 May '10 - 17:15 
GeneralRe: Location change problemmemberClaudio Nicora25 May '10 - 20:58 
GeneralRe: Location change problemmemberpampasit25 May '10 - 22:05 
GeneralRe: Location change problemmemberClaudio Nicora26 May '10 - 5:32 
GeneralRe: Location change problemmemberpampasit26 May '10 - 16:09 
GeneralFeature suggestion - wrap min / max valuesmemberYosiHakel13 Mar '10 - 5:33 
GeneralRe: Feature suggestion - wrap min / max valuesmemberClaudio Nicora15 Mar '10 - 9:29 
GeneralRe: Feature suggestion - wrap min / max valuesmemberYosiHakel15 Mar '10 - 11:10 
GeneralIn need of c# versionmemberMember 7795279 Feb '10 - 12:19 
GeneralRe: In need of c# versionmemberClaudio Nicora9 Feb '10 - 21:27 
GeneralRe: In need of c# versionmemberRay Cassick12 Feb '10 - 2:00 
GeneralRe: In need of c# versionmemberClaudio Nicora12 Feb '10 - 3:40 
GeneralRe: In need of c# versionmemberJohnny J.13 Mar '13 - 13:46 

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

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130513.1 | Last Updated 13 Mar 2013
Article Copyright 2008 by Claudio Nicora
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid