SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

5分でわかるActiveReports帳票

5分でわかるActiveReports帳票-集計処理と改ページ(2007年度版)

ActiveReports for .NET 3.0Jで作るサンプル帳票

  • このエントリーをはてなブックマークに追加

 本連載では帳票作成コンポーネント「ActiveReports for .NET 3.0J」を使って帳票アプリケーションを作成していきます。今回はDBから値を取得・集計して出力する方法と、帳票の改ページ指定方法について解説します。

  • このエントリーをはてなブックマークに追加
編集部注

 本稿の内容を増補改訂したアップデート版が提供されています。詳しくは連載の目次「5分でわかるActiveReports帳票」をご参照ください。

はじめに

 ActiveReports for .NET(以下ActiveReports)はVisual Studioと統合された使いやすいレポートデザイナや高機能なレポートビューア、多彩な出力形態をサポートする帳票作成コンポーネントです。前回はActiveReportsの最新版3.0Jを利用した帳票アプリケーション開発の流れを簡単に紹介しましたが、今回はActiveReportsを利用した集計処理機能を中心に紹介していきます。

前回の記事

対象読者

  • Visual Basic 2005またはVisual C# 2005を使ってプログラムを作ったことのある方。
  • 帳票作成ツールに興味のある方。

必要な環境

(注:Express EditionではActiveReportsをインストールできません)
  • Visual Studio 2005、Visual Studio .NET 2003でプログラムが作れる環境。

 本記事のサンプルコードは、C# 2.0/Visual Basic 2005で記述しています。

集計を含む帳票の作成

 ActiveReportsが提供する各種の集計機能を利用すると、データソースやSQLの構造を複雑にすることなくデータの総合計やグループごとの小計、ページ数といった集計データを簡単に表示させることができます。

 それでは、帳票側でデータを加工して表示する帳票を作成していきましょう。

最終的な完成イメージ

 今回作成する帳票は、お店の注文伝票を月ごとに集計した「注文集計表」です。

「注文集計表」帳票(1)
「注文集計表」帳票(1)
「注文集計表」帳票(2)
「注文集計表」帳票(2)

 各月の受注額を注文単位の小計、日単位の小計、月単位の合計3つで集計して表示します。また、月単位に宅配料金、値引料金の合計も表示します。

データの説明

 今回使用するデータは、注文が入ると商品を複数まとめて宅配便で送るお店の受注データです。データベースは前回と同じく、ActiveReports 3.0J付属のNwind.mdbを使用し、注文伝票(Orders)、注文伝票明細(Order Details)、商品(Products)の3テーブルにアクセスします。

  1. 注文伝票テーブル(注文日、注文番号、宅配料金)
  2. 注文伝票詳細テーブル(注文番号、商品番号、単価、個数、割引率)
  3. 商品テーブル(商品番号、商品名)
今回使用する受注データの構造
今回使用する受注データの構造

 次に、レポート出力するデータを取得するためのSQLを記述します。今回は「注文日」「注文番号」「宅配料金」「商品番号」「商品名」「単価」「数量」「割引額」「注文金額」を取得する、次のようなクエリを設定します(便宜のため、小数点以下の値を含む列はRound関数で丸めています)。

 なお、クエリではSUMやCOUNTといった集計関数やGROUP BY句による集計処理は行いません。

帳票に出力する項目を取り出すSQL
SELECT  YEAR(Orders.OrderDate) AS OrderYear,
        MONTH(Orders.OrderDate) AS OrderMonth, 
        Orders.OrderDate,
        Orders.OrderID,
        Round(Orders.Freight, 0) As Freight,
        Products.ProductID,
        Products.ProductName,
        Round([Order Details].UnitPrice, 0) As UnitPrice, 
        [Order Details].Quantity,
        Round(Round([Order Details].UnitPrice,0)
                    * [Quantity] * [Discount], 0) AS DiscountPrice,
        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.セクションの設定

 Visual Studioのプロジェクトに「ActiveReports 3.0ファイル」を追加し、レポートデザイナで帳票のレイアウトを作成します。

 前回と同様、detailセクションのアイコンをダブルクリックして、レポートデータソースにクエリを設定します。レポートデータソース設定の詳細については前回の記事を参照ください。

 また、今回は取得したデータをグループ単位で集計・表示するために[グループヘッダ/フッタ]を設定します。右クリックメニューの[挿入]から「グループヘッダ/フッタ」を追加します。

