Delphi Programming / Object Pascal
サイトのトップページへ リンクのページへ ロゴマーク
[掲載 2013年01月18日] [更新 2016年12月08日] Delphi サンプルプログラム集
   
122_複数のフォームを使用するプロジェクト ( 入門者用 )
動作確認等 Windows 7 U64(SP1) + Delphi XE Pro
122_PluralForms.zip [4,297 KB] 2016年12月08日版(EXE同梱)



  • 2013年06月08日
  • public 部と published 部の違い等の記事を追加




複数のフォームを使用するプロジェクト


メインフォーム以外のフォームを必要とするプロジェクトを作成する時の参考サンプルです.
フォームが 1 つだけのアプリケーション作成の知識はあるものとします.
別のフォーム,他のフォームのメンバ (プロパティやメソッド,関数類) の値を取得したり,値を設定,セット,受け渡しする時等の参考用です.

他のサンプルでは,主に,どのような動作をするかをまず図で示してから,その動作をするためのコード示すようにしていますが,このページのサンプルは,最初にコードを示して,それを実行した結果としての図を示すようにしています.

当然ですが,入門者用の記事ですから,読むだけでなく,実際に順番に操作することを前提にしてサンプルと記事を書いています.



[備考 1]
Delphi に限らず,1 つのプロジェクトは 1 つのプログラム単位です.1 つのプロジェクトから 1 つの EXE あるいは DLL ができます.たとえ同じ Delphi のプロジェクトであっても,別のプロジェクトで作成した EXE は全く別のアプリケーションとなります.
Delphi の IDE では,複数のプロジェクトを開いて編集ができますが,異なるプロジェクトの間で,本ページの方法が使えるわけではありません.


[備考 2]
ツールバーにコンポーネントパレットを表示するようにしておくと便利です.





01_メインフォーム以外のフォームの作成方法


まず,新規にプロジェクトを作成します.新規にプロジェクトを作成するには,[ファイル] メニューから [VCL フォームアプリケーション] を選択します.

プロジェクトにフォームを追加するには,次の図のように,メニューの[ファイル] の [新規作成] から [フォーム] を選択します.いくつでも追加できます.
追加すると,フォームの名前は,Form2, Form3, ... となります.ユニット名は Unit2, Unit3, ... となります.

新規に VCL フォームアプリケーションでプロジェクトを作成した時のフォームをメインフォーム,追加作成したフォームを子フォームと言いいます.



図1
フォーム追加
図2
Unit2 ができる

フォームを追加すると,プロジェクトのソースは以下のようになります.
uses に Unit1.pas と Unit2.pas が追加され.これらが,このプロジェクトに含まれていることを意味しています.また,begin 以降の CreateForm で Form1 と Form2 が生成され,このプロジェクトの実行時にこれらのフォームが生成されることになります.
そして,Application.Run で各種の初期化が行われ,メインフォームのユニットである Unit1 のコードが実行されます.


リスト1
プロジェクトのソースコード
メニューの [プロジェクト] [ソース表示] で表示する
program Project1;

uses
  Forms,
  Unit1 in 'Unit1.pas' {Form1},
  Unit2 in 'Unit2.pas' {Form2};

{$R *.res}

begin
  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  Application.CreateForm(TForm1, Form1);
  Application.CreateForm(TForm2, Form2);
  Application.Run;
end.


[備考]
アプリケーションの起動時,つまり EXE を実行すると最初に表示するフォームをメインフォームと言います.
追加した子フォームをメインフォームにすることもできます.
メニューの [プロジェクト] [オプション] でダイアログを表示します.左ペインの [フォーム] を選択して,[メインフォーム] のリストからメインフォームにするフォームを選択します.





02_使用するフォームのユニット名を uses 部に追加する


以降の全てのサンプルでは,追加したフォームを,メインフォームから表示します.
そのためには,メインフォームの Unit1 で,それらのフォームのユニット名を uses 部に追加しておかなければなりません.
ここでは,implementation に追加します.interface 部で追加したフォームを参照する必要がなければ,implementation 部の uses に追加しておくと,循環参照エラーが発生するのを防止できる効果があります.


リスト2
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, StdCtrls;

type
  TForm1 = class(TForm)
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses Unit2;  //これを追加する

