SHOEISHA iD

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

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

現役エンジニア直伝! 「現場」で使えるコンポーネント活用術(ActiveReports)

帳票Webアプリ実践構築ガイド
ActiveReports for .NETを使いこなす!~その2

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

 Windowsアプリと同じような品質の帳票印刷を行えるWebアプリケーションを開発するのはなかなか難しく、大変手間のかかる作業です。今回は、ActiveReportsを使い、効率の良く、機能的なWeb帳票アプリケーションを構築する方法を紹介していきます。

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

はじめに

 Webアプリにおいて帳票印刷は鬼門の1つです。ブラウザにも印刷機能があり、綺麗に印刷するための工夫も組み込まれていますが、Windowsアプリと同じような品質の帳票印刷を行えるレベルにまでは達していません。また、オンライン掲示板などで時々見かける「印刷イメージ画面の表示なしに、Webから印刷ボタンをクリックするだけで、直接プリンタに印刷したい」というような要望も簡単には解決できません。

 このようにとかく相性の悪い、Webアプリと帳票印刷という2つの要素の架け橋となる.NETコンポーネントがActiveReportsです。

ブラウザから印刷を行うための基本的な仕組み

ブラウザごとの自作アドオン作成は手間がかかる

 ブラウザの標準機能では満足した結果を得られないときに活用するのが、アドオン(プラグインとも言う)です。アドオンの作り方はブラウザごとに異なるため、同じアドオンでもInternet Explorer用とFirefox用とでは、それぞれ別に用意しなければなりません。その手間がどれくらい大変かは、容易に想像がつくと思います。

図1 ブラウザごとのアドオンの例
図1 ブラウザごとのアドオンの例

 そうした手間を省く解決策として、印刷関連であればPDFファイルを利用するシステム構成、つまりAdobe Readerのアドオン利用を前提にすることを、考えてみるのがよいでしょう。Adobe ReaderのアドオンであればOSもWindowsに限定されておらず、主要なブラウザに対応しているため、いろいろなPC環境で使えるというWebアプリの特色も失われません。

PDFファイルをブラウザで表示させる流れ

 PDFファイルをブラウザで表示するためには、Webサーバとブラウザの両方で認識を合わせて協調する必要があります。

図2 PDFファイルをブラウザで表示するときの流れ
図2 PDFファイルをブラウザで表示するときの流れ

 そのための識別が図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に接続しないようにした方がよいからです。

図3 データベースWebアプリケーションのお勧めシステム構成
図3 データベースWebアプリケーションのお勧めシステム構成

データアクセス用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サービスのプロジェクトを作成します。

図4 ASP.NET Webサービスのプロジェクトを作成
図4 ASP.NET Webサービスのプロジェクトを作成

クラスにコードを記述

 新しい項目の追加で、WebサービスとしてBillBound.asmxをプロジェクトに追加します。

図5 BillBound.asmxの追加
図5 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メソッドの戻り値として呼び出し元に返却されます。

リスト1 BillBound.vb
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の概要は、下記のとおりです。

  1. 返却用DataSetとしてdsデータセットを生成
  2. OleDbConnectionの接続先として、App_DataフォルダのBILL.MDBファイルをJET(Windows OS標準搭載のMDBファイル用データアクセスミドルウェア)OLE DBプロバイダを使って指定し、Openメソッドで指定したMDBファイルの利用を開始
  3. OleDbCommandのCommandTextプロパティにデータ取得用SELECT SQL文を設定し、接続先としてOleDbConnectionを設定
  4. OleDbDataAdapterを生成して3.で定義したOleDbCommandをSelectCommandプロパティに設定し、FillメソッドでのSELECT文として採用
  5. FillメソッドでdsデータセットにSELECT結果をすべて格納
  6. For Each row As DataRow In ds.Tables(0).Rowsにより、すべての行のPrice列を計算

ASP.NET Webサービスをテスト

 ASP.NET Webサービスにデータアクセスのコードが記述できたら、テスト実行してみましょう。

 [デバッグ]-[デバッグ実行]メニューをクリックすると、自動的にブラウザが起動され、ASP.NET Webサービスのポータルサイトが表示されます。

