procedure TForm1.CheckBox1Click(Sender: TObject);
begin
if CheckBox1.Checked then
begin
Image2.Visible := True;
end else begin
Image2.Visible := False;
end;
end;
procedure TForm1.CheckBox2Click(Sender: TObject);
begin
if CheckBox2.Checked then
begin
Image1.BringToFront;
end else begin
Image1.SendToBack;
end;
end;
procedure TForm1.CheckBox2Click(Sender: TObject);
begin
if CheckBox2.Checked then
begin
Image1.BringToFront;
end else begin
Image1.SendToBack;
end;
end;
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;
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;
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;
・・・ 省略 ・・・
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;
・・・ 省略 ・・・
そこで思い切って問題を単純化し、「高速入力モード」を作成して、それが 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;
こんなことにならないよう、画像処理(その2)の手続きの最初に、先に述べたように、Image1.Enabled := True; と記述して強制的にエラー防止策をとるか、「TImageのEnabledプロパティがFalseで変更できません!」みたいなエラーメッセージが表示されるよう、if not Image1.Enabled then のようなエラー回避の処理を入れておくべきだったのだ。そうすれば、もっと早く間違いを発見できたと思うのだが、実際には、EnabledプロパティがFalse状態のTImageをクリックしても「何も起こらない」(もちろんエラーも起きない)ので、Enabledプロパティの設定が原因だと気づくまでに(なんでかなー?)っと、考えに考え、それなりに時間がかかってしまったのだ。
GDI+で書いた元々のプログラムは、ファイルとして存在する画像データをOpenDialogを使ってGDI+ビットマップに読み込み、SaveDialogでファイル名を含めて保存パスを指定して処理するものだった。だから、ビットマップ変換用の変数は必要なく、bmp:TGPBitmap; として、ビットマップデータを入れる変数を1つだけ var 宣言して、もちろん、読み込み時にも、書き込み時にも、それぞれの手続きで同じように、これをローカル変数として使用した。
【誤りのあるコード】
//指定された拡張子を付けて保存
if GetEncoderClsid('image/'+strExt, ImgGUID) >= 0 then
begin
bmp.Save(ChangeFileExt(SaveDialog1.FileName, dotExt), ImgGUID);
end;
【正しいコード】
//指定された拡張子を付けて保存
if GetEncoderClsid('image/'+strExt, ImgGUID) >= 0 then
begin
//bmp.Save(ChangeFileExt(SaveDialog1.FileName, dotExt), ImgGUID);
dstbmp.Save(ChangeFileExt(SaveDialog1.FileName, dotExt), ImgGUID);
end;