ゲームエフェクトデザイナーのブログ | A Real-Time VFX Artist's Blog

About Making Materials on UE, Making Tools with C#, etc

C# TextBoxを数値専用にする際のTIPS

フォームアプリでユーザーが数値入力だけ行える入力ボックスを用意したい場合に、コントロールを自作したり継承して改造せずに標準機能だけで実現しようと思うと主に2つの方法があるかと思います。

NumericUpDownを使う方法

1つは NumericUpDown コントロールを使う方法です。

f:id:moko_03_25:20200920162740p:plain

こちらは数値入力のみ入力可能なコントロールで、最小値と最大値を指定するプロパティが用意されているため、容易に0の入力を無効にできます。

描画する線の太さや画像サイズなど 0を指定してはいけない場面は多いため便利です。

ただしいくつか面倒な点があり‥横の上下ボタンが不要な場合に非表示にすると背景画くり抜かれたような見た目になったり、数値を変更した際のイベント「ValueChanged」では数値を変更する前の値しか取得できないため変更後の値を取得するのに困ります‥(こちら勘違いだったり簡単な解決法があれば教えてください)

TextBoxを使う方法

そこで TextBox コントロールを数値入力のみ受け付けるようにして利用する場合ですが、今回は1以上の整数値のみ入力可能にしてあげたかったため、下記のような挙動を実装しました。

・TextBoxがアクティブになったら全選択状態にする
・TextBoxでEnterを押した時も全選択状態にする

・TextBoxでEnterやEscapeキーの入力時にビープ音を鳴らさない

・半角0~9 / Enter / Escape / Delete /Backspace の入力のみ受け付ける

・Enterを押した際に入力が0の場合はエラーメッセージを出す
・Enterを押した際にゼロパディングを打ち消す

・フォーカスが移動した際に入力が0の場合はエラーメッセージを出す
 そして元のTextBoxにフォーカスを戻す
・フォーカスが移動した際にゼロパディングを打ち消す


こうして見ると分かるのですがTextBox1つ追加するのにこれだけ手を入れてやらないと快適にならないというのがUIを作る際の非常に面倒臭いところですね。。

フォーム上に TextBoxを2つ並べた形でテストしました。

f:id:moko_03_25:20200920165506p:plain

そして下記のように実装することでうまくいきました。
(フォームデザイナ側で TextBox の ImeMode Disable に設定しています)

using System;
using System.Windows.Forms;

namespace InputNumber
{
	public partial class Form1 : Form
	{
		double d = 0;

		public Form1()
		{
			InitializeComponent();
		}

		// アクティブになったら選択状態
		private void textBox1_Enter(object sender, EventArgs e)
		{
			textBox1.SelectAll();
		}
		private void textBox2_Enter(object sender, EventArgs e)
		{
			textBox2.SelectAll();
		}

		private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
		{
			// EnterやEscapeキーでビープ音が鳴らないようにする
			if (e.KeyChar == (char)Keys.Enter || e.KeyChar == (char)Keys.Escape)
			{
				e.Handled = true;
			}

			// Enterを押したら選択状態
			if (e.KeyChar == (char)Keys.Enter)
			{
				if (textBox1.Text == "0")
				{
					MessageBox.Show("1以上の値を入力してください");
				}
				else
				{
					// ゼロパティングを打ち消す
					string s = textBox1.Text;
					textBox1.Text = s.TrimStart((char)'0');
				}

				textBox1.SelectAll();
			}
			else
			{
				// 0~9と、バックスペース以外の時は、イベントをキャンセルする
				if ((e.KeyChar < '0' || '9' < e.KeyChar) && e.KeyChar != '\b')
				{
					e.Handled = true;
				}
			}
		}

		private void textBox2_KeyPress(object sender, KeyPressEventArgs e)
		{
			// EnterやEscapeキーでビープ音が鳴らないようにする
			if (e.KeyChar == (char)Keys.Enter || e.KeyChar == (char)Keys.Escape)
			{
				e.Handled = true;
			}

			// Enterを押したら選択状態
			if (e.KeyChar == (char)Keys.Enter)
			{
				if (textBox2.Text == "0")
				{
					MessageBox.Show("1以上の値を入力してください");
				}
				else
				{
					// ゼロパティングを打ち消す
					string s = textBox2.Text;
					textBox2.Text = s.TrimStart((char)'0');
				}

				textBox2.SelectAll();
			}
			else
			{
				// 0~9と、バックスペース以外の時は、イベントをキャンセルする
				if ((e.KeyChar < '0' || '9' < e.KeyChar) && e.KeyChar != '\b')
				{
					e.Handled = true;
				}
			}
		}

		private void textBox1_Leave(object sender, EventArgs e)
		{
			if (textBox1.Text == "0")
			{
				MessageBox.Show("1以上の値を入力してください");

				// フォーカスを戻す必要がある
				textBox1.Select();
			}
			else
			{
				// ゼロパティングを打ち消す
				string s = textBox1.Text;
				textBox1.Text = s.TrimStart((char)'0');
			}
		}

		private void textBox2_Leave(object sender, EventArgs e)
		{
			if (textBox2.Text == "0")
			{
				MessageBox.Show("1以上の値を入力してください");

				// フォーカスを戻す必要がある
				textBox2.Select();
			}
			else
			{
				// ゼロパティングを打ち消す
				string s = textBox2.Text;
				textBox2.Text = s.TrimStart((char)'0');
			}
		}
	}
}

 

こちらの記事を参考にさせていただきました。

TextBoxに数字しか入力できないようにする - .NET Tips (VB.NET,C#...)
単一行テキストボックスでEnterやEscapeキーを押した時にビープ音が鳴らないようにする - .NET Tips (VB.NET,C#...)

 

今回、ユーザーが画像を自由に拡大縮小できる機能を追加しようと思ったのですが、「縦横比を固定フラグをONにしていたら片方のサイズが自動で決まるようにしたい」と思いました。

f:id:moko_03_25:20200920171355p:plain

その際には TextChanged イベントでは値を入力している最中にも計算してしまうので、ユーザーがEnterキーを押した際またはフォーカスを別のコントロールに移した際に0が入力されていないか判定した後に実行してあげる方が不具合が起こりにくい気がします。

でもそうして考えると「TextBox を使う方法」ではなく「NumericUpDown を使用して ValueChanged は利用しない」方がスマートかも知れないですね。。