図6 BillBoundサービスのテスト実行
図6 BillBoundサービスのテスト実行

 [GetRecords]をクリックすると、GetRecordsメソッドによってテストサイトが開きます。ここで[userID]パラメタに「admin」と入力して[起動]ボタンをクリックすれば、Webメソッドが実行され、帳票に表示するデータがXML形式で表示されます。

レポートを定義する

 データ取得用ASP.NET Webサービスができたら、次にVisual Studioの画面上で[ファイル]‐[新しいWebサイト]から、新しいASP.NET Webサイトのプロジェクトを作成します。そして、ソリューションエクスプローラの[新しい項目の追加]で「ActiveReports 3.0ファイル」を選択し、ActiveReports帳票定義体を「Seikyu_Report.vb」という名前でプロジェクトに追加します。このとき、【図7】のようなメッセージが表示されますが、気にせず[はい]ボタンをクリックしてください。

図7 ActiveReportsの帳票定義体はApp_Codeに配置される
図7 ActiveReportsの帳票定義体はApp_Codeに配置される

サンプル帳票デザイン

 帳票定義体がプロジェクトに追加できたら、ソリューションエクスプローラで帳票定義体「Seikyu_Report.vb」を右クリックしショートカットメニューを表示させ、[デザイナの表示]からActiveReportsの帳票デザイナを起動します。

 なお、今回のサンプルは、前回のWindowsアプリ編の請求明細書と、同じデザインで作成します。重要なプロパティ値などは前回の記事を参照ください。

図8 CZ0906PdfBoundサンプル帳票デザイン
図8 CZ0906PdfBoundサンプル帳票デザイン

帳票定義体にコードを記述

 ActiveReports帳票定義体は、「DataDyanmics.ActiveReports.ActiveReports3」を継承したクラスで、実行時の各種イベントに対してコードを組み込むことができます。

 CZ0905Boundサンプルに組み込んだコードはリスト1になります。

リスト2 Seikyu_Report.vb
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つの機能を実現するためのコードをそれぞれ記述します。

  1. ページヘッダで計算結果を印字
    PageHeader.BeforePrintイベントで[PageHeader]セクションにある「今回消費税額」と「今回ご請求額」を計算しています。BeforePrintイベントで処理する事で印字内容に介入できます。
  2. 一覧部分の背景色を交互に切替え
    [Detail]セクションの背景色を交互に切り替えるために、Detail.Formatイベントで背景色を設定しています。背景色の判断はグループごとの現在行の行位置が奇数か偶数かでおこなっているため、Bill_GroupHeader.BeforePrintイベントで行数を0にクリアしています。

 以上が、帳票定義体クラスの準備作業となります。

PDFを自動生成

 帳票定義体が作成できたら、CZ0906PdfBoundプロジェクトに帳票定義体 からPDFを生成するコードを追加します。

 今回のサンプルでは、帳票定義体に設定するデータはASP.NET WebサービスのCZ0906Boundから受け取ることになるので、帳票定義体のあるCZ0906PdfBoundプロジェクトと同一ソリューションにしておくと、テスト時にデバッグ実行しやすくなります。

図9 ASP.NET WebサイトとASP.NET Webサービスのソリューションを作成
図9 ASP.NET WebサイトとASP.NET Webサービスのソリューションを作成

PDF出力用の参照設定

 CZ0906PdfBoundサンプルでは、PDFの出力を行うためにActiveReportsのPDF出力用の参照設定を追加します。

図10 PdfExportへの参照を追加
図10 PdfExportへの参照を追加

Webフォームにコードを記述

 ソリューションの準備ができたなら、帳票定義体からPDFを生成する コードを記述します。

リスト3 Default.aspx.vb
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の処理内容です。

  1. BillBoundサービスを呼び出してDataSetを取得し、帳票定義体のDataSourceプロパティに設定
  2. PDFファイルを生成する際に参照するプリンタとして、仮想プリンタを設定
  3. Runメソッドで帳票を生成
  4. PdfExportを使ってDocumentをMemoryStreamに出力
  5. Webアプリのレスポンスとして、Response.BinaryWriteでMemoryStreamに設定されたPDFファイルの内容をブラウザに返却
