フォームの位置・サイズをJSON形式で読み書きする

この記事は、レコードデータを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については、指定した文字列で文字列分割をご覧ください。
 

この他に、画面解像度の変更とマルチモニタに関する問題点がありますが、
それに関しては記事を改めて。

また、ファイルから読込・保存するタイミングについても記事を改めて。

シェアする

  • このエントリーをはてなブックマークに追加

フォローする