🚀The world's best VBA AI has evolved. ExcelMaster is now an autonomous Agent.Read more →
Back to Blog

Excel VBA の Workbook_BeforePrint — 印刷をブロックし、ヘッダーを刻み、印刷プレビューの落とし穴

|

Excel VBA の Workbook_BeforePrint — 印刷をブロックし、ヘッダーを刻み、印刷プレビューの落とし穴

TL;DRWorkbook_BeforePrint は、ブック内の何かが印刷される前に Excel が 発火させるイベントです。渡される引数はただ 1 つ、Cancel です。Cancel = True にすると印刷は取りやめになります — だから、まず検証したり(必須項目が空なら印刷を ブロックする)、印刷日を刻んだり、誰が印刷したかを記録したりできます。誰もが驚く点が 2 つあります:これは実際の印刷だけでなく印刷プレビューでも発火するので、ここでの 重い処理は、誰かがプレビューするたびに走ります。そして、シートごとに一度ではなく、 ブック全体で一度だけ発火します。コードは ThisWorkbook に置きます。

' ThisWorkbook に置く — Module でも、シートでもない。
Private Sub Workbook_BeforePrint(Cancel As Boolean)
    If ThisWorkbook.Sheets("Invoice").Range("Total").Value = "" Then
        MsgBox "Enter a Total before printing this invoice.", vbExclamation
        Cancel = True          ' <-- 印刷をブロックする。プリンターには何も送られない
    End If
End Sub

ほとんどのマクロは、ユーザーが求めたときに走ります — ボタン、ショートカット。 Workbook_BeforePrint は、ユーザーが印刷しようとしたときに走り、そしてジョブが プリンターに届くに走ります。そのタイミングこそ、土壇場の確認と刻印にうって つけです:シートが印刷可能な状態かを確かめる、ヘッダーに今日の日付を書く、誰がいつ 印刷したかを記録する — あるいは印刷を丸ごと止める。同時にこれは、人をつまずかせる 2 つの挙動を持つイベントでもあり、その両方が、ハンドラーをどう書くべきかを変えます。

この記事で学べること

  • 考え方の軸 — 印刷への関門であり、拒否権を行使できる Cancel を伴う
  • いちばん大事なルール — Cancel = True が印刷をブロックし、無言のブロックはバグ
  • 印刷プレビューの落とし穴 — このイベントはプレビューでも発火するので、軽く保つ
  • シートごとではなくブック全体で一度だけ走る理由 — そしてヘッダーはどうするか
  • コードを置かねばならない場所、そしてそれが仕草イベントとどう違うか

考え方の軸:印刷ボタンではなく、印刷への関門

Workbook_BeforePrint は、印刷が要求された瞬間に発火します — ファイル ▸ 印刷、 Ctrl+P.PrintOut を呼ぶマクロ、あるいは印刷ビューを開いたとき — そして Excel が 何かをプリンターに送るに発火します。渡すのは、参照渡しの Boolean を 1 つ、 Cancel です。この Sub をあなたが呼ぶことはありません。Excel があなたを呼び、待ち、 それから Cancel を見ます。放っておけば印刷は進みます。Cancel = True にすれば、 Excel は印刷を放棄します。

つまりハンドラーは、あなたが押す印刷ボタンではなく、プリンターへ向かう途中の関門 です。これは、門番イベントのいとこである Workbook_BeforeCloseWorkbook_BeforeSave と同じかたちです:どれもが、 Excel がまさに取ろうとしている動作に拒否権を投じられる Cancel を手渡します。 BeforePrint を「誰かが印刷したら教えて」と読んでしまうと、配線する値打ちを生むその 拒否権を取り逃がします。そしてこの 2 つと同じく、そのコードは ThisWorkbook モジュールに置かれます — プロジェクトエクスプローラーの「Microsoft Excel Objects」の下で ThisWorkbook をダブルクリックします。標準の Module では決して発火しません。

