はじめに
Webアプリにおいて帳票印刷は鬼門の1つです。ブラウザにも印刷機能があり、綺麗に印刷するための工夫も組み込まれていますが、Windowsアプリと同じような品質の帳票印刷を行えるレベルにまでは達していません。また、オンライン掲示板などで時々見かける「印刷イメージ画面の表示なしに、Webから印刷ボタンをクリックするだけで、直接プリンタに印刷したい」というような要望も簡単には解決できません。
このようにとかく相性の悪い、Webアプリと帳票印刷という2つの要素の架け橋となる.NETコンポーネントがActiveReportsです。
ブラウザから印刷を行うための基本的な仕組み
ブラウザごとの自作アドオン作成は手間がかかる
ブラウザの標準機能では満足した結果を得られないときに活用するのが、アドオン(プラグインとも言う)です。アドオンの作り方はブラウザごとに異なるため、同じアドオンでもInternet Explorer用とFirefox用とでは、それぞれ別に用意しなければなりません。その手間がどれくらい大変かは、容易に想像がつくと思います。
そうした手間を省く解決策として、印刷関連であればPDFファイルを利用するシステム構成、つまりAdobe Readerのアドオン利用を前提にすることを、考えてみるのがよいでしょう。Adobe ReaderのアドオンであればOSもWindowsに限定されておらず、主要なブラウザに対応しているため、いろいろなPC環境で使えるというWebアプリの特色も失われません。
PDFファイルをブラウザで表示させる流れ
PDFファイルをブラウザで表示するためには、Webサーバとブラウザの両方で認識を合わせて協調する必要があります。

そのための識別が図2にある「Context-Type: application/pdf」です。このヘッダをブラウザが受け取ると、PDF表示用のアドオンを探してデータを渡し、後はアドオンがPDFファイルを表示するのでブラウザの機能に左右されません。
ActiveReports for .NETを使えば、WindowsアプリでPDFファイルを出力するだけではなく、Adobe Readerアドオンと組み合わせ、WebアプリでPDFファイルをブラウザに表示することもできます。それでは具体的にその方法を確認していきましょう。
データアクセスサービスを作成
実際のシステム構築でWebアプリを作成する際に、重要なポイントの1つとして「データをどこに保存するか」ということがあります。データの保存方法によってアプリ構造も変わりますし、セキュリティに関しても相違がでます。そこで、まずデータの保存場所から考察してみたいと思います。
今回のサンプルでは、データ取得部分をASP.NET Webサービスとして実装します。これは、Webアプリの場合、セキュリティを考慮すると、【図3】のようにWebサーバとDBアクセスロジックはファイアウォールにて分離しておき、外部から接続できるゾーンからはDBに接続しないようにした方がよいからです。
データアクセス用ASP.NET Webサービスを作成
実務に耐え得るものを作るのであれば、SQL ServerやOracle DatabaseなどのRDBMSをデータ格納場所とした方がよいのですが、今回は、サンプル環境構築の手軽さを考慮してmdbファイルをデータ格納場所としています。そのため、新しいWebサイトとしてASP.NET Webサービスのプロジェクトを作成して、mdbファイルをASP.NET Webサービスの「App_Data」フォルダに配置しています。
なお、ASP.NET WebサービスでApp_Dataフォルダに配置したmdbファイルを指定する場合、「System.IO.Path.Combine(My.Request.PhysicalApplicationPath, "App_Data\BILL.MDB")」のように指定します。
ASP.NET Webサービスを作成するには、Visual Studioの画面上で[ファイル]‐[新しいWebサイト]より、新しいASP.NET Webサービスのプロジェクトを作成します。

