質問者
テキストボックスへの入力を整数部はN桁、小数点以下はN桁と制限を設けたいです。

質問
-
Visyal Studio Community 2017 version15.7.2 を使用しております。
テキストボックスへの入力を整数部、小数部とそれぞれ桁数の制限をかけたいです。(結果として0.1~99.9 までの範囲など)整数とドットとバックスペースのみなどの制限をかけて入力後に評価して丸めるなどの方法でいろいろやっていますが上手く行きません。プロパティの設定などで出来るものなのでしょうか?
どうか宜しくお願い致します。
windows Form で作成しております。
お手数をお掛けして大変もうしわけありません。
本件いろいろな方々にご意見頂きまして大変ありがとうございました。業務の優先順位などでなかなか手が付けられず返信なども遅くなって申し訳ありませんでした。
結果今回は数値を入力して確定ボタンを押したときに単純に値を評価してメッセージでガードすることで落ち着きました。頂いたご意見は今後の参考に致します。いろいろとありがとうございました
- 編集済み kinto-to-clave 2019年4月23日 1:43
すべての返信
-
小生の経験からすると、プロパティーの設定で工夫することは無理じゃないかと思います。取り組んでおられる環境がWPFか従来のWinFormかで実装が違いますが、以下、小生も似たような制御をプログラムした際に使った共通する基本的な考え方です。
○個々のキーストロークを拾うイベントを利用します。WinFormsならKeyDownイベントとかKeyPressイベントとかです。
○イベント発生時にそれまでに入力済みの内容に対して押されたキーが受け入れられるか拒否すべきか判断します。この判断に「整数部はN桁、小数点以下はN桁」というようなルールを適用します。小数点が含まれているのにまた小数点が押されたか? 含まれていないところに小数点が押されたか? 新たな数値キーの押し下げで小数点の前の桁数が制限におさまるのか、小数点の後の桁数が制限におさまるのか、等を判定します。
○KeyEventArgsとかKeyPressEventArgsのHandledプロパティーで受け入れか拒否か設定します。
ただですねぇ・・・、
これ、相当にユーザーに制限を強います。「実験」や「作業」の現場でのキーボード入力のような状況に限定できれば良いんですが。オフィス仕事だとコピペなんかに慣れきっているユーザーがルール破りしてしまうとかになりがち。結局、テキストボックスに一通り入力を終えたある区切りの時点(例えば、フォーカスが外れるとき、ダイアログならOKボタンを押したとき)で適切なフォーマットで入力を終えているか判定して受け入れか拒否かを決める方法になってしまいます。小生の場合、ですが。- 編集済み 外池 2019年1月22日 1:15
-
【追伸】
過去スレッドを見ると Windows Forms アプリのようですが、であれば以下の記事が参考になると思います。
Windows フォームでのユーザー入力の検証
https://docs.microsoft.com/ja-jp/dotnet/framework/winforms/user-input-validation-in-windows-forms -
kinto-to-claveさん、こんにちは。フォーラムオペレーターのHarukaです。
MSDNフォーラムにご投稿くださいましてありがとうございます。
下記のコードを試すことができます。private void Test_Load(object sender, EventArgs e)
{
textBox1.MaxLength = 4;
}
private void TextBox1_KeyPress(object sender, KeyPressEventArgs e)
{
if (char.IsNumber(e.KeyChar) || e.KeyChar == '.')
{
if (Regex.IsMatch(
textBox1.Text,
"^\\d*\\.\\d{1}$")) e.Handled = true;
}
else e.Handled = e.KeyChar != (char)Keys.Back;
}
どうぞよろしくお願いします。
MSDN/ TechNet Community Support Haruka
ご協力くださいますようお願いいたします。また、MSDNサポートに賛辞や苦情がある場合は、MSDNFSF@microsoft.comまでお気軽にお問い合わせください。~ -
textBox1をダブルクリックすると、以下のコードが自動生成されます。
テキストが書き換わったら、処理する内容を記述する事ができるイベント処理です。
private void textBox1_TextChanged(object sender, EventArgs e)
{
ここに、プログラムを書きます。
string Test = textBox1.text;
Test変数の中の文字列を検査します。
第一分岐
文字列操作関数を使用して、.(どっと)があるかを検査します。Contains関数を使用する。
.(どっと)があるならば、
.ドット以下の文字列を全て取得します。.(ドット)で分割するのも良い。分割するには、Split関数を使う。
桁数を調べます。(桁数は、文字数です。) Length関数を使用する。
第二分岐
決めた小数点以下の桁数かどうかを検査します。数値比較で検査
決めた桁数以下の場合は、何もしない
決めた桁数以上の場合は、桁数を切り捨てるか、丸めて、メッセージボックスでこれ以上の小数点は以下は入力出来ないと表示します。切り捨ては、Substrin関数を使います。
分割した左辺と、.(ドツト)と、切り捨てた数値文字を連結して文字列を作成してTextBox1に代入する。
第二分岐終了
.(どっと)がないならば、
なにもしない。
第一分岐終了
}
以上でできます。自分で、どこまで書いたか記述して下さい。
たたき台にします。
何も、努力しないで聞けばいいやと言う態度が気に入らないのです。
がんばれ!
- 編集済み Wupu One 2019年4月18日 23:53
-
マウス操作だけでペーストする(右クリックでローカルなメニューを出してペーストを選ぶ)と、キーボード系のイベントが発生しません。ですので、キーボード系のイベントで操作可否の判定と反応をプログラムしたところで、マウス操作ではどんな文字列でもペースト出来てしまいます。
質問者さんの説明によれば、キーボード操作だけができれば良いようなので、マウス操作は殺すことになるのかもしれません。いやいや、そんなことはできないとなると、TextChangedでも可否を点検するとかなってきて、「同じルールを適用する操作可否判定と反応を複数の局面(イベント)でプログラムしないといけない」という事態になってきます。しかも、ルールに合わない操作ができないようにするのではなく、ルールに合わない入力になったと警告するだけです。
だったらもう、「最終局面」つまり「ここで最低限必ず操作可否を判定しないといけない局面」を決めて、そこでルールに合ってなければ再入力を求めるプログラムだけにすれば良いんじゃないかと。(小生も経験してまして、先の投稿で言いたかったことです。)
- 編集済み 外池 2019年4月19日 5:38
-
今のPCで、マウス操作無視する事はできない、、じゃないかと思います。 他からのデータを手入力したくないって方も多いし、、。
こちらでも、同じ様な処理(指定桁数しか入力できない)を頑張った方がいましたが、コピペの問題とかあり、結局、入力確定時に弾く処理に変更しました。
ここで示されている、KeyInput とか、TextChangeとか使えば、出来そうなのですが、(と言うか、使っていた) メンテナンスとか、考えて依頼元の了解の元、確定時(or 別画面遷移時)にチェックする方向で。色々と凝るのは良いですが、複雑なコードはバグが入り込みやすいし、Ver.Upとの対応で、苦労が増えるだけと思います。
- 編集済み pepperleaf01 2019年4月19日 12:10 追記
-
うちではカスタムコントロールを作って使いまわしてます。
ペースト対策はこんな感じ。
protected override void WndProc(ref Message m) { switch (m.Msg) { case WM_PASTE: WmPaste(ref m); break; ・ ・ ・ } } private void WmPaste(ref Message m) { if (Clipboard.ContainsText()) { string clipText = Clipboard.GetText(); string prevText = base.Text; string left = prevText.Substring(0, SelectionStart); string right = prevText.Substring(SelectionStart + SelectionLength); string selectedText = string.Empty; foreach (char c in clipText) { string newText = left + selectedText + c + right; if (IsValidText(newText)) { selectedText += c; } } base.Paste(selectedText); } }
基本的に操作を受け入れたとき、テキストがどのように変化するかをふまえてチェックを行い、妥当でなければ操作をキャンセルすることになります。
OnKeyPress では、
・クリップボード操作キー(CTRL+C, CTRL+V, CTRL+X, CTRL+Z) は通す。
・数字や符号文字なら Text の値をキャレットを境に左右に分け、左側 + e.KeyChar + 右側をチェックする。
・BackSpace で「1234.5678」の「.」を消すと、「12345678」と整数桁が増えてしまい、妥当性を確保できなくなる可能性があるのでチェックする。
「.」より右側を削除してしまうのもひとつの手です。
OnKeyDown では Delete キーなら削除後の文字列をチェックする。
WM_CLEAR/WM_CUT はキャレットの選択文字が消えるので キャレットの左側 + 右側をチェックする。
WM_UNDO/EM_UNDO は、変更後の値を取得する手段がなさそうなので受け入れた結果が不正なら受け入れ前の状態に戻す。
この辺の動きって 16ビット Windows から変わっていなくて腐ることってなさそうに思います。
- 編集済み KOZ6.0 2019年4月20日 17:10
-
さすがですね。やっぱり、凄い。こんなコード見た事ない。
試してみました。
private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
//小数点第一位までの処理
if (char.IsNumber(e.KeyChar) || e.KeyChar == '.')
{
if (System.Text.RegularExpressions.Regex.IsMatch(textBox1.Text,"^\\d*\\.\\d{1}$")) e.Handled = true;
//"^\\d*\\.\\d{2}$" →小数点第二位の場合
}
else e.Handled = e.KeyChar != (char)Keys.Back;
}たったの一行でした。凄い、Tipsありがとうござました。
意地悪しているわけじゃないんですけど、全部、プログラムコードを教えると、次から次へと、結局、全部教えている方が書いていると言う状態になるのと、うのみにして動いていないコードを動いていると勘違いして実装すると言う事もあるので、努力する事が重要だと考えているのです。マイクロソフトさんは、商売だと言う事を重々承知しております。
-
わんくま同盟さん達が僕を馬鹿にしてどうしようもないので腹が立ちました。
MSの方が、一行で書けるプログラムを紹介していましたので、凄いと思い、これ以上書かなくて良いと思った次第です。
でも、わんくま同盟の方達が、サンプルコードを回答で書いて、それが、凡長でどうしようもない物だったら、質問者は、どう思いますか?
などと、私に文句たらたらいってきたのです。くだらない理由ですが、僕が提案した書き方を完全に動作するプログラムに書きましたので、ご査収下さい。
マイクロソフトさんのプログラムは、一行で書かれていますが、結局は、内部の処理でどのような処理をやっているのかが、全く、分からない処理です。その為、改良する事も出来ないものとなっています。また、処理スピードも果たして速いのかどうか解りません。
おもいっきり、バカにされているプログラマーとして、以上の事を前置きとして書かせてもらった次第です。
テキストボックスをマウスでダブルクリック下ください。ダブルクリックすると、イベント処理用の関数が作られます。作られた関数は、
private void textBox1_TextChanged(object sender, EventArgs e)
{
}と言う関数になります。マウスでテキストボックスをダブルクリックしても反応がない場合があります。この場合は、プロパティーウィンドの稲妻マークのボタンをクリックして下さい。表示されたイベント処理の中から、TextChangedと書いてある項目を探して、またもや、同じく、マウスでダブルクリックして下さい。ここでダブルクリックすれば、たいていは、TextChanged関数のスケルトンが自動で書き込まれます。
書き込まれたら、以下の様に記述して下さい。
private void textBox1_TextChanged(object sender, EventArgs e)
{
string Test = textBox1.Text;
string[] Test2 = new string[0];
string Test3 = "";
int keta = 0; //小数点以下の総桁
//int SetKeta = 1; //小数点以下の指定の桁数 1の場合
//int UpDat = 0; //四捨五入の場合
if (Test.Contains(".")) //.(ドツト)があるかどうかを検査
{
Test2 = Test.Split('.'); //.(ドット)で分割 この方が楽なので処理を変更しました。
keta = Test2[1].Length;
if (keta <= SetKeta)
{
; //何もしない
}
else if(keta > SetKeta)
{
Test3 = Test2[1].Substring(0, SetKeta);
textBox1.Text = Test2[0] + "." + Test3.ToString(); //切り捨ての場合
/* 四捨五入の場合は、上の二行は、コメントアウトして、こちらをコメント指定の(//)を消してアクティブにして下さい。変数二個も、アクティブにして下さい。
UpDat = int.Parse(Test2[1].Substring(SetKeta, 1));
if (UpDat >= 5) //5以上の場合 四捨五入処理
{
Test3 = Test2[1].Substring(SetKeta - 1, 1);
UpDat = int.Parse(Test3);
UpDat++;
textBox1.Text = Test2[0] + "." + UpDat.ToString(); //五入
}
else
{
Test3 = Test2[1].Substring(0, SetKeta);
textBox1.Text = Test2[0] + "." + Test3.ToString(); //四捨
}
*/
}
}
else
{
; //何もしない
}動作確認済みです。
-
訂正。TextChanged はダメですね。
TextChanged でやると、Text プロパティが書き換わった後にチェックすることになります。たとえば、「12.3」と入力しておいて、「125.3」と、「5」を挿入します。99.9をオーバーするので「12.3」に Text プロパティを書き換えますが、その時にカーソルが「3」の後ろに移動します。これは使いにくい。
また、NumericUpDown コントロールで、Maximun, Minimum, DecimalPlaces を設定しても、キーボードからの入力はそれを無視してできます。そして、フォーカスがよそに移動した時に、最大値などに表示が切り替わります。さらに、アルファベットをペーストすることもできてしまう。もっとも、スピンボックスによる変更は、それぞれのプロパティの影響を受けます。キーボードからの入力は、数字とピリオド以外はじかれるので、お勧めだと思ったのだけれど。
提案としては、こんなところ?
- InputMan 他の入力コンポーネントを導入する
- 入力のタイミングで検査するのではなく、フォーカスが離れる時に検査する Validating イベントを使用する
Jitta@わんくま同盟