はじめに
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
ディレクティブ)の解説はしません。
フィールド
ボタン,マウスホイール操作時の増分を保持するためのフィールドです。
絶対値を使用することも多いので,絶対値も持っておけるようにしてあります。
プロパティ
Increment
,ScrollIncrement
それぞれ,ボタン,マウスホイール操作時の増分を表しています。
値の設定時には,それぞれの値の絶対値を計算して保存しています。
Increment
については親に同名のプロパティが存在するためnew
で修飾しています。
ValueAsInt
Value
をint
で操作したいという要望に応えるべく追加したプロパティです。
実装はご覧の通りです。
コンストラクタ
C#では値型は全ビットが0で初期化されるので,何もしないとIncrement
とScrollIncrement
が0になってしまいます。
それではあまりにもかわいそうなことになってしまうので,初期値を設定しています。
メソッド
UpButton
,DownButton
この人たちをオーバーライドすることで矢印ボタンクリック時の挙動を変更できます。
ここでは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
を使用するものに変更しました。