クラスにコードを記述
新しい項目の追加で、WebサービスとしてBillBound.asmxをプロジェクトに追加します。
BillBound.asmxに対するコードは、App_CodeフォルダのBillBound.vbの中に記述します。その中で、mdbファイルからデータを取得してDataSetを作成するコードを記述します。このようにデータアクセス部分はASP.NET Webサービスとして分離しておいた方が、コードの見通しがよくなります。また、データソースをSQL ServerやOracleなどに切り替える時も、ASP.NET Webサービス側の差し替えだけで対応可能な構造で作りやすいです。
ASP.NET Webサービスのメソッドは、WebMethod属性をつける以外、クラスファイルのメソッドと基本的に同じです(リスト1参照)。GetRecordsメソッドの中では、System.Data.OleDbのOleDbDataAdapterを使って、mdbファイルからDataSetに帳票に表示するデータを格納しています。このDataSetが、Webメソッドの戻り値として呼び出し元に返却されます。
Imports System.Web Imports System.Web.Services Imports System.Web.Services.Protocols Imports System.Data Imports System.Data.OleDb <WebService(Description:="ActiveReportsサンプル用サービス", _ Namespace:="http://hatsune.wankuma.local/")> _ <WebServiceBinding(ConformsTo:=WsiProfiles.BasicProfile1_1)> _ <Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _ Public Class BillBound Inherits System.Web.Services.WebService Private block As Object = Nothing ''' <summary> ''' 請求書データを取得する ''' </summary> ''' <param name="userID">(IN)DBユーザ</param> ''' <param name="password">(IN)DBパスワード</param> ''' <param name="customerID">お客様ID(String.Emptyの場合すべて)</param> ''' <returns>DataSet</returns> ''' <remarks></remarks> <WebMethod(EnableSession:=False, _ Description:="請求書データを取得する")> _ Public Function GetRecords(ByVal userID As String, _ ByVal password As String, _ ByVal customerID As String) As DataSet Dim connectionString As String = _ "Provider=Microsoft.Jet.OLEDB.4.0;Password={1};User ID={0};" & _ "Data Source=" & _ System.IO.Path.Combine(My.Request.PhysicalApplicationPath, _ "App_Data\BILL.MDB") Dim ds As New DataSet Using _cn As New OleDb.OleDbConnection(String.Format(connectionString, _ userID, password)) _cn.Open() Using _cmd As New OleDb.OleDbCommand Dim sqlString As String = _ "SELECT Bill.*, BillCondition.*, Customers.CustomerName " & _ "FROM Customers " & _ "INNER JOIN (Bill INNER JOIN BillCondition " & _ "ON Bill.BillNo = BillCondition.BillNo)" & _ "ON Customers.CustomerID = Bill.CustomerID " & _ "ORDER BY Bill.CustomerID,Bill.BillNo,Bill.Date,Bill.SlipNo" _cmd.CommandText = sqlString _cmd.Connection = _cn Using _da As New OleDb.OleDbDataAdapter _da.SelectCommand = _cmd _da.Fill(ds) 'Priceを計算する For Each row As DataRow In ds.Tables(0).Rows row.BeginEdit() row("Price") = CType(row("Number"), Decimal) * _ CType(row("UnitPrice"), Decimal) row.EndEdit() Next End Using End Using _cn.Close() End Using Return ds End Function End Class
リスト1の概要は、下記のとおりです。
- 返却用DataSetとしてdsデータセットを生成
- OleDbConnectionの接続先として、App_DataフォルダのBILL.MDBファイルをJET(Windows OS標準搭載のMDBファイル用データアクセスミドルウェア)OLE DBプロバイダを使って指定し、Openメソッドで指定したMDBファイルの利用を開始
- OleDbCommandのCommandTextプロパティにデータ取得用SELECT SQL文を設定し、接続先としてOleDbConnectionを設定
- OleDbDataAdapterを生成して3.で定義したOleDbCommandをSelectCommandプロパティに設定し、FillメソッドでのSELECT文として採用
- FillメソッドでdsデータセットにSELECT結果をすべて格納
- For Each row As DataRow In ds.Tables(0).Rowsにより、すべての行のPrice列を計算
ASP.NET Webサービスをテスト
ASP.NET Webサービスにデータアクセスのコードが記述できたら、テスト実行してみましょう。
[デバッグ]-[デバッグ実行]メニューをクリックすると、自動的にブラウザが起動され、ASP.NET Webサービスのポータルサイトが表示されます。
[GetRecords]をクリックすると、GetRecordsメソッドによってテストサイトが開きます。ここで[userID]パラメタに「admin」と入力して[起動]ボタンをクリックすれば、Webメソッドが実行され、帳票に表示するデータがXML形式で表示されます。
レポートを定義する
データ取得用ASP.NET Webサービスができたら、次にVisual Studioの画面上で[ファイル]‐[新しいWebサイト]から、新しいASP.NET Webサイトのプロジェクトを作成します。そして、ソリューションエクスプローラの[新しい項目の追加]で「ActiveReports 3.0ファイル」を選択し、ActiveReports帳票定義体を「Seikyu_Report.vb」という名前でプロジェクトに追加します。このとき、【図7】のようなメッセージが表示されますが、気にせず[はい]ボタンをクリックしてください。

