この記事は、レコードデータをJSON形式で読み書きするの記事の続きです。
今回は「フォームの位置・サイズをJSON形式で読み書きする」というテーマで
書いていきたいと思います。
前回と同様に、こちらのサイトを参考にさせてもらいました。
・TJsonSerializerの使い方。
⇒ https://lyna.hateblo.jp/entry/20170331/1490975195
・TJsonSerializerの実用例
⇒ https://lyna.hateblo.jp/entry/20170402/1491141694
上記のサイトで解説されているように、
フォームのプロパティをJSON形式で読み書きしてみようかと思います。
まず事前準備として、interface部のusesに以下を追加します。
REST.Json,System.JSON.Serializers
次にフォームの宣言前に
[JsonSerialize(TJsonMemberSerialization.In)]
を記述します。位置的にはこんな(↓)感じ。
type [JsonSerialize(TJsonMemberSerialization.In)] TForm1 = class(TForm)
次に、保存したいプロパティの再宣言をします。
//--------------------- // Property 再定義 //--------------------- [JsonIn] property Name; [JsonIn] property Left; [JsonIn] property Top; [JsonIn] property width; [JsonIn] property height; [JsonIn] property WindowState;
こういうもんだと覚えた方が早いかもしれません。
これらを保存したいフォームすべてに追記します。
追記を忘れるとスタックオーバーフローを起こしたりして
うまく動かないと思います。
プロパティについては必要に応じて追加するなどしてください。
これをアプリケーションで使用しているすべてのフォームに対して行います。
これで準備完了。
テストソースとしてはこちら。
フォームにボタンコンポーネントを置いて
次の様なイベントハンドラを設定します。
procedure TForm1.Button1Click(Sender: TObject); var serializer: TJsonSerializer; s:string; begin serializer := TJsonSerializer.Create; try s:=serializer.Serialize(Self); finally serializer.Free; end; Clipboard.AsText:=s; end;
クリップボードを使用しているので、
usesにclipbrdを追加してください。
実行すると、こんな感じでデータがクリップボードにコピーされます。
{“Name”:”Form1″,”Left”:87,”Top”:63,”width”:1668,”height”:987,”WindowState”:0}
後はすべてのフォームに対して行ってあげればOK。
....とはいきません。
この点に関しては、こちらのサイトを参考にさせていただきました。
・ウィンドウ位置の正しい復元
⇒ https://www.ruche-home.net/program/tips/window-place
ここで問題となるのは、
・フォームを最大化表示や最小化表示でアプリケーションを終了した場合
です。
先ほど提示したやり方でフォーム位置を保存・復帰させてみると、
フォームを最大化して終了させ、再度起動した場合に
フォームサイズが最大化した時のサイズになってしまいます。
ということで、残念ながらこの方法は使えないという事になります。
フォームの最大化や最小化を使わないフォームであれば使えるかと思います。
この方法が使えるのであれば、いちいちレコードを作らなくて良いので
いいかなと思ったんですけどね。
以下に紹介するGetWindowPlacement関数で通常のフォームサイズを取得し、
クラスヘルパーでプロパティ化して上記の方法でできないか試してみたのですが、
どういうわけか内部エラーが出てコンパイルできませんでした。
ということで、サイズを変更できるフォームを使うアプリケーションに関しては
別の方法を考える必要があります。
これまでのやり方通り、
レコードを用いてデータを読み書きする方法を考えます。
最大化、最小化中のフォームサイズの取得については
GetWindowPlacement関数を使用します。
ということで、以下サンプルソースです。
type TWindowPosSize=Record Name :string; Left :integer; Top :integer; Width :integer; Height:integer; WindowState:TWindowState; end; procedure TForm1.GetWindowPosSize; var i:integer; s:string; Rt:TWindowPosSize; wm:TWindowPlacement; Frm:TForm; function SaveToJson(Rt:TWindowPosSize):string; var serializer: TJsonSerializer; begin serializer := TJsonSerializer.Create; try Result:=serializer.Serialize(Rt); finally FreeAndNil(serializer); end; end; begin s:=''; for i:=0 to Application.ComponentCount -1 do begin if Application.Components[I] is TForm then begin Frm:=TForm(Application.Components[I]); wm.length := SizeOf(wm); GetWindowPlacement(Frm.Handle, @wm); with wm.rcNormalPosition do begin Rt.Name :=Frm.Name; Rt.WindowState:=Frm.WindowState; Rt.Left :=Left; Rt.Top :=Top; Rt.Width :=Width; Rt.Height:=Height; end; s:=s+SaveToJson(Rt)+#13#10; end; // end of if end; // end of loop Memo2.Lines.Text:=s; end; procedure TForm1.SetWindowPosSize; var Rt:TWindowPosSize; s,t:string; Frm:TForm; BL:boolean; function LoadFromJson(Val:string):TWindowPosSize; var serializer: TJsonSerializer; begin serializer := TJsonSerializer.Create; try serializer.Populate(Val,Result); finally FreeAndNil(serializer); end; end; function GetFormInstance(aName:string):TForm; var i:integer; begin for i:=0 to Application.ComponentCount -1 do begin if Application.Components[I] is TForm then begin Result:=TForm(Application.Components[I]); if Result.Name=aName then exit; end; // end of if end; // end of loop Result:=nil; end; begin s:=Memo2.Lines.Text; while s<>'' do begin t:=NP_CutFirst(s,_CRLF); Rt:=LoadFromJson(t); Frm:=GetFormInstance(Rt.Name); if Assigned(Frm) then begin BL:=Frm.Visible; if Rt.WindowState=wsMaximized then begin Frm.WindowState:=wsNormal; Frm.Visible:=False; end; with Frm do begin SetBounds(Rt.Left,Rt.Top,Rt.Width,Rt.Height); Visible:=BL; WindowState:=Rt.WindowState; end; end; end; end;
NP_CutFirstについては、指定した文字列で文字列分割をご覧ください。
この他に、画面解像度の変更とマルチモニタに関する問題点がありますが、
それに関しては記事を改めて。
また、ファイルから読込・保存するタイミングについても記事を改めて。