Delphi Programming / Object Pascal
サイトのトップページへ リンクのページへ ロゴマーク
[掲載 2025年04月11日] [更新 2025年04月12日] 画像・図形の移動リサイズ関係サンプル
画像・図形の移動リサイズ関係サンプル 
R070_描画用コントロールの保存と読み出し
動作確認等 Windows 10 ビルド 19045 Pro 64 bit + Delphi XE5(UP2) Pro VCL-32
R070_SaveCtrl.zip [3.1 MB] 2025年04月16日版 (EXE 同梱)
描画用クラスのソースコードは [概要] ページからダウンロードする







描画用コントロールの保存と読み出し


本ページより前のページのサンプルにおける保存は,描画内容をビットマップなどの画像として保存するものでした.本ページのサンプルは,描画コントロールそのものをコンポーネントとして保存して読み出す手順です.

描画用のコントロールを保存する処理は,プリケーションで利用する時に便利なことがあります.設定ダイアログなどを表示してプロパティを設定する場合,プロパティの設定の前に描画用コントロールを保存して退避しておけば,[キャンセル] の時に元に戻すことができます.





01_図形としての保存と読み出し


描画用のコントロールのクラスが 1 種類だけの場合の例です.このサンプルでは楕円描画用のコントロールだけを使用しています.


図1
[生成] ボタンをクリック
  • 位置とサイズを適当に操作

図2
[図保存してクリア] ボタンをクリック
  • コンテナコントロール上に配置している 描画用コントロールの保存となる
図3
[読み込む] ボタンをクリック
  • ラバーバンド,グラブハンドルは保存しないので再現しない

リスト1
図形としての保存と読み出し.描画用クラスが 1 つだけの例
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ExtCtrls, StdCtrls, GDIPOBJ, GDIPAPI, GDIPUTIL,
  plGPResizeImage,
  plGPResizeLine,
  plGPResizeEllipseEx,
  plContainerPanel,
  plBaseDragResize;

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
  private
    { Private 宣言 }
    FBackCtrl : TplContainerPanel;
    FFileName : String;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

//=============================================================================
//  フォーム生成時の処理
//  専用のコンテナコントロールを生成
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Panel1.Caption := '';

  FBackCtrl := TplContainerPanel.Create(Self);
  FBackCtrl.Parent    := Self;
  FBackCtrl.Align     := alClient;

  FFileName := '01_SavedCtrols.dat';
end;

//=============================================================================
//  [生成] ボタン
//  ここで生成するのは TplGPResizeEllipseEx だけ
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LCtrlElli : TplGPResizeEllipseEx;
begin
  // 生成済みの描画用コントロールを削除
  FBackCtrl.DeleteAllCtrls;

  LCtrlElli := TplGPResizeEllipseEx.Create(Self);
  LCtrlElli.Parent := FBackCtrl;
  LCtrlElli.InverseDisp := True;
  LCtrlElli.PenColor := aclRed;

  LCtrlElli := TplGPResizeEllipseEx.Create(Self);
  LCtrlElli.Parent := FBackCtrl;
  LCtrlElli.InverseDisp := True;

  LCtrlElli := TplGPResizeEllipseEx.Create(Self);
  LCtrlElli.Parent := FBackCtrl;
  LCtrlElli.InverseDisp := True;
  LCtrlElli.PenColor := aclGreen;
end;

//=============================================================================
//  [保存してクリア] ボタン
//  描画用コントロールをファイルに保存
//  このコードは単一の描画用クラス専用
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  LFileStream : TFileStream;
  LCtrl       : TplBaseDragResize;
  Lindex      : Integer;
begin
  LFileStream := TFileStream.Create(FFileName, fmCreate);
  try
    for Lindex := 0 to FBackCtrl.CtrlList.Count - 1 do begin
      LCtrl := FBackCtrl.CtrlList[Lindex];
      LFileStream.WriteComponent(LCtrl);
      Application.ProcessMessages;
    end;
  finally
    FreeAndNil(LFileStream);
  end;
  FBackCtrl.DeleteAllCtrls;
end;

//=============================================================================
//  [読み込み] ボタン
//  保存時の描画図形を再現
//=============================================================================
procedure TForm1.Button3Click(Sender: TObject);
var
  LFileStream : TFileStream;
  LCtrl       : TplBaseDragResize;
begin
  FBackCtrl.DeleteAllCtrls;

  LFileStream := TFileStream.Create(FFileName, fmOpenRead);
  LockWindowUpdate(FBackCtrl.Handle);
  try
    if LFileStream.Size <= 0 then Exit;

    LFileStream.Position := 0;
    while True do begin
      if LFileStream.Position >= LFileStream.Size then Break;
      LCtrl := TplGPResizeEllipseEx.Create(Self);
      LCtrl.Parent := FBackCtrl;
      LFileStream.ReadComponent(LCtrl);
      Application.ProcessMessages;
    end;
  finally
    FreeAndNil(LFileStream);
    LockWindowUpdate(0);
  end;
