以下のコードがよいと思います。
Public Class SlowWheelNumericUpDown
Inherits NumericUpDown
Private wheeldelta As Integer = 0
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
If m.Msg = &H20A Then
If SystemInformation.MouseWheelScrollLines > 0 Then
Dim delta As Integer
wheeldelta += (m.WParam.ToInt32 >> 16)
delta = (wheeldelta \ 120) * 120 \ SystemInformation.MouseWheelScrollLines
wheeldelta = wheeldelta Mod 120
m.WParam = New IntPtr((delta << 16) Or (m.WParam.ToInt32 And &HFFFF))
End If
End If
MyBase.WndProc(m)
End Sub
End Class
マウスのホイールによる移動量は将来の拡張やスムーズな移動を実現するために、
ちょっと癖がある仕様になっています。
デフォルトだと
マウスホイールの1目盛りに対して120単位の移動量に相当するメッセージが送られてきて、
それに対して3行スクロールするよう設定されています。
これはマウスのプロパティから設定を変えることができます。
120単位で何行スクロールすべきかは、
SystemInformation.MouseWheelScrollLines
で取得できます。
Windowsの仕様上は、
120単位の整数倍や整数分の1の移動量が送られてくる場合もありえますし、0の場合もあります。
複数回のホイールメッセージが積算されて送られてくることもありえます。
ホイールメッセージを適当に書き換えてマウスの移動量を制御するようなソフトも存在します。
中途半端な移動量が送られてきた場合の実装は実装者にまかされていますが、
積算して120単位ごとにスクロールするとか、
送られてきた量に合わせて少しだけスクロールするとか、
そういった常識的処理をする必要があります。
TextBoxやRichTextBoxは内部で移動量を積算し、
120単位になって初めてスクロールを行います。
一方、NumericUpDownも同じように内部で移動量を積算しますが、
(120単位/MouseWheelScrollLines)で1行スクロールを行います。
仕様上は、送られてくるメッセージの回数が実際のホイール移動回数に対応していない可能性があります。
例えば、システムが重いときはメッセージがまとめられる場合もありえますし、
ホイールの移動量を調節するソフトが入っている場合は
適当に調節された120の整数倍でない移動量が送られてくる場合があります。
ですので、
「マウスの移動量に関係なく+/-1づつ値をnumericUpDown.Valueに入れたい」というのを
メッセージを見て移動量が正の時には+1、負の時には-1、0の時にはなにもしない、というコードで済ませると、
動かしたノッチの数と、結果として動いた量がずれてしまう可能性があります。
マウスホイールの移動に関して決まっているのは
ホイール1ノッチが120単位であることだけですので、
きちんとやるならば、移動量を積算し、120単位ごとに1ノッチと数えなければいけません。
「マウスの移動量に関係なく+/-1づつ値をnumericUpDown.Valueに入れたい」は
「1ノッチで1lineずつスクロールしたい」と読み替えるべきでしょう。
NumericUpDownは(120単位/MouseWheelScrollLines)で1行スクロールを行ってくれますので、
移動量を積算し、総ノッチ数を数え、
ノッチ数 x 1行分の移動量(120単位/MouseWheelScrollLines)を設定したWM_MOUSEWHEELを送ってやれば
きちんと動作してくれます。
TextBoxやRichTextBoxは120単位でしか動かず、
動く場合はMouseWheelScrollLinesだけ動いてしまうので、
この方法ではだめで、自前でスクロール機構をいれなければいけません。