「入力確定のEnterキーは押したくない!」
TStringGridを使って何らかの入力作業を行う時、任意のあるキーを押したら直ちに、予め指定した内容をアクティブなセルに入力し(入力を確定)、次のセル(右 or 下)へフォーカスを移したいことがある。これは、そんな時のための備忘録。
1.Bキー押し下げでゼロを入力したい理由
2.StringGridを準備する
3.Bキー押し下げでゼロ入力を実装(その1)
4.任意の1文字+数字の入力を負の数に変換
5.Bキー押し下げでゼロ入力を実装(その2)
6.まとめ
7.お願いとお断り
1.Bキー押し下げでゼロを入力したい理由
手書き答案をスキャナーで読み込み、採点するプログラムを書いた。元の答案画像から設問ごとに解答欄をかき集めて一覧表示し、まとめて採点すれば効率よく採点できると思ったのだ(実際、試してみたら驚くほど速く採点できた!)。その手順を紹介。
採点スタイルとして予定した(考えた)のは、「左手で入力作業、右手はマウス操作(解答欄のクリックと画像のスクロール)に専念する」というカタチ。
解答欄画像をクリックしたら、その座標から解答番号を計算し、採点欄のフォーカスが自動で移動するようプログラミング。(その方法は以下のリンク先を参照してください)
で、正の数値を入力したら、そのまま採点欄に、その数値が入力され、
Qとか、Sとか、何か文字を入力して確定したら、採点欄には0(ゼロ)が入り、さらに
上の設定であれば、aキーに続けて数字を入力して確定した場合は、採点欄に負の値が入力されるようにプログラミング。なぜaなのかというと、左手小指のホームポジションだからまず間違えずに(位置を確かめずに)押し下げ可能だと思ったから。
そもそも、なんで、こんな仕様(入力値が正負の数およびゼロ)にしたかというと、採点欄への数値入力と同時に、入力された数値に応じて、解答欄画像の方にも、採点記号と得点を(透過状態で)表示するプログラムにしたかったから。具体的な表示内容は次の通り。
(1)入力が正の数なら、解答欄画像の上に採点記号〇と得点を表示、
(2)入力が0(ゼロ)なら、解答欄画像の上に採点記号 × のみを表示
(ゼロは〇:まるとまぎらわしいのでデフォルト設定では表示しない)、
(3)入力が負の数なら、解答欄画像の上に採点記号△と部分点を表示。
当初、この採点補助プログラムでは、採点記号として〇と × しか利用できなかった(△とした場合に、それを見分ける良いフラグが用意できなかった)が、コピペしたプログラムコード中に残していた負の数は赤で表示するコードを見て、負の数を「部分点あり」のフラグとして利用できることに気づき、「部分点あり」の採点記号△も使えるように改良。
マウスは右手で操作する(左利きの方も?)ので、自ずと採点は左手で行うことに。
多くの場合、1問あたりの得点は5点未満だろうから、これらの数字キーはキーボードの左側にあって押しやすい。もし、数値でなく文字が入力された場合は、有無を言わさず0(ゼロ)に変換してしまえば、左手側にある1~5の数字キーの下には押しやすい文字キーがたくさんあるから、キーボード右側にあって、左手が届きにくい0(ゼロ)キーは押さなくてすむ。
あとは右手でマウスを操作し、解答欄画像を次々にクリックして、採点欄のフォーカスを切り替えて(=入力を確定して)行けば・・・
採点補助プログラムとして、十分使えるかなー?っと思ったんだけれど、
実際使ってみたら、入力後、次の解答欄画像をいちいちクリックして( or Enterキーを押し下げして)入力を確定 & 次の採点欄へフォーカスを移動させるのが、非常にめんどくさい。
せめて × の場合だけでも、採点欄に0(ゼロ)を入力した瞬間に、解答欄画像上に × を表示し、フォーカスが自動で次のセルへ移動するようにできないか?
そんな理由から、採点記号「 × 」は「ばってん」だから、BATTENで、Bキー押し下げ、即、0(ゼロ)を入力 & 確定、フォーカスは次のセルへ自動で移動するプログラムを書くことに決めました(Bキーも左手で押しやすい位置にあるのがうれしい!)。
2.StringGridを準備する
Bキー押し下げ、即、入力確定のプログラム自体は、前にStringGridで矢印キーの動作を制限したことがあったので、その時学んだテクニックを応用すれば、きっと書けると思ったので全然心配はなかったが、それを設定する対象のTStringGridは実に設定し甲斐のあるコントロールで、ある目的を実現(実装)しようとすると、そこに行きつくまでの工程が何段階も必要だったりする。
今回、この記事を書くのにあたり、いい機会だからStringGridの設定について(自分自身の勉強の復習の意味も込めて)まとめてみた。練習用に手間をかけずに作成したFormとコントロールは次の通り(Formに各VCLコントロールを置いただけ!)。
で、FormCreate時の手続きは・・・
procedure TForm1.FormCreate(Sender: TObject);
begin
//[Enter]でコントロールを移動させるために、Form上のコンポーネント
//より先にキーボードイベントを取得する。
KeyPreview := True;
//描画処理は自前で行わずDelphiにおまかせ
StringGrid1.DefaultDrawing := True;
//Fixed(固定セル)のスタイル
//現在のオペレーティングシステムのテーマを使用
StringGrid1.DrawingStyle := gdsThemed;
//標準のテーマの指定がないスタイル
//StringGrid1.DrawingStyle := gdsClassic;
//グラデーションのあるスタイル
//StringGrid1.DrawingStyle := gdsGradient;
//セルを強調表示
StringGrid1.Options := StringGrid1.Options + [goDrawFocusSelected];
//Clickでセル編集を可能にする-> [goEditing]をTrueに設定(方法は以下の通り)
StringGrid1.Options := StringGrid1.Options + [goEditing];
//常に編集可能に設定
StringGrid1.Options := StringGrid1.Options + [goAlwaysShowEditor];
end;
KeyPreview := True の設定の他は、すべてStringGrid関係。僕がこのコントロールを使う時は、常に編集可能状態で起動するように設定することがほとんど。
続けてFormShow手続き。
procedure TForm1.FormShow(Sender: TObject);
var
i : integer;
begin
//行数と列数を適当に指定(Fixedセルを除いて10行10列あればテスト用途には十分)
StringGrid1.RowCount := 11;
StringGrid1.ColCount := 11;
//FixedCols & FixedRows(固定列と固定行)を設定
StringGrid1.FixedCols := 1;
StringGrid1.FixedRows := 1;
//フィールド名をセット(Rowに一括設定)
//',Aの[,]に注意!-> セル[0,0]は空欄(フィールド名は入れない)
//プログラムが長くなる時は['+]を使用してフィールド名を設定する
StringGrid1.Rows[0].CommaText := ',A,B,C,D,E,F,'+
'G,H,I,J';
//FixedRows(固定行)に値をセット
for i := 1 to 10 do
begin
StringGrid1.Rows[i].Append(IntToStr(i));
end;
//StringGrid1へフォーカスを移す。
//下のようにまずフォーカスを移してからCol, Rowを指定。
//でないとエラーになる。
StringGrid1.SetFocus;
StringGrid1.Col := 1;
StringGrid1.Row := 1;
StringGrid1.SetFocus;
//カーソルが見えるようにする
StringGrid1.EditorMode := True;
end;
さらにStringGrid1DrawCell手続きで、Fixed(固定)セルの表示方法と、入力された数値の右寄せ表示を指定。
implementation
uses
Vcl.GraphUtil;
//GraphUtilはFixedセルのセンタリング用に追加
procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
Rect: TRect; State: TGridDrawState);
var
i : integer;
begin
//Fixedセルをセンタリング
with StringGrid1 do
begin
if (gdFixed in State) then
begin
//usesにGraphUtilを追加(Vcl.GraphUtilではないことに注意!)
//->Vcl.GraphUtilとすると「未定義の識別子エラー」になる!
//GraphUtil.GradientFillCanvas(Canvas, GradientStartColor,
// GradientEndColor, Rect,gdVertical);
//Vcl.GraphUtilとusesした場合
//これは未定義の識別子エラーにならない
Vcl.GraphUtil.GradientFillCanvas(Canvas, GradientStartColor,
GradientEndColor, Rect,gdVertical);
//センタリング
DrawText(Canvas.Handle, PChar(Cells[ACol, ARow]),
-1, Rect, DT_CENTER OR DT_VCENTER OR DT_SINGLELINE);
end;
end;
//セルの表示を制御
if not (gdFixed in state) then
begin
if StringGrid1.Cells[ACol,ARow] <> '' then
begin
//数値であるかどうかをCheck
if not TryStrToInt(StringGrid1.Cells[ACol,ARow],i) then Exit;
{数値である場合}
//背景色を白に設定
StringGrid1.Canvas.Brush.Color := clWhite;
//正負をチェック
if StrToInt(StringGrid1.Cells[ACol,ARow]) < 0 then
begin
StringGrid1.Canvas.Font.Color := clRed;
end else begin
StringGrid1.Canvas.Font.Color := clBlack;
end;
//セルを塗りつぶす
StringGrid1.Canvas.FillRect(Rect);
//数値は中央寄せで表示
{DrawText(StringGrid1.Canvas.Handle,
PChar(StringGrid1.Cells[ACol,ARow]),
//[+1]は数値描画位置の調整のため
Length(StringGrid1.Cells[ACol,ARow])+1,Rect,
DT_CENTER or DT_VCENTER or DT_SINGLELINE);}
//数値は右寄せで表示
DrawText(StringGrid1.Canvas.Handle,
PChar(StringGrid1.Cells[ACol,ARow]),
//[+1]は数値描画位置の調整のため
Length(StringGrid1.Cells[ACol,ARow])+1,Rect,
DT_RIGHT or DT_VCENTER or DT_SINGLELINE);
end;
end;
end;
ついでにIMEも設定(IME ONの列は任意指定)。まず、次のように宣言しておいて・・・
//Col毎のIMEの制御(制御内容はStringGrid1GetEditTextを参照)
type
_TGrid = class(TCustomGrid);
var
Form1: TForm1;
implementation
StringGrid1GetEditText手続きで、次のように設定。
procedure TForm1.StringGrid1GetEditText(Sender: TObject; ACol, ARow: Integer;
var Value: string);
begin
//IMEの制御
with TEdit(_TGrid(Sender).InplaceEditor) do
begin
case ACol of //最初のAColは「 0 」
2: ImeMode := imHira; //日本語入力ON
else
//ImeMode := imClose; //日本語入力OFF-> ×
ImeMode := imDisable; //日本語入力OFFは imDisable
end;
end;
end;
ここまでの設定で、実行時の画面は、こんな感じ。
Enterキーでフォーカスを移動するために、FormKeyPress手続きで、次のように設定。
procedure TForm1.FormKeyPress(Sender: TObject; var Key: Char);
begin
//[Enter]キーでコントロールを移動
//StringGridは編集可能にFormCreateで設定しておく
//->忘れるとセルの移動にEnter×2回必要!
//この方法を使う時はKeyPreview:=True;をFormCreateで指定。
if Ord(Key) = VK_RETURN then
begin
if ActiveControl is TStringGrid then
begin
if TStringGrid(ActiveControl).EditorMode then
begin
//VK_TABではカーソルがレコードの項目を右へ移動。
//ActiveControl.Perform(WM_KEYDOWN,VK_TAB,0);
//VK_DOWNにすると同じ項目の次のレコードへ移動。
//if intStringGrid1ActiveRow < StringGrid1.RowCount-1 then
if TargetRow < StringGrid1.RowCount-1 then
begin
ActiveControl.Perform(WM_KEYDOWN,VK_DOWN,0);
end else begin
ActiveControl.Perform(WM_KEYDOWN,VK_UP,0);
end;
Key := #0;
end;
end else begin
SelectNext(ActiveControl,True,True);
Key := #0;
end;
end;
end;
さらに、列幅を自動調整したい場合は・・・
procedure TForm1.CheckBox1Click(Sender: TObject);
var
iCOL: Integer;
iROW: Integer;
MaxColWidth: Integer;
TmpColWidth: Integer;
begin
//DefaultColWidthを設定(これでCheck OFF時に元に戻る!)
StringGrid1.DefaultColWidth:=64;
//AutoAllColFit(全列幅の自動調整)
if CheckBox1.Checked then
begin
for iCOL := 0 to StringGrid1.ColCount-1 do begin
MaxColWidth := 0;
for iROW := 0 to StringGrid1.RowCount-1 do
begin
//数字は列幅の調整用
TmpColWidth := Canvas.TextWidth(StringGrid1.Cells[iCOL,iROW]) + 40;
if MaxColWidth < TmpColWidth then
MaxColWidth := TmpColWidth;
end;
StringGrid1.ColWidths[iCOL] := MaxColWidth;
end;
end;
end;
列幅自動調整実行時の画面は・・・(チェックOFFで、列幅は元に戻る)
これでStringGridの準備が完了!
3.Bキー押し下げでゼロ入力を実装(その1)
※ 各セルに対して10以上の値の入力がないことが前提です!
この機能の実装にあたり、次のWebサイトにあった情報を参考にさせていただきました。質問者様と解答者様のご両名に対して、心から厚く御礼申し上げます。
@NIFTY FDELPHI Delphi Users’ Forum15番会議室「FAQ編纂委員会」に寄せられた「よくある質問の答え」
http://delfusa.main.jp/delfusafloor/archive/www.nifty.ne.jp_forum_fdelphi/faq/00075.htm
https://www.petitmonte.com/bbs/answers?question_id=7289
Private宣言に、次のローカル変数とAppMessage手続きを追加。
private
{ Private 宣言 }
//入力=確定&フォーカスの移動用に追加
//行・列位置を記憶する変数
TargetRow:integer;
TargetCol:integer;
//ある(矢印他)キーが押されたことを知る
procedure AppMessage(var Msg: TMsg; var Handled: Boolean);
Shift+Ctrl+C で AppMessage手続きを作成して、次の内容を設定。
※ usesに System.UITypes を追加するのを忘れないこと!(忘れるとBキーを意味するVKBが「未定義の識別子エラー」になる。
重要 次のコードでは、各セルに対して10以上の数値の入力は「ない」ものとしている。
implementation
uses
Vcl.GraphUtil,
System.UITypes;
//GraphUtilはFixedセルのセンタリング用に追加
//System.UITypesはキーコードでBキー(=VKB)を指定するために追加
{$R *.dfm}
procedure TForm1.AppMessage(var Msg: TMsg; var Handled: Boolean);
begin
//任意のキーの押し下げをキャッチ
if Msg.message = WM_KEYDOWN then
begin
//StringGridがアクティブだったら
if ActiveControl is TStringGrid then
begin
//StringGridが編集可能だったら
if TStringGrid(ActiveControl).EditorMode then
begin
//Bキー or 0キー押し下げでゼロを入力(入力値は10未満であることが前提)
if (Msg.wParam=VKB) or (Msg.wParam=VK0) then
begin
//keybd_event(VK_TAB,0,0,0);
//VK_TABではカーソルがレコードの項目を右へ移動。
//ActiveControl.Perform(WM_KEYDOWN,VK_TAB,0);
//VK_DOWNにすると同じ項目の次のレコードへ移動。
if TargetRow < StringGrid1.RowCount-1 then
begin
//アクティブなセルが最終行でない場合はフォーカスは下へ移動
StringGrid1.Cells[TargetCol, TargetRow]:='0';
ActiveControl.Perform(WM_KEYDOWN,VK_DOWN,0);
end else begin
//最終行ならフォーカスは上へ移動
StringGrid1.Cells[TargetCol, TargetRow]:='0';
ActiveControl.Perform(WM_KEYDOWN,VK_UP,0);
end;
//Msg.wParam:=#0; //エラーになる
Msg.wParam:=0;
end;
end;
end;
end;
end;
FormCreate時に、AppMessageを有効にする。これを忘れると動かない!
procedure TForm1.FormCreate(Sender: TObject);
begin
・・・ 省略(StringGridその他の初期設定) ・・・
//入力=確定&フォーカスの移動用に追加
//StringGridの初期位置の設定
TargetRow := 1;
TargetCol := 1;
//AppMessageを有効にする
Application.OnMessage := AppMessage;
end;
AppMessage手続きの引数にはACol, ARowがないから、その代わりにStringGrid1SelectCell手続きの最後で、行列位置を変数に取得。
procedure TForm1.StringGrid1SelectCell(Sender: TObject; ACol, ARow: Integer;
var CanSelect: Boolean);
begin
//入力=確定&フォーカスの移動用に追加
//セルを選んだときに行位置を記憶
TargetRow := ARow;
//セルを選んだときに列位置を記憶
TargetCol := ACol;
end;
実行時の様子は・・・
4.任意の1文字+数字の入力を負の数に変換
Formに用意したLabel1のCaptionプロパティには「マイナス記号に置換する文字:」を設定し、ComboBox1のTextプロパティに「a」を設定。
FormCreate手続きの最後で、マイナス記号に置換する文字の選択肢を準備。
procedure TForm1.FormCreate(Sender: TObject);
begin
・・・ 省略 ・・・
//入力=確定&フォーカスの移動用に追加
//StringGridの初期位置の設定
TargetRow := 1;
TargetCol := 1;
//AppMessageを有効にする
Application.OnMessage := AppMessage;
//マイナス記号に変換する文字の選択肢
ComboBox1.Items.Add('q');
ComboBox1.Items.Add('a');
ComboBox1.Items.Add('z');
end;
StringGrid1DrawCell手続きに、次の赤字の部分を追加。
(1列目であったら、文字の入力はすべてゼロに変換する処理も追加している)
procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
Rect: TRect; State: TGridDrawState);
var
i: integer;
str1, str2: string;
begin
・・・ 省略 ・・・
//セルの表示を制御(中央寄せ・負の数は赤で表示)
if not (gdFixed in state) then
begin
if StringGrid1.Cells[ACol,ARow] <> '' then
begin
//文字数が2文字なら実行
if Length(WideString(StringGrid1.Cells[ACol,ARow])) = 2 then
begin
//指定文字が入力されたら'-'に変換
str1 := LowerCase(Copy(StringGrid1.Cells[ACol,ARow],1,1));
str2 := Copy(StringGrid1.Cells[ACol,ARow],2,1);
if str1 = LowerCase(ComboBox1.Text) then
begin
StringGrid1.Cells[ACol,ARow] := '-'+str2;
end;
end;
if ACol = 1 then
begin
//「文字」はすべて'0'に変換
if not TryStrToInt(StringGrid1.Cells[ACol,ARow], i) then
begin
StringGrid1.Cells[ACol,ARow] := '0';
end;
end;
・・・ 省略 ・・・
実行時の様子は・・・
左手だけで、視線をキーボードに落とすことなく、負の数も簡単に入力できるようになった☆
ただし、上の手続きでは、StringGridのセルへの入力が3桁であった場合に対応できない。こと採点に関しては、部分点が2桁の数値になることは、多分アリエナイから、採点補助プログラム用のアルゴリズムとしての利用に限れば、上の手続きでも、まず問題は起きないと思うが・・・もし、どうしても3桁以上の入力値に対応させたいなら、コードを次のように変更すればOK!
//StringReplaceuses関数を使用するので uses節に System.SysUtils を追加
uses
System.SysUtils
procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
Rect: TRect; State: TGridDrawState);
var
i: integer;
str1, str2: string;
begin
・・・ 省略 ・・・
//セルの表示を制御(中央寄せ・負の数は赤で表示)
if not (gdFixed in state) then
begin
if StringGrid1.Cells[ACol,ARow] <> '' then
begin
//文字数が2文字なら実行 -> コメント化
{if Length(WideString(StringGrid1.Cells[ACol,ARow])) = 2 then
begin
//指定文字が入力されたら'-'に変換
str1 := LowerCase(Copy(StringGrid1.Cells[ACol,ARow],1,1));
str2 := Copy(StringGrid1.Cells[ACol,ARow],2,1);
if str1 = LowerCase(ComboBox1.Text) then
begin
StringGrid1.Cells[ACol,ARow] := '-' + str2;
end;
end;}
//文字数が2文字以上なら実行
if Length(WideString(StringGrid1.Cells[ACol,ARow])) >= 2 then
begin
//指定文字が入力されたら'-'に変換
str1 := LowerCase(Copy(StringGrid1.Cells[ACol,ARow],1,1));
//2桁以上の入力値に対応
str2 := StringReplace(
LowerCase(StringGrid1.Cells[ACol,ARow]),
str1, '', [rfReplaceAll, rfIgnoreCase]);
if str1=LowerCase(ComboBox1.Text) then
begin
StringGrid1.Cells[ACol,ARow] := '-'+str2;
end;
end;
if ACol = 1 then
begin
//「文字」はすべて'0'に変換
if not TryStrToInt(StringGrid1.Cells[ACol,ARow], i) then
begin
StringGrid1.Cells[ACol,ARow] := '0';
end;
end;
・・・ 省略 ・・・
5.Bキー押し下げでゼロ入力を実装(その2)
※ 各セルに対して10以上の値の入力がある場合
各セルに対して10以上の値の入力がある場合は、入力された0(ゼロ)が不正解の0(ゼロ)なのか、10の2桁目の0(ゼロ)なのか、判定する工夫が必要になるが、良い判定方法が思いつかなかった。
そこで思い切って問題を単純化し、「高速入力モード」を作成して、それが ON の場合は入力値を0-9に限定し、ユーザーがそのことを理解した上で操作できるように工夫してみた。もし、各セルに対して10以上の値の入力がある場合は、「高速入力モード」は OFF で使用して貰い、Bキーが押された場合のみ、0(ゼロ)に変換して入力確定 ⇨ フォーカスを移動することにして、数字キーの0(ゼロ)の入力に対しては、直ちに入力の確定としないことにした。
あと、ついでだから、「高速入力モード」の名に恥じないよう、それが ON の場合は、0-9の数字キー押し下げで、直ちに入力確定、次のセルへフォーカスが移動する処理も追加してみた。以下、その実装。
procedure TForm1.AppMessage(var Msg: TMsg; var Handled: Boolean);
var
str1:string;
begin
//任意のキーの押し下げをキャッチ
if Msg.message = WM_KEYDOWN then
begin
//StringGridがアクティブだったら
if ActiveControl is TStringGrid then
begin
//StringGridが編集可能だったら
if TStringGrid(ActiveControl).EditorMode then
begin
//高速入力使用の有無で処理を切り替え
if not CheckBox2.Checked then
begin
//高速入力を使用しない場合の処理
//Bキー押し下げでゼロを入力
//0キー押し下げは無視
if (Msg.wParam=VKB) then
begin
//keybd_event(VK_TAB,0,0,0);
//VK_TABではカーソルがレコードの項目を右へ移動。
//ActiveControl.Perform(WM_KEYDOWN,VK_TAB,0);
//VK_DOWNにすると同じ項目の次のレコードへ移動。
if TargetRow < StringGrid1.RowCount-1 then
begin
//下のセルへ移動
StringGrid1.Cells[TargetCol, TargetRow]:='0';
ActiveControl.Perform(WM_KEYDOWN,VK_DOWN,0);
end else begin
//上のセルへ移動
StringGrid1.Cells[TargetCol, TargetRow]:='0';
ActiveControl.Perform(WM_KEYDOWN,VK_UP,0);
end;
//Msg.wParam:=#0; //エラーになる
Msg.wParam:=0;
end;
end else begin
//高速入力を使用する場合の処理
//Bキー押し下げでゼロを入力
//0キー押し下げにも対応
if (Msg.wParam=VKB) or (Msg.wParam=VK0) then
begin
//keybd_event(VK_TAB,0,0,0);
//VK_TABではカーソルがレコードの項目を右へ移動。
//ActiveControl.Perform(WM_KEYDOWN,VK_TAB,0);
//VK_DOWNにすると同じ項目の次のレコードへ移動。
if TargetRow < StringGrid1.RowCount-1 then
begin
//下のセルへ移動
StringGrid1.Cells[TargetCol, TargetRow]:='0';
ActiveControl.Perform(WM_KEYDOWN,VK_DOWN,0);
end else begin
//上のセルへ移動
StringGrid1.Cells[TargetCol, TargetRow]:='0';
ActiveControl.Perform(WM_KEYDOWN,VK_UP,0);
end;
//Msg.wParam := #0; //エラーになる
Msg.wParam := 0;
end;
//1-9の入力があった場合
if StringGrid1.Cells[TargetCol, TargetRow] <> '' then
begin
str1:=Copy(StringGrid1.Cells[TargetCol, TargetRow],1,1);
end else begin
str1 := '';
end;
//任意の1文字+数字の入力を負の数に変換する処理用に追加
if (str1 <> '-') and (str1 <> ComboBox1.Text) then
begin
if (Msg.wParam = VK1) then
begin
//keybd_event(VK_TAB,0,0,0);
//VK_TABではカーソルがレコードの項目を右へ移動。
//ActiveControl.Perform(WM_KEYDOWN,VK_TAB,0);
//VK_DOWNにすると同じ項目の次のレコードへ移動。
//if intStringGrid1ActiveRow < StringGrid1.RowCount-1 then
if TargetRow < StringGrid1.RowCount-1 then
begin
//下のセルに移動
StringGrid1.Cells[TargetCol, TargetRow] := '1';
ActiveControl.Perform(WM_KEYDOWN,VK_DOWN,0);
end else begin
//上のセルに移動
StringGrid1.Cells[TargetCol, TargetRow] := '1';
ActiveControl.Perform(WM_KEYDOWN,VK_UP,0);
end;
//Msg.wParam := #0; //エラーになる
Msg.wParam := 0;
end;
if (Msg.wParam = VK2) then
begin
if TargetRow < StringGrid1.RowCount-1 then
begin
//下のセルに移動
StringGrid1.Cells[TargetCol, TargetRow] := '2';
ActiveControl.Perform(WM_KEYDOWN,VK_DOWN,0);
end else begin
//上のセルに移動
StringGrid1.Cells[TargetCol, TargetRow] := '2';
ActiveControl.Perform(WM_KEYDOWN,VK_UP,0);
end;
Msg.wParam := 0;
end;
if (Msg.wParam = VK3) then
begin
if TargetRow < StringGrid1.RowCount-1 then
begin
//下のセルに移動
StringGrid1.Cells[TargetCol, TargetRow] := '3';
ActiveControl.Perform(WM_KEYDOWN,VK_DOWN,0);
end else begin
//上のセルに移動
StringGrid1.Cells[TargetCol, TargetRow] := '3';
ActiveControl.Perform(WM_KEYDOWN,VK_UP,0);
end;
Msg.wParam := 0;
end;
if (Msg.wParam = VK4) then
begin
if TargetRow < StringGrid1.RowCount-1 then
begin
//下のセルに移動
StringGrid1.Cells[TargetCol, TargetRow] := '4';
ActiveControl.Perform(WM_KEYDOWN,VK_DOWN,0);
end else begin
//上のセルに移動
StringGrid1.Cells[TargetCol, TargetRow] := '4';
ActiveControl.Perform(WM_KEYDOWN,VK_UP,0);
end;
Msg.wParam := 0;
end;
if (Msg.wParam = VK5) then
begin
if TargetRow < StringGrid1.RowCount-1 then
begin
//下のセルに移動
StringGrid1.Cells[TargetCol, TargetRow] := '5';
ActiveControl.Perform(WM_KEYDOWN,VK_DOWN,0);
end else begin
//上のセルに移動
StringGrid1.Cells[TargetCol, TargetRow] := '5';
ActiveControl.Perform(WM_KEYDOWN,VK_UP,0);
end;
Msg.wParam := 0;
end;
if (Msg.wParam = VK6) then
begin
if TargetRow < StringGrid1.RowCount-1 then
begin
//下のセルに移動
StringGrid1.Cells[TargetCol, TargetRow] := '6';
ActiveControl.Perform(WM_KEYDOWN,VK_DOWN,0);
end else begin
//上のセルに移動
StringGrid1.Cells[TargetCol, TargetRow] := '6';
ActiveControl.Perform(WM_KEYDOWN,VK_UP,0);
end;
Msg.wParam := 0;
end;
if (Msg.wParam = VK7) then
begin
if TargetRow < StringGrid1.RowCount-1 then
begin
//下のセルに移動
StringGrid1.Cells[TargetCol, TargetRow] := '7';
ActiveControl.Perform(WM_KEYDOWN,VK_DOWN,0);
end else begin
//上のセルに移動
StringGrid1.Cells[TargetCol, TargetRow] := '7';
ActiveControl.Perform(WM_KEYDOWN,VK_UP,0);
end;
Msg.wParam := 0;
end;
if (Msg.wParam = VK8) then
begin
if TargetRow < StringGrid1.RowCount-1 then
begin
//下のセルに移動
StringGrid1.Cells[TargetCol, TargetRow] := '8';
ActiveControl.Perform(WM_KEYDOWN,VK_DOWN,0);
end else begin
//上のセルに移動
StringGrid1.Cells[TargetCol, TargetRow] := '8';
ActiveControl.Perform(WM_KEYDOWN,VK_UP,0);
end;
Msg.wParam := 0;
end;
if (Msg.wParam = VK9) then
begin
if TargetRow < StringGrid1.RowCount-1 then
begin
//下のセルに移動
StringGrid1.Cells[TargetCol, TargetRow] := '9';
ActiveControl.Perform(WM_KEYDOWN,VK_DOWN,0);
end else begin
//上のセルに移動
StringGrid1.Cells[TargetCol, TargetRow] := '9';
ActiveControl.Perform(WM_KEYDOWN,VK_UP,0);
end;
Msg.wParam := 0;
end;
end;
end;
end;
end;
end;
end;
6.まとめ
重要 各セルへの入力値が10未満であることが前提のコードです!
Bキーを押すだけでStringGridのアクティブなセルにゼロを入力し、フォーカスを次のセルへ移動するプログラムで、必要な変数と手続きは次の通り。
各セルへの入力値が10以上の場合、「まとめ」のコードは期待通りに動作しません。
10以上の入力値にも対応させたい場合は、「5.各セルに対して10以上の値の入力がある場合」が参考になるかもしれません。
private
{ Private 宣言 }
//行・列位置を記憶する変数
TargetRow:integer;
TargetCol:integer;
//ある(矢印他)キーが押されたことを知る
procedure AppMessage(var Msg: TMsg; var Handled: Boolean);
//Col毎のIMEの制御(制御内容はStringGrid1GetEditTextを参照)
type
_TGrid = class(TCustomGrid);
var
Form1: TForm1;
implementation
uses
Vcl.GraphUtil,
System.UITypes;
//GraphUtilはFixedセルのセンタリング用に追加
//System.UITypesはキーコードでBキー(=VKB)を指定するために追加
{$R *.dfm}
procedure TFormCollaboration.FormCreate(Sender: TObject);
begin
//StringGridの初期位置の設定
TargetRow := 1;
TargetCol := 1;
//AppMessageを有効にする <- 忘れないこと!
Application.OnMessage := AppMessage;
//[Enter]でコントロールを移動させるために、Form上のコンポーネント
//より先にFormがキーボードイベントを取得する。
KeyPreview := True;
end;
procedure TForm1.StringGrid1SelectCell(Sender: TObject; ACol, ARow: Integer;
var CanSelect: Boolean);
begin
//入力=確定&フォーカスの移動用に追加
//セルを選んだときに行位置を記憶
TargetRow := ARow;
//セルを選んだときに列位置を記憶
TargetCol := ACol;
end;
procedure TForm1.AppMessage(var Msg: TMsg; var Handled: Boolean);
begin
//任意のキーの押し下げをキャッチ
if Msg.message = WM_KEYDOWN then
begin
//StringGridがアクティブだったら
if ActiveControl is TStringGrid then
begin
//StringGridが編集可能だったら
if TStringGrid(ActiveControl).EditorMode then
begin
//Bキー or 0キー押し下げでゼロを入力(入力値は10未満であることが前提)
if (Msg.wParam=VKB) or (Msg.wParam=VK0) then
begin
//keybd_event(VK_TAB,0,0,0);
//VK_TABではカーソルがレコードの項目を右へ移動。
//ActiveControl.Perform(WM_KEYDOWN,VK_TAB,0);
//VK_DOWNにすると同じ項目の次のレコードへ移動。
if TargetRow < StringGrid1.RowCount-1 then
begin
//アクティブなセルが最終行でない場合はフォーカスは下へ移動
StringGrid1.Cells[TargetCol, TargetRow] := '0';
ActiveControl.Perform(WM_KEYDOWN,VK_DOWN,0);
end else begin
//最終行ならフォーカスは上へ移動
StringGrid1.Cells[TargetCol, TargetRow] := '0';
ActiveControl.Perform(WM_KEYDOWN,VK_UP,0);
end;
//Msg.wParam := #0; //エラーになる
Msg.wParam := 0;
end;
end;
end;
end;
end;
7.お願いとお断り
このサイトの内容を利用される場合は、自己責任でお願いします。記載した内容を利用した結果、利用者および第三者に損害が発生したとしても、このサイトの管理者は一切責任を負えません。予め、ご了承ください。