Numericupdown mousewheel event increases decimal more than one increment
Asked Answered
C

7

14

I'm trying to override the mousewheel control so that when the mouse wheel is moved up or down it only increases the value in the numericupdown field by 1. I believe it is currently using what is stored in the control panel and increasing/decreasing the value by 3 each time.

I'm using the following code. Even when numberOfTextLinesToMove is only 1 and I see that txtPrice.Value is getting populated as expected, something else is overwriting it because the value I set is not what is displayed in the numericupdown box

void txtPrice_MouseWheel(object sender, MouseEventArgs e)
        {
            int numberOfTextLinesToMove = e.Delta  / 120;
            if (numberOfTextLinesToMove > 0)
            {
                txtPrice.Value = txtPrice.Value + (txtPrice.Increment * numberOfTextLinesToMove);
            }
            else 
            {

                txtPrice.Value = txtPrice.Value - (txtPrice.Increment * numberOfTextLinesToMove);
            }

        }
Cheroot answered 7/3, 2011 at 23:40 Comment(0)
G
0

This is a bug reported here: NumericUpDown - use of mouse wheel may result in different increment

Microsoft's response in Feb 2007 states they cannot address this Visual Studio 2008.

There are two posted workarounds, both of which subclass NumericUpDown. Check the Workaround tab on the link.

The one I tried worked for me (posted by 'NanoWizard'):

using System;
using System.Windows.Forms;

internal class NumericUpDownControl : NumericUpDown
{
#region Constants
protected const String UpKey = "{UP}";
protected const String DownKey = "{DOWN}";
#endregion Constants

#region Base Class Overrides
protected override void OnMouseWheel(MouseEventArgs e_)
{
    String key = GetKey(e_.Delta);
    SendKeys.Send(key);
}
#endregion Base Class Overrides

#region Protected Methods
protected static String GetKey(int delta_)
{
    String key = (delta_ < 0) ? DownKey : UpKey;
    return key;
}
#endregion Protected Methods
} 
Goddaughter answered 8/3, 2011 at 0:10 Comment(3)
It is quite yucky code but I suppose it works most of the time. Don't put it on a scrollable panel. Best thing to do is to start with the code you can get from Reflector or the Reference Source, UpDownBase.OnMouseWheel method. Just replace the call to SystemInformation.MouseWheelScrollLines with 1.Premed
I agree it's yucky, why not: protected override void OnMouseWheel(MouseEventArgs e) { decimal newValue = Value; if (e.Delta > 0) newValue += MouseWheelIncrement; else newValue -= MouseWheelIncrement; if (newValue > Maximum) newValue = Maximum; else if (newValue < Minimum) newValue = Minimum; Value = newValue; }Gillen
The answer mentions Visual Studio 2008, but note that this is not fixed in Visual Studio 2015. Also, the bug report link is dead and I can't find any archives of it. It's too bad, I would have liked to read it, I was curious as to why the mouse wheel event didn't use the built in Increment property value. Might be a good topic for a Raymond Chen blog?Jannelle
B
14

I recently had this problem, and got around it by changing the increment.

numericUpDown1.Increment = 1m / SystemInformation.MouseWheelScrollLines;

Edit: This is only a good solution if you intend to only use the mousewheel to change the value. To fix this for all situations, you have to override the class. Here's a simple version.

public class NumericUpDownFix : System.Windows.Forms.NumericUpDown
{
    protected override void OnMouseWheel(MouseEventArgs e)
    {
        HandledMouseEventArgs hme = e as HandledMouseEventArgs;
        if (hme != null)
            hme.Handled = true;

        if (e.Delta > 0)
            this.Value += this.Increment;
        else if (e.Delta < 0)
            this.Value -= this.Increment;
    }
}
Bores answered 2/5, 2013 at 12:25 Comment(1)
+1 for sleek solution, but add bounds-checking. I used if (e.Delta > 0 && (this.Value + this.Increment) <= this.Maximum) and else if (e.Delta < 0 && (this.Value - this.Increment) >= this.Minimum).Colcannon
N
7