{$R *.dfm}

end.


次の図のような方法でも uses 部にユニット名を追加できます.


図3
[ファイル][ユニットを使う]
図4
Unit2 を選択して [OK]




03_アプリ生成時に他のフォームも生成  -  自動生成


これでプロジェクトにフォームを追加することができるようになりました.実際にやってみます.
  • (1) 新規にプロジェクトを作成
  • (2) フォームを 1 つ追加
  • (3) uses 部に Unit2 を追加
  • (4) メインフォームに TButton を追加して,そのクリックイベントを下のコードとする
このコードは,Form2 の表示位置を設定して,ShowModal メソッドで Form2 を表示します.



リスト3
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses Unit2;  

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
  Form2.Left := Self.Left - 50;
  Form2.Top  := Self.Top  + 80;

  Form2.ShowModal;
end;

end.


コンパイルして実行します.Button1 をクリックすると下図のようになります.
Form1 と Form2 のサイズは,デフォルトの値でなく,変更しています.


図5
実行画面
  • このサンプルでは,Form2 を閉じないとメインフォームには戻れない

上のコードには,Form2 という変数の定義文がありません.Form2 の変数の定義は,Unit2 で以下のように,グローバル変数として定義されています.
グローバル変数は,その変数が定義されているユニット名を uses 部に追加すれば,どのユニットからも参照できます.

var
  Form2 : TForm2;

Form2 は Unit2 にありますから,厳密には以下のように,Unit2.Form2 です.
この Unit2 のことを (ユニット) スコープ名とも言いますが,同じ名前のフォームは複数作成することはできません.したがって,Unit2 を付ける必要はありません.

フォーム以外の他のユニットにある関数類も,重複していなければユニット名を付ける必要はありません.Delphi が提供している関数等も,通常はユニット名を付けないで使用しています.
ただし,どのユニットにある関数なのかを,明らかにした方がいい場合は,ユニット名付けることがあります.それは,プログラマ自身の判断ということになります.

同じ名前の関数が複数のユニットにあって,それらのユニットを同時に使用する場合は,ユニット名を付けないと区別がつきません.


procedure TForm1.Button1Click(Sender: TObject);
begin
  Unit2.Form2.Left := Self.Left - 50;
  Unit2.Form2.Top  := Self.Top  + 80;

  Unit2.Form2.ShowModal;
end;


[備考]
上のコードに Self というのがあります.Self は和訳すると自分自身です.
メソッドやイベントの呼出し元のクラスのインスタンス (自分) を表す変数です.上のコードの場合は Form1 のイベント内で使用していますから,Form1 のことです.






04_メインフォームを閉じるとアプリケーション終了


複数のフォームを使用するアプリケーションでは,メインフォームを閉じると,アプリケーションが終了することになります.したがって,メインフォームの OnClose, OnCloseQuery, OnDestroy イベント等に,アプリケーション終了時の処理を実行させることができます.
逆に,アプリケーション開始時の処理には,メインフォームの OnCreate イベントを使用することができます.ただし,OnCreate イベントで時間のかかる初期化処理を行うと,EXE の起動が遅くなることがあります.

一方,追加したフォームを閉じてもアプリケーションは終了しません.その追加したフォームが閉じるだけです.




05_アプリ実行時に他のフォームを生成  -  使用可能フォーム


新規にプロジェクトを作成して,上のサンプルと同じように,1 つだけ別のフォーム作成し,同じ作業を行いますが,Button1 のクリックイベントを次のようにします.
このテストは,新規にプロジェクトを作成しなくても,上のテストで使用したプロジェクトを使用しても構いません.


リスト4
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses Unit2;

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
  //TForm2というクラスのフォームのインスタンスを生成する
  Form2 := TForm2.Create(Self);
  try
    //表示位置を設定
    Form2.Left := Self.Left - 50;
    Form2.Top  := Self.Top  + 80;

    //ShowModalメソッドで表示
    Form2.ShowModal;
  finally
    FreeAndNil(Form2);
  end;
end;

end.


続いて,今度は,下図のように,[プロジェクト] [オプション] でダイアログを表示し,[フォーム] を選択し,[自動生成フォーム] にある Form2 を [>] ボタンで [使用可能フォーム] に移動します.


