// これは...
これは Delphi Advent Calendar 2017 15 日目の記事です。
長くなってしまったので機能単位で分割しています。
[編集(E)]
[編集(E)] メニューの残りをやりましょう。
[行へ移動(G)]
このメニューは、
- 別ダイアログで数字を入力する (数字以外は入力できない)。
- ダイアログはモーダル
- 総行数を超える位置にはジャンプできない。
- 初期値は現在行
という処理を行う必要があります。本物のメモ帳ではこうなっています。
つまり、この 数字入力画面は自前で作る しかありません。
Delphi で別のフォームを作るには [ファイル | 新規作成 | VCL フォーム - Delphi] を選びます。
新しい "空のフォーム付きのユニット" が作られました。
Unit2.pas という名前になっていますので〔F2〕を押して名前を frmuGotoLine に変更します。
マウスで二回ゆっくりクリックしても名前を変更できます。
新規作成されたフォームは自動生成の対象となっています。今回、このフォームはダイアログとして "呼び出される度に生成する" 事にしますので、[プロジェクト | オプション] で自動生成から外します。
自動生成から外したら、フォーム (Form2) のプロパティを変更します。
プロパティ | 値 | 説明 |
---|---|---|
BorderICons | [biSystemMenu] | フォーム右上のアイコンです。?ボタンだけにします。 |
BorderStyle | bsSingle | サイズ変更のできないフォームにします。 |
Caption | 行へ移動 | フォームのキャプションバーのキャプションです。 |
ClientHeight | 113 | フォームのクライアント領域の高さです。 |
ClientWidth | 291 | フォームのクライアント領域の幅です。 |
Font.Name | Tahoma | フォームで使われるデフォルトのフォント名です。 |
Font.Size | 9 | フォームで使われるデフォルトのフォントサイズです。 |
Position | poMainFormCenter | フォームが表示される位置です。親ウィンドウの中心に表示します |
こんな見た目になったと思います。
フォームのサイズを Width / Height プロパティで設定しないのは、OS によってフォームの Width / Height は変化するからです。このような場合、キャプションバーとフレームを抜いたクライアント領域のサイズで指定する必要があります。
さて、このフォームに以下のコントロールを貼り付けましょう。
- TLabel (ラベル)
- TEdit (エディットボックス)
- TButton (ボタン) x 2
すべてツールパレットの [Standard] タブにあります。 そして大体の位置に置いてください。
TLabel (Lablel1) のプロパティを設定します。
プロパティ | 値 | 説明 |
---|---|---|
Caption | 行番号(&L): | ラベルのキャプションです。アクセラレータが設定されています。 |
FocusedControl | Edit1 | ラベルのアクセラレータキーが押された時にフォーカス移動するコントロールです。 |
Left | 12 | ラベルの左位置です。 |
Top | 12 | ラベルの上位置です。 |
TEdit (Edit1) のプロパティを設定します。
プロパティ | 値 | 説明 |
---|---|---|
AutoSize | False | エディットコントロールの高さを自動で調整しないようにします。 |
Height | 26 | エディットの左位置です。 |
Left | 12 | エディットの左位置です。 |
NumbersOnly | True | 数値以外の入力を許可しません。 |
TabOrder | 0 | タブキーでコントロールが移動する順序です。 |
Top | 32 | エディットの上位置です。 |
Width | 264 | エディットの幅です。 |
TButton (Button1) のプロパティを設定します。
プロパティ | 値 | 説明 |
---|---|---|
Caption | 移動 | ボタンのキャプションです。 |
Defalut | True | 〔Enter〕キーが押されたらこのボタンを押した事にします。 |
Height | 26 | ボタンの左位置です。 |
Left | 96 | ボタンの左位置です。 |
ModalResult | mrOK | モーダルなフォームでボタンが押された時の戻り値です。 |
TabOrder | 1 | タブキーでコントロールが移動する順序です。 |
Top | 73 | ボタンの上位置です。 |
Width | 88 | ボタンの幅です。 |
TButton (Button2) のプロパティを設定します。
プロパティ | 値 | 説明 |
---|---|---|
Cancel | True | 〔Esc〕キーが押されたらこのボタンを押した事にします。 |
Caption | キャンセル | ボタンのキャプションです。 |
Height | 26 | ボタンの左位置です。 |
Left | 191 | ボタンの左位置です。 |
ModalResult | mrCancel | モーダルなフォームでボタンが押された時の戻り値です。 |
TabOrder | 2 | タブキーでコントロールが移動する順序です。 |
Top | 73 | ボタンの上位置です。 |
Width | 88 | ボタンの幅です。 |
次に TForm2 に GotoLine プロパティと LineCount プロパティを追加します。プロパティのスコープは public にし、他のフォームからも参照できるようにしておきます。
unit frmuGotoLine;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TForm2 = class(TForm)
Label1: TLabel;
Edit1: TEdit;
Button1: TButton;
Button2: TButton;
private
{ Private 宣言 }
FGotoLine: Integer; // <-- 追加
FLineCount: Integer; // <-- 追加
procedure SetGotoLine(Value: Integer); // <-- 追加
public
{ Public 宣言 }
property GotoLine: Integer read FGotoLine write SetGotoLine; // <-- 追加
property LineCount: Integer read FLineCount write FLineCount; // <-- 追加
end;
var
Form2: TForm2;
implementation
{$R *.dfm}
procedure TForm2.SetGotoLine(Value: Integer);
// SetGotoLine プロパティのセッター
begin
FGotoLine := Value;
Edit1.Text := FGotoLine.ToString;
end;
end.
プロパティ | 説明 |
---|---|
GotoLine | 移動先の行を格納します。 |
LineCount | ここにメモ帳の最大行数を渡すようにします。 |
次に Form2 の OnCloseQuery イベントにイベントハンドラを生成します。オブジェクトインスペクタで値の部分をダブルクリックします。
このイベントはフォームを閉じようとしたときに発生し、パラメータ CanClose に False を設定するとフォームを閉じれなくする事ができます。このイベントハンドラでは、指定した行が 0 以下だったり最大行を超えていたりした場合にはエラーメッセージを出して閉じれなくします。以下コードです。
unit frmuGotoLine;
interface
uses
..., System.UITypes; // <- 追加
...
procedure TForm2.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
// フォーム修了確認時
var
dGoToLine: Integer;
begin
// mrOK でないボタンが押された場合には処理しない
if ModalResult <> mrOK then
begin
CanClose := True;
Exit;
end;
dGoToLine := StrToInt(Edit1.Text);
CanClose := (dGoToLine > 0) and (dGoToLine <= LineCount);
if CanClose then
begin
// 移動可能
FGotoLine := dGoToLine;
end
else
begin
// 移動不可
MessageDlg('指定した行番号は行の総数を超えています', TMsgDlgType.mtCustom, [TMsgDlgBtn.mbOK], -1);
GotoLine := FGotoLine;
Edit1.SelectAll;
Edit1.SetFocus;
end;
end;
[行へ移動] 用のダイアログは完成したので、これをメインウィンドウから開きます。frmuMain のタブに移動し、コードエディタにします。implementation の下に uses を記述します。ここにユニット名 (ユニットファイル名から拡張子を抜いたもの) を記述する事で参照できるようになります。
...
implementation
{$R *.dfm}
uses // <-- 追加
frmuGotoLine; // <-- 追加
procedure TForm1.FormCreate(Sender: TObject);
...
今度は フォームエディタにして acGoTo アクションのイベントハンドラを作ります。
acGoToExecute イベントハンドラの実装は以下のようになります。
procedure TForm1.acGoToExecute(Sender: TObject);
// Action: 行へ移動(G)
begin
Form2 := TForm2.Create(Self);
try
// 現在行と最大行を Form2 に渡す
Form2.GotoLine := Memo1.Perform(EM_LINEFROMCHAR, WPARAM(-1), 0) + 1; // 0 オリジンなので + 1 する
Form2.LineCount := Memo1.Perform(EM_GETLINECOUNT, 0, 0);
// Form2 をモーダルウィンドウで表示
if Form2.ShowModal = mrOK then
begin
// [OK] ボタンが押されたら
Memo1.SelLength := 0;
Memo1.SelStart := Memo1.Perform(EM_LINEINDEX, Form2.GotoLine - 1, 0);
Memo1.Perform(EM_SCROLLCARET,0 , 0);
end;
finally
Form2.Free;
end;
end;
モーダルウィンドウというのはこのウィンドウを処理しない限り親ウィンドウを操作できないものを言います。エラーメッセージのウィンドウなんかがそうですね。
Perform() は Windows API の SendMessage() と同じものと考えてください。
メモコントロールに送っているメッセージの詳細は以下にあります。
ここでやっている事は Form2 のインスタンスを生成し、ダイアログ (フォーム) を Showmodal() メソッドでモーダルで表示、[OK] ボタンが押されたら Showmodal() メソッドは mrOK を返すので、移動可能な行位置であればそこへ移動する...というものです。
最終的にはこのようなダイアログになります。
〔Alt〕,〔E〕,〔G〕や〔Ctrl〕+〔G〕で [行へ移動(G)...] が実行される事を確認してください。
[すべて選択(A)]
このメニューは、
- 文字をすべて選択。
という処理を行う必要があります。TMemo に SelectAll() メソッドがあるのでこれを呼べばいいです。 acSelectAll アクションのイベントハンドラを作ります。
イベントハンドラは以下のようになります。
procedure TForm1.acSelectAllExecute(Sender: TObject);
// Action: すべて選択(A)
begin
Memo1.SelectAll;
end;
〔Alt〕,〔E〕,〔A〕や〔Ctrl〕+〔A〕で [すべて選択(A)] が実行される事を確認してください。
[日付と時刻(D)]
このメニューは、
- カーソル位置に日付文字列を挿入
という処理を行う必要があります。正確には、
- 選択文字列を日付文字列で置換
です。acTimeDate アクションのイベントハンドラを作ります。
イベントハンドラは以下のようになります。
procedure TForm1.acTimeDateExecute(Sender: TObject);
// Action: 日付と時刻(D)
begin
Memo1.SelText := FormatDateTime('hh:nn yyyy/mm/dd', Now);
end;
〔Alt〕,〔E〕,〔D〕や〔F5〕で [日付と時刻(D)] が実行される事を確認してください。
// 戻る
今回はここまでです。ここまでの全ソースを掲載しておきます。
[メインフォーム]
unit frmuMain;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, System.Actions,
Vcl.ActnList, Vcl.Menus, Vcl.ExtDlgs, System.IOUtils, Vcl.Clipbrd,
System.RegularExpressions, System.UITypes;
type
TForm1 = class(TForm)
Memo1: TMemo;
MainMenu1: TMainMenu;
ActionList1: TActionList;
acNew: TAction;
acOpen: TAction;
acSave: TAction;
acSaveAs: TAction;
acPageSetup: TAction;
acPrint: TAction;
acExit: TAction;
F1: TMenuItem;
N1: TMenuItem;
O1: TMenuItem;
S1: TMenuItem;
A1: TMenuItem;
U1: TMenuItem;
P1: TMenuItem;
X1: TMenuItem;
N2: TMenuItem;
N3: TMenuItem;
OpenTextFileDialog1: TOpenTextFileDialog;
SaveTextFileDialog1: TSaveTextFileDialog;
acUndo: TAction;
acCut: TAction;
acCopy: TAction;
acPaste: TAction;
acDelete: TAction;
acFind: TAction;
acFindNext: TAction;
acReplace: TAction;
acGoTo: TAction;
acSelectAll: TAction;
acTimeDate: TAction;
E1: TMenuItem;
U2: TMenuItem;
N4: TMenuItem;
C1: TMenuItem;
T1: TMenuItem;
P2: TMenuItem;
L1: TMenuItem;
N5: TMenuItem;
F2: TMenuItem;
N6: TMenuItem;
R1: TMenuItem;
G1: TMenuItem;
N7: TMenuItem;
A2: TMenuItem;
D1: TMenuItem;
FindDialog1: TFindDialog;
ReplaceDialog1: TReplaceDialog;
procedure FormCreate(Sender: TObject);
procedure acExitExecute(Sender: TObject);
procedure acOpenExecute(Sender: TObject);
procedure acSaveExecute(Sender: TObject);
procedure acSaveAsExecute(Sender: TObject);
procedure acNewExecute(Sender: TObject);
procedure acUndoExecute(Sender: TObject);
procedure ActionList1Update(Action: TBasicAction; var Handled: Boolean);
procedure acCutExecute(Sender: TObject);
procedure acCopyExecute(Sender: TObject);
procedure acPasteExecute(Sender: TObject);
procedure E1Click(Sender: TObject);
procedure acFindExecute(Sender: TObject);
procedure Dialog_Find(Sender: TObject);
procedure acFindNextExecute(Sender: TObject);
procedure acDeleteExecute(Sender: TObject);
procedure acReplaceExecute(Sender: TObject);
procedure ReplaceDialog1Replace(Sender: TObject);
procedure acGoToExecute(Sender: TObject);
procedure acSelectAllExecute(Sender: TObject);
procedure acTimeDateExecute(Sender: TObject);
private
{ Private 宣言 }
FFileName: String;
FEncodingIndex: Integer;
procedure Init;
procedure SaveFile;
procedure UpdateCaption;
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
uses
frmuGotoLine;
procedure TForm1.FormCreate(Sender: TObject);
// フォーム作成時
begin
Init;
end;
// [FILE] Actions
procedure TForm1.acNewExecute(Sender: TObject);
// Action: 新規(N)
begin
Init;
end;
procedure TForm1.acOpenExecute(Sender: TObject);
// Action: 開く(O)...
var
Enc: TEncoding;
begin
if OpenTextFileDialog1.Execute then
begin
// 値を保存
FFileName := OpenTextFileDialog1.FileName;
FEncodingIndex := OpenTextFileDialog1.EncodingIndex;
// キャプションを変更
UpdateCaption;
// EncodingIndex によりエンコーディングを指定
case FEncodingIndex of
1: Enc := TEncoding.Unicode;
2: Enc := TEncoding.BigEndianUnicode;
3: Enc := TEncoding.UTF8;
else
Enc := TEncoding.Default;
end;
// エンコーディングを指定して読み込み
Memo1.Lines.LoadFromFile(FFileName, Enc);
end;
end;
procedure TForm1.acSaveExecute(Sender: TObject);
// Action: 上書き保存(S)
begin
if (FFileName = '') then
begin
// 名前を付けて保存
acSaveAs.Execute;
end
else
begin
// 上書き保存
SaveFile;
end;
end;
procedure TForm1.acSaveAsExecute(Sender: TObject);
// Action: 名前を付けて保存(A)...
begin
if SaveTextFileDialog1.Execute then
begin
// 値を保存
FFileName := SaveTextFileDialog1.FileName;
FEncodingIndex := SaveTextFileDialog1.EncodingIndex;
// キャプションを変更
UpdateCaption;
// ファイルへ保存
SaveFile;
end;
end;
procedure TForm1.acExitExecute(Sender: TObject);
// Action: メモ帳の終了(X)
begin
Self.Close;
end;
// [EDIT] Actions
procedure TForm1.E1Click(Sender: TObject);
begin
acDelete.ShortCut := TextToShortCut('Del');
end;
procedure TForm1.acUndoExecute(Sender: TObject);
// Action: 元に戻す(U)
begin
Memo1.Undo;
end;
procedure TForm1.acCutExecute(Sender: TObject);
// Action: 切り取り(T)
begin
Memo1.CutToClipboard;
end;
procedure TForm1.acCopyExecute(Sender: TObject);
// Action: コピー(C)
begin
Memo1.CopyToClipboard;
end;
procedure TForm1.acPasteExecute(Sender: TObject);
// Action: 貼り付け(P)
begin
Memo1.PasteFromClipboard;
end;
procedure TForm1.acDeleteExecute(Sender: TObject);
// Action: 削除(L)
begin
Memo1.ClearSelection;
end;
procedure TForm1.acFindExecute(Sender: TObject);
// Action: 検索(F)
begin
// 検索ダイアログを開く
FindDialog1.Execute;
end;
procedure TForm1.acFindNextExecute(Sender: TObject);
// Action: 次を検索(N)
begin
if FindDialog1.FindText = '' then
acFind.Execute
else
Dialog_Find(FindDialog1);
end;
procedure TForm1.acReplaceExecute(Sender: TObject);
// Action: 置換(H)
begin
// 置換ダイアログを開く
ReplaceDialog1.Execute;
end;
procedure TForm1.Dialog_Find(Sender: TObject);
// 検索イベントハンドラ
var
RegEx: TRegEx;
Exp, Msg: String;
Options: TRegExOptions;
Match, dMatch: TMatch;
StartPos, EndPos: Integer;
begin
// 検索文字列を正規表現文字列へエスケープ
Exp := TRegEx.Escape(FindDialog1.FindText);
// "大文字と小文字を区別する" にチェックが入っていなければ
// オプションに roIgnoreCase (大文字小文字を区別しない) を追加
if not (frMatchCase in FindDialog1.Options) then
Include(Options, roIgnoreCase);
// 正規表現オブジェクトの生成
RegEx := TRegEx.Create(Exp, Options);
if (frDown in FindDialog1.Options) then
begin
// 順方向検索 (下へ)
StartPos := Memo1.SelStart + Memo1.SelLength + 1;
Match := RegEx.Match(Memo1.Lines.Text, StartPos);
end
else
begin
// 逆方向検索 (上へ)
StartPos := 0;
EndPos := Memo1.SelStart + 1;
Match := RegEx.Match(Memo1.Lines.Text, StartPos, EndPos);
while Match.Success do
begin
// 最後にマッチした箇所を探す
dMatch := Match.NextMatch;
if dMatch.Success then
Match := dMatch
else
break;
end;
end;
// 検索文字列を共有
FindDialog1.FindText := (Sender as TFindDialog).FindText;
ReplaceDialog1.FindText := (Sender as TFindDialog).FindText;
// 検索文字列を正規表現文字列へエスケープ
Exp := TRegEx.Escape((Sender as TFindDialog).FindText);
// "大文字と小文字を区別する" にチェックが入っていなければ
// オプションに roIgnoreCase (大文字小文字を区別しない) を追加
if not (frMatchCase in (Sender as TFindDialog).Options) then
Include(Options, roIgnoreCase);
// 正規表現オブジェクトの生成
RegEx := TRegEx.Create(Exp, Options);
if (frDown in (Sender as TFindDialog).Options) then
begin
// 順方向検索 (下へ)
StartPos := Memo1.SelStart + Memo1.SelLength + 1;
Match := RegEx.Match(Memo1.Lines.Text, StartPos);
end
else
begin
// 逆方向検索 (上へ)
StartPos := 0;
EndPos := Memo1.SelStart + 1;
Match := RegEx.Match(Memo1.Lines.Text, StartPos, EndPos);
while Match.Success do
begin
// 最後にマッチした箇所を探す
dMatch := Match.NextMatch;
if dMatch.Success then
Match := dMatch
else
break;
end;
end;
if Match.Success then
begin
// 一致する文字列があった
SetFocus;
Memo1.SelStart := Match.Index - 1;
Memo1.SelLength := Match.Length;
end
else
begin
// 一致する文字列がなかった
Msg := Format('"%s" が見つかりません。', [(Sender as TFindDialog).FindText]);
MessageDlg(Msg, TMsgDlgType.mtInformation, [TMsgDlgBtn.mbOK], -1);
end;
end;
procedure TForm1.ReplaceDialog1Replace(Sender: TObject);
// 置換イベントハンドラ
var
srFlgs: TReplaceFlags;
FindText: string;
SelectFlg: Boolean;
begin
// 検索文字列を共有
FindDialog1.FindText := (Sender as TFindDialog).FindText;
ReplaceDialog1.FindText := (Sender as TFindDialog).FindText;
// フラグ
if (frReplaceAll in ReplaceDialog1.Options) then
begin
// [すべて置換]
srFlgs := [rfReplaceAll]; // すべて置換
if not (frMatchCase in ReplaceDialog1.Options) then
Include(srFlgs, rfIgnoreCase);
Memo1.Lines.Text := StringReplace(Memo1.Lines.Text, ReplaceDialog1.FindText, ReplaceDialog1.ReplaceText, srFlgs);
end
else
begin
// [置換して次へ]
srFlgs := []; // 最初の一回だけ置換
if not (frMatchCase in ReplaceDialog1.Options) then
Include(srFlgs, rfIgnoreCase);
// 検索済みかどうかの判定
FindText := (Sender as TFindDialog).FindText;
if (frMatchCase in ReplaceDialog1.Options) then
SelectFlg := SameStr(Memo1.SelText, FindText) // 大文字小文字を区別する
else
SelectFlg := SameText(Memo1.SelText, FindText); // 大文字小文字を区別しない
// 検索済みか?
if SelectFlg then
Memo1.SelText := ReplaceDialog1.ReplaceText;
// 検索
Dialog_Find(ReplaceDialog1);
end;
end;
procedure TForm1.acGoToExecute(Sender: TObject);
// Action: 行へ移動(G)
begin
Form2 := TForm2.Create(Self);
try
// 現在行と最大行を Form2 に渡す
Form2.GotoLine := Memo1.Perform(EM_LINEFROMCHAR, WPARAM(-1), 0) + 1; // 0 オリジンなので + 1 する
Form2.LineCount := Memo1.Perform(EM_GETLINECOUNT, 0, 0);
// Form2 をモーダルで表示
if Form2.ShowModal = mrOK then
begin
// [OK] ボタンが押されたら
Memo1.SelLength := 0;
Memo1.SelStart := Memo1.Perform(EM_LINEINDEX, Form2.GotoLine - 1, 0);
Memo1.Perform(EM_SCROLLCARET,0 , 0);
end;
finally
Form2.Free;
end;
end;
procedure TForm1.acSelectAllExecute(Sender: TObject);
// Action: すべて選択(A)
begin
Memo1.SelectAll;
end;
procedure TForm1.acTimeDateExecute(Sender: TObject);
// Action: 日付と時刻(D)
begin
Memo1.SelText := FormatDateTime('hh:nn yyyy/mm/dd', Now);
end;
// リスト更新
procedure TForm1.ActionList1Update(Action: TBasicAction; var Handled: Boolean);
begin
// [元に戻す(U)] の有効/無効
acUndo.Enabled := Memo1.CanUndo;
// [切り取り(T)] の有効/無効
acCut.Enabled := Memo1.SelLength <> 0;
// [コピー(C)] の有効/無効
acCopy.Enabled := Memo1.SelLength <> 0;
// [貼り付け(P)] の有効/無効
acPaste.Enabled := ClipBoard.HasFormat(CF_TEXT);
// [削除(L)] の有効/無効
acDelete.Enabled := Memo1.SelLength <> 0;
acDelete.ShortCut := 0;
// [検索(F)] の有効/無効
acFind.Enabled := Memo1.Lines.Text.Length > 0;
// [次を検索(N)] の有効/無効
acFindNext.Enabled := Memo1.Lines.Text.Length > 0;
end;
// メソッド
procedure TForm1.Init;
// エディタの初期化
begin
// 値を初期化
FFileName := '';
FEncodingIndex := 0;
Memo1.Lines.Clear; // Memo1 の中身を消去する。
// キャプションを変更
UpdateCaption;
end;
procedure TForm1.SaveFile;
var
Enc: TEncoding;
begin
// EncodingIndex によりエンコーディングを指定
case FEncodingIndex of
1: Enc := TEncoding.Unicode;
2: Enc := TEncoding.BigEndianUnicode;
3: Enc := TEncoding.UTF8;
else
Enc := TEncoding.Default;
end;
// エンコーディングを指定して保存
Memo1.Lines.SaveToFile(FFileName, Enc);
end;
procedure TForm1.UpdateCaption;
// キャプション (ファイル名) の更新
var
Dmy: String;
begin
if FFileName = '' then
Dmy := '無題'
else
Dmy := TPath.GetFileName(FFileName);
Self.Caption := Dmy + ' - メモ帳クローン';
end;
end.
[行へ移動] ダイアログ
unit frmuGotoLine;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, System.UITypes;
type
TForm2 = class(TForm)
Label1: TLabel;
Edit1: TEdit;
Button1: TButton;
Button2: TButton;
procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
private
{ Private 宣言 }
FGotoLine: Integer; // <-- 追加
FLineCount: Integer; // <-- 追加
procedure SetGotoLine(Value: Integer); // <-- 追加
public
{ Public 宣言 }
property GotoLine: Integer read FGotoLine write SetGotoLine; // <-- 追加
property LineCount: Integer read FLineCount write FLineCount; // <-- 追加
end;
var
Form2: TForm2;
implementation
{$R *.dfm}
procedure TForm2.SetGotoLine(Value: Integer);
// SetGotoLine プロパティのセッター
begin
FGotoLine := Value;
Edit1.Text := FGotoLine.ToString;
end;
procedure TForm2.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
// フォーム修了確認時
var
dGoToLine: Integer;
begin
// mrOK でないボタンが押された場合には処理しない
if ModalResult <> mrOK then
begin
CanClose := True;
Exit;
end;
dGoToLine := StrToInt(Edit1.Text);
CanClose := (dGoToLine > 0) and (dGoToLine <= LineCount);
if CanClose then
begin
// 移動可能
FGotoLine := dGoToLine;
end
else
begin
// 移動不可
MessageDlg('指定した行番号は行の総数を超えています', TMsgDlgType.mtCustom, [TMsgDlgBtn.mbOK], -1);
GotoLine := FGotoLine;
Edit1.SelectAll;
Edit1.SetFocus;
end;
end;
end.