TStringGrid は、テキストデータを表形式にして表示するコンポーネントです。
それぞれのセルの文字列の取得、及び設定は Cells プロパティで行い、そのセルに関連付けられるオブジェクトは、Objects プロパティで設定可能です。
各列に対して、文字列およびオブジェクトを操作するには Col プロパティを使用します。同様に、各行に対しては Rows プロパティを使用します。
TStringGrid のサンプルプログラムとして、「九九の表」を作ってみよう。
図のような表を作成します。このプログラムを作成するに当たって、重要な点は、行(Row)、列(Col)が、それぞれいくつ必要で、どのセル(Cell)に何を表示するかということです。それさえ把握すれば、作成自体は簡単です。
コードは以下のようになります。
procedure TForm1.Button1Click(Sender: TObject); const MAX_COL = 10; MAX_ROW = 10; var i, j: Integer; begin StringGrid1.RowCount := MAX_ROW; StringGrid1.ColCount := MAX_COL; for i := 0 to MAX_COL-1 do begin StringGrid1.Cells[i, 0] := IntToStr(i); StringGrid1.Cells[0, i] := IntToStr(i); end; for i := 1 to MAX_COL-1 do for j := 1 to MAX_ROW-1 do StringGrid1.Cells[i, j] := IntToStr(i*j); end; procedure TForm1.FormCreate(Sender: TObject); begin StringGrid1.DefaultColWidth := 30; end;
このコードを実行すると、示した図と同じものが表示されます。それぞれのコードについて、詳しく見ていきましょう。
procedure TForm1.FormCreate(Sender: TObject); begin StringGrid1.DefaultColWidth := 30; end;
このコードは、各セルの幅を(ピクセル単位)で調節しています。DefaultColWidth プロパティの設定を変更した場合、それは全てのセルに影響します。また、DefaultRowWidth で高さの変更を行えます。
次に、実際にセルに文字列を表示させるコードを見ていきます。このとき、Cells プロパティが使用されていますが、この Cells プロパティは、以下のように定義されています。
property Cells[ACol, ARow: Integer]: string;
この Cells プロパティの定義に注意しながら、以下の「セルに文字列を表示」するプログラムを見てください。
procedure TForm1.Button1Click(Sender: TObject); const MAX_COL = 10; MAX_ROW = 10; var i, j: Integer; begin StringGrid1.RowCount := MAX_ROW; StringGrid1.ColCount := MAX_COL; for i := 0 to MAX_COL-1 do begin StringGrid1.Cells[i, 0] := IntToStr(i); StringGrid1.Cells[0, i] := IntToStr(i); end; for i := 1 to MAX_COL-1 do for j := 1 to MAX_ROW-1 do StringGrid1.Cells[i, j] := IntToStr(i*j); end;
定数 MAX_COL, MAX_ROW は、行、列の最大値を保持し、その値を RowCount, ColCount に代入しています。RowCount がグリッドの行に対応し、ColCount がグリッドの列に対応しています。
for i := 0 to MAX_COL-1 do begin StringGrid1.Cells[i, 0] := IntToStr(i); StringGrid1.Cells[0, i] := IntToStr(i); end;
このコードでは、固定セルに対しての設定を行っています。Cells プロパティを利用することによって、特定のセルにアクセス可能です。最初の行、及び列は、共に 0 であるということに注意が必要です。一番左上のセルが [0, 0] です。
固定セルとは、図では、灰色で表示されているセルを指します。TStringGrid では、セル内に直接入力可能にすることができますが、固定セルでは出来ません。(※固定セルは FixedRows, FixedCols で設定します。これらの値はデフォルトでは 1 にセットされています)
for i := 1 to MAX_COL-1 do for j := 1 to MAX_ROW-1 do StringGrid1.Cells[i, j] := IntToStr(i*j);
このコードで、実際に九九を計算して、適切な位置に描画されるようにしています。
グリッド内のセルをマウスでクリックした際、セルの位置を取得するには、Col, Row プロパティが使用できます。この Col, Row プロパティと共に Cells プロパティを使用することで、クリックされたセルに表示されている文字列の取得(及び設定)が可能です。
最初に作成した「九九の表」で、クリックされたセルに記述されている内容を取得するようにしてみましょう。
procedure TForm1.Button1Click(Sender: TObject); const MAX_COL = 10; MAX_ROW = 10; var i, j: Integer; begin StringGrid1.RowCount := MAX_ROW; StringGrid1.ColCount := MAX_COL; for i := 0 to MAX_COL-1 do begin StringGrid1.Cells[i, 0] := IntToStr(i); StringGrid1.Cells[0, i] := IntToStr(i); end; for i := 1 to MAX_COL-1 do for j := 1 to MAX_ROW-1 do StringGrid1.Cells[i, j] := IntToStr(i*j); end; procedure TForm1.FormCreate(Sender: TObject); begin StringGrid1.DefaultColWidth := 30; end; procedure TForm1.StringGrid1Click(Sender: TObject); var Col, Row: Integer; begin Col := StringGrid1.Col; Row := StringGrid1.Row; Label1.Caption := StringGrid1.Cells[Col, Row] + 'が選択されました'; end;
このコードを実行し、適当なセルをクリックすると、ラベル(Label1)にそのセルの文字列が表示されます。
クリックされたセルが、点線で囲まれるようになっていますが、セルを強調表示されるようにする場合は、Options プロパティに goDrawFocusSelected を含めるようにします。
goDrawFocusSelected を含めた場合、次のように表示されます。
また、個々のセルではなく、行全体を強調表示する場合には、Options プロパティに goRowSelected を含めます。
OnSelectCell イベントは、グリッド内のセルが選択される前に発生します。
type TSelectCellEvent = procedure (Sender: TObject; ACol, ARow: Longint; var CanSelect: Boolean) of object; property OnSelectCell: TSelectCellEvent;
CalSelect パラメータを false に設定した場合、セルが選択されないようにすることが出来ます。今回のサンプルプログラムでは、OnSelectCell イベントは使用していませんが、例えば、セルがクリックされる際に、そのクリックを許可するかどうかを判別しなければならない場合には、このイベントが利用できます。
セルにオブジェクトを関連付けし、セルをクリックすると、それに関連付けられたオブジェクトの情報を ListBox に表示させるサンプルプログラムを作成してみましょう。
特定のセルにオブジェクトを関連付けるには、Objects プロパティを使用します。
property Objects [ACol, ARow: Integer]: TObject;
Objects プロパティは、少し前に出てきた Cells プロパティと殆ど形が同じですね。Cells プロパティでの string が TObject に変わっただけです。
Objects プロパティの使い方も Cells プロパティでのそれと殆ど同じですが、注意しなければならない点が一つだけあります(以降の解説で出てきます)
コードは以下のようになります。
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Grids; type TForm1 = class(TForm) StringGrid1: TStringGrid; ListBox1: TListBox; procedure FormCreate(Sender: TObject); procedure StringGrid1SelectCell(Sender: TObject; ACol, ARow: Integer; var CanSelect: Boolean); procedure FormDestroy(Sender: TObject); private { Private 宣言 } procedure SetObject; procedure SetMyDataItem(Name: string; Age: Integer; SpecialAbility: string); public { Public 宣言 } end; var Form1: TForm1; implementation {$R *.dfm} type TMyData = class(TObject) private fName: string; fAge: Integer; fSpecialAbility: string; public constructor Create(Name: string; Age: Integer; SpecialAbility: string); property Name: string read fName; property Age: Integer read fAge; property SpecialAbility: string read fSpecialAbility; end; { TMyData } constructor TMyData.Create(Name: string; Age: Integer; SpecialAbility: string); begin fName := Name; fAge := Age; fSpecialAbility := SpecialAbility; end; { TForm1 } procedure TForm1.SetObject; var Taro, Jiro, Hanako: TMyData; begin // オブジェクト生成 Taro := TMyData.Create('太郎', 10, 'サッカー'); Jiro := TMyData.Create('次郎', 20, '野球'); Hanako := TMyData.Create('花子', 30, 'テニス'); // 適宜セルの文字列の設定と、そのセルに関連付けるオブジェクトの設定を行う StringGrid1.Cells[1, 1] := '太郎'; StringGrid1.Objects[1, 1] := Taro; StringGrid1.Cells[1, 2] := '次郎'; StringGrid1.Objects[1, 2] := Jiro; StringGrid1.Cells[1, 3] := '花子'; StringGrid1.Objects[1, 3] := Hanako; end; procedure TForm1.SetMyDataItem(Name: string; Age: Integer; SpecialAbility: string); begin ListBox1.Clear; ListBox1.Items.Add('名前: ' + Name); ListBox1.Items.Add('年齢: ' + IntToStr(Age)); ListBox1.Items.Add('特技: ' + SpecialAbility); end; procedure TForm1.FormCreate(Sender: TObject); var i: Integer; begin StringGrid1.ColCount := 2; StringGrid1.RowCount := 4; StringGrid1.Cells[1, 0] := '名前'; for i := 1 to 3 do StringGrid1.Cells[0, i] := IntToStr(i); // 固定セルに番号を描画 SetObject; end; procedure TForm1.FormDestroy(Sender: TObject); var i: Integer; begin for i := 1 to 3 do StringGrid1.Objects[1, i].Free; // セルに関連付けたオブジェクトの破棄 end; procedure TForm1.StringGrid1Click(Sender: TObject); var Col, Row: Integer; TargetObj: TMyData; begin Col := StringGrid1.Col; Row := StringGrid1.Row; TargetObj := TMyData(StringGrid1.Objects[Col, Row]); SetMyDataItem(TargetObj.Name, TargetObj.Age, TargetObj.SpecialAbility); end; end.
まず、セルに関連付けるインスタンスの定義から見ていきます。
type TMyData = class(TObject) private fName: string; fAge: Integer; fSpecialAbility: string; public constructor Create(Name: string; Age: Integer; SpecialAbility: string); property Name: string read fName; property Age: Integer read fAge; property SpecialAbility: string read fSpecialAbility; end;
クラス TMyData は、fName, fAge, fSpecialAbility フィールドを持っているだけのクラスです。メソッドは一つもありません。この場合では record でも十分ですが、Objects プロパティにセットできるのは、TObject 型の変数ですので record を関連付けすることはできません。
コンストラクタの中身をみてみます。
constructor TMyData.Create(Name: string; Age: Integer; SpecialAbility: string); begin fName := Name; fAge := Age; fSpecialAbility := SpecialAbility; end;
フィールドに、それぞれ対応した値を代入しているだけです。
プロパティに関しては、読み取り専用のプロパティを、それぞれのフィールドに対応したものを用意しています。
次に、TStringGrid の設定を見ていきます。
procedure TForm1.FormCreate(Sender: TObject); var i: Integer; begin StringGrid1.ColCount := 2; StringGrid1.RowCount := 4; StringGrid1.Cells[1, 0] := '名前'; for i := 1 to 3 do StringGrid1.Cells[0, i] := IntToStr(i); // 固定セルに番号を描画 SetObject; end;
行を 4 にセットし、列を 2 にセットしています。そして、固定セル(灰色で表示されるセル)に、番号を表示させています。この番号には、特にこれといった意味はありません。
そして、SetObject を呼び出しています。このメソッドは、新たに作成されたメソッドです。TForm1 の private なメソッドとして定義しました。中身は次のようになっています。
procedure TForm1.SetObject; var Taro, Jiro, Hanako: TMyData; begin // オブジェクト生成 Taro := TMyData.Create('太郎', 10, 'サッカー'); Jiro := TMyData.Create('次郎', 20, '野球'); Hanako := TMyData.Create('花子', 30, 'テニス'); // 適宜セルの文字列の設定と、そのセルに関連付けるオブジェクトの設定を行う StringGrid1.Cells[1, 1] := '太郎'; StringGrid1.Objects[1, 1] := Taro; StringGrid1.Cells[1, 2] := '次郎'; StringGrid1.Objects[1, 2] := Jiro; StringGrid1.Cells[1, 3] := '花子'; StringGrid1.Objects[1, 3] := Hanako; end;
今回のサンプルプログラムでは、3 つ設定可能なセルを作成しています。それぞれのセルには、"太郎", "次郎", "花子" と文字列を表示させ、それに対応させた TMyData クラスのインスタンスを作成し、Objects プロパティにセットしています。
次は、セルをクリックした際、そのセルに関連付けられたオブジェクトの情報を ListBox に表示させるコードです。
procedure TForm1.StringGrid1Click(Sender: TObject); var Col, Row: Integer; TargetObj: TMyData; begin Col := StringGrid1.Col; Row := StringGrid1.Row; TargetObj := TMyData(StringGrid1.Objects[Col, Row]); SetMyDataItem(TargetObj.Name, TargetObj.Age, TargetObj.SpecialAbility); end;
Col, Row プロパティを使用することで、クリックされたセルの位置が分かります。
Objects プロパティで返される型は TObject ですので、TMyData クラスの情報にアクセスできるよう型キャストを行っています。
そして、SetMyDataItem に、インスタンスの情報をパラメータとして渡します。SetMyDataItem は、新しく作成したメソッドです。TForm1 の private なメソッドとして定義しています。SetMyDataItem は以下のように定義してあります。
procedure TForm1.SetMyDataItem(Name: string; Age: Integer; SpecialAbility: string); begin ListBox1.Clear; ListBox1.Items.Add('名前: ' + Name); ListBox1.Items.Add('年齢: ' + IntToStr(Age)); ListBox1.Items.Add('特技: ' + SpecialAbility); end;
受け取ったパラメータを順に ListBox に表示しています。
TStringGrid に関連付けられたオブジェクトは、TStringGrid が破棄されても、自動的に破棄されることはありません。ですから、オブジェクトが不要になったら、明示的にコードで破棄する必要があります。サンプルプログラムでは、TForm1 の OnDestroy イベントで、これを行っています。
procedure TForm1.FormDestroy(Sender: TObject); var i: Integer; begin for i := 1 to 3 do StringGrid1.Objects[1, i].Free; // セルに関連付けたオブジェクトの破棄 end;