end;

end.




02_クラス名と選択状態も保存して読み出す


異なるクラスの描画用コトンロールを複数保存して読み出す例です.
保存の時はクラス名を意識する必要はありませんが,読み込む時はクラスで型キャストする必要があります.以下のサンプルでは,クラス名の文字列と Selected プロパティの値を保存した後に描画用クラスのコントロール本体を保存するようにする例です.読み出す時はクラス名を読み出して,そのクラス名で処理を分岐します.
これは,より汎用的な方法と言えます.



図4
[生成] をクリック
  • 位置とサイズを適当に操作

図5
[図保存してクリア] ボタンをクリック

図6
[読み込む] ボタンをクリック
  • 保存前の状態を再現
    選択状態も再現

リスト2
汎用的と言える描画用コントロールの保存と読み出し
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ExtCtrls, StdCtrls, GDIPOBJ, GDIPAPI, GDIPUTIL, Generics.Collections,
  plGPResizeImage,
  plGPResizeLine,
  plGPResizeEllipseEx,
  plContainerPanel,
  plBaseDragResize;

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
  private
    { Private 宣言 }
    FBackCtrl : TplContainerPanel;
    FFileName : String;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

//=============================================================================
//  フォーム生成時の処理
//  専用のコンテナコントロールを生成
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Panel1.Caption := '';

  FBackCtrl := TplContainerPanel.Create(Self);
  FBackCtrl.Parent    := Self;
  FBackCtrl.Align     := alClient;

  FFileName := '01_SavedCtrols.dat';
end;

//=============================================================================
//  [生成] ボタン
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LCtrl : TplBaseDragResize;
begin
  // 生成済みの描画用コントロールを削除
  FBackCtrl.DeleteAllCtrls;

  LCtrl := TplGPResizeImage.Create(Self);
  LCtrl.Parent      := FBackCtrl;
  LCtrl.InverseDisp := True;
  TplGPResizeImage(LCtrl).LoadFromFile('../../Images/503_Transe.bmp');

  LCtrl := TplGPResizeLine.Create(Self);
  LCtrl.Parent      := FBackCtrl;
  LCtrl.Left := 120;
  LCtrl.InverseDisp := True;
  TplGPResizeLine(LCtrl).PenWidth    := 20.0;
  TplGPResizeLine(LCtrl).PenColor    := $FF00A0FF;
  TplGPResizeLine(LCtrl).StartArrow  := True;

  LCtrl := TplGPResizeEllipseEx.Create(Self);
  LCtrl.Parent := FBackCtrl;
  LCtrl.Left := 240;
  LCtrl.InverseDisp := True;
  TplGPResizeEllipseEx(LCtrl).PenColor := aclGreen;
end;

