JonF
JonF

Reputation: 2436

Numericupdown mousewheel event increases decimal more than one increment

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);
            }

        }

Upvotes: 14

Views: 12005

Answers (7)

somebody4
somebody4

Reputation: 515

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

Upvotes: 1

user3610013
user3610013

Reputation: 51

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.

Upvotes: 5

Itay Gal
Itay Gal

Reputation: 322

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;
        }
    }
}

Upvotes: 1

vjou
vjou

Reputation: 103

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));
}

Upvotes: 8

Evan
Evan

Reputation: 141

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;
    }
}

Upvotes: 14

Paccc
Paccc

Reputation: 1211

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));
                    }
                }
            }
        }
    }
}

Upvotes: 5

Jay Riggs
Jay Riggs

Reputation: 53593

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
} 

Upvotes: 0

Related Questions