レポートデザイナでグループヘッダ/フッタを追加する
レポートデザイナでグループヘッダ/フッタを追加する

 今回の集計単位は、「注文月」と「注文日」「注文番号」の3つになるので、上位グループ(外側)から順に「注文月(OrderYear, OrderMonth)」「注文日(OrderDate)」「注文番号(OrderID)」単位のグループヘッダ/フッタを設定します。グループヘッダ/フッタを追加したら、それぞれキーになるデータソースの列名を、グループヘッダのDataFieldプロパティに設定します。このうち、注文月だけは「注文年+月」の単位で集計する必要があるため、DataFieldプロパティには計算式の=OrderYear*100 + OrderMonthを入力します。

グループヘッダのDataFieldプロパティに値を設定
グループヘッダのDataFieldプロパティに値を設定

2.コントロールの追加

 次に、ツールボックスからコントロールをドロップして、帳票レイアウトに貼り付けていきます。帳票タイトルなどの固定値表示や集計のないデータ項目表示はLabelコントロールとTextBoxコントロールのどちらを使用しても構いませんが、Labelは集計設定のためのSummaryプロパティを持たないため、集計値の表示欄にはTextBoxを使う必要があります。

今回のコントロール配置。Labelコントロールは緑、TextBoxコントロールは青で色分け
今回のコントロール配置。Labelコントロールは緑、TextBoxコントロールは青で色分け
TextBoxはSummaryプロパティをもつ
TextBoxはSummaryプロパティをもつ
LabelはSummaryプロパティをもたない
LabelはSummaryプロパティをもたない

3.集計項目のプロパティ設定

 集計データを設定するためのSummaryプロパティには、以下の4つがあります(表1)。このうち、SummaryGroupはグループ単位で合計するときのみ使用する項目で、SubTotal/PageCountの場合に作用します。また、SummaryRunningはNoneに設定すると、値の初期化が行われません。

プロパティ名 内容
SummaryFunc 集計関数の種類
SummaryGroup どのグループを対象とするかの設定
SummaryRunning 値の初期化のタイミング。None/Group/Allのいずれかを設定する
SummaryType 合計する単位。None/GrandTotal/PageTotal/SubTotal/PageCountのいずれかを設定する

 例えば、グループ単位で、合計値を表示したい場合は次のように設定します(合計単位とグループを設定してあるので、値の初期化はGROUPに設定する必要はありません)。

プロパティ名 設定値
SummaryFunc Sum(合計関数)
SummaryGroup [集計したいグループ]
SummaryRunning None
SummaryType SubTotal(グループ単位の小計)
DataField [集計したい値]

 Summaryプロパティを使うと、アプリケーション側で集計値を加算していく処理や複雑なSQLの記述が不要になるだけでなく、グループヘッダに明細の合計値欄を配置させることもできます。

今回作成する帳票のコントロール設定

 続いて、具体的に今回作成する帳票の集計値の設定について説明します。

今回の帳票で、集計値を表示する箇所(一部)
今回の帳票で、集計値を表示する箇所(一部)

A.ページ番号を表示する

 帳票にページ番号を表示するには、SummaryTypeプロパティにSummaryType.PageCountを設定します。

ページ プロパティ 設定値
現在ページ SummaryRunning All
SummaryType PageCount
総ページ SummaryRunning None
SummaryType PageCount

 現在ページ番号と総ページ数の切り替えは、SummaryRunningプロパティの設定で行います。SummaryFuncは何を設定しても同じ値が表示されます。なお、グループごとにページ数を設定する場合や、2ページ目からページを数えたいなどの場合については、後ほど説明します。

B.総合計値を表示する

 明細の全データを集計する場合は、SummaryType.GrandTotalを設定します。

C.グループごとの小計値を表示する。

 前項で説明した集計項目のプロパティ設定例と同様に設定します。商品小計項目は次のように設定します。

プロパティ 設定例
SummaryFunc Sum
SummaryGroup [注文番号グループヘッダ]
SummaryRunning None
SummaryType SubTotal(グループ単位の小計)
DataField [Price]

 どのセクションでも小計の取得は可能ですが、位置によってはうまく合計されないこともあります。

 特に、グループヘッダ/フッタを複数もつ帳票の場合、グループの順序に気をつけないと集計の単位が大きく変わってしまい、正しい集計結果を表示させることができません。今回はグループの順序が「注文日→注文番号→商品」の順序で入れ子構造になるようにグループを作成する必要があります。

 グループの順序を確認・変更するには、右クリックメニューの[グループ順]を選択すると表示されるグループ順ダイアログで変更することができます。

グループの順序が正しく設定されている場合
グループの順序が正しく設定されている場合
グループの順序が正しくない場合
グループの順序が正しくない場合