The user3610013's answer is working very well. This is a slight modification and may be handy to someone.

private void ScrollHandlerFunction(object sender, MouseEventArgs e)
{
    NumericUpDown control = (NumericUpDown)sender;
    ((HandledMouseEventArgs)e).Handled = true;
    decimal value = control.Value + ((e.Delta > 0) ? control.Increment : -control.Increment);
    control.Value = Math.Max(control.Minimum, Math.Min(value, control.Maximum));
}
Nicholson answered 5/9, 2014 at 19:25 Comment(0)
F
5

I took Hans Passant's advice and subclassed NumericUpDown to add this functionality. It uses a modified version of Reflector's code for the OnMouseWheel method. Here's what I ended up with for anyone who's interested.

using System;
using System.ComponentModel;
using System.Windows.Forms;

/// <summary>
/// Extends the NumericUpDown control by providing a property to 
/// set the number of steps that are added to or subtracted from
/// the value when the mouse wheel is scrolled.
/// </summary>
public class NumericUpDownExt : NumericUpDown
{
    private int wheelDelta = 0;
    private int mouseWheelScrollLines = 1;

    /// <summary>
    /// Gets or sets the number of step sizes to increment or 
    /// decrement from the value when the mouse wheel is scrolled.
    /// Set this value to -1 to use the system default.
    /// </summary>
    [DefaultValue(1)]
    [Description("Gets or sets the number of step sizes to increment or decrement from the value when the mouse wheel is scrolled. Set this value to -1 to use the system default.")]
    public Int32 MouseWheelScrollLines {
        get { return mouseWheelScrollLines; }
        set {
            if (value < -1)
                throw new ArgumentOutOfRangeException("value must be greater than or equal to -1.");
            if (value == -1)
                mouseWheelScrollLines = SystemInformation.MouseWheelScrollLines;
            else
                mouseWheelScrollLines = value;
        }
    }

    protected override void OnMouseWheel(MouseEventArgs e) {
        HandledMouseEventArgs args = e as HandledMouseEventArgs;
        if (args != null) {
            if (args.Handled) {
                base.OnMouseWheel(e);
                return;
            }
            args.Handled = true;
        }

        base.OnMouseWheel(e);

        if ((Control.ModifierKeys & (Keys.Alt | Keys.Shift)) == Keys.None && Control.MouseButtons == MouseButtons.None) {
            if (mouseWheelScrollLines != 0) {
                this.wheelDelta += e.Delta;
                float num2 = (float)this.wheelDelta / 120f;
                if (mouseWheelScrollLines == -1) 
                    mouseWheelScrollLines = 1;
                int num3 = (int)(mouseWheelScrollLines * num2);
                if (num3 != 0) {
                    int num4;
                    if (num3 > 0) {
                        for (num4 = num3; num4 > 0; num4--) 
                            this.UpButton();
                        this.wheelDelta -= (int)(num3 * (120f / (float)mouseWheelScrollLines));
                    } else {
                        for (num4 = -num3; num4 > 0; num4--) 
                            this.DownButton();
                        this.wheelDelta -= (int)(num3 * (120f / (float)mouseWheelScrollLines));
                    }
                }
            }
        }
    }
}
Fatness answered 10/1, 2013 at 17:57 Comment(0)
W
5

After looking around at similar questions, I discovered that you can also work around this without creating a subclass - for example, this code will give you a numericUpDown that will only scroll by one (and only when the control has focus).

Set up the delegate in the container's constructor:

numericUpDown1.MouseWheel += new MouseEventHandler(this.ScrollHandlerFunction);

... then define the function elsewhere in the class:

private void ScrollHandlerFunction(object sender, MouseEventArgs e)
{
    HandledMouseEventArgs handledArgs = e as HandledMouseEventArgs;
    handledArgs.Handled = true;
    numericUpDown1.Value += (handledArgs.Delta > 0) ? 1 : -1;
}

You should also check to make sure that you're not trying to scroll out of the control's range, because that will crash.

