Article

【Excel VBA】データがあるシート・ページだけ印刷するマクロ|空白除外と複数条件

この記事で分かること

特定セルにデータがある行・ページだけを Excel VBA で印刷するマクロを、判定 4 パターン別にコピペ可能な完成コードで解説します。



毎月 50 枚あるシートのうち「データが入っている 8 枚だけ」を印刷したい、というケースを Excel VBA で解決します。配布の .xlsm をダウンロードすればコピペで動きます。本記事は シート単位 / ページ単位 / 行単位 / 範囲単位 の 4 視点でマクロを使い分ける構成で、元データを削除しない非破壊判定を標準にしています。

条件付き印刷 4 視点振り分けフローチャート (シート単位 / ページ単位 / 行単位 / 範囲単位)

あなたのケース主に使う API本記事の該当箇所
シート単位50 シート中 8 シートだけ印刷Worksheets(Array(...)).PrintOuth2-1 / h2-4-1
ページ単位1 シート 5 ページ中 3 ページだけ印刷PrintOut From / To + HPageBreaksh2-4-2
行単位30 行中 8 行だけ印刷SpecialCells / PrintArea 動的設定h2-4-5
範囲単位表のサイズが変わる範囲を印刷PrintArea = "A1:D" & lastRowh2-4-5
Excel マクロ有効ブック (.xlsm) / 約 34.4 KB / Excel 2024 build 19929 で動作確認。8 シート構成で、プレビュー / PDF 出力 / 確認付き印刷 / 復元マクロの 4 つのボタンが用意されています。
  1. マクロの完成コード — シート単位(最短解)
    1. 3 段ガードの意味
  2. マクロの実行手順
  3. コードの解説と、判定セルの設計
    1. このコードは ThisWorkbook 前提です
    2. エラー値セル(#N/A 等)の扱い
    3. 判定セルの設計(4 パターン早見表)
    4. 用途別の推奨 VBA 式(IsError 除外後の前提)
    5. PrintOut の引数 9 個
    6. PrintOut の戻り値について
  4. 応用 1: シート単位の判定(配列収集 1 回 PrintOut)
    1. 配列収集 1 回 vs 個別 N 回の速度差
  5. 応用 2: ページ単位の判定(PrintOut From/To と HPageBreaks)
    1. From / To の事前検証(必須)
    2. HPageBreaks の挙動(auto vs user-defined)
  6. 応用 3: 印刷前のプレビュー表示
  7. 応用 4: 印刷ダイアログを表示する
  8. 応用 5: 動的に印刷範囲を変える
    1. 空シート判定の罠
  9. 応用 6: 非表示シートの扱い(3 方式)
    1. 方式 A: 直接 Export しようとすると失敗する
    2. 方式 B: Workbook 経由なら自動除外
    3. 方式 C: 一時的に表示してから出力、終了後に復元
    4. 「対象外シートを Hidden にして除外」設計の落とし穴
  10. 応用 7: 別のブックを対象にする(ActiveWorkbook 版)
  11. 行削除パターンとの違い(非破壊 vs 破壊)
  12. 注意: Microsoft 系仮想プリンタへのファイル出力
  13. FAQ
    1. Q1. Mac 版 Excel / Excel for the web でも動きますか?
    2. Q2. 1 シートに表が複数あり、表ごとに分けて印刷したい
    3. Q3. 印刷の途中で停止したい
    4. Q4. 部数を変えたい
    5. Q5. 印刷したシート名をログに残したい
  14. うまく動かないときの確認ポイント
  15. まとめ

マクロの完成コード — シート単位(最短解)

最短コードはシート単位です。ページ単位は h2-4-2、行 / 範囲単位は h2-4-5 を参照してください。

下の PreviewIfDataExists は、ブック内の 表示中シートのうち、A1 セルにデータがあるシートだけを 1 回の操作で印刷プレビューに表示します。プレビューなので 紙は出ません。プレビューを確認したあと、Excel 画面の上部にある「印刷」ボタンを押すと実印刷に進みます。

Option Explicit

' データがあるシートだけを印刷プレビューに表示します(紙は出ません)。
Sub PreviewIfDataExists()
    Dim sh As Worksheet
    Dim targetNames() As String
    Dim cnt As Long
    Dim v As Variant

    ReDim targetNames(1 To ThisWorkbook.Worksheets.Count)
    cnt = 0

    For Each sh In ThisWorkbook.Worksheets
        If sh.Visible = xlSheetVisible Then
            v = sh.Range("A1").Value
            ' 3 段ガード: エラー値 → 完全空白 → 空文字列
            If Not IsError(v) Then
                If Not IsEmpty(v) Then
                    If Trim(CStr(v)) <> "" Then
                        cnt = cnt + 1
                        targetNames(cnt) = sh.Name
                    End If
                End If
            End If
        End If
    Next sh

    If cnt = 0 Then
        MsgBox "印刷対象のシートがありません。"
        Exit Sub
    End If

    ReDim Preserve targetNames(1 To cnt)
    ThisWorkbook.Worksheets(targetNames).PrintOut Preview:=True
End Sub

同じ判定で実プリンタへ送る PrintIfDataExists と、PDF ファイルに出力する ExportIfDataExists_AsPdf も配布ブックに同梱しています。実印刷版は誤って紙を大量に出す事故を避けるため、実行前に確認ダイアログを挟みます(後述 h2-4-3 参照)。

3 段ガードの意味

判定部分は IsErrorIsEmptyTrim(CStr(...)) の 3 段に分けています。これは 1 行にまとめると VBA で罠を踏むためで、詳細は h2-3 で説明します。

マクロの実行手順

配布のサンプルブック (.xlsm) を開く場合は、上部のボタンを押すだけで動きます。自分のブックに同じマクロを入れる場合は、次の 5 ステップで実行できます。

  1. マクロを使いたい Excel ブックを開きます。
  2. Alt + F11 キーで VBE(Visual Basic Editor、VBA の編集画面)を起動します。
  3. VBE の「挿入」メニューから「標準モジュール」を選んで、空のモジュールを追加します。
  4. 上の PreviewIfDataExists のコードを貼り付けます。
  5. コード内をクリックしてカーソルを置いた状態で F5 キーを押すと実行されます。

VBE 画面では、左側のプロジェクト ウィンドウに「VBAProject (ブック名.xlsm)」が表示され、右側のコード ウィンドウに PreviewIfDataExists 全文が見えている状態が正しく貼り付けできた目安です。F5 キーを押すと、A1 にデータがあるシートが順に判定され、対象が見つかれば印刷プレビューが表示されます。対象が 0 件のときは「印刷対象のシートがありません。」のメッセージボックスが出るだけで、紙は出ません。

初回はプレビュー版の PreviewIfDataExists を使うことを推奨します。判定がうまく行っていない場合、確認ダイアログなしの実印刷版で大量の用紙が出てしまう事故を防げます。

配布ブックには 4 つのボタン(プレビュー表示 / PDF に出力 / 印刷(確認あり) / 復元マクロ)と、判定動作を試せる 8 シートが含まれています。

コードの解説と、判定セルの設計

完成コード PreviewIfDataExists の中で、覚えておくと応用が利く主要な行は次の 5 つです。

意味
For Each sh In ThisWorkbook.Worksheetsマクロが入っているブックの全シートを順に走査。ThisWorkbook は「このコードが書かれているブック」を指します
If sh.Visible = xlSheetVisible Then表示中シートだけを対象にする。非表示 (Hidden / VeryHidden) シートを除外します
v = sh.Range("A1").ValueA1 セルの値を変数に取り出す。Variant 型なのでエラー値や空白も格納可能
If Not IsError(v) Then ... If Not IsEmpty(v) Then ... If Trim(CStr(v)) <> "" Then3 段ガード。エラー値 / 完全空白 / 空白文字列を順に除外
ThisWorkbook.Worksheets(targetNames).PrintOut Preview:=Trueシート名の配列を渡して 1 回でプレビュー表示

このコードは ThisWorkbook 前提です

ThisWorkbook.Worksheets は「このマクロが書かれているブック」を見ます。配布の .xlsm 内で動かす場合は問題ありませんが、PERSONAL.XLSB に登録した個人用マクロから別ブックを印刷したい場合は ActiveWorkbook 版を使う必要があります。具体的なコードは h2-4-7 に掲載しています。

エラー値セル(#N/A 等)の扱い

A1 セルが #N/A#VALUE! といったエラー値の場合、3 段ガードの最初にある IsError(v) で除外しています。これを入れないと、A1 がエラー値のシートも「データあり」と判定されてしまい、誤って印刷対象に入ります。Codex 観点での実機検証(5 シート中 #N/A セル 1 シートを含む構成)で、ガードなしだと印刷対象として数えられることを確認しています。

判定セルの設計(4 パターン早見表)

A1 セルの値が「データあり」かどうかを判定する書き方は何通りもあり、それぞれ拾える条件が異なります。下の表は Excel 2024 build 19929 で実機評価した結果です。T★ 印は直感に反する重要結果で、本文の後段で詳しく説明します。

表の前提: いずれの式も「If Not IsError(v) Then でエラー値を先に除外したあと」の挙動です。#N/A 等を先に除外しないと、ほとんどの判定が型エラー (実行時エラー 13) で止まります。

セル内容VBA 型IsEmpty=""Trim(CStr)=""=0IsNumeric ∧ =0Val()=0
完全空白EmptyTrueTrueTrueTrue★True★True★
“” (空文字)StringFalseTrueTrueFalseFalseTrue
” ” (半角スペース)StringFalseFalseTrueFalseFalseTrue
“ ” (全角スペース U+3000)StringFalseFalseTrue★FalseFalseTrue
0 (数値)DoubleFalseFalseFalseTrueTrueTrue
“0” (文字)StringFalseFalseFalseTrue★True★True
“ABC” (文字)StringFalseFalseFalseFalseFalseTrue★

直感に反する重要結果(T★ 印):

  • 完全空白セルと文字列 “0” は = 0 でどちらも True になります。VBA の暗黙型変換で、Empty も 0 とみなされ、文字列 “0” も数値化されて 0 とみなされるためです。
  • IsNumeric(v) And v = 0 でも、完全空白と文字列 “0” の両方が True になります。VBA の And短絡評価されず両辺が評価されるため、IsNumeric の結果に関係なく v = 0 も評価されます。文字列 “0” を完全に除外したいなら IsEmpty 除外を先に置いて、続けて VarType(v) <> vbString で文字列を弾く段階 If が必要です。
  • Val("ABC") = 0 は True になります。Val は数値変換できない文字列をすべて 0 として返すため、ゼロ判定の用途には向きません。memory に同様の罠(Val("1,200,000") = 1)も整理しています。
  • 全角スペース U+3000 は、Excel 2024 build 19929 の実機では Trim で削除されました。「VBA Trim は半角スペースだけ削除」とよく説明されますが、少なくとも本環境では全角も削除されています。環境差を避けたい場合は Replace(Trim(CStr(v)), ChrW(&H3000), "") で明示的に全角スペースを除去します。Chr(&H3000) は ANSI レンジ外で “0” を返してしまうため、必ず ChrW を使ってください

用途別の推奨 VBA 式(IsError 除外後の前提)

やりたいこと推奨 VBA 式注意点
完全空白セルだけ判定If IsEmpty(v) Then数式 ="" は False(数式の結果は Empty ではない)
完全空白 + 空文字数式の両方段階 If: If IsEmpty(v) Then ... ElseIf v = "" Then ...VBA Or は短絡評価せず両辺評価。#N/A で右辺が型エラーになるため IsError 先除外必須
空白 + 任意の空白文字(半角・全角)If Trim(CStr(v)) = "" Then環境差を避けるなら Replace(..., ChrW(&H3000), "") で全角を明示削除
数値ゼロだけ(文字列 “0” と完全空白を除外)段階 If: If Not IsError(v) ThenIf Not IsEmpty(v) ThenIf VarType(v) <> vbString ThenIf v = 0 Then1 行版 If VarType(v) <> vbString And v = 0 Then は完全空白 Empty も True になるため不適
任意の数値 0(”0″ 含む)If v = 0 Then完全空白も True。先に IsEmpty で除外
エラー値(#N/A 等)を除外If Not IsError(v) Then を全判定の前に挟むこれがないと ="", =0, IsNumeric で実行時エラー 13
部分一致If InStr(CStr(v), "東京") > 0 Then大文字小文字区別、Option Compare Text で挙動変化
前方一致If Left(CStr(v), 1) = "A" Then
パターン一致If v Like "A*社" Thenワイルドカード *(任意の文字列)、?(任意の 1 文字)、英大文字 1 文字を表す文字クラスなどが使える
複数条件 ANDIf A1.Value >= 100000 And B1.Value = "A社" Then短絡評価なし、両辺評価
複数条件 ORIf A1.Value = "完了" Or A1.Value = "確定" Then短絡評価なし

PrintOut の引数 9 個

PrintOut メソッドは引数を 9 個取れます。よく使うものを表にまとめます。

引数意味備考
From印刷を開始するページ番号“Pages” は印刷されたページを指します(シート上の物理ページや行番号ではありません)
To印刷を終了するページ番号From と To は事前に 1 ≦ From ≦ To ≦ 総ページ数 を確認することを推奨
Copies印刷部数省略時は 1 部
PreviewTrue で印刷プレビュー表示プレビュー中はマクロ実行がブロックされます(modal)
ActivePrinter使用するプリンタ名ポート付き正式名(例: “Microsoft Print to PDF on PORTPROMPT:”)との完全一致が必要。不一致時は引数が無視され既定プリンタに流れます
PrintToFileTrue でファイルへ出力Microsoft 系仮想プリンタ(Print to PDF / XPS Document Writer)では PrToFileName を指定しても保存ダイアログが優先されファイル作成されません。ファイル出力は ExportAsFixedFormat 推奨(h2-5-1 参照)
CollateTrue で部単位印刷
PrToFileNamePrintToFile=True の出力先パス同上、Microsoft 系仮想プリンタでは無視
IgnorePrintAreasTrue で PrintArea 無視シート全体を強制印刷

PrintOut の戻り値について

Microsoft Learn 公式では PrintOut の戻り値は Variant となっていますが、実機検証で受け取った値は経路によって挙動が異なります。VBA で戻り値を受けても通常は使い物にならないので、引数の検証や成功・失敗の判定は呼び出し側で実装する必要があります。

応用 1: シート単位の判定(配列収集 1 回 PrintOut)

h2-1 の PreviewIfDataExists も内部でこの仕組みを使っていますが、応用としてもう少し明示的に書いてみます。Worksheets(Array("Sheet2", "Sheet5", "Sheet10")).PrintOut のようにシート名の配列を渡すと、1 回の PrintOut で複数シートをまとめて印刷できます

Sub PrintTargetSheets_Array()
    ' 印刷したいシート名を配列で渡す(実プリンタへ送信されます)
    Dim targetNames As Variant
    targetNames = Array("Sheet2", "Sheet5", "Sheet10")
    ThisWorkbook.Worksheets(targetNames).PrintOut
End Sub

シート判定マクロから動的に配列を組み立てるパターンが、h2-1 の完成コードです。

配列収集 1 回 vs 個別 N 回の速度差

「シートを 1 枚ずつ For Each で回しながら sh.PrintOut を N 回呼ぶ」書き方も動きますが、印刷ジョブの登録処理が N 回走るぶん遅くなります。Microsoft XPS Document Writer を経由した実 .xps ファイル出力ベンチマーク(3 シート)では次の結果でした。

経路所要時間XPS ファイル倍率
Worksheets(Array(...)).PrintOut 1 回7.446 秒1 ファイル(454.4 KB)1.00x
3 シート個別 PrintOut14.885 秒3 ファイル(合計 354.3 KB)2.00x(個別が遅い)

枚数が増えれば差は広がる傾向ですが、ドライバや環境によって倍率は変わるため、本記事では「配列 1 回のほうが 2 倍程度速い場合がある」とだけ覚えておけば十分です。

PDF にまとめたい場合は ExportAsFixedFormat を使いますが、こちらは「選択中のシートだけを 1 ファイルに」する Python COM 経由のショートカット (SelectedSheets.ExportAsFixedFormat) が AttributeError で使えません。PDF 経路では、まず対象外シートを xlSheetHidden にしてから ThisWorkbook.ExportAsFixedFormat を 1 回呼ぶ、という方式(h2-4-6 方式 B / h2-1 の PDF 版)を使います。

応用 2: ページ単位の判定(PrintOut From/To と HPageBreaks)

「1 シートに 5 ページ分のデータがあり、3 ページ目の表だけを印刷したい」という場面では、PrintOutFrom / To 引数を使います。

Sub PrintSpecificPage()
    ' Sheet1 の 3 ページ目だけ印刷
    ThisWorkbook.Worksheets("Sheet1").PrintOut From:=3, To:=3
End Sub

Microsoft Learn の注釈に「From と To の “Pages” は、シートまたはブックのページ全体ではなく、印刷されたページを指します」と記載があります。FitToPages 設定を有効にしていると、HPageBreaks の数と実際に印刷されるページ数が一致しないケースがあるため注意してください(後述)。

From / To の事前検証(必須)

VBA 内で ExportAsFixedFormat の名前付き引数で実機検証したところ、次のような結果でした(8 ページの PDF サンプルで確認)。

呼び出し結果
From:=1, To:=11 ページ PDF 作成
From:=99実行時エラー 1004「印刷する対象がありません。」PDF 未作成
From:=999, To:=999同上のエラー、PDF 未作成
From:=3, To:=1(逆転)エラーにならず 3 ページ PDF 作成(想定外の動作)

「範囲外でも自動で丸めてくれる」ことを期待してはいけません。マクロ側で 1 ≦ From ≦ To ≦ 総ページ数 を事前にチェックするのが安全です。

Sub PrintPageRange_Safe(ws As Worksheet, pageFrom As Long, pageTo As Long)
    Dim totalPages As Long
    ' HPageBreaks の数 + 1 で概算(Zoom=100、FitToPages=False が前提)
    totalPages = ws.HPageBreaks.Count + 1

    ' 範囲チェック: From < 1 / To < From (逆転) / To > totalPages を弾く
    ' 注: From = To は 1 ページ印刷として正常 (例: From:=2, To:=2)
    If pageFrom < 1 Or pageTo < pageFrom Or pageTo > totalPages Then
        MsgBox "ページ指定が不正です(1 ≦ From ≦ To ≦ " & totalPages & ")"
        Exit Sub
    End If

    ws.PrintOut From:=pageFrom, To:=pageTo
End Sub

HPageBreaks の挙動(auto vs user-defined)

Microsoft Learn の HPageBreaks オブジェクトの解説に重要な記述があります。

自動的に決められた印刷範囲の場合、HPageBreaks プロパティは印刷範囲内の改ページにのみ使用できます。
ユーザーが指定した印刷範囲の場合、HPageBreaks プロパティはすべての改ページに使用できます。

つまり「ページ単位で判定したい」場合、PrintArea を "$A$1:$Z$200" のように明示的に設定してから HPageBreaks.Count を読むのが安全です。

実機検証(25 列 × 120 行サンプル)で、PageSetup の組み合わせによる HPB.Count と実 PDF ページ数の関係を比較した結果は次のとおりです。

PageSetup 条件HPBVPB実 PDF ページ関係
PrintArea 未設定 + Zoom=100 + FitToPages=False3212(HPB+1)×(VPB+1) と一致 ✅
PrintArea 明示 + Zoom=10012(auto と異なる)
PrintArea 明示 + FitW=1, FitH=False, Zoom=False122HPB から推測不可 ❌
PrintArea 明示 + FitW=False, FitH=1, Zoom=False101HPB から推測不可 ❌

FitToPages を有効にすると HPB と実 PDF ページ数は一致しません。HPB ベースのページ判定をするなら、Zoom=100 + FitToPages=False を明示してから取得してください。

応用 3: 印刷前のプレビュー表示

印刷ボタンを押す前に画面で確認したい場合は、PrintOut Preview:=True を使います。h2-1 の PreviewIfDataExists はこれを使っています。プレビュー API の単体解説は post 547「印刷プレビューを表示するマクロ・VBAメソッド」で詳述しています。

Preview:=Truemodal(マクロ実行を止めて画面が応答待ちになる)動作になるため、配布用マクロでは「プレビューだけ見せて、印刷は手動で押してもらう」流れがおすすめです。配布のサンプルブックでは PreviewIfDataExists をデフォルトのボタンに割り当てています。

応用 4: 印刷ダイアログを表示する

プリンタ選択やページ範囲指定をユーザーに任せたい場合は、Excel の組み込みダイアログを呼びます。

Sub ShowPrintDialog()
    Application.Dialogs(xlDialogPrint).Show
End Sub

ダイアログ単体の使い方は post 544「印刷ダイアログを表示するマクロ・VBAメソッド」で詳しく書いています。条件付き印刷では、まずシートの絞り込みを VBA でやってから、最後に xlDialogPrint を表示してユーザー確認 → 印刷、という組み合わせも有効です。

応用 5: 動的に印刷範囲を変える

「データの行数が月ごとに変わる」ケースでは、PrintArea を文字列連結で動的に組み立てます。

Sub SetPrintAreaDynamic()
    Dim ws As Worksheet
    Dim lastRow As Long
    Set ws = ActiveSheet
    lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
    ws.PageSetup.PrintArea = "$A$1:$D$" & lastRow
End Sub

名前付き範囲 Print_Area 経由で設定することもできます(仕様の詳細は post 5905「印刷範囲と連動する名前付き範囲『Print_Area』」を参照)。

空シート判定の罠

A 列に値が 1 つもない空シートで Cells(Rows.Count, 1).End(xlUp).Row を呼ぶと、0 ではなく 1 を返します。「lastRow = 0 のとき例外処理」を書いても発動しません。空シートかどうかの判定は次のように書きます。

If WorksheetFunction.CountA(ws.Range("A:A")) = 0 Then
    MsgBox "A 列にデータがありません"
    Exit Sub
End If

また PageSetup.PrintArea = False または PageSetup.PrintArea = "" で「シート全体を印刷対象」に戻せます。両方とも readback は空文字列です。

応用 6: 非表示シートの扱い(3 方式)

判定対象に非表示(Hidden / VeryHidden)シートが含まれている場合、扱い方は 3 通りあります。

方式 A: 直接 Export しようとすると失敗する

Sub TryExportHidden_FailsExample()
    ' このコードは「エラーになる」ことを示すための例です(配布シート 7「エラー再現」に同梱)
    Dim ws As Worksheet
    Set ws = ThisWorkbook.Worksheets("エラー再現")
    ws.Visible = xlSheetHidden

    On Error Resume Next
    ws.ExportAsFixedFormat _
        Type:=xlTypePDF, _
        Filename:=ThisWorkbook.Path & "\hidden_export_should_fail.pdf"

    If Err.Number <> 0 Then
        MsgBox "非表示シートの直接 Export は失敗します。" & vbCrLf & _
               "Err.Number=" & Err.Number, vbExclamation
    End If
    On Error GoTo 0
    ws.Visible = xlSheetVisible
End Sub

実行すると com_error -2147352567(内部 HRESULT -2147024809 = E_INVALIDARG)でエラーになります。xlSheetHidden / xlSheetVeryHidden どちらでも同じです。

方式 B: Workbook 経由なら自動除外

Sub ExportAllVisibleSheets_AsOnePdf()
    ' ブック未保存時のガード
    If ThisWorkbook.Path = "" Then
        MsgBox "先にブックを保存してから実行してください。", vbExclamation
        Exit Sub
    End If

    ' 非表示シート / VeryHidden シートは自動的に除外されます
    ThisWorkbook.ExportAsFixedFormat _
        Type:=xlTypePDF, _
        Filename:=ThisWorkbook.Path & "\all_visible_sheets.pdf", _
        OpenAfterPublish:=True
End Sub

Workbook.ExportAsFixedFormat なら非表示シートは自動的に除外され、表示中シートだけが PDF に出力されます。50 シート中 hidden / very hidden 各 1 枚を含むブックで、PDF は 48 ページ出力されることを実機確認済みです。

方式 C: 一時的に表示してから出力、終了後に復元

非表示シートも含めて全部出力したい場合は、一時的に Visible に戻して Export し、終了後に元の Visible 値に戻します。エラー時にも必ず復元が走るよう On Error GoTo CleanUp パターンを使います。

Sub PrintAllSheets_IncludingHidden()
    Dim sh As Worksheet
    Dim originalVisibility As Object

    If ThisWorkbook.Path = "" Then
        MsgBox "先にブックを保存してから実行してください。", vbExclamation
        Exit Sub
    End If

    Set originalVisibility = CreateObject("Scripting.Dictionary")

    ' 1. 元の Visibility を保存
    For Each sh In ThisWorkbook.Worksheets
        originalVisibility(sh.Name) = sh.Visible
    Next sh

    ' 2. すべてを Visible に(On Error は最初の Visible 変更より前に置く)
    On Error GoTo CleanUp
    For Each sh In ThisWorkbook.Worksheets
        If sh.Visible <> xlSheetVisible Then
            sh.Visible = xlSheetVisible
        End If
    Next sh

    ' 3. 全シートを 1 PDF に
    ThisWorkbook.ExportAsFixedFormat _
        Type:=0, _
        Filename:=ThisWorkbook.Path & "\all_sheets_including_hidden.pdf"

CleanUp:
    ' 4. 元の Visibility を復元(成功・失敗どちらでも実行)
    Dim errNum As Long, errDesc As String
    errNum = Err.Number
    errDesc = Err.Description
    On Error Resume Next
    For Each sh In ThisWorkbook.Worksheets
        sh.Visible = originalVisibility(sh.Name)
    Next sh
    On Error GoTo 0
    If errNum <> 0 Then
        MsgBox "Export 中にエラー(番号 " & errNum & ")。シート表示は元に戻しました。", vbExclamation
    End If
End Sub

VeryHidden シートはシートタブの右クリックメニューから戻せません。配布のサンプルブックには救済用の RestoreAllSheetsVisible マクロを同梱しています。

「対象外シートを Hidden にして除外」設計の落とし穴

条件付き PDF 出力(h2-1 の ExportIfDataExists_AsPdf)は、対象外シートを xlSheetHidden にしてから ThisWorkbook.ExportAsFixedFormat を呼ぶ設計です。ただし走査しながら 1 枚ずつ Hidden 化する書き方には罠があります。

全表示シートが対象外(A1 が全部空白など)の場合、最後の表示シートを Hidden にしようとすると Excel は「Worksheet クラスの Visible プロパティを設定できません」(実行時エラー 1004 相当)を出します。これはブック内に少なくとも 1 枚の表示シートが必要、という Excel 自身の制約によるものです。

配布マクロではこれを避けるため、2 パス方式を使っています。1 パス目で対象有無だけ判定し(Visible は触らない)、対象が 0 件なら一切表示状態を変えずに終了します。対象が 1 枚以上あると確定してから 2 パス目で Hidden 化に進みます。

応用 7: 別のブックを対象にする(ActiveWorkbook 版)

マクロを PERSONAL.XLSB(個人用マクロブック)や開発用ブックに登録して、開いている別のブックに対して条件付き印刷を実行したい場合は、ThisWorkbook ではなく ActiveWorkbook を使います。

Option Explicit

' PERSONAL.XLSB / 別ブックの標準モジュールに置き、
' 開いているうちのアクティブなブックに対して条件付き印刷します。
Sub PrintIfDataExists_ForActiveWorkbook()
    Dim wb As Workbook
    Dim sh As Worksheet
    Dim targetNames() As String
    Dim cnt As Long
    Dim answer As VbMsgBoxResult
    Dim v As Variant

    Set wb = ActiveWorkbook
    If wb Is Nothing Then
        MsgBox "アクティブなブックがありません。"
        Exit Sub
    End If

    ReDim targetNames(1 To wb.Worksheets.Count)
    cnt = 0

    For Each sh In wb.Worksheets
        If sh.Visible = xlSheetVisible Then
            v = sh.Range("A1").Value
            ' 3 段ガード(h2-1 と同じ)
            If Not IsError(v) Then
                If Not IsEmpty(v) Then
                    If Trim(CStr(v)) <> "" Then
                        cnt = cnt + 1
                        targetNames(cnt) = sh.Name
                    End If
                End If
            End If
        End If
    Next sh

    If cnt = 0 Then
        MsgBox "印刷対象のシートがありません。"
        Exit Sub
    End If

    ReDim Preserve targetNames(1 To cnt)

    answer = MsgBox(cnt & " 枚を " & Application.ActivePrinter & " に印刷します。よろしいですか?", _
                    vbYesNo + vbQuestion, "印刷の確認")
    If answer <> vbYes Then Exit Sub

    wb.Worksheets(targetNames).PrintOut
End Sub

同じ判定式 + ActiveWorkbook 参照に置き換えるだけで、別ブックに対しても動作します。配布の .xlsm 内で動かす場合は ThisWorkbook 版(h2-1)を使ってください

行削除パターンとの違い(非破壊 vs 破壊)

SNS や検索結果でよく見かける「印刷前に空白行を削除する」方式は、簡単に書けますが 元データを破壊します。月次集計の元帳のようなブックでは、いったん削除すると後でデータを戻せません。本記事の方式は、対象判定だけを行ってシート / 行 / 範囲の選択を変えることで、元データを 1 行も触らずに条件付き印刷を実現します。

注意: Microsoft 系仮想プリンタへのファイル出力

「Microsoft Print to PDF」や「Microsoft XPS Document Writer」に PrintOut で直接ファイル出力したい、というニーズがあります。ただし、これらの仮想プリンタは 本検証環境ではファイル保存ダイアログを優先する仕様になっており、PrintToFile:=True + PrToFileName:=... を指定しても ファイルが作成されませんでした(検証根拠は配布物の `tmp/vba-conditional-print-verify-20260517/result_v1_v4.json` V1-E/F + `result_v7_v8_v12_v13.json` V7、Python COM 経由)。

確実にファイル出力したい場合は ExportAsFixedFormat を使います。

Sub PrintToPdfFile()
    If ThisWorkbook.Path = "" Then
        MsgBox "先にブックを保存してから実行してください。", vbExclamation
        Exit Sub
    End If

    ActiveSheet.ExportAsFixedFormat _
        Type:=xlTypePDF, _
        Filename:=ThisWorkbook.Path & "\output_" & Format(Now, "yyyymmdd_hhnnss") & ".pdf", _
        OpenAfterPublish:=True
End Sub

ActivePrinter 引数指定にも注意が必要です。PrintOut(ActivePrinter:="Microsoft Print to PDF") のように指定しても、ポート付き正式名(例: "Microsoft Print to PDF on PORTPROMPT:")と完全一致しなければ引数は無視され、既定の物理プリンタにジョブが流れます。物理プリンタが接続されていれば実際に紙が出ます。プリンタを切り替えたい場合は、引数指定ではなく Application.ActivePrinter = "正式名" プロパティで設定してから、引数なしで PrintOut を呼ぶのが安全です。

FAQ

Q1. Mac 版 Excel / Excel for the web でも動きますか?

本記事は Windows 版 Excel 2024 (build 19929) で検証しています。Mac 版や Excel for the web では、PrintOut の挙動・ActivePrinter の取り扱い・VBA オブジェクトモデルに差があるため対象外とします。とくに Mac 版は ActivePrinter プロパティ自体が動作しない場合があります。

Q2. 1 シートに表が複数あり、表ごとに分けて印刷したい

各表の範囲を文字列で組み立てて PrintArea に順次セットしながら、PrintOut を表ごとに呼びます。表ごとに別シートに分けるほうが管理が楽な場合も多いです。

Q3. 印刷の途中で停止したい

VBA からは 送信後の印刷ジョブはキャンセルできません。Windows の印刷キュー(設定 → Bluetooth とデバイス → プリンターとスキャナー → 対象のプリンタ → 印刷キューを開く)から手動でキャンセルしてください。マクロ側の対策としては、For Each ループの先頭で g_StopFlag 変数を確認するパターンも有効です。

Q4. 部数を変えたい

PrintOutCopies:=3 のように指定します。ThisWorkbook.Worksheets(targetNames).PrintOut Copies:=3 のように配列指定とも組み合わせ可能です。

Q5. 印刷したシート名をログに残したい

判定マクロの中で Open ThisWorkbook.Path & "\printlog.txt" For Append As #1 でファイルを開き、targetNames の各要素と現在時刻を書き出します。

うまく動かないときの確認ポイント

症状原因と対処
非表示シートで ws.ExportAsFixedFormat がエラーcom_error -2147024809(E_INVALIDARG)。Workbook.ExportAsFixedFormat 経由(方式 B)か、一時的に Visible に戻す方式(方式 C)を使う
PrintArea 未設定時に HPageBreaks.Count が思ったより少ないauto print area は印刷範囲内の改ページしか返さない。PrintArea を明示してから取得
ExportAsFixedFormat From:=99 でエラー実行時エラー 1004「印刷する対象がありません。」。事前に 1 ≦ From ≦ To ≦ 総ページ数 を検証する
From:=3, To:=1(逆転)でエラーにならず想定外のページが出るエラーになりません。マクロ側で From > To の逆転と、To が総ページ数を超える条件を弾く(From:=1, To:=1 のような From=To は正常な 1 ページ印刷)
PrintOut 戻り値が想定の値にならないVBA で受けても通常使い物にならない。引数バリデーションと事前判定は呼び出し側で実装
If Range("A1") = 0 Then が文字列 “0” でも TrueVBA 暗黙型変換。先に IsEmpty 除外、続けて VarType(v) <> vbString で文字列を弾く段階 If
全角スペース U+3000 を除去できないReplace(Trim(CStr(v)), ChrW(&H3000), "") を使う。Chr(&H3000) は ANSI レンジ外で “0” を返してしまう破壊コード
印刷範囲が変更できないシート保護や Apple Numbers 由来の制限が原因の場合あり。post 14761「ページ設定ダイアログボックスで印刷設定が変更できない」を参照
プリンタが選択できない / 既定と違うプリンタに送られるApplication.ActivePrinter でポート付き正式名を確認。引数指定は名前不一致時に silent fail で既定に流れる罠あり
FitToPages 適用時に HPB の数と実 PDF ページ数が一致しないFitToPages 有効時は HPB から物理ページ数を推測しない。Zoom=100 + FitToPages=False 前提で取得
空シートで End(xlUp).Row = 0 を期待してしまう空シートでも 1 を返す。WorksheetFunction.CountA(ws.Range("A:A")) = 0 で判定
VeryHidden シートをシートタブから戻せないUI に出ない仕様。配布の RestoreAllSheetsVisible マクロを実行
A1 が #N/A のシートが印刷対象に数えられる3 段ガードの最初に If Not IsError(v) Then を入れる

まとめ

  • 条件付き印刷は、シート単位 / ページ単位 / 行単位 / 範囲単位の 4 視点で考えます。
  • シート単位の最短解は Worksheets(Array(...)).PrintOut。配列を渡せば 1 回の呼び出しで複数シート出力でき、N 回個別 PrintOut より速い場合があります。
  • 判定セルは IsErrorIsEmptyTrim(CStr(...)) の 3 段ガードが安全。#N/A 等のエラー値を先に除外しないと実行時エラー 13 で止まります。
  • 初回実行は必ず PreviewIfDataExists(プレビュー版)を使い、判定結果を画面で確認してから実印刷に進んでください。
  • 配布の .xlsm には プレビュー / PDF 出力 / 確認付き印刷 / 復元マクロの 4 ボタンが用意されています。

コピペ用の最短コード(再掲):

Sub PreviewIfDataExists()
    Dim sh As Worksheet, targetNames() As String, cnt As Long, v As Variant
    ReDim targetNames(1 To ThisWorkbook.Worksheets.Count)
    For Each sh In ThisWorkbook.Worksheets
        If sh.Visible = xlSheetVisible Then
            v = sh.Range("A1").Value
            If Not IsError(v) Then
                If Not IsEmpty(v) Then
                    If Trim(CStr(v)) <> "" Then
                        cnt = cnt + 1
                        targetNames(cnt) = sh.Name
                    End If
                End If
            End If
        End If
    Next sh
    If cnt = 0 Then MsgBox "印刷対象のシートがありません。": Exit Sub
    ReDim Preserve targetNames(1 To cnt)
    ThisWorkbook.Worksheets(targetNames).PrintOut Preview:=True
End Sub
配布ブックでマクロの動作確認ができます。プレビュー / PDF / 確認付き印刷 / 復元の 4 ボタン付き。

関連記事として、PageSetup プロパティの一括変更を扱う post 32438「全シートの印刷設定(用紙サイズ・向き・余白)を一括変更するマクロ」、印刷プレビュー API の post 547、印刷ダイアログ表示の post 544 も併用すると印刷系マクロの完成度が上がります。

Next Read

このあと読む記事

今の内容に近い記事から、次の1本と補助記事を続けて見つけられるようにしています。

Keep Exploring

このテーマをさらに探す

同じテーマの入口記事と更新記事を、一覧の形でまとめています。

コメント