図6
フォームの設定
  • Form2 を [→] ボタンをクリックして,[使用可能なフォーム] に移動する

[OK] ボタンでダイアログを閉じたら,[F9] を押下して実行します.実行結果は,前のサンプルと全く同じとなります.

プロジェクトのソースを見ると,次のようになっています.CreateForm では Form2 を生成していません.つまり,Form2 は,このプロジェクト実行開始時には生成されないことになります.
そこで,Form2 を使用する時は,実行時に Form2 のインスタンス (実体) を生成します.



リスト5
プロジェクトのソースコード
メニューの [プロジェクト] [ソース表示] で表示する
program Project1;

uses
  Forms,
  Unit1 in 'Unit1.pas' {Form1},
  Unit2 in 'Unit2.pas' {Form2};

{$R *.res}

begin
  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.


Form2 のインスタンス (実体) を実行時に生成するコードが,


  Form2 := TForm2.Create(Self);

です.もし,このコードを書かないでコンパイルして実行すると,下図のように,アドレスに対する読み取り違反のエラーが発生します.



図7
フォームを Create しない時のエラー

フォームだけでなく,TButton 等のコンポーネント,コントロールも,設計時にはフォームに配置しないで,実行時にそのインスタンス (実体) を生成して利用できます.
これを,[実行時生成] とか [動的生成] と言っています.



TForm2 のインスタンスを生成する際,次の AForm のように別の名前を定義して生成することもできます.TButton 等も,いろいろな名前で使用できるのと同じです.
この方法は,この後のサンプルでも使用しています.

procedure TForm1.Button1Click(Sender: TObject);
var
  AForm : TForm2;
begin
  //TForm2というクラスのフォームのインスタンスをAFormという名前で生成する
  AForm := TForm2.Create(Self);
  try
    //表示位置を設定
    AForm.Left := Self.Left - 50;
    AForm.Top  := Self.Top  + 80;

    //ShowModalメソッドで表示
    AForm.ShowModal;
  finally
    FreeAndNil(AForm);
  end;
end;


[備考]
業務アプリの開発では,時には数十のフォームを使用することがあります.この時,フォームをデフォルトの [自動生成] にしていると,作成した EXE を起動する時に全てのフォームの初期化が行われます.そのため,EXE の起動が遅くなることがあります.
メインフォーム以外のフォームを [使用可能なフォーム] にして実行時に生成すれば,この現象を回避することができる場合があります.リソース (メモリ) の節約にもなります.
どのフォームを [自動生成] や [使用可能なフォーム] にするかは,そのフォームの処理内容やメインフォームとの関係によって決めることになります.子フォームから更に別のフォームを使用する場合も同じです.
後述の [プロジェクトにないフォームの利用...] の説明のように,子フォームをプロジェクトに追加しないで利用することもあります.





06_モーダル表示とモードレス表示 - ShowModal と Show メソッド


いままでのサンプルでは,モーダルと呼ばれる形式で別のフォームを表示していました.
別のフォームを表示する際,モードレス形式というのもあります.この両方の表示形式で,別のフォームを表示して,それらがどんなのかを確認するテストです.

新規にプロジェクトを作成します.そして,フォームを 2 つ作成します.もちろん,uses には Unit2 と Unit3 を追加します.そして,プロジェクトのソースを表示して,MainFormOnTaskbar の値を False にします.

以下に参考リンクを示していますが,実際にやってみて,体で覚える方が確実で早道です.




リスト6
program Project1;

uses
  Forms,
  Unit1 in 'Unit1.pas' {Form1},
  Unit2 in 'Unit2.pas' {Form2},
  Unit3 in 'Unit3.pas' {Form3};

{$R *.res}

begin
  Application.Initialize;
  Application.MainFormOnTaskbar := False;  //Falseにする
  Application.CreateForm(TForm1, Form1);
  Application.CreateForm(TForm2, Form2);
  Application.CreateForm(TForm3, Form3);
  Application.Run;
end.


Button1 と Button2 のクリックイベントを各々次のようにします.


リスト7
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, StdCtrls;

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

var
  Form1: TForm1;

implementation

uses Unit2, Unit3;

{$R *.dfm}

