4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

NumericUpDownを改造してみる (C#)

Last updated at Posted at 2019-10-05

はじめに

C#でNumericUpDownを使おうとすると,いろいろと不満が出てくることがあります。
例えば

  • マウスホイール操作時の増分を変更したい
  • Incrementに負の値を設定したい (稀に需要がある)
  • intで値を操作できると嬉しい

といった感じです。
文句があるなら自作してしまえということで上記の不満を解決してくれるコントロールを作成しました。
(使いまわせばいいのですがいつも探し回ることになるので自分用のメモも兼ねて投稿)

ソースコード

using System;
using System.Windows.Forms;

namespace Qiita
{
    public class MyNumericUpDown : NumericUpDown
    {
        private int _abs_increment, _increment;
        private int _abs_scroll_increment, _scroll_increment;

        /// <summary>
        /// Gets or sets the value to increment or decrement the spin box (also known as an up-down control) when the up or down buttons are clicked.
        /// </summary>
        new public int Increment
        {
            set
            {
                this._increment = value;
                this._abs_increment = Math.Abs(value);
            }
            get => this._increment;
        }

        /// <summary>
        /// Gets or sets the value to increment or decrement the spin box (also known as an up-down control) when th mousee wheel spined.
        /// </summary>
        public int ScrollIncrement
        {
            set
            {
                this._scroll_increment = value;
                this._abs_scroll_increment = Math.Abs(value);
            }
            get => this._scroll_increment;
        }

        public int ValueAsInt
        {
            set => this.Value = value;
            get => (int)this.Value;
        }

        public MyNumericUpDown() : base()
        {
            this.Increment= 1;
            this.ScrollIncrement= 1;
            this.ImeMode = ImeMode.Disable;
        } // ctor ()

        override public void UpButton() => UpDown(this._increment > 0);

        override public void DownButton() => UpDown(this._increment < 0);

        private void UpDown(bool up)
            => this.Value = up
                ? Math.Min(this.Value + this._abs_increment, this.Maximum) // increment
                : Math.Max(this.Value - this._abs_increment, this.Minimum) // decrement
                ;

        override protected void OnMouseWheel(MouseEventArgs e)
        {
            if (e is HandledMouseEventArgs hme) hme.Handled = true;

            this.Value = e.Delta > 0 ^ this._scroll_increment > 0
                ? Math.Max(this.Value - this._abs_increment, this.Minimum) // decrement
                : Math.Min(this.Value + this._abs_increment, this.Maximum) // increment
                ;
        } // override protected void OnMouseWheel (MouseEventArgs)
    } // public class MyNumericUpDown : NumericUpDown
} // namespace Qiita

解説

ここではC#の基本的な構文(e.g., usingディレクティブ)の解説はしません。

フィールド

ボタン,マウスホイール操作時の増分を保持するためのフィールドです。
絶対値を使用することも多いので,絶対値も持っておけるようにしてあります。

プロパティ

IncrementScrollIncrement

それぞれ,ボタン,マウスホイール操作時の増分を表しています。
値の設定時には,それぞれの値の絶対値を計算して保存しています。
Incrementについては親に同名のプロパティが存在するためnewで修飾しています。

ValueAsInt

Valueintで操作したいという要望に応えるべく追加したプロパティです。
実装はご覧の通りです。

コンストラクタ

C#では値型は全ビットが0で初期化されるので,何もしないとIncrementScrollIncrementが0になってしまいます。
それではあまりにもかわいそうなことになってしまうので,初期値を設定しています。

メソッド

UpButtonDownButton

この人たちをオーバーライドすることで矢印ボタンクリック時の挙動を変更できます。
ここではbool型の引数を取るUpDownを参照しています。
渡している値についてはUpDownの項で説明します。

UpDown

ボタン操作時の動作を規定しているメソッドです。

引数

引数は,「(Incrementプロパティの符号に関わらず)Valueが増加する方向に変化するか」を表しています。
例えば,上向きのボタンが押された場合

  • _incrementが正ならば増加
  • _incrementが負ならば減少

となるので,「増加するかどうか」は_increment > 0で表せます。
下向きのボタンについては条件が逆になるだけで考え方は同じです。

値の更新

特に難しいことはしていません。
増加の場合は,増分を足した値とMaximumの小さい方をMath.Minで選択することで最大値を超えないようにしています。
減少の場合も考え方は同じです。

本当はオーバーフローした際には最大値/最小値を設定するようにした方がいいような気はしますが,そこまで絶対値が大きい値を扱う需要が今のところないので実装していません。 (手抜き)

OnMouseWheel

この人をオーバーライドするとマウスホイール操作時の挙動を変更できます。
やりたいことはUpDownと同じなのですが,増加か減少かの判定が若干怪しいので少しまとめておきます。

増加させるか減少させるかを表にまとめると以下のようになります。

マウスホイール操作方向 _scroll_increment > 0 _scroll_increment < 0
増加 減少
減少 増加

この表を眺めていると,なんとなく排他的論理和に見えてきます。
マウスホイールの操作量はe.Deltaで取得できるので,排他的論理和をとってe.Delta > 0 ^ _scroll_increment > 0で増加か減少かを判定できます。
ここまで来れば,あとはやっていることはUpDownと全く同じです。

さいごに

この記事で紹介したソースコードは自由に利用していただいて構いません。
よりよい書き方や改善すべき点などがありましたら(C#のコードもQiitaの記事も),コメントなどで教えていただけると幸いです。

追加・変更点など

変更 (2019/10/06)

増加/減少の処理の実装を条件演算子とMath.Min/Math.Minを使用するものに変更しました。

4
4
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?