「数値のみ入力可能なInputQuery」
ユーザーからの数値入力を受け取って動作するプログラムを作成した。このような場合には、以前からInputQueryを使用してきた(自前のDialogを作成したこともあった)が、今回、「数値だけ入力可能」なInputQueryを作成してみた。これは、その覚書。
1.入力をチェックして数値のみの入力を実現
2.MyInputQueryを作る
3.まとめ
4.お願いとお断り
1.入力をチェックして数値のみの入力を実現
Delphiでユーザーからの入力を受け取るプログラムを作る時、戻り値がString型のInputBox関数や、Boolean型のInputQuery関数を使う。僕はこれまでユーザーが「どのボタンを押したのか?」がはっきりわかるInputQuery関数を多用してきた(対し、InputBox関数ではデフォルトで設定しておいた文字列が返る)。
procedure TForm1.Button1Click(Sender: TObject);
var
Ret:string;
begin
if InputQuery('InputQuery', '値を入力:', Ret) then
begin
//OKボタンがクリックされた時
end else begin
//キャンセルボタンがクリックされた時(ESCキーで閉じた場合もFalseになる)
end;
end;
今回、多くの画像の印刷を実行するプログラムの中で、何ページめの画像を印刷するのか、ユーザーに指定してもらう必要があり、そこで、やはりInputQuery関数を利用した。
以前から、このようなシーンで「数値のみ入力可能」なInputQuery関数が欲しいなー、とずっと思ってきたんだけど、とりあえず、プログラムの完成を急ぎたくて、そのたびに、この問題は先送りにされてきた。 僕の中で、もう長いこと ずっと・・・。
Excel VBAで帳票印刷を行う時は、「ちゃっちゃ」っと次のようなFormを作成して
「ちゃっちゃ」っと次のコードを書いて・・・
Private Sub CommandButton1_Click()
Dim PrintNo1 As Integer
Dim PrintNo2 As Integer
Dim i As Integer
If UserForm1.TextBox1.Text = "" Then
MsgBox ("開始番号を半角数字で入力してください。")
TextBox1.SetFocus
Exit Sub
End If
If UserForm1.TextBox2.Text = "" Then
MsgBox ("終了番号を半角数字で入力してください。")
TextBox2.SetFocus
Exit Sub
End If
PrintNo1 = UserForm1.TextBox1.Text
PrintNo2 = UserForm1.TextBox2.Text
i = PrintNo1
For i = PrintNo1 To PrintNo2
Range("A2").Select
ActiveCell.FormulaR1C1 = i
Range("B6:AB38").Select
ActiveSheet.PageSetup.PrintArea = "$B$6:$AB$38"
ActiveWindow.SelectedSheets.PrintOut Copies:=1, Collate:=True
Next i
Range("A2").Select
End Sub
Private Sub CommandButton2_Click()
'キャンセルボタンがクリックされた場合
Unload UserForm1
Exit Sub
End Sub
で、FormのTextBoxをクリックして・・・
IME ModeプロパティをDisableに設定。
あとは実行!
サク・・・っと印刷して終わり。みたいな感じで、VBAならカンタンなんだけど、これをDelphiでやるとなると、全然できるんだけど、でも、ちょっと・・・めんどくさい。
今回も、「数値のみ入力可能なInputQuery」は(現時点で)実現できないから、とりあえず、ユーザーが入力した値をチェック(数値であるか・どうか)して対応してしまった・・・。
それが次のコード。
implementation
uses
System.UITypes;
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var
Ret: string;
intNum: integer;
//全角 -> 半角に変換
Chr: array [0..255] of char;
begin
if InputQuery('印刷', 'ページを指定', Ret) then
begin
//OKボタンがクリックされた時
//全角->半角変換
//全角だった場合は半角数字に変換、すでに半角のものは半角のまま
//半角にできない文字、たとえばひらがな等は変換されない
Winapi.Windows.LCMapString(
GetUserDefaultLCID(),
LCMAP_HALFWIDTH,
PChar(Ret), //変換する文字列
Length(Ret)+1, //サイズ
chr, //変換結果
Sizeof(chr) //サイズ
);
Ret := Chr;
//数値であるかチェック
if TryStrToInt(Ret, intNum) then
begin
//数値である
j := StrToInt(Ret);
//本当に使える数値か、さらにチェック
if (j = 0) or (j > 印刷ページの上限値) then
begin
MessageDlg('入力された値は印刷できない番号です。'+#13#10+
'処理を中止します。', mtInformation, [mbOk] , 0);
Exit;
end else begin
// j の値を使って印刷実行
・・・ 省略 ・・・
end;
end else begin
MessageDlg('入力された値は数値ではありません!'+#13#10+
'処理を中止します。', mtInformation, [mbOk] , 0);
Exit;
end;
end;
end;
まぁ、確かにこれで目的は実現できてるから、イイっちゃイイんだけど・・・。
なんか、スマートじゃない・・・気がして。
あと出しジャンケンみたいで・・・
そこで、今回だけは逃げずに自分と戦うことにしました☆
2.MyInputQueryを作る
とりあえずの目標はVBAでやったように、InputQueryのテキストボックスのIMEモードをDisableに設定すること。
Google先生に訊いたら、次の情報を教えてくれた。
https://docwiki.embarcadero.com/Libraries/Sydney/ja/Vcl.StdCtrls.TCustomEdit.NumbersOnly
Delphi2009からNumbersOnlyプロパティがTEditに実装されたとのこと。で、さらに、そのTEditを内部に抱えているInputQueryの正体については、次のサイト他で情報をGet!
https://www.petitmonte.com/bbs/answers?question_id=4867
どうやらいちばんの問題解決方法は、InputQueryのソースコードをそのままコピペして(挙動不審にならないように)名前を変更し、それぞれの目的が実現できるようにコードを修正することのようだ。
上記Webサイト他に掲載されていたInputQueryのコードを読むと、自分でもなんとかなりそうだったので、さっそくやってみることにした。
で、書いたのが次のコード(ほとんどコピペですが・・・)。
//Formのメンバーにはしていません。
//名前は MyInputQuery に変更
function MyInputQuery(const ACaption, APrompt: string;
var Value: string): Boolean;
var
Form: TForm;
Prompt: TLabel;
Edit: TEdit;
DialogUnits: TPoint;
ButtonTop, ButtonWidth, ButtonHeight: Integer;
function GetAveCharSize(Canvas: TCanvas): TPoint;
var
I: Integer;
Buffer: array[0..51] of Char;
begin
for I := 0 to 25 do Buffer[I] := Chr(I + Ord('A'));
for I := 0 to 25 do Buffer[I + 26] := Chr(I + Ord('a'));
GetTextExtentPoint(Canvas.Handle, Buffer, 52, TSize(Result));
Result.X := Result.X div 52;
end;
begin
Result := False;
Form := TForm.Create(Application);
with Form do begin
try
Canvas.Font := Font;
DialogUnits := GetAveCharSize(Canvas);
BorderStyle := bsDialog;
Caption := ACaption;
ClientWidth := MulDiv(180, DialogUnits.X, 4);
Position := poScreenCenter;
Prompt := TLabel.Create(Form);
with Prompt do
begin
Parent := Form;
Caption := APrompt;
Left := MulDiv(8, DialogUnits.X, 4);
Top := MulDiv(8, DialogUnits.Y, 8);
Constraints.MaxWidth := MulDiv(164, DialogUnits.X, 4);
WordWrap := True;
end;
Edit := TEdit.Create(Form);
with Edit do
begin
Parent := Form;
Left := Prompt.Left;
Top := Prompt.Top + Prompt.Height + 5;
Width := MulDiv(164, DialogUnits.X, 4);
MaxLength := 255;
Text := Value;
SelectAll;
//Password入力用にInputQueryを使用するための設定(Password Mask)
//EditコントロールではPasswordCharに設定した文字が
//入力した文字の代わりに表示される(デフォルトは'#0')
//パスワードマスクするなら
//PasswordChar:= '*';
//これでマスクしなくなる('#0'として文字列化しないこと)
PasswordChar := #0;
//Delphi2009からTEditにNumbersOnlyプロパティ(数字だけを入力可能にする)が
//実装されているそうなので、せっかくだからTrueにしてみた!
//全角文字の「123」も「数値である」と判断してくれます・・・
NumbersOnly := True;
//IMEは使用不可(この1行がどうしても書きたかった!)
ImeMode := imDisable;
//文字位置
//Alignment := taCenter;
Alignment := taLeftJustify;
//Alignment := taRightJustify;
end;
ButtonTop := Edit.Top + Edit.Height + 15;
ButtonWidth := MulDiv(50, DialogUnits.X, 4);
ButtonHeight := MulDiv(14, DialogUnits.Y, 8);
with TButton.Create(Form) do
begin
Parent := Form;
Caption := 'OK';
ModalResult := mrOk;
Default := True;
SetBounds(MulDiv(38, DialogUnits.X, 4), ButtonTop, ButtonWidth,
ButtonHeight);
end;
with TButton.Create(Form) do
begin
Parent := Form;
Caption := 'キャンセル';
ModalResult := mrCancel;
Cancel := True;
SetBounds(MulDiv(92, DialogUnits.X, 4), Edit.Top + Edit.Height + 15,
ButtonWidth, ButtonHeight);
Form.ClientHeight := Top + Height + 13;
end;
if ShowModal = mrOk then
begin
Value := Edit.Text;
Result := True;
end;
finally
Form.Free;
end;
end;
end;
このInputQueryをボタンクリックで呼び出します。
procedure TForm1.Button1Click(Sender: TObject);
var
Ret:string;
begin
if MyInputQuery('Dialog Caption', 'Please Enter the number:', Ret) then
begin
ShowMessage('Entered: '+ Ret);
end else begin
ShowMessage('False!');
end;
end;
上のコードを実行すると・・・
これで、ずっと夢だった「数値のみ入力可能な」InputQueryが完成しました☆
さらに工夫すれば、入力できる数値の範囲を制限したりすることもできると思います。
また、次のWebサイトではマウスポインタの近くにInputQueryを表示させるという技も紹介されていました。
https://www.petitmonte.com/bbs/answers?question_id=6520
3.まとめ
数値のみ入力可能なInputQueryを実現するには、InputQueryのソースコードをコピペして、名前を変更したInputQuery関数(例:MyInputQuery)を作成し、その中でやりたいことを書いていけばイイということ。
ここでは、「IMEModeの設定」を主に、それに加えて「パスワードマスク、NumbersOnlyプロパティ、文字位置(Alignment)」等の設定を行ってみた。
4.お願いとお断り
このサイトの内容を利用される場合は、自己責任でお願いします。記載した内容を利用した結果、利用者および第三者に損害が発生したとしても、このサイトの管理者は一切責任を負えません。予め、ご了承ください。