//-----------------------------------------------------------------------------
//  Form2をモーダルで表示
//-----------------------------------------------------------------------------
procedure TForm1.Button1Click(Sender: TObject);
begin
  Form2.Left := Self.Left - 50;
  Form2.Top  := Self.Top  + 80;

  Form2.ShowModal;
end;

//-----------------------------------------------------------------------------
//  Form2をモードレスで表示
//-----------------------------------------------------------------------------
procedure TForm1.Button2Click(Sender: TObject);
begin
  Form3.Left := Self.Left - 50;
  Form3.Top  := Self.Top  + 80;

  Form3.Show;
end;

end.


コンパイルして実行します.次の図のようになります.


図8
Button1 をクリック
  • Form2 を モーダル表示する
  • Form2 を閉じないとメインフォームには戻れない
図9
Form2 を閉じて Button2 をクリック
  • Form3 を モードレス表示
  • メインフォーム,From3 のどちらにもフォーカス移動ができる





07_メインフォームを表示する前に別のフォームを表示


アプリの実行開始時に,メインフォームを表示する前に,別のフォームを表示する例です.
新規にプロジェクトを作成したら,フォームを 1 つ追加します.追加したフォームは [使用可能なフォーム] にしておきます.
次に,メインフォームの OnCreate イベントを作成して,そのイベントのコードを次のようにします.
このサンプルでは,メインフォームの OnCreate 内で位置の取得できません.そこで,具体的な値を設定しています.


リスト8
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses Unit2;

{$R *.dfm}

//-----------------------------------------------------------------------------
//  メインフォーム生成時に別のフォームを表示
//  メインフォームの位置は取得できないので,Form2の位置は具体的な値にする
//  Form2は,自動生成から除外して,使用可能フォームにしておく
//-----------------------------------------------------------------------------
procedure TForm1.FormCreate(Sender: TObject);
var
  AForm : TForm2;
begin
  AForm := TForm2.Create(Self);
  try
    AForm.Left := 500;
    AForm.Top  := 300;

    AForm.ShowModal;
  finally
    FreeAndNil(AForm);
  end;
end;

end.


実行した時の画面です.EXE を起動すると,まず,Form2 が表示されます.この Form2 を閉じるとメインフォームが表示されます.


図10
EXE を起動すると Form2 が表示される
図11
Form2 を閉じるとメインフォーム表示




08_メインフォームの表示直後に別のフォームを表示


アプリの実行開始時に,メインフォームを表示した直後に別のフォームを表示する例です.
新規にプロジェクトを作成したら,フォームを 1 つ追加します.追加したフォームは [自動生成フォーム] のままにしておきます.
次に,CM_SHOWINGCHANGED メッセージの処理コードを追加します.追加方法は,[メッセージ処理メソッドの作成] を参考にしてください.



リスト9
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
  private
    { Private 宣言 }
  protected
    procedure CMShowingchanged(var Message: TMessage);
      message CM_SHOWINGCHANGED;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses Unit2;

{$R *.dfm}

//-----------------------------------------------------------------------------
//  CM_SHOWINGCHANGEDメッセージの処理
//  CM_SHOWINGCHANGEDは表示の状態が変化した時に発生する
//  非表示になる時も発生するので,Visibleプロパティで判断する
//-----------------------------------------------------------------------------
procedure TForm1.CMShowingchanged(var Message: TMessage);
begin
  inherited;  //必ず必要

  if Self.Visible then begin
    Form2.Left := Self.Left - 50;
    Form2.Top  := Self.Top  + 80;

    Form2.ShowModal;
  end;
end;

end.


準備ができたら,コンパイルして実行します.


図12
EXE 起動直後の画面
  • メインフォーム表示直後に Form2 を表示
  • Form2 を閉じないと,メインフォームにはフォーカス移動ができない




09_他のフォームのユニットの変数やメソッド類を使う


複数のフォームを使用するアプリケーションでは,他のフォームに配置したコンポーネントを操作したり,他のフォームのユニットの変数の値を取得,設定したり,あるいは,関数等を実行したりすることがあります.その簡単なテストです.

新規にプロジェクトを作成したら,メインフォームとなる Form1 に TButton を 1 つ,TPanel を 2 つ配置します.


図13
メインフォームの設計時画面