いちばん大事なルール:Cancel = True がブロックする — ただし理由を伝えて

印刷をブロックすることは、多くの人がこのイベントに手を伸ばす理由そのものです — 印刷前に 検証する、あるいは草案が出ていくのを止める。仕組みは 1 行、Cancel = True です。 間違いは、それを無言でやることです:

Private Sub Workbook_BeforePrint(Cancel As Boolean)
    If Sheets("Invoice").Range("Approved").Value <> "Yes" Then Cancel = True  ' ブロックする…無言で
End Sub

ユーザーが Ctrl+P を押すと、何も印刷されず、説明もありません。彼らはプリンターが 壊れた、あるいは Excel が壊れたと思い込み、あなたに対してバグを報告します。無言の Cancel は、機能ではなくバグです。 印刷をブロックするなら、その理由をユーザーに 伝えて、何も起こらないことが明らかにあなたの判断だとわかるようにしましょう:

Private Sub Workbook_BeforePrint(Cancel As Boolean)
    If Sheets("Invoice").Range("Approved").Value <> "Yes" Then
        MsgBox "This invoice isn't approved yet - printing is blocked.", vbExclamation
        Cancel = True
    End If
End Sub

これでブロックは読み取れます:ユーザーは、何を直せばよいかを正確に知ります。同じ 心遣いが Workbook_BeforeSave にも当てはまります — ユーザーが予期しなかった拒否権には、どれも、それを説明する一文が要ります。

パフォーマンスを台無しにする落とし穴:印刷プレビューでも発火する

これが、役に立つハンドラーをのろまなものに変える挙動です。Workbook_BeforePrint は、 実際の印刷のときだけでなく、ユーザーが印刷プレビュー(Backstage の印刷ビュー)を 開いたときにも発火します。だからハンドラーが何か重いこと — データ接続をリフレッシュ する、Web クエリを走らせる、巨大なモデルを再計算する、シートを組み直す — をすれば、その 処理は誰かがプレビューするたびに走ります。そしてプレビューは、人々が気軽に、しょっ ちゅう行うものです。シートは這うように遅くなり、しかも誰も、その遅延を印刷イベントに 結びつけません。

ルールはこうです:BeforePrint は軽く保つ。 検証、日付の刻印、ログの 1 行 — けっこうです。重いリフレッシュは、明示的なアクション(「更新」ボタン)や時間起動の タスクに属するものであって、印刷プレビューをちらっと見るたびに発火するイベントには 属しません。もし本当に、実際の印刷のときにだけ重い処理をせねばならないとしても、この 古典的なイベントの中で、プレビューと印刷とをきれいに、普遍的に見分ける方法はありません — そのこと自体が、そもそもここに重い処理を置かないことの、いちばん強い論拠です。

スコープについてのルール:ブック全体で一度だけ走る

Workbook_BeforePrint は、ブック全体に対して、印刷要求ごとに一度発火します — シートごとに一度ではなく、しかもどのシートが印刷されるのかを教えてくれません。 Worksheet_BeforePrint は存在しません。だから、「印刷の前にこれを整える」ロジックは、 ブックを単位として扱わねばなりません。すべてのシートに印刷の刻印がほしいなら、自分で シートをループするか、あるいは — もっと良いのは — Excel が代わりに保守してくれる PageSetup のヘッダーを使うことです:

Private Sub Workbook_BeforePrint(Cancel As Boolean)
    Dim ws As Worksheet
    For Each ws In ThisWorkbook.Worksheets
        ws.PageSetup.LeftFooter = "Printed " & Format(Date, "yyyy-mm-dd")
    Next ws
End Sub