//=============================================================================
//  [保存してクリア] ボタン
//  描画用コントロールをファイルに保存
//
//  クラス名,Selected プロパティの値,コントロールの順に格納
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  LFileStream : TFileStream;
  LpBuffer    : array [0..49of Char;
  LCtrl       : TplBaseDragResize;
  Lindex      : Integer;
begin
  if FBackCtrl.CtrlList.Count = 0 then Exit;

  LFileStream := TFileStream.Create(FFileName, fmCreate);
  try
    for Lindex := 0 to FBackCtrl.CtrlList.Count - 1 do begin
      LCtrl := FBackCtrl.CtrlList[Lindex];
      FillChar(LpBuffer, SizeOf(LpBuffer), #0);
      StrPCopy(@LpBuffer[0], LCtrl.ClassName);

      LFileStream.Write(@LpBuffer[0], SizeOf(LpBuffer));
      LFileStream.WriteData(LCtrl.Selected);
      LFileStream.WriteComponent(LCtrl);

      Application.ProcessMessages;
    end;
  finally
    FreeAndNil(LFileStream);
  end;
  FBackCtrl.DeleteAllCtrls;
end;

//=============================================================================
//  [読み込み] ボタン
//  保存時の図描画状態を再現
//
//  クラス名,Selected プロパティの値,コントロールの順に読み出す
//  uses に Generics.Collections が必要
//=============================================================================
procedure TForm1.Button3Click(Sender: TObject);
var
  LFileStream : TFileStream;
  LCtrl       : TplBaseDragResize;
  LpBuffer    : array [0..49of Char;
  LClassName  : String;
  LSelected   : Boolean;
  LSelList    : TList<Boolean>;
  LIndex      : Integer;
begin
  FBackCtrl.DeleteAllCtrls;

  LFileStream := TFileStream.Create(FFileName, fmOpenRead);
  LSelList := TList<Boolean>.Create;
  LockWindowUpdate(FBackCtrl.Handle);
  try
    if LFileStream.Size <= 0 then Exit;

    LFileStream.Position := 0;
    while True do begin
      if LFileStream.Position >= LFileStream.Size then Break;


      FillChar(LpBuffer, SizeOf(LpBuffer), #0);
      LFileStream.Read(LpBuffer[0], SizeOf(LpBuffer));
      LClassName := Trim(LpBuffer);
      LFileStream.ReadData(LSelected);
      LSelList.Add(LSelected); 

      if  LClassName = 'TplBaseDragResize' then begin
        LCtrl := TplBaseDragResize.Create(Self);
      end else
      if LClassName = 'TplGPResizeImage' then begin
        LCtrl := TplGPResizeImage.Create(Self);
      end else
      if LClassName = 'TplGPResizeLine' then begin
        LCtrl := TplGPResizeLine.Create(Self);
      end else
      if  LClassName = 'TplGPResizeEllipseEx' then begin
        LCtrl := TplGPResizeEllipseEx.Create(Self);
      end else begin
        // 失敗
        FBackCtrl.DeleteAllCtrls;
        Exit;
      end;
      LCtrl.Parent := FBackCtrl;
      LFileStream.ReadComponent(LCtrl);

      Application.ProcessMessages;
    end;

    // 選択状態の再現
    for LIndex := 0 to FBackCtrl.CtrlList.Count -1 do begin
      FBackCtrl.CtrlList[LIndex].Selected := LSelList[LIndex];
    end;
  finally
    FreeAndNil(LFileStream);
    FreeAndNil(LSelList);
    LockWindowUpdate(0);
  end;
end;

end.




03_画像や図形の複製 ( 画像や図形のコピー )


描画用コントロールの複製,つまり,図形をコピーして貼り付ける例です.描画用コントロールをメモリストリームに格納して読み出します.複数の描画用コントロールを複製するには,前項のように順番にメモリストリームに格納していきます.


図7
[生成] をクリック
  • 位置とサイズを適当に操作

図8
[図形を複製] をクリック
  • 左上が複製したコントロール

リスト3
画像や図形の複製 ( 画像や図形のコピー
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ExtCtrls, StdCtrls, GDIPOBJ, GDIPAPI, GDIPUTIL,
  plGPResizeImage,
  plGPResizeLine,
  plGPResizeEllipseEx,
  plContainerPanel,
  plBaseDragResize;

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    Button1: TButton;
    Button2: TButton;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private 宣言 }
    FBackCtrl : TplContainerPanel;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

//=============================================================================
//  フォーム生成時の処理
//  専用のコンテナコントロールを生成
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Panel1.Caption := '';

  FBackCtrl := TplContainerPanel.Create(Self);
  FBackCtrl.Parent    := Self;
  FBackCtrl.Align     := alClient;
end;

//=============================================================================
//  [生成] ボタン
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LCtrlElli : TplGPResizeEllipseEx;
begin
  // 生成済みの描画用コントロールを削除
  FBackCtrl.DeleteAllCtrls;

  LCtrlElli := TplGPResizeEllipseEx.Create(Self);
  LCtrlElli.Parent := FBackCtrl;
  LCtrlElli.InverseDisp := True;
end;

//=============================================================================
//  [図形を複製] ボタン
//  [コピーして貼り付け] の機能
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  LMemStream : TMemoryStream;
  LCtrl      : TplBaseDragResize;
  Lindex     : Integer;
begin
  // 選択中のコントロールがない場合と 2 以上の時は何もしない
  if FBackCtrl.CtrlList.SelCount = 0 then Exit;
  if FBackCtrl.CtrlList.SelCount > 1 then Exit;

  Lindex := FBackCtrl.CtrlList.ItemIndex;

  LCtrl := FBackCtrl.CtrlList[Lindex];

  LMemStream := TMemoryStream.Create;
  try
    if LCtrl is TplGPResizeEllipseEx then begin
      LMemStream.WriteComponent(TplGPResizeEllipseEx(LCtrl));

      LCtrl:= TplGPResizeEllipseEx.Create(Self);
      LCtrl.Parent := FBackCtrl;
      LMemStream.Position := 0;
      LMemStream.ReadComponent(LCtrl);
      LCtrl.Left := 0;
      LCtrl.Top := 0;
      LCtrl.Selected := True;
    end;
  finally
    FreeAndNil(LMemStream);
  end;
end;

end.