次に,別のフォームを 1 つ作成します.これは Form2 となります.
この Form2 に TPanel を 1 つ配置して,Caption プロパティの値を,'テスト文字列' とします.
public 部に,TestStr という文字列型の変数を定義します.また,PutTextString というメソッドを作成して,そのコードを次のようにします.
メソッド類の作成は,[関数等の宣言文から実装部 ( スケルトン ) を作成] や [実装部から関数等の宣言文を作成]を参考にしてください.


[備考 1]
自作のメソッドや関数類の定義は,private部 や public 部に書きます.そうすれば,オブジェクトインスペクタで設定したイベント (もメソッドの一種です) と区別できます.また,他のユニットから,それらのメソッド,関数類を参照する際のアクセス制限も可能となります.このような慣習には従った方が無難です.


リスト10
メインフォームから表示する Form2 のユニットのコード
unit Unit2;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls;

type
  TForm2 = class(TForm)
    Panel1: TPanel;
  private
    { Private 宣言 }
  public
    { Public 宣言 }
    TestStr : String;
    procedure PutTextString;
  end;

var
  Form2: TForm2;

implementation

{$R *.dfm}

{ TForm2 }

//-----------------------------------------------------------------------------
//  文字列型の変数TestStrに文字列を代入するメソッド
//-----------------------------------------------------------------------------
procedure TForm2.PutTextString;
begin
  TestStr := 'DELPHI XE';
end;

end.


次にメインフォームの Button1 クリックのイベントを次のようなコードにします.

このコードは,Form2 の PutTextString メソッドを実行します.このメソッドは TestStr という文字列型の変数に文字列を代入します.
本サンプルでは,メインフォームからこの TestStr の値を取得して表示します.また,Form2 に配置している Panel1 の Caption の文字列を取得して表示します.


リスト11
メインフォームのユニットのコード
Form2 のメソッドを実行して,Form2 の変数の値を取得して表示する
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, StdCtrls;

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

var
  Form1: TForm1;

implementation

uses Unit2;

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
  Panel1.Caption := '';
  Panel2.Caption := '';

  //Form2のPutTextStringメソッドを実行
  Form2.PutTextString;
  //Form2の変数であるTestStrの値をPanel1に表示
  Panel1.Caption := Form2.TestStr;

  //Form2のPanel1のCaptionの値を表示
  Panel2.Caption := Form2.Panel1.Caption;
end;

end.


実行結果です.


図14
EXE 起動直後の画面
図15
Button1 をクリック
  • 上の TPanel は Form2 の PutTextString メソッドの実行結果が格納された TestStr の値
  • 下の TPanel は Form2 上に配置したPanel1 の Caption プロパティの値

このサンプルのように,他のフォームあるいはユニットの変数の取得や設定ができます.他のフォームに配置してあるコンポーネントの操作も可能です.またメソッドや関数も実行できます.

ただし,他のユニットの変数等の操作が全てできるわけではありません.次のように,それらの変数やメソッドあるいは関数類がどこに定義されているかにもよります.



type
  TMyClass = class(TObject)
    //ここには書かないこと(Delphiが使用する特別な領域)
  private
    //ここに書いた変数やメソッド,関数類は同じユニット内だけでアクセス可能
    //他のユニットからはアクセスできない
  protected
    //ここに書いた変数やメソッド,関数類は同じユニット内だけでアクセス可能
    //他のユニットからはアクセスできない
  public
    //ここに書いた変数やメソッド,関数類はどのユニットからもアクセス可能
  published
    //ここに書いた変数やメソッド,関数類はどのユニットからもアクセス可能
  end;


[備考 2]
他のフォーム等,他のクラスの変数メンバを使用する際,実用的には,それらの変数をプロパティとして実装します.そうすれば,それらのプロパティの値を取得したり,セットしたりする時に,特定の処理を行うことができます.呼出し側で処理しなくても,呼び出される側で処理すれば,アプリケーションの動作仕様の変更にも柔軟に対応できるようになります.


[備考 3]
複数のフォームを使用するアプリケーションでは,共通に使用する定数や変数,あるいはメソッドが必要な場合があります.そのような場合,フォームがないユニットを作成して,そのユニットを uses して使用するのが一般的です.
そのユニット内の変数を使用する場合,ユニット名.変数名 という使い方をすることがあります.そうすると,その変数は,現在のユニットにはないことが明確に分かります.
あるいは,クラス型として実装すれば,クラスとしての機能が利用でき便利です.





