カラムを使った多段組レイアウト 2
SubReportコントロールで行ヘッダを作成する
今回の帳票で1番左側に表示する商品名列は、表示される値が決まっていれば直接Labelコントロールなどに書いて並べてもよいのですが、ここではSubReportコントロールを使って、表示される値が可変の商品名列を作成する方法を紹介します。
ActiveReportsのSubReportコントロールを使うと、レポート内に別のレポート(子レポート)を埋め込んで使うことができます。子レポートの出力は親レポートのセクションが出力されるたびに実行されます。
子レポートは、普通のActiveReports帳票を作成するのと同じ方法で作成します。プロジェクトに新しいActiveReports 3.0ファイルを追加し、親レポートと同様にグループヘッダ、detail、グループフッタセクションをもつ小さな帳票を作成します。
次に、作成した子レポートを親レポートに埋め込みます。親レポートの商品名グループヘッダに、ツールボックスからSubReportコントロールをドロップし、商品名グループヘッダのFormatイベントで、SubReportコントロールのReportプロパティに新しい子レポートのインスタンスを設定します。
private void groupHeader1_Format(object sender, EventArgs e) { //子レポートのインスタンスを作り、SubReportコントロールへ設定する SubReport1 sub1 = new SubReport1(); this.subReport1.Report = sub1; }
Private Sub GroupHeader1_Format(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles GroupHeader1.Format '子レポートのインスタンスを作り、SubReportコントロールへ設定する Dim sub1 As New SubReport1() Me.SubReport1.Report = sub1 End Sub
Formatイベントの記述が終わったら、商品名グループヘッダのRepeatStyleプロパティに「OnPageIncludeNoDetail」を設定し、商品名グループフッタのNewColumnプロパティには「After」を設定します。
テキストファイルからデータを読み込む
これまで紹介した帳票サンプルでは、detailセクション作成時にデータソースの設定をすることで製品付属の「NWind.mdb」にアクセスしていましたが、今回は実行時にテキストファイルを直接読み取ってレポートに出力する方法を紹介します。このように、レポートに対してデータソースを指定せず、コードを記述して実行時にデータをロードする帳票を「アンバウンドレポート」と呼びます。
今回の帳票では、親レポートで使う売上データと子レポートで使う商品名データを別々に用意しています。売上データは、「通し番号」「商品名」「売上日」「売り上げた数量」がカンマ区切りで入っている、いわゆるCSVファイルです。
一方、子レポート側で必要なのは商品名だけなので、データはカンマ区切りのない単純なテキストファイルです。
アンバウンドレポートでは、DataInitializeイベントとFetchDataイベントにデータの初期化と読み取りを記述します。また、今回はファイルからデータを読み取るため、ReportEndイベントで文字ストリームを閉じる必要があります。
以下に各イベントで実装するコードを掲載します。便宜のため例外処理を省略していますが、実際の開発では適切な例外処理を追加してください。また、ファイル名もハードコーディングせず、パラメータとして外部から与えられるようにしておくべきでしょう。
private StreamReader reader; //DataInitializeイベントの処理 private void DailySalesList_DataInitialize(object sender, System.EventArgs eArgs) { // データフィールドを追加 this.Fields.Add("SlipNo"); this.Fields.Add("ProductName"); this.Fields.Add("SlipDate"); this.Fields.Add("Quantity"); // テキストファイルをオープン reader = System.IO.File.OpenText( @"C:\Codezine\ActiveReports\SalesData.txt"); } //FetchDataイベントの処理 private void DailySalesList_FetchData(object sender, DataDynamics.ActiveReports.ActiveReport3.FetchEventArgs eArgs) { //ファイルよりデータを読み込み string line = reader.ReadLine(); if (line != null) { //CSVデータを配列に変換し、フィールドに取り込む string[] arrayValues = line.Split(",".ToCharArray()); Fields["SlipNo"].Value = arrayValues[0]; Fields["ProductName"].Value = arrayValues[1]; Fields["SlipDate"].Value = arrayValues[2]; Fields["Quantity"].Value = arrayValues[3]; eArgs.EOF = false; } else { eArgs.EOF = true; } } //ReportEndイベントの処理 private void DailySalesList_ReportEnd(object sender, EventArgs e) { reader.Close(); }
Private reader As System.IO.StreamReader 'DataInitializeイベントの処理 Private Sub DailySalesList_DataInitialize(_ ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.DataInitialize 'データフィールドを追加 Me.Fields.Add("SlipNo") Me.Fields.Add("ProductName") Me.Fields.Add("SlipDate") Me.Fields.Add("Quantity") 'テキストファイルをオープン reader = System.IO.File.OpenText(_ "C:\\Codezine\\ActiveReports\\SalesData.txt") End Sub 'FetchDataイベントの処理 Private Sub DailySalesList_FetchData(ByVal sender As System.Object, _ ByVal eArgs As _ DataDynamics.ActiveReports.ActiveReport3.FetchEventArgs) _ Handles MyBase.FetchData Dim line As String If line Is Nothing Then eArgs.EOF = True Else Dim arrayValues As String() 'CSVデータを配列に変換し、フィールドに取り込む arrayValues = line.Split(",".ToCharArray()) Me.Fields("SlipNo").Value = arrayValues(0) Me.Fields("ProductName").Value = arrayValues(1) Me.Fields("SlipDate").Value = arrayValues(2) Me.Fields("Quantity").Value = arrayValues(3) eArgs.EOF = False End If End Sub 'ReportEndイベントの処理 Private Sub DailySalesList_ReportEnd(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.ReportEnd reader.Close() End Sub
同様に、サブレポートに対してもイベント処理コードを実装します。CSVの変換処理がないところ以外は、親レポートとほとんど同じです。
private StreamReader reader; //DataInitializeイベントの処理 private void SubReport1_DataInitialize(object sender, System.EventArgs eArgs) { // データフィールドを追加 this.Fields.Add("ProductName"); // テキストファイルをオープン reader = System.IO.File.OpenText( @"C:\Codezine\ActiveReports\ProductName.txt"); } //FetchDataイベントの処理 private void SubReport11_FetchData(object sender, DataDynamics.ActiveReports.ActiveReport3.FetchEventArgs eArgs) { // ファイルよりデータを読み込み string productName = reader.ReadLine(); if (productName != null) { //読み込んだデータをフィールドに設定します。 Fields["ProductName"].Value = productName; eArgs.EOF = false; } else { eArgs.EOF = true; } } //ReportEndイベントの処理 private void SubReport1_ReportEnd(object sender, EventArgs e) { reader.Close(); }
Private reader As System.IO.StreamReader 'DataInitializeイベントの処理 Private Sub SubReport1_DataInitialize(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.DataInitialize 'データフィールドを追加 Me.Fields.Add("ProductName") 'テキストファイルをオープン reader = System.IO.File.OpenText(_ "C:\\Codezine\\ActiveReports\\ProductName.txt") End Sub 'FetchDataイベントの処理 Private Sub SubReport1_FetchData(ByVal sender As System.Object, _ ByVal eArgs As _ DataDynamics.ActiveReports.ActiveReport3.FetchEventArgs) _ Handles MyBase.FetchData Dim productName As String productName = reader.ReadLine() If productName Is Nothing Then eArgs.EOF = True Else Me.Fields("ProductName").Value = productName eArgs.EOF = False End If End Sub 'ReportEndイベントの処理 Private Sub SubReport1_ReportEnd(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.ReportEnd reader.Close() End Sub
注意事項
今回作成した帳票では、「商品名」行と「売上日」列で構成されるマトリックスに対して端から順番にデータを配置していくため、「売上データ、商品名データが順序保証されていること」「すべての組み合わせのデータが存在し、抜けがないこと」を前提としています。データの順序が決まっていない場合や、実際に売上のあった日のデータのみが取り込まれる場合は、必要な処理を追加していくことになります。
まとめ
今回は改ページ制御と多段組レイアウトについて紹介しました。スクロールによって描画領域を(ほぼ無限に)広げられるWebページと異なり、「用紙サイズ」という制約のある帳票開発においては、改ページ制御1つ取ってもいろいろと細かい設定が必要になることがおわかりいただけたかと思います。
これまで3回にわたって、帳票アプリケーション開発の基本である「表示、集計、改ページ」についてひと通り紹介しました。次回以降も、実際の帳票アプリケーション開発に役立つテクニックを紹介していきますので、お楽しみに。