ACCESS にて、印刷ダイアログ1回で複数のレポートを印刷したい

検索すると、質問としてはかなりの数が発見できるが、満足のいく回答はみあたらない。というわけで、Access VBA 初心者っぽく作ってみました。

  • レポート RptA, RptB, RptC, ... と不特定にある
  • UI には「個々のレポートの印刷ボタン」と「すべて印刷ボタン」がある。
  • 実稼働環境では、プリンタ P1, P2, P3, ... があり、P1 がデフォルトプリンタである
  • レポートは、任意のプリンタから出力できるようにプリンタが選択できる。
  • 「すべて印刷ボタン」を押すと選択した1つのプリンタから複数のレポートすべてが出力される。

「同時に印刷したいレポートを、すべてサブレポートとして参照したレポートを作成する」という手法は、設計可能なレポートサイズが用紙サイズの2倍までという制限から、多くの場合は採用できない。というわけで、VBA のコーディングで頑張る方向になる。

まずは、印刷ボタン→印刷ダイアログ→印刷という処理。

Const PrintSuccess      = 0
Const PrintCancel       = 1
Const PrintErrorOpen    = 4
Const PrintErrorUnknown = 15

' TargetReport : 印刷対象のレポート
' PrintFilter  : 印刷対象のフィルタ
' ShowPreview  : プレビューを表示するかどうか
Public Function PrintOne(TargetReport As String, PrintFilter as String, Optional ShowPreview as Boolean = True) As Integer
    PrintOne = PrintErrorUnknown

    ' レポートを開く
    On Error Goto CatchOpenReportException
    DoCmd.OpenReport TargetReport, acViewPreview, , PrintFilter, IIf(ShowPreview, acWindowNormal, acHidden)

    ' プレビューありの場合、フォーマット&レンダリングが完了するのを待つ
    If ShowPreview Then DoEvents

    ' 印刷ダイアログを呼び出す
    On Error Goto CatchCancelPrintingException
    DoCmd.SelectObject acReport, TargetReport, False
    DoCmd.RunCommand acCmdPrint

    PrintOne = PrintSuccess

ExitPrintOne:
    ' 終了処理
    DoCmd.Close acReport, TargetReport, acSaveYes
    Exit Sub

CatchOpenReportException:
    ' レポートの初期化中および関連イベントのどこかで例外が発生したり、
    ' イベントハンドラが Cancel = True とした場合に、この例外ハンドラに到達する。
    '     例えば NoData イベントで Cancel = True としておくと、フィルタ結果が0件に
    '     なった場合に、このエラーハンドラに飛びます。
    PrintOne = PrintErrorOpen
    Goto ExitPrintOne

CatchCancelPrintingException:
    ' 印刷ダイアログでキャンセルボタンを押した
    PrintOne = PrintCancel
    Goto ExitPrintOne
End Sub

続いて、すべて印刷ボタン→印刷ダイアログ→印刷→印刷→印刷...

' TargetReports : 印刷対象のレポートの配列
' PrintFilter   : 印刷対象のフィルタ
Public Function PrintAll(TargetReports() As String, PrintFilter as String) As Integer
    PrintAll = PrintErrorUnknown

    Dim ub as Integer
    Dim i as Integer
    ub = LBound(TargetReports)

    ' レポートを非表示で開く
    On Error Goto CatchOpenReportException
    DoCmd.OpenReport TargetReports(ub), acViewPreview, , PrintFilter, acHidden

    ' 印刷ダイアログを呼び出す
    On Error Goto CatchCancelPrintingException
    DoCmd.SelectObject acReport, TargetReports(ub), False
    DoCmd.RunCommand acCmdPrint

    On Error Goto 0
    Dim rpt As Report
    Dim prt As String
    Dim dm As String

    ' 印刷に使用したプリンタを取得する
    Set rpt = Reports(TargetReports(ub))   ' 直接 Reports().Printer では取得できない
    prt = rpt.Printer.DeviceName           ' なぜだろうか?
    dm  = rpt.Printer.PrtDevMode

    ' 残りのレポートを印刷する
    For i = LBound(TargetReports) To UBound(TargetReports)
      If Not i = ub Then
        ' レポートを非表示で開く
        On Error Goto CatchOpenReportException
        DoCmd.OpenReport TargetReports(i), acViewPreview, , PrintFilter, acHidden
        
        ' プリンタを設定する
        Set rpt = Reports(TargetReports(i))
        rpt.Printer = Application.Printers(prt)
        rpt.PrtDevMode = dm

        ' 印刷ダイアログなしで印刷
        On Error Goto ExitPrintAll
        DoCmd.OpenReport TargetReports(i), acViewNormal, , PrintFilter
      End If

      ' レポートを閉じる
      DoCmd.Close acReport, TargetReports(i), acSaveYes
    Next i

    PrintAll = PrintSuccess

ExitPrintAll:
    ' 終了処理
    On Error Resume Next
    For i = LBound(TargetReports) To UBound(TargetReports)
      DoCmd.Close acReport, TargetReports(i), acSaveYes
    Next i
    Exit Sub

CatchOpenReportException:
    ' レポートの初期化中および関連イベントのどこかで例外が発生したり、
    ' イベントハンドラが Cancel = True とした場合に、この例外ハンドラに到達する。
    '     例えば NoData イベントで Cancel = True としておくと、フィルタ結果が0件に
    '     なった場合に、このエラーハンドラに飛びます。
    PrintAll = i * 16 + PrintErrorOpen
    Goto ExitPrintAll

CatchCancelPrintingException:
    ' 印刷ダイアログでキャンセルボタンを押した
    PrintAll = PrintCancel
    Goto ExitPrintAll
End Sub

実際は、複数レコードを RptA(1) → RptB(1) → RptC(1) → RptA(2) → RptB(2) → RptC(2) といった部単位印刷のサポートも含めたものになっているので、これは適当に hatena 上に直接書いたものである。