10_private 部と protected 部の違い


どちらも他のユニットからも参照,アクセスできませんが,クラスの機能を拡張したり,仕様を変更した継承クラスを作成することによって,他のユニットからアクセス可能にすることができます.そのためには,継承したクラスで,public 部あるいは published 部へ「移動」するのですが,その方法はプロパティとメソッドとで違います.
また,protected 部のメンバーは,他のユニットで単に継承クラスを定義すれば,継承クラスの型にキャスト (型変換) することで,他のユニットからもアクセスできるようになります.private 部はまさにプライベートのままで,他のユニットからはアクセスできません.
上のコードでも,TMyClass は TObject を継承したクラスということになります.

Delphi では,コンポーネントパレットに登録するものを「コンポーネント」,そうでないものを単に「クラス」と言っていますが,実際にはどちらも「クラス」であることに間違いありません.コンポーネントパレットに登録できない「クラス」でも,便宜上「コンポーネント」と言う場合があります.あまり厳密に考える必要はないかも知れません.





11_public 部と published 部の違い


どちらも他のユニットから参照できます.主にプロパティやイベントの定義に使用します (実際にはイベントもプロパティの一種です).
published 部で定義したメンバーは,そのクラスをコンポーネントとして登録すると,オブジェクトインスペクタに表示され,設計時に値を設定できるようになります.これらの設定値は,フォームの生成時,OnCreate イベントが発生する前に設定されます.

また,published 部のメンバーは,クラスのインスタンス (TButton ならば Button1 等) をファイルに保存すると,その設定値もファイルに保存されます.public 部の値は保存されません.




12_ローカル変数とグローバル変数


イベント内,あるいは関数やメソッド内で定義した変数は,ローカル変数です.
ローカル変数は,そのイベント内,あるいは関数やメソッド内だけで有効です.他の関数やメソッドでは使用できまません.

一方,上で説明した private 部, protected 部, public 部, published 部で定義した変数はグローバル変数です.ユニット内のどこからも参照できます.
public 部, published 部のグローバル変数は,上で説明したように,そのユニット内だけでなく他のユニットからも参照できます.つまり利用できます.
private 部で定義した変数は,その変数が実際に,そのユニット内で使用されているかを,コンパイル時にチェックしてくれます.

次のように,implementation 部で定義した変数類もグローバル変数ですが,この変数はこのユニット内の implementation 部だけで有効な変数となります.implementation 部で定義した変数や関数類は,その変数が実際にそのユニット内で使用されているかはチェックされません.
他のユニットからは参照されたくない場合や,コールバック関数と呼ばれる関数内で参照する変数に使われることがあります.



var
  Form1: TForm1;  //どのユニットからも使用可能なグローバル変数

implementation

{$R *.dfm}

var
  UTestText : String;  //ユニット内のimplementation部だけで使用可能なグローバル変数





13_プロジェクトにないフォームの利用と ModalResult


他のフォームあるいはユニットは,プロジェクトに追加されていなくても操作可能です.
このことを確認してみます.
上で使用したプロジェクトを変更していきます.

まず Form2 に TButton を 2 つ配置して,各々の Caption を図のようにします.
[OK] ボタンの Default プロパティを True にして,ModalResult プロパティを mrOkにします.[キャンセル] ボタンの Cancel プロパティを True に,ModalResult プロパティを mrCancel にします.オブジェクト・インスペクタで設定します.



図16
Form2 にボタンを 2 つ配置

次に,メニューの [プロジェクト] [プロジェクトから削除] でダイアログを表示します.
ここで Unit2 を選択して [OK] ボタンで終了します.


図17
[プロジェクト] から削除
図18
Unit2 を削除

プロジェクトのソースが次のようになります.
これは,フォームが 1 つだけのプロジェクトの場合と同じです.


リスト12
program Project1;

uses
  Forms,
  Unit1 in 'Unit1.pas' {Form1};

{$R *.res}

begin
  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.


メインフォームの Button1 の OnClick イベントを次のように変更します.