※注

(2)は、サーバにつながっているプリンタの情報を取得しないようにするためです。仮想プリンタを設定することにより、サーバにつながっているプリンタに依存せずPDFファイルを生成できます。

実行

 CZ0906PdfBoundを実行すると、以下のようになります。

図11 CZ0906PdfBoundの実行結果
図11 CZ0906PdfBoundの実行結果
※注

 ブラウザが稼働しているPCの環境によっては、拡大率が低いときに罫線が非表示になる場合があります。

実運用環境展開時の注意点

 開発環境にActiveReportsをインストールして開発を行っているときは、ActiveReportsのライセンスの付与や、ActiveReportsコンポーネントの配置などは特に意識する必要はありません。しかし、ASP.NETに作成したものを配置するときには、ライセンス付与とコンポーネント配置に関する作業が必要になります。

ライセンス付与

 Webアプリに必要なライセンスはActiveReportsのライセンスだけですが、実行時ライセンス料は不要ですので、ライセンスを供与する設定を行うだけで済みます。また、今回のサンプルではPDFファイルを生成していますが、サーバ側へのAdobe Readerのインストールやライセンスは不要です。

 WebアプリにActiveReportsのライセンスを供与するには、web.configのappSettingsセクションにkeyを追加します。

  1. 製品付属のweb.configキー生成ツール(図12)を使ってWebキーを生成
  2. 図12 Webキー生成
    図12 Webキー生成
  3. web.configファイルを開き、appSettingsセクションに生成された部分をコピー&ペーストします。今回、ASP.NET Webサービスを参照設定しているため、リスト4のようにWeb参照が設定されているので、<add key="DataDynamicsARLic"・・・・・・ />の行だけを追記します。
  4. リスト4 web.config
        :
    <appSettings>
     <add key="WebBillBound.BillBound" value="http://localhost:9661/CZ0906Bound/BillBound.asmx" />
    <!--ここに追加します -->
    </appSettings>
        :
    
  5. [Webサイトの配布]を実行し、リビルドと配布を行います。実運用サイトにいきなり配布せずに、配布先を開発機の別フォルダにして、そのフォルダの中身を実運用サイトにコピーすると、予期せぬ事態が発生しても運用が止まらずに済みます。

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の専用のフォルダを作成し、コマンドラインから次のコマンドを実行します。

GAC登録のコマンド
Gacutil -I 配布したDLLファイル名

 1つずつ登録するのも大変ですから、必要なActiveReportsコンポーネントを参照設定したWindowsアプリを作成してからmsiファイルを作成し、そのmsiでActiveReportsコンポーネントをGACに登録するように定義しておくとよいでしょう。後は、このmsiファイルをASP.NETの稼働サーバで実行するようにしておくと、サーバが何台あっても少ない手間で済みます。

まとめ

 今回は、ブラウザを限定しないためにPDFを使う方式を説明しましたが、ActiveReportsのProfessional EditionにあるWebビューアのActiveXコントロールを併用することで、プレビューなしの印刷なども実現できます。しかし、この方式は、対象ブラウザがInternet Explorerに限定されるので、システム全体の用途などを考えて、採用方式を決定する必要があります。

図13 ActiveXコントロールのダウンロードダイアログ
図13 ActiveXコントロールのダウンロードダイアログ
ワンポイントテクニック [Detail]セクションの高さを微調整しよう

 ActiveReportsの帳票定義では、区切り線などのY1プロパティやY2プロパティの値は0.01単位ですが、[Detail]セクションの高さは0.001単位で指定ができます。そのため、仮に明細一覧の項目の高さを0.19とし、区切り線の縦方向の位置も0.19とした場合に、区切り線がうまくプレビュー表示されなかったり、印刷されなかったりすることがあります。その場合、[Detail]セクションの高さを0.191とすれば、罫線に隙間があいたり印刷されなかったりせず、プレビュー表示と印刷ができます。

図14 セクション高の調整結果
図14 セクション高の調整結果

製品情報

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

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

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

この記事をシェア

  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/4026 2009/06/12 15:00

イベント

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

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

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

メールバックナンバー

アクセスランキング

アクセスランキング