D.重複を排除した小計値を表示する

 宅配料金は注文ごとに発生するデータなので、明細単位ですべて合計するとデータが重複し、正しい値を得られなくなってしまいます。このような場合はSum関数の代わりに、Dsum関数を使用します。Dsum関数は、指定されたキー値をもとに重複を除いた値の合計を計算します。Dsum関数がキーとして使用する値は、DistinctDataFieldプロパティに設定します。

 今回は宅配料金を注文番号(OrderID)ごとに集計したいので、Summaryプロパティを次のように設定します。

プロパティ 設定値
SummaryFunc DSum
SummaryGroup [注文月グループヘッダ]
SummaryRunning None
SummaryType SubTotal(グループ単位の小計)
DistinctDataField OrderID(注文番号)
DataField Freight(宅配料金)

ページ数の取得について

グループごとの総ページ数・現在ページ数の設置

 グループごとの総ページ数と現在ページ数を取得する場合のプロパティ設定について説明します。TextBoxコントロールのSummaryGroupプロパティを設定すると、グループごとにページカウントが行われます。

 グループ内の現在ページ番号を取得するときはSummaryRunningプロパティをGroupに設定します。総ページ数を取得するときは帳票全体のときと同じく、Noneに設定します。

ページ プロパティ 設定値
現在ページ SummaryGroup [集計したいグループ]
SummaryRunning Group
SummaryType PageCount
総ページ SummaryGroup [集計したいグループ]
SummaryRunning None
SummaryType PageCount

ページ数のスタートを1以外に設定したい場合

 PageCountの最終的な値は描画が終了したReport_Endイベント内で捕捉することはできますが、ページヘッダ・ページフッタのSummaryプロパティからは直接取得することはできません。また、ActiveReports帳票では現在ページ数を取得するためにPageNumberプロパティを使用しますが、このプロパティは読み取り専用なので直接変更することはできません。

 よって、ページ番号をアプリケーション側で制御する場合は、別の変数に保持しておいたページ番号を表示させる必要があります。

 ページ番号をカスタマイズするにはページ番号を保持するフィールドを追加し、ページヘッダのFormatイベントで現在のページ番号と処理を追加します。

