本稿は、連載『5分でわかるActiveReports帳票(2007年度版)』(渡辺俊史・宮本奈紗 著)の増補改訂版です。
対象読者
- Visual Basic.NETまたはVisual C#を使ってプログラムを作ったことのある方
- 帳票作成ツールに興味のある方
必要な環境
-
Visual Studio 2008 SP1/2010/2012のいずれかでプログラムが作れる環境
(Express EditionではActiveReportsをインストールできません)
本記事のサンプルコードは、C#/Visual Basic 2012で記述しています。
グループ化・集計
ActiveReportsが提供する集計機能を利用すると、データソースやSQLの構造を複雑にすることなくデータの総合計やグループごとの小計、ページ数といった集計データを簡単に表示させることができます。ここでは、そうしたデータを集計して表示する帳票を作成します。
グループ化したレポート
今回作成する帳票は、お店の注文伝票を注文日付・注文番号ごとに、一覧化・集計した「注文表」です。
データベースは前回と同じく、ActiveReports 7.0J付属のNwind.mdbを使用し、注文伝票(Orders)、注文伝票明細(Order Details)、商品(Products)の3テーブルにアクセスします。
SELECT Orders.OrderDate, Orders.OrderID, Products.ProductID, Products.ProductName, Round([Order Details].UnitPrice, 0) As UnitPrice, [Order Details].Quantity, [Order Details].Discount, Round(Round([Order Details].UnitPrice,0) * [Quantity] * (1 - [Discount]), 0) AS Price FROM Orders INNER JOIN (Products INNER JOIN [Order Details] ON Products.ProductID = [Order Details].ProductID) ON Orders.OrderID = [Order Details].OrderID ORDER BY Orders.OrderDate, Orders.OrderID,Products.ProductID ;
グループ
帳票では、同じデータが続く行のまとまりを「グループ」として扱います。グループの中にさらにグループを入れ子にすることもできます。
データテーブルに「フィールド1」と「フィールド2」が定義されているとします。外側のグループが「フィールド1」をもとにグループ化されるように設定した場合、下図「データのグループ化」のデータソース図の左側の赤字のようにグループ分けがされます。一方、内側のグループが「フィールド2」をもとにグループ化されるように設定した場合、データソース図の右側の青じのようにグループ分けがされます。「フィールド2」の20や30のように同じ値が並んでいても、外側のグループが別のグループになれば内側のグループも別のグループになることに注意してください。
セクションレポートでは、デザイナ画面上で「グループヘッダ/フッタ」のセクションを追加することによってグループを配置します。グループヘッダセクションはグループの最初に表示され、グループフッタセクションはグループの最後に表示されることになります(表示させないことも可能です)。
グループヘッダセクションのDataFieldプロパティにフィールド名を設定することで、グループ化の基準となるフィールドを指定します。
なお、グループ化が正しく働くためには、出力するデータをあらかじめグループ化したい順番に整列させておく必要があります。上記のSQL文では、ORDER BY句でデータを整列させています。
今回は、「注文日付」「注文番号」ごとにグループ化を行うので、グループヘッダ/フッタを2重に追加し、外側のグループヘッダのDataFieldプロパティに「OrderDate(注文日付)」、内側のグループヘッダのDataFieldプロパティに「OrderID(注文番号)」を設定します。また、内側のグループヘッダのUnderlayNextプロパティをTrueに設定します。
コントロールの追加
次に、ツールボックスからコントロールをドロップして、帳票レイアウトに貼り付けていきます。帳票タイトルなどの固定値表示や集計のないデータ項目表示はLabelコントロールとTextBoxコントロールのどちらを使用しても構いませんが、Labelコントロールは集計設定のためのSummaryプロパティを持たないため、集計値の表示欄にはTextBoxコントロールを使う必要があります。
今回集計値を表示するのは、以下4か所です。
- PageHeader1右上のページ番号と総ページ数
- その下の価格総計表示
- GroupFooter2の「注文小計」
- GroupFooter1の「当日小計」
集計処理
TextBoxコントロールの集計処理について見てみましょう。
TextBoxコントロールの集計関連プロパティ(SummaryFunc、SummaryGroup、SummaryRunning、SummaryType)の設定によって、レポート上に集計値を表示することができます。具体的には、以下のような値を表示することができます。
- あるフィールドの値の全体の集計値(合計値、平均値など)
- あるフィールドの値のグループ内での集計値
- あるフィールドの値のそれまでのデータ行の累計値
- ページ数(現在のページ数、総ページ数)
これらを表示するために使用する各集計関連プロパティの役割は、以下のとおりです。
-
SummaryFuncプロパティ:どのような集計値を表示するかを設定します。デフォルトは「Sum」であり、加算合計値を表します。他にも「Avg」(平均)、「Count」(データの個数)、「Max」「Min」(最大値・最小値)などがあります。
-
SummaryGroupプロパティ:どのグループについて集計値を計算するかを設定します。そのレポートにあるグループヘッダ名を設定します。このプロパティは、SummaryTypeを「SubTotal」に設定した時のみ有効です。
-
SummaryRunningプロパティ:累積しながら集計値を出力するか、累積する場合にどのタイミングで集計をリセットするかを設定します。「None」「Group」「All」があり、「None」は累積せずに集計結果を得ます。「Group」ではグループごとの累計値が得られ、その集計はグループごとにリセットされます。「All」はリセットすることなく全体に対する累計値が得られます。
- SummaryTypeプロパティ:集計処理の動作を設定します。「None」では、集計を行いません(デフォルト)。「GrandTotal」では全体に対しての集計を行います。「PageTotal」はページごとの集計を行います。「SubTotal」はグループごとの集計を行います。「PageCount」はページ数の表示を行います。
なお、フィールドの値の集計を行うためには、そのTextBoxコントロールのDataFieldプロパティに、「集計したいフィールド」を設定しておく必要があります。今回のレポートでは「Price」フィールドを設定しています。
(1)ページ番号・総ページ数の表示
帳票にページ番号を表示するには、SummaryTypeプロパティに「PageCount」を設定します。ページ番号の表示の場合にはSummaryFuncプロパティは関係ありません。
プロパティ | 設定値 | |
---|---|---|
現在のページ番号 | SummaryRunning | All |
SummaryType | PageCount | |
総ページ数 | SummaryRunning | None |
SummaryType | PageCount |
なお、セクションレポートには「ReportInfo」というコントロールが用意されております。今回は集計機能の解説のためにTextBoxを使用しておりますが、ページ番号を表示するだけであればReportInfoコントロールを使用する方が簡単に実現できます。ご使用ください。
(2)総計の表示
明細の全データを集計する場合は、SummaryTypeプロパティに「GrandTotal」を設定します。
プロパティ | 設定値 | |
---|---|---|
前データの集計 |
SummaryFunc | Sum |
SummaryGroup | (なし) | |
SummaryRunning | None | |
SummaryType | GrandTotal |
(3)グループごとの小計値の表示
グループごとの小計項目は、以下のように設定します。
TextBox | プロパティ | 値 |
---|---|---|
注文番号ごとの小計 | SummaryFunc | Sum |
SummaryGroup | 「注文番号」のグループヘッダ | |
SummaryRunning | None | |
SummaryType | SubTotal | |
注文日付ごとの小計 | SummaryFunc | Sum |
SummaryGroup | 「注文日付」のグループヘッダ | |
SummaryRunning | None | |
SummaryType | SubTotal |
今回の例では小計を表示するTextBoxコントロールはグループフッタに配置しています。これは一般的な配置ですが、SummaryRunningプロパティが「None」の場合は、グループ内のセクションならばどこに置いても同じ値が得られます。グループヘッダやDetailセクションに配置しても構いません。
ページヘッダに項目タイトル用のLabelコントロールを配置するなどしてレポートの体裁を整えたら、デザイナの「プレビュータブ」などを使用して、レポートを表示させてください。一日に複数の注文がある日(例:1994/8/8)のグループフッタには、その日の合計値が表示されているのが確認できます。
データフィールドをTextBoxコントロールにバウンドする(DataFieldプロパティにフィールド名を設定する)と、そのフィールドの値がTextBoxコントロールに表示されます。デフォルトではToString()で得られる文字列が表示されます。
ただしそのままでは要件を満たせない場合があります。例えば日付/時刻型データの場合、デフォルトでは「1994/04/01 0:00:00」のように時刻まで文字列化されてしまいます。日付だけが欲しい場合にはこのままでは使えません。
このように出力する書式を設定したい時のために、TextBoxコントロールやLabelコントロールには「OutputFormat」プロパティが用意されています。このプロパティには、.NET FrameworkのString.Formatメソッドと同じ書式が使用できます。直接書式文字列を記述することもできますが、プロパティウィンドウから「出力書式の設定」ダイアログが出るので、それを利用すると便利です。
累計値の表示
レポートの要件によっては「それまでの累計値を表示したい」という場合があります。この場合もTextBoxコントロールの集計機能が有効です。
累計値を表示するレポートを作ってみましょう。先程と同じデータベースから、以下のSQLで「注文日付および注文番号ごとの金額」(フィールド名:Price)を取得します。
SELECT Orders.OrderDate, Orders.OrderID, Sum(Round(Round([Order Details].UnitPrice,0) * [Order Details].[Quantity] * (1 - [Order Details].[Discount]), 0)) AS Price FROM Orders INNER JOIN (Products INNER JOIN [Order Details] ON Products.ProductID = [Order Details].ProductID) ON Orders.OrderID = [Order Details].OrderID GROUP BY Orders.OrderDate, Orders.OrderID ORDER BY Orders.OrderDate, Orders.OrderID;
このデータから「それまでの金額の累計」を表示するレポートを作成します。
まず、グループヘッダ/フッタを1つ追加し、そのグループヘッダのDataFieldプロパティに「OrderDate(注文日付)」を、UnderlayNextプロパティにTrueを設定します。
次に、詳細(Detail)セクション上に「注文番号」を表示するためのTextBoxコントロール(DataFieldプロパティ=「OrderID」)と「金額」を表示するためのTextBoxコントロール(DataFieldプロパティ=「Price」)を配置します。さらに「グループ内累計」「全体累計」を表示するTextBoxコントロールを配置します。これらの2つのTextBoxコントロールのプロパティは、以下のように設定します。
TextBox | プロパティ | 設定値 |
---|---|---|
グループ内累計 | SummaryFunc | Sum |
SummaryGroup | グループヘッダ | |
SummaryRunning | Group | |
SummaryType | SubTotal | |
全体累計 | SummaryFunc | Sum |
SummaryGroup | (なし) | |
SummaryRunning | All | |
SummaryType | GrandTotal |
前述の「注文表」における「グループ内小計」「全体合計」との違いは、SummaryRunningプロパティがNoneではない点です。
最後に、ページヘッダに項目タイトル用のLabelコントロールを配置したり、グループヘッダ上にLineコントロールを配置するなどして、レポートの体裁を整えます。
このレポートをプレビュー表示すると、以下のような結果になります。
このレポートは「注文日付(OrderDate)」ごとにグループ化されており、罫線で区切られている部分が1つのグループになります。
1注文日付あたり1注文というデータがほとんどですが、「注文日付=1994/8/8」というグループには、2つ注文が存在することが確認できます。
このグループの「グループ内累計」をご覧ください。最初の注文の「金額」が1546なので、「グループ内累計」も同じ1546になります。2つ目の注文の「金額」が665なので、「グループ内累計」は2211(1546+665)となっているのが確認できます。
一方、「全体累計」は、レポート全体の累計になります。最初の注文の「金額」が40250なので、最初の「全体累計」もそれと同じ数値になります。次の注文の「金額」が443なので、その行の「全体累計」は40693(40250+443)です。それ以降の行についても「金額」の分だけ累積した値が「全体累計」に表示されているのが確認できます。
改ページとレイアウトの制御
ここからは、改ページとレイアウトに関する設定を紹介します。
(1)KeepTogetherプロパティ
最初にKeepTogetherプロパティについて説明します。ページヘッダ/フッタ以外のセクション(Detailセクション、グループヘッダ/フッタセクションなど)にはKeepTogetherプロパティが用意されています。
この値をTrueに設定すると、基本的にセクションがページをまたがらないように表示します。つまり、ページの下端にスペースが残っていたとしても、そのスペースがセクション全体を表示するには足りない場合、セクションごと次のページに送られます。
このプロパティは、デフォルトではFalseに設定されておりますが「セクションの途中で改ページされたくない」場合には、Trueに変更してください
(2)CanGrow/CanShrinkプロパティ
セクションやTextBoxコントロールに用意されているプロパティであり、高さの自動変更を許可するかしないかを設定します。
例えば、高さが1行分、横幅が5文字分しか表示できない大きさのTextBoxコントロールに15文字の文字列を設定した場合、そのコントロールのCanGrowプロパティがTrueに設定されているとコントロールの高さが3行分に自動的に伸長します(Falseに設定されている場合、5文字分だけ表示され、残りの10文字は無視されます)。
また、セクションにもCanGrowプロパティは用意されています。セクションのCanGrowプロパティがTrueに設定されていれば、そのセクション上のTextBoxコントロールの自動伸長にあわせて、セクションの高さも自動的に伸長します。
一方、CanShrinkプロパティはCanGrowプロパティとは逆に、高さを自動縮小するかどうかを決定するプロパティです。このプロパティをTrueに設定している場合、その文字列の高さにあわせてTextBoxコントロールの高さを自動的に縮小させます。CanGrowプロパティと同様にセクションとTextBoxコントロールの両方に用意されています。
デフォルトではCanGrow=True、CanShrink=Falseとなっています。セクションの高さを一定に保ちたい場合はどちらもFalseに設定してください。またそのとき、そのセクション上に配置されているすべてのTextBoxコントロールのCanGrow/CanShrinkプロパティもFalseに設定してください。
(3)NewPageプロパティ
セクションの前か後で改ページを行うことを指定します。ページヘッダ/フッタ以外のセクションに用意されており、「None」「Before」「After」「BeforeAfter」が設定できます。
- None:改ページ制御を行いません。(デフォルト)
- Before:セクションの直前で改ページを行います。
- After:セクションの直後で改ページを行います。
- BeforeAfter:セクションの直前と直後で改ページを行います。
例えば、グループごとに改ページしたい場合には、グループヘッダのNewPageプロパティにBeforeを設定するか、グループフッタのNewPageプロパティにAfterを設定してください。
次のRepeatStyleプロパティとUnderlayNextプロパティは、グループヘッダ専用のプロパティです。
(4)RepeatStyleプロパティ
通常、グループヘッダはグループの最初に一度だけ表示されます。RepeatStyleプロパティは、グループが複数のページやカラムにまたがって出力される場合に、ページやカラムの先頭に、同じグループヘッダを繰り返し表示するかどうかを決定するプロパティです。
例えば、レポートデータソースから取得したグループ単位のデータを、各ページの見出しとして表示したい場合などにこのプロパティを使用して、グループヘッダを毎ページに表示させます(ページヘッダ上にデータソース内の値を直接表示することが、推奨されないためです)。
- None:グループヘッダの繰り返し表示を行いません。
- OnPage:改ページごとにグループヘッダを表示します。
- OnColumn:改カラム(後述)ごとにグループヘッダを表示します。
- All:改ページ、および改カラムごとにグループヘッダを表示します。
- OnPageIncludeNoDetail:改ページごとにグループヘッダを表示します。OnPageとの違いは、そのページに出力されているセクションがグループフッタのみである場合にも表示されるということです。OnPageに設定した場合、そのページに表示されているセクションがグループフッタのみである時には、そのグループヘッダが表示されることはありません。
(5)UnderlayNextプロパティ
グループヘッダと、次のセクションを重ねて表示することを指定します。グループヘッダにあるコントロールと、Detailセクションにあるコントロールを高さが同じ位置に表示できます。
例えば、前出の「注文表」では、内側のグループヘッダのUnderlayNextをTrueに設定しています。このように設定することで、内側のグループヘッダに配置されている「注文番号」と1行目のDetailセクションの「商品名」を横に並べて表示することを可能としています。
(6)RepeatToFillプロパティ
Detailセクションにのみ用意されているプロパティです。
セクションレポートの場合、ページ内に表示するべきデータが尽きると、それ以降の部分は基本的に空白になります。しかし、「表示するデータがなくても、ページの下までDetailセクションを空欄のまま表示したい」場合がございます。こうした要求仕様に応えるために用意されたのがRepeatToFillプロパティです。
このプロパティをTrueに設定すると、Detailセクションの「罫線」(Lineコントロール、Shapeコントロールや、TextBoxコントロールの枠線など)だけをページ一杯になるまで繰り返し表示します。
ただし、このプロパティには、グループヘッダ/フッタを複数定義している場合には使用できないなど、いくつかの制限事項があります。詳細については、製品ヘルプの[クラスライブラリリファレンス]-[GrapeCity.ActiveReports.v7 アセンブリ]-[GrapeCity.ActiveReports.SectionReportModel名前空間]-[Detailクラス]-[RepeatToFillプロパティ]をご覧ください。
ここでは、NewPage、RepeatStyle、UnderlayNext、RepeatToFillプロパティを使用したレポートを作成します。商品を種類ごとにグループに分け、グループごとにページ分けしています。実際の設定内容については、公開しているサンプルに含まれている「LayoutReport.vb(cs)」をご覧ください。
(7)ShrinkToFitプロパティ
TextBoxやLabelコントロールに用意されているプロパティであり、レポート実行時にコントロール内に収まりきらない文字列を縮小するかどうかを指定します。
前述の「(2)CanGrow/CanShrinkプロパティ」は、出力する文字列にあわせてコントロールの高さを自動的に調整するプロパティであるのに対し、ShrinkToFitプロパティは、コントロールの高さは変えずに、フォントサイズを調整します。
コントロールのShrinkToFitプロパティをTrueに設定した場合、そのコントロールに収まりきらない長さの文字列が設定されても、コントロール内に全ての文字列が収まるように、自動的にフォント サイズを縮小します。
なお、ShrinkToFitプロパティをTrueに設定した場合、CanGrowプロパティの設定は無視されます。
カラム出力
最後にカラム形式のレポートについて紹介します。ColumnCount、ColumnDirection、ColumnLayout、NewColumnプロパティなどを使用することで、カラム形式(多段組)のレポートを作成できます。
このレポートを作成するために使用しているプロパティは、以下のとおりです。なお、実際の設定内容については、公開しているサンプルに含まれている「ColumnReport.vb(cs)」をご覧ください。
(1)ColumnCountプロパティ
Detailセクションにあるプロパティであり、この値を1より大きくすることで、段組を使用したレイアウトが可能です。
ColumnCountの数だけ、Detailセクションが横に並んで表示されます。デザイン画面では、Detailセクションの編集可能領域が、横幅の(1/ColumnCount)倍です。
(2)ColumnDirectionプロパティ
データの展開方向を決めます。ColumnDirectionプロパティには、以下のいずれかの値を設定できます。
- AcrossDown(まず横方向へ出力し、その後次の行へ改行する)
- DownAcross(まず縦方向に出力し、その後隣の列へ折り返す)
この機能を使ってカードやタックシールなどの印刷を行うことができます。上記の「カラム形式のレポートのデザイン画面」のレイアウトは、地域別にグループヘッダを表示して、会社名や氏名といった情報をタックシール状に並べて印刷しています。
(3)ColumnLayoutプロパティ
グループヘッダにあるプロパティで、これはグループヘッダも詳細セクションと同じ幅にするか、それともレポートの横幅のままにするかを設定します。
Falseに設定すると、詳細セクションのカラム数にかかわらず、グループヘッダの横幅はレポートの横幅のままです。デフォルトはTrueで、詳細セクションと同じ幅になります。
(4)NewColumnプロパティ
グループヘッダ/フッタやDetailセクションにあるプロパティです。改ページで使用したNewPageプロパティと基本的に同様の機能であり、改カラムを行うかどうかを指定します。
まとめ
今回はグループ化、集計処理、レイアウトに関する設定を紹介しました。ActiveReportsのセクションレポートは、コントロールやセクションのプロパティを設定するだけでも、さまざまなレイアウトを実現できます。
次回は、ページレポートでのグループ化と式の使用方法を紹介します。