刻印をセルに書き込むよりも、PageSetup のヘッダーとフッター — 日付には &D、ページ 番号には &P、ファイル名には &F — を優先しましょう。フッターは印刷専用で、ひとりでに 片づきます。印刷のために書き換えたセルは、あとで元に戻さねばならず、しかも信頼できる 「AfterPrint」の相方イベントはなく、プレビューもこのハンドラーを発火させるので、その 復元は正しくやるのが厄介です。紙は Excel に持たせ、あなたのセルはデータのために取って おきましょう。

クラスターを枠づける区別:Target なし、ブック専用

この一族にある 2 つの仕草イベント — Worksheet_BeforeDoubleClickWorksheet_BeforeRightClick — はシートごとで、 Target セルを手渡します。クリックはどこか特定の場所で起こるからです。 Workbook_BeforePrint はその逆です:ブックレベルで、Target はありません。印刷は、 シート上の一点ではなく、文書全体の動作だからです。3 つすべてが共有するのは Cancel フラグ — イベントを通知から拒否権へと変える、あの 1 つの引数です。それが、横取り一族 まるごとの背骨です:ダブルクリック、右クリック、印刷 — そのどれについても、Excel は あなたのコードに「このまま進めていい?」と尋ね、あなたの Cancel が答えになります。

ExcelMaster の活用

印刷のガードは、微妙に間違えやすいいくつかの小さな判断です:ジョブをブロックする Cancel = True、ブロックが謎めかないようにする MsgBox、プレビューを速く保つために ハンドラーを軽く保つ規律、そしてセルを書き換える代わりに PageSetup を使うこと。

ExcelMaster なら、代わりに振る舞いを述べられます。「印刷の前に、請求書が承認されていなければ、印刷を ブロックしてユーザーに伝えて。そして印刷日を各シートのフッターに刻んで」と言えば、 Cancel を設定し、ブロックを説明し、刻印に PageSetup を使う Workbook_BeforePrintThisWorkbook に書きます。ブックもコードもあなたのもの。プレビューの落とし穴を痛い目で 学ぶ部分だけを飛ばせるのです。

よくある質問

Excel で印刷の前にマクロを走らせるには?

ThisWorkbook モジュールに Workbook_BeforePrint プロシージャを置きます: Private Sub Workbook_BeforePrint(Cancel As Boolean)。ブックのどの印刷の前にも Excel が これを呼びます。準備や確認はそこに足します。標準の Module からは発火しません。

VBA でブックの印刷を止めるには?

Workbook_BeforePrint の中で Cancel = True を設定します。Excel はハンドラーが走った あとに Cancel を調べ、True なら印刷をブロックします。理由を説明する MsgBox を必ず 出しましょう — 無言のブロックは、ユーザーには壊れたプリンターのように見えます。

Workbook_BeforePrint は印刷プレビューでも発火しますか?

はい。実際の印刷だけでなく、印刷/印刷プレビューのビューを開くことがこれを引き起こし ます。だからハンドラーは軽く保つべきです — 重いリフレッシュや再計算は、誰かがプレビュー するたびに走ってしまいます。

1 枚のシート用に Worksheet_BeforePrint イベントはありますか?

いいえ。BeforePrint はブックレベル専用で、ブック全体に対して一度だけ発火し、どの シートが印刷されるかは教えてくれません。シートごとに動くには、ハンドラーの中で ThisWorkbook.Worksheets をループするか、各シートの PageSetup のヘッダー/フッターを 設定します。

印刷日をすべてのページに入れるには?

日付を入れた PageSetup のフッターを使います。組み込みの &D コードか、 Format(Date, "yyyy-mm-dd") のどちらかを、Workbook_BeforePrint の中で各シートに適用 します。フッターは印刷専用で、セルに書き込んだ値と違って、後片づけが要りません。

検証環境

検証環境: Excel 365 (Windows 11), VBA 7.1 — 最終確認 2026-08-06。

関連ガイド: VBA Worksheet_BeforeDoubleClick · VBA Worksheet_BeforeRightClick · VBA Workbook_BeforeSave · VBA Workbook_BeforeClose · VBA Workbook_Open