PageNumberプロパティの値を利用して現在ページ番号を設定する(C#)
private int anotherPageCount = 0;
protected const int AddNumber = 4; //ページ数を5から開始する場合

//Formatイベント
private void pageHeader_Format(object sender, EventArgs e)
{
    //現在ページ数を計算
    anotherPageCount = PageNumber + AddNumber;
    //TextBoxに代入
    this.currentPage.Text = anotherPageCount.ToString();
}
ageNumberプロパティの値を利用して現在ページ番号を設定する(Visual Basic 2005)
Dim anotherPageCount = 0
Protected Const addNumber = 4 'ページ数を5から開始する

Private Sub PageHeader_Format(ByVal sender As System.Object, _
                              ByVal e As System.EventArgs) _
                                     Handles PageHeader.Format
    '現在ページ数を計算
    anotherPageCount = Me.PageNumber + addNumber
    'TextBoxに代入
    Me.currentPage.Text = anotherPageCount.ToString()
End Sub

 カスタマイズした総ページ数を取得する場合も、PageCountプロパティは使わず現在ページ数をMAX関数で集計した値を表示させます。

 今回は、アンバウンドフィールドを追加して表示させる例を紹介します。

アンバウンドフィールドを追加する(C#)
private void NewActiveReport1_DataInitialize(object sender, 
                                             System.EventArgs e)
{
    Fields.Add("GetCount");
}
アンバウンドフィールドを追加する(Visual Basic 2005)
Private Sub NewActiveReport1_DataInitialize( _
        ByVal sender As System.Object, _
        ByVal e As System.EventArgs) Handles MyBase.DataInitialize
    Fields.Add("GetCount")
End Sub
ページヘッダで作った現在ページ番号をカラムにいれる(C#)
private void NewActiveReport1_FetchData(object sender, 
        DataDynamics.ActiveReports.ActiveReport3.FetchEventArgs eArgs)
{
    try
    {
        Fields["GetCount"].Value = anotherPageCount;
    }
    catch
    {
        eArgs.EOF = true;
    }
}
ページヘッダで作った現在ページ番号をカラムにいれる(Visual Basic 2005)
Private Sub NewActiveReport1_FetchData(ByVal sender As System.Object, _
        ByVal eArgs As DataDynamics.ActiveReports.ActiveReport3.FetchEventArgs) _
        Handles MyBase.FetchData
    Try
        Fields("GetCount").Value = anotherPageCount
    Catch ex As Exception
        eArgs.EOF = True
    End Try
End Sub

 総ページ数Textboxのプロパティを以下のように設定し、現在ページの最大値をとるようにします。

プロパティ 設定値
SummaryFunc Max
SummaryGroup なし
SummaryRunning None
SummaryType GrandTotal(総合計)
DataField GetCount(現在ページ数)

重複する値を表示させないようにする

 今回のサンプル帳票レイアウトでは、注文番号と注文単位の小計値を明細の1行目に表示させ、2行目以降は何も表示していません。重複値の表示、非表示を切り替えるには、detailセクションのFormatイベントでTextBoxコントロールのVisibleプロパティを切り替えればよいのですが、以下のようにすることでコード追加が不要になります。

 重複する項目を次の行に表示させないようにするには、初回だけ表示させたい列のTextBoxコントロールをグループヘッダに設定し、UnderlayNextプロパティにTrueを設定します。UnderlayNextプロパティをTrueに設定したセクションは、次のセクションの開始位置に重なって表示されるため、結果として1行目だけ集計値が表示されることになります。

 なお、コードを追加して表示、非表示を切り替える方法はActiveReportsのコードスニペットに含まれているので、ぜひ試してみてください。

改ページ処理について

 最後に、改ページの処理について簡単に説明します。出力結果が設定した用紙サイズに収まらない場合、ActiveReportsでは自動的に改ページが行われますが、帳票レイアウト側で明示的に改ページ位置を指定することもできます。

 改ページを指定するにはPageBreakコントロールを使用する方法と、各レポートセクションのNewPageプロパティを設定する方法の2つがあります。

PageBreakコントロールを使った改ページ

 PageBreakコントロールはMicrosoft Wordの[改ページの挿入]とほぼ同様の機能です。コントロールを配置した位置で、毎回改ページを行います。

PageBreakコントロール
PageBreakコントロール

 PageBreakコントロールはセクション途中の改ページに使用するコントロールです。例えば、ページヘッダに印刷する画像サイズが大きいので、2ページに分けたい場合などに使用します。

 ただ、最終ページであっても指定位置で改ページしてしまうため「セクションが終了したところで改ページする」といったケースにはあまり向いていません。

 セクション単位で改ページを行うには後述するNewPageプロパティを使用するのが便利ですが、PageBreakコントロールを使う場合はFetchDataイベントにコードを追加してEnabledプロパティを制御するなどの方法があります。以下のコードでは、最終行だった場合(eArgs.EOFプロパティの値がTrue)、改ページを起こさないように制御しています。

PageBreakコントロールの改ページ制御(C#)
 private void NewActiveReport1_FetchData(object sender,
        DataDynamics.ActiveReports.ActiveReport.FetchEventArgs eArgs)
{
    if (!eArgs.EOF)
    {
        this.pageBreak1.Enabled = true;
    }
    else
    {
        this.pageBreak1.Enabled = false;
    }
}
PageBreakコントロールの改ページ制御(Visual Basic 2005)
Private Sub NewActiveReport1_FetchData(ByVal sender As System.Object, _
        ByVal eArgs As DataDynamics.ActiveReports.ActiveReport3.FetchEventArgs) _
        Handles MyBase.FetchData
    If Not eArgs.EOF Then
        PageBreak1.Enabled = True
    Else
        PageBreak1.Enabled = False
    End If
End Sub

集計グループ単位での改ページ

 グループヘッダ・グループフッタのNewPageプロパティを設定すると、集計グループの単位で改ページが行われます。NewPageプロパティにはNone、Before、After、BeforeAfterのいずれかを設定できます。

 例えば、グループフッタのNewPageプロパティを「After」に設定すると、グループフッタが印刷されたあとで改ページしますが、PageBreakコントロールのように最後に空白ページが入ることはありません。

まとめ

 ActiveReportsの集計機能はデータソースの変更をすることなく、Summaryプロパティを設定するだけで高度な集計処理を簡単に行うことができます。また、必要に応じてコードを追加することで、より柔軟なカスタマイズが可能となっています。

 改ページ制御については簡単な機能紹介にとどまりましたが、次回は、より掘り下げたページ制御のノウハウや実際の帳票印刷などのテーマを取り上げる予定です。

この記事は参考になりましたか?

  • このエントリーをはてなブックマークに追加

この記事は参考になりましたか?

この記事をシェア

  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/1664 2014/02/06 11:25

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング