グループ ヘッダーでのグループの合計の出力
この手法はかなり頻繁に使用されるものですが、スクリプトの使用を必要とします。なぜなら、グループ合計の値は、そのグループ内のすべてのレコードが処理された後にしかわからないからです。グループ ヘッダー(つまり、グループがレポートに出力される前)に合計を表示するために、次のアルゴリズムが使用されます。
レポートのダブル パス オプションをオンにする([レポート]>[オプション]メニュー項目)
最初のパスで、各グループの合計を計算し、それらを配列に保存する
第 2 パスで、その配列から値を抽出し、グループ ヘッダーにそれらを表示する
このタスクを実行するための 2 つの方法を見てみましょう。初めに、Delphi で新しいプロジェクトを作成し、フォーム上に TQuery
、TfrxReport
、および TfrxDBDataSet
コンポーネントを置いて、次のように設定します。
Query1:
DatabaseName = 'DBDEMOS'
SQL =
select * from customer, orders
where orders.CustNo = customer.CustNo
order by customer.CustNo, orders.OrderNo
frxDBDataSet1:
DataSet = Query1
UserName = 'Group'
デザイナーを開いて、データ ソースをレポートに接続します。レポートの設定で、ダブル パスを有効にします([レポート]>[オプション]メニュー項目)。グループ ヘッダーとマスター データの 2 つのバンドをレポートに追加します。グループ ヘッダー バンドのエディターで、Group."CustNo" データ フィールドを入力します。データ バンドを Group データ ソースに接続し、いくつかのオブジェクトを次のように配置します。
合計を表示するために、図のデザイン内で矢印の付いているオブジェクトを使用します(この例では、"Memo8" という名前のオブジェクトです)。
1 つ目の方法
合計を格納する配列として TStringList
クラスを使用します。つまり、数値を文字列として格納します。StringList 内の最初の項目は最初のグループの合計に対応し、2 番目の項目は 2 番目のグループの合計に対応する、という具合になります。整数値の変数(各グループの出力後に増加させる)は、グループのインデックス番号を求めるために使用されます。
したがって、スクリプトは次のようになります。
Pascal スクリプト:
var
List: TStringList;
i: Integer;
procedure frReport1OnStartReport(Sender: TfrxComponent);
begin
List := TStringList.Create;
end;
procedure frReport1OnStopReport(Sender: TfrxComponent);
begin
List.Free;
end;
procedure Page1OnBeforePrint(Sender: TfrxComponent);
begin
i := 0;
end;
procedure GroupHeader1OnBeforePrint(Sender: TfrxComponent);
begin
if Engine.FinalPass then
Memo8.Text := 'Sum: ' + List[i];
end;
procedure GroupFooter1OnBeforePrint(Sender: TfrxComponent);
begin
if not Engine.FinalPass then
List.Add(FloatToStr(SUM(<Group."ItemsTotal">,MasterData1)));
Inc(i);
end;
begin
end.
C++ スクリプト:
TStringList List;
int i;
void frReport1OnStartReport(TfrxComponent Sender)
{
List = TStringList.Create();
}
void frReport1OnStopReport(TfrxComponent Sender)
{
List.Free();
}
void Page1OnBeforePrint(TfrxComponent Sender)
{
i = 0;
}
void GroupHeader1OnBeforePrint(TfrxComponent Sender)
{
if (Engine.FinalPass)
Memo8.Text = "Sum: " + List[i];
}
void GroupFooter1OnBeforePrint(TfrxComponent Sender)
{
List.Add(FloatToStr(SUM(<Group."ItemsTotal">,MasterData1)));
i++;
}
{
}
スクリプト内のプロシージャ名は、使用したイベントを示しています。それらは、Report.OnStartReport、Report.OnStopReport、Page1.OnBeforePrint、GroupHeader1.OnBeforePrint、および GroupFooter1.OnBeforePrint です。最初の 2 つのイベントは、それぞれレポートの最初と最後に呼び出されます。
これら 2 つのイベントのハンドラーを作成するには、オブジェクト インスペクターのドロップダウン リストで Report オブジェクトを選択します。そうすると、そのプロパティがオブジェクト インスペクターに表示されます。その後、オブジェクト インスペクターの[イベント]タブに切り替えて、ハンドラーを作成します。
スクリプトのメイン プロシージャで List 変数を作成しなかったのはなぜでしょうか?作成した変数はレポート完成後に動的に破棄する必要があるため、この変数は OnStartReport イベントで作成しました。OnStartReport イベントで動的変数を作成し、OnStopReport イベントでそれらを破棄するのは、理にかなっています。ほかの場合(スクリプトの完了時にメモリを空にする必要がないとき)には、スクリプトのメイン プロシージャを使用して変数の初期化を行えます。
List 変数の作成と破棄はごく簡単です。では、スクリプトがどのように働くか見てみましょう。
- ページの先頭で、現在のグループのカウンター(変数 "i")がゼロにリセットされ、各グループの出力後にカウンター値が増分されます(GroupFooter1.OnBeforePrint イベント)。
- 計算された合計の値は、カウンター値が増分される前に、このイベントで List に追加されます。
- GroupHeader1.OnBeforePrint イベントは、最初のパス(If Engine.FinalPass 条件)の間は何もしません。第 2 パス(List に値が入れられるとき)の間に、現在のグループに対応する合計が List から取り出され、グループ ヘッダーに合計を表示するための "Memo8" オブジェクトに出力されます。
完成したレポートでは、次のように表示されます。
このアルゴリズムは非常に簡単です。それでもなお、簡略化することができます。
2 つ目の方法
グループ合計を格納する配列として、レポート変数のコレクションを使用します。レポート変数には、Get
関数および Set
関数によってアクセスするということを思い出してください。これらの関数を使用すると、変数を明示的に作成して破棄する手間も省かれます。スクリプトは次のようになります。
Pascal スクリプト:
procedure GroupHeader1OnBeforePrint(Sender: TfrxComponent);
begin
if Engine.FinalPass then
Memo8.Text := 'Sum: ' + Get(<Group."CustNo">);
end;
procedure GroupFooter1OnBeforePrint(Sender: TfrxComponent);
begin
Set(<Group."CustNo">,
FloatToStr(SUM(<Group."ItemsTotal">,MasterData1)));
end;
begin
end.
C++ スクリプト:
void GroupHeader1OnBeforePrint(TfrxComponent Sender)
{
if (Engine.FinalPass)
Memo8.Text = "Sum:" + Get(<Group."CustNo">);
}
void GroupFooter1OnBeforePrint(TfrxComponent Sender)
{
Set(<Group."CustNo">,
FloatToStr(SUM(<Group."ItemsTotal">,MasterData1)));
}
{
}
ご覧のように、このスクリプトは割に簡単です。GroupFooter1.OnBeforePrint ハンドラーのコードでは、クライアント番号から派生した名前を持つ変数(あるいは、明確にクライアントを識別する任意の識別子、たとえば、<Group."Company"> などを使用できます)の値を設定します。
そのような名前の変数が存在しない場合は、スクリプトが自動的に変数を作成します。存在する場合は、その値が更新されます。GroupHeader1.OnBeforePrint ハンドラーでは、適切な変数の値が取得されます。