リスト13
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, StdCtrls;

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

var
  Form1: TForm1;

implementation

uses Unit2;

{$R *.dfm}

//-----------------------------------------------------------------------------
//  プロジェクトにないフォームの変数やメソッドを使用
//  Form2は,このプロジェクトには含まれていない
//
//  Form2を生成して使用する
//  Form2のButton1のModalResultはmrOkで,DefaultをTrueにしている
//  Form2のButton2のModalResultはmrCancel
//-----------------------------------------------------------------------------
procedure TForm1.Button1Click(Sender: TObject);
var
  AForm : TForm2;
begin
  Panel1.Caption := '';
  Panel2.Caption := '';

  AForm := TForm2.Create(Self);
  try
    AForm.Left := Self.Left - 50;
    AForm.Top  := Self.Top  + 70;

    if AForm.ShowModal = mrOk then begin
      AForm.PutTextString;
      Panel1.Caption := AForm.TestStr;

      Panel2.Caption := AForm.Panel1.Caption;
    end;
  finally
    FreeAndNil(AForm);
  end;
end;

end.


実行結果は次のようになります.
このサンプルでは,{OK] ボタンのクリックと同じ動作を {Enter] キー押下で行うことができます.また,[Esc] キーは [キャンセル] ボタンのクリックと同じ動作となります.
この機能を実現するために,ModalResult プロパティを使用しました.


図19
EXE を起動して Button1 をクリック
  • 表示した Form2 は [Enter] または [Esc] で閉じることもできる
図20
Form2 を [OK] で閉じた場合
  • [Enter] キーで閉じた場合も同じ

図21
Form2 を [キャンセル] で閉じた場合
  • [Esc] キーで閉じた場合も同じ




14_他のユニットは uses 部に追加して使用する


以上のように,プロジェクトに含まれていないユニットも,uses 部に追加すると利用できますが,プロジェクトに含まれていないユニットは,プロジェクトマネージャーには表示されません.

このサンプルでは Unit2 ですが,Unit2.pas を表示したい場合は,uses 部の Unit2 にマウスカーソルを移動し,[Ctrl] キー を押したまま,マウスの左ボタンをクリックするか,[Ctrl] キーを押したまま [Enter] キーを押下します.
もし開かない時は,プロジェクトを再構築またはビルドしてみてください.

pas ファイルで提供されているコンポーネントやユーティリティ類も,uses 部に該当するユニット名を追加すれば利用可能となります.
ユニット名とは,ファイル名から,拡張子 .pas を除いたものです.

コンポーネントやユーティリティ類は複数のファイルで提供されていることがありますが,uses に追加するのは必ずしも,それらの全てが必要とは限りません.普通はそれらに添付している文書に書いてあります.
ただし,使用するユニットは,ライブラリのパスが通っていないと使用できません.




図22
Unit2 は表示されない
  • [プロジェクトに追加] していないユニットは表示されない
  • uses 部に追加すれば [プロジェクトに追加] していないユニットも使用できる
図23
Unit2.pas を開く
  • [Ctrl] + マウス左ボタンクリック
  • または [Ctrl] + [Enter]
図24
メニューからUnit2.pas を開く
  • ポップアップメニューからも開ける

新規にプロジェクトを作成すると,以下のように,interface 部のすぐ下の uses 部にいろいろな ユニット名があります.
つまり,メインフォームだけのプロジェクトでも,これらのユニットの変数 (プロパティ) やメソッドあるいは関数類を使用することになるわけです.したがって,プロジェクトに含まれていないフォームでも,そのユニット名を uses 部に追加すれば,同じように利用可能になるわけです.



interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, StdCtrls;


フォームの場合は,Form2.Button2 のように,フォーム名の後に . (ドット・ピリオド) を付けて,関数,メソッド,あるいは変数等にアクセスします.他のユニット名の場合は,特にユニット名を指定しなくても,uses 部にあるユニットファイルの中から自動的に探し出してくれます.もちろん,明示的にユニット名を付けても構いません.

プロジェクトに追加しないで使用するフォームは,実行時に生成して使用します.プロジェクトに含まれていませんから,[自動生成] の設定はできません.
コンポーネントも同じです.フォームに配置したコンポーネントは,自動で,そのインスタンス (実体) を生成しますが,uses にそのコンポーネントのユニット名を追加しただけの場合は,実行時に生成して使用することになります.