Wilfredwilfreda answered 6/5, 2014 at 21:54 Comment(2)
This does not compile - handledArgs should presumably be handledArgs.Handled; also this does not deal with values that may e.g. exceed the control's Maximum property. See vjou's answer for a much better version.Kaja
Works perfectly! - C#.net 4.7Asclepiadaceous
G
1

Based on Jay Riggs answer, here is a cleaner version (for my opinion):

namespace MyControls
{
    public partial class NumericUpDown : System.Windows.Forms.NumericUpDown
    {
        public Decimal MouseWheelIncrement { get; set; }

        public NumericUpDown()
        {
            MouseWheelIncrement = 1;
            InitializeComponent();
        }

        protected override void OnMouseWheel(MouseEventArgs e)
        {
            decimal newValue = Value;
            if (e.Delta > 0)
                newValue += MouseWheelIncrement;
            else
                newValue -= MouseWheelIncrement;
            if (newValue > Maximum)
                newValue = Maximum;
            else
                if (newValue < Minimum)
                    newValue = Minimum;
            Value = newValue;
        }
    }
}
Gillen answered 17/12, 2014 at 7:36 Comment(0)
C
1

The following code will walk down your entire Form and fix every decendent NumericUpDown Control.

Rewritten based on this and this

add the following code to your class (or some utility class):

static void FixNumericUpDownMouseWheel(Control c) {
    foreach (var num in c.Controls.OfType<NumericUpDown>())
        num.MouseWheel += FixNumericUpDownMouseWheelHandler;

    foreach (var child in c.Controls.OfType<Control>())
        FixNumericUpDownMouseWheel(child);
}

static private void FixNumericUpDownMouseWheelHandler(object sender, MouseEventArgs e) {
    ((HandledMouseEventArgs)e).Handled = true;
    var self = ((NumericUpDown)sender);
    self.Value = Math.Max(Math.Min(
        self.Value + ((e.Delta > 0) ? self.Increment : -self.Increment), self.Maximum), self.Minimum);

}

then in contructor of the form, after InitializeComponent(), insert line:

FixNumericUpDownMouseWheel(this);
Captor answered 18/7, 2018 at 5:25 Comment(0)
G
0

This is a bug reported here: NumericUpDown - use of mouse wheel may result in different increment

Microsoft's response in Feb 2007 states they cannot address this Visual Studio 2008.

There are two posted workarounds, both of which subclass NumericUpDown. Check the Workaround tab on the link.

The one I tried worked for me (posted by 'NanoWizard'):

using System;
using System.Windows.Forms;

internal class NumericUpDownControl : NumericUpDown
{
#region Constants
protected const String UpKey = "{UP}";
protected const String DownKey = "{DOWN}";
#endregion Constants

#region Base Class Overrides
protected override void OnMouseWheel(MouseEventArgs e_)
{
    String key = GetKey(e_.Delta);
    SendKeys.Send(key);
}
#endregion Base Class Overrides

#region Protected Methods
protected static String GetKey(int delta_)
{
    String key = (delta_ < 0) ? DownKey : UpKey;
    return key;
}
#endregion Protected Methods
} 
Goddaughter answered 8/3, 2011 at 0:10 Comment(3)
It is quite yucky code but I suppose it works most of the time. Don't put it on a scrollable panel. Best thing to do is to start with the code you can get from Reflector or the Reference Source, UpDownBase.OnMouseWheel method. Just replace the call to SystemInformation.MouseWheelScrollLines with 1.Premed
I agree it's yucky, why not: protected override void OnMouseWheel(MouseEventArgs e) { decimal newValue = Value; if (e.Delta > 0) newValue += MouseWheelIncrement; else newValue -= MouseWheelIncrement; if (newValue > Maximum) newValue = Maximum; else if (newValue < Minimum) newValue = Minimum; Value = newValue; }Gillen
The answer mentions Visual Studio 2008, but note that this is not fixed in Visual Studio 2015. Also, the bug report link is dead and I can't find any archives of it. It's too bad, I would have liked to read it, I was curious as to why the mouse wheel event didn't use the built in Increment property value. Might be a good topic for a Raymond Chen blog?Jannelle

© 2022 - 2024 — McMap. All rights reserved.