サンプル帳票デザイン
帳票定義体がプロジェクトに追加できたら、ソリューションエクスプローラで帳票定義体「Seikyu_Report.vb」を右クリックしショートカットメニューを表示させ、[デザイナの表示]からActiveReportsの帳票デザイナを起動します。
なお、今回のサンプルは、前回のWindowsアプリ編の請求明細書と、同じデザインで作成します。重要なプロパティ値などは前回の記事を参照ください。
帳票定義体にコードを記述
ActiveReports帳票定義体は、「DataDyanmics.ActiveReports.ActiveReports3」を継承したクラスで、実行時の各種イベントに対してコードを組み込むことができます。
CZ0905Boundサンプルに組み込んだコードはリスト1になります。
Imports DataDynamics.ActiveReports Imports DataDynamics.ActiveReports.Document Public Class Seikyu_Report Inherits DataDynamics.ActiveReports.ActiveReport3 '#####ここに自動生成されたコードが記述されている Private m_Row As Integer Private Sub PageHeader_BeforePrint(ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles PageHeader.BeforePrint '集計値を参照して、別の Field コントロールに値を設定します。 txtExcise.Value = CType(txtTotal.Value, Decimal) * 0.05 '消費税額 txtBillTotal.Value = CType(txtTotal.Value, Decimal) * 1.05 + _ CType(txtCarryOver.Value, Decimal) '請求額合計 End Sub Private Sub GroupHeader1_BeforePrint(ByVal sender As Object, _ ByVal e As System.EventArgs) _ Handles Bill_GroupHeader.BeforePrint m_Row = 0 End Sub Private Sub Detail_Format(ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles Detail.Format '行番号をインクリメントします。 m_Row = m_Row + 1 '1行ごとに背景色を切り替えます。 If (m_Row Mod 2) = 0 Then shpDetailBack1.BackColor = Drawing.Color.White '白色 Else shpDetailBack1.BackColor = Drawing.Color.LightYellow '黄色 End If End Sub End Class
Windowsアプリと異なりWebアプリの場合、ActiveReports帳票定義体はPartial Classを使った分離コード構造になっていません。そのため、リスト1では除外してありますが、Inherits文の直後にNewメソッド、Disposeメソッドおよび帳票定義用コード(分離コード構造の場合、designer.vbに書かれるコード)が含まれています。
自動生成されたコードを変更すると、帳票定義体をデザイナ表示できなくなったり、実行時にエラーになったりする可能性もあるので、変更しないように注意してください。そして、自動生成されたコードの後に次の2つの機能を実現するためのコードをそれぞれ記述します。
- ページヘッダで計算結果を印字
PageHeader.BeforePrintイベントで[PageHeader]セクションにある「今回消費税額」と「今回ご請求額」を計算しています。BeforePrintイベントで処理する事で印字内容に介入できます。 - 一覧部分の背景色を交互に切替え
[Detail]セクションの背景色を交互に切り替えるために、Detail.Formatイベントで背景色を設定しています。背景色の判断はグループごとの現在行の行位置が奇数か偶数かでおこなっているため、Bill_GroupHeader.BeforePrintイベントで行数を0にクリアしています。
以上が、帳票定義体クラスの準備作業となります。
PDFを自動生成
帳票定義体が作成できたら、CZ0906PdfBoundプロジェクトに帳票定義体 からPDFを生成するコードを追加します。
今回のサンプルでは、帳票定義体に設定するデータはASP.NET WebサービスのCZ0906Boundから受け取ることになるので、帳票定義体のあるCZ0906PdfBoundプロジェクトと同一ソリューションにしておくと、テスト時にデバッグ実行しやすくなります。

PDF出力用の参照設定
CZ0906PdfBoundサンプルでは、PDFの出力を行うためにActiveReportsのPDF出力用の参照設定を追加します。
Webフォームにコードを記述
ソリューションの準備ができたなら、帳票定義体からPDFを生成する コードを記述します。
Imports DataDynamics.ActiveReports Partial Class _Default Inherits System.Web.UI.Page Private Sub Page_Load(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles MyBase.Load If Not Me.IsPostBack Then Using _web As New WebBillBound.BillBound Dim rpt As New Seikyu_Report() rpt.DataSource = _web.GetRecords("admin", "", "").Tables(0) ' 仮想プリンタを設定します。 rpt.Document.Printer.PrinterName = "" rpt.PageSettings.PaperKind = Drawing.Printing.PaperKind.A4 rpt.PageSettings.Orientation = Document.PageOrientation.Portrait rpt.PageSettings.Margins.Top = ActiveReport3.CmToInch(0.5F) rpt.PageSettings.Margins.Bottom = ActiveReport3.CmToInch(0.5F) ' rpt.Run() Using _stream As New System.IO.MemoryStream() Dim p As New DataDynamics.ActiveReports.Export.Pdf.PdfExport() p.Export(rpt.Document, _stream) _stream.Position = 0 Response.ContentType = "application/pdf" Response.AddHeader("content-disposition", _ "inline; filename=sample.pdf") Response.BufferOutput = True Response.BinaryWrite(_stream.ToArray()) End Using Response.End() End Using End If End Sub End Class
ブラウザでURLにDefault.aspxを指定すると、ASP.NETはDefault.aspx.vbのPage_Loadイベントを呼び出します。下記が、リスト3の処理内容です。
- BillBoundサービスを呼び出してDataSetを取得し、帳票定義体のDataSourceプロパティに設定
- PDFファイルを生成する際に参照するプリンタとして、仮想プリンタを設定
- Runメソッドで帳票を生成
- PdfExportを使ってDocumentをMemoryStreamに出力
- Webアプリのレスポンスとして、Response.BinaryWriteでMemoryStreamに設定されたPDFファイルの内容をブラウザに返却
(2)は、サーバにつながっているプリンタの情報を取得しないようにするためです。仮想プリンタを設定することにより、サーバにつながっているプリンタに依存せずPDFファイルを生成できます。
実行
CZ0906PdfBoundを実行すると、以下のようになります。
ブラウザが稼働しているPCの環境によっては、拡大率が低いときに罫線が非表示になる場合があります。
実運用環境展開時の注意点
開発環境にActiveReportsをインストールして開発を行っているときは、ActiveReportsのライセンスの付与や、ActiveReportsコンポーネントの配置などは特に意識する必要はありません。しかし、ASP.NETに作成したものを配置するときには、ライセンス付与とコンポーネント配置に関する作業が必要になります。
ライセンス付与
Webアプリに必要なライセンスはActiveReportsのライセンスだけですが、実行時ライセンス料は不要ですので、ライセンスを供与する設定を行うだけで済みます。また、今回のサンプルではPDFファイルを生成していますが、サーバ側へのAdobe Readerのインストールやライセンスは不要です。
WebアプリにActiveReportsのライセンスを供与するには、web.configのappSettingsセクションにkeyを追加します。
- 製品付属のweb.configキー生成ツール(図12)を使ってWebキーを生成
- web.configファイルを開き、appSettingsセクションに生成された部分をコピー&ペーストします。今回、ASP.NET Webサービスを参照設定しているため、リスト4のようにWeb参照が設定されているので、<add key="DataDynamicsARLic"・・・・・・ />の行だけを追記します。
- [Webサイトの配布]を実行し、リビルドと配布を行います。実運用サイトにいきなり配布せずに、配布先を開発機の別フォルダにして、そのフォルダの中身を実運用サイトにコピーすると、予期せぬ事態が発生しても運用が止まらずに済みます。
: <appSettings> <add key="WebBillBound.BillBound" value="http://localhost:9661/CZ0906Bound/BillBound.asmx" /> <!--ここに追加します --> </appSettings> :
ActiveReportsコンポーネントをアプリと同じフォルダに配置する方法
ActiveReportsコンポーネントは、作成したWebアプリをビルドしたDLLファイルが格納されている仮想フォルダのbinフォルダに配布すれば、実行することができます。どのファイルを配布するかは、web.configのassembliesセクションに書かれている内容を参考にするとよいでしょう。
次に、binフォルダの中にjaフォルダをつくって「ActiveReports3.resources.dll」「ActiveReports.Viewer3.resources.dll」を配置すれば、各種メッセージなども日本語化できます。こちらの作業も忘れずに、併せて実施するようにしましょう。
ActiveReportsコンポーネントをGACとして配置する方法
ActiveReportsは厳密な名前が付与されたコンポーネントなので、ASP.NETはGACに登録されていることが基本的な動作環境となります。アプリと同じフォルダに配置する方法でも動作はしますが、GACに登録するのがベストです。
GACに登録するならば、作成したアプリと同じ場所に配布する必要はないので、ASP.NETの稼働サーバでActiveReportsの専用のフォルダを作成し、コマンドラインから次のコマンドを実行します。
Gacutil -I 配布したDLLファイル名
1つずつ登録するのも大変ですから、必要なActiveReportsコンポーネントを参照設定したWindowsアプリを作成してからmsiファイルを作成し、そのmsiでActiveReportsコンポーネントをGACに登録するように定義しておくとよいでしょう。後は、このmsiファイルをASP.NETの稼働サーバで実行するようにしておくと、サーバが何台あっても少ない手間で済みます。
まとめ
今回は、ブラウザを限定しないためにPDFを使う方式を説明しましたが、ActiveReportsのProfessional EditionにあるWebビューアのActiveXコントロールを併用することで、プレビューなしの印刷なども実現できます。しかし、この方式は、対象ブラウザがInternet Explorerに限定されるので、システム全体の用途などを考えて、採用方式を決定する必要があります。