15_uses 部にないユニットの機能は使用できない


ここまでで説明したように,他のフォームも,フォームを使用しないユニットでも,そのユニット名を uses 部に追加すれば,そのユニット内の関数,メソッド,変数,コンポーネント類等にアクセスできることが分かりました.

逆に,uses 部にないユニット内の関数や変数にはアクセスできません.使用しようとすると,未定義の識別子等のエラーが発生します.ただし例外があります.System.pas 内の関数や変数,定数等は,uses 部に System がなくてもアクセスできるようになっています.

コンポーネントの場合は,フォームにコンポーネントを配置すると,uses 部に,自動的に必要なユニット名が追加される仕組みになっています.



[備考]
Delphi に実装されている定数や変数,関類類がどのユニットに定義されているかは,ヘルプで調べることができます.





16_他のプロジェクトでも使用可能な定数や変数,関数等の定義


ユニットはフォームやコンポーネントのユニットだけではありません.
関数 (function, procedure) や定数等だけを記述したユニットを作成することもあります.uses 部にそのユニット名を追加することにより,他のプロジェクトでも利用できます.
例えば [ファイル] [新規作成] [ユニット Delphi] でユニットを新規作成して,HogeHoge.pas という名前で保存します.そして,コードを以下のようにしたとします.



リスト14
定数や変数,関数等だけを定義したユニット
グローバルな定数や変数,関数として使用
unit HogeHoge;

interface

uses
  Windows;

const
  hgHogeConst = 300;

var
  hgHogeValue : string;

// 関数の宣言部(interface部)
function AddValeu(A: Integer; B: Integer): Integer;

implementation

// 関数の実装部(implementation部)
function AddValeu(A: Integer; B: Integer): Integer;
begin
  Result := A + B;
end;

end.


上のユニットを使用する例です.uses にユニット名の HogeHoge を追加します.
ユニット HogeHoge 内の定数,変数,関数類はクラスのメンバとしての実装していません.したがってこれらはグローバルな定数,変数,関数類として使用できます.グローバルな定数,変数,関数類はユニット名を省略して使用できます.
ただし,他のユニットで定義している変数類の名前と重複しないように注意が必要です.



リスト15
上のユニットを使用するプログラムの例
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, StdCtrls;

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

var
  Form1: TForm1;

implementation

uses
  HogeHoge;

{$R *.dfm}

//=============================================================================
//  ユニットHogeHoge.pasの定数,変数,関数類を使用する例
//
//  usesにユニット名のHogeHogeが必要
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LValue : Integer;
  LText  : string;
begin
  HogeHoge.hgHogeValue := 'ABC';
  LValue := HogeHoge.AddValeu(200, 300);

  LText := IntToStr(HogeHoge.hgHogeConst) + sLineBreak
         + HogeHoge.hgHogeValue + sLineBreak
         + IntToStr(LValue);

  MessageBox(Handle, PChar(LText), '結果', MB_ICONINFORMATION);
end;

//=============================================================================
//  ユニットHogeHoge.pasの定数,変数,関数類を使用する例
//  グローバルな定数,変数,関数名はユニット名を省略できる
//
//  usesにユニット名のHogeHogeが必要
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  LValue : Integer;
  LText  : string;
begin
  hgHogeValue := 'ABC';
  LValue := AddValeu(200, 300);

  LText := IntToStr(hgHogeConst) + sLineBreak
         + hgHogeValue + sLineBreak
         + IntToStr(LValue);

  MessageBox(Handle, PChar(LText), '結果', MB_ICONINFORMATION);
end;

end.


図25
上のコードの実行結果

[備考]
Delphi が提供しているユニット類も,他のプロジェクトで使用可能なユニットです.ただし,本項のユニットの使用例と同じように,uses 部にユニット名を追加しなければそのユニット内の機能は利用できません.
また,既に述べたように,そのユニットのファイル (拡張子は .pas),または,そのユニットのコンパイル済ファイル (拡張子は .dcu) のどちらかがライブラリのパスが通った場所にないと利用できません.
uses というのは「使う」と言う意味です,つまり「使う」指定をしなければ使えません.