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

VBA Workbook_BeforePrint in Excel — Block a Print, Stamp a Header, and the Print-Preview Gotcha

|

VBA Workbook_BeforePrint in Excel — Block a Print, Stamp a Header, and the Print-Preview Gotcha

TL;DRWorkbook_BeforePrint is an event Excel fires before anything in the workbook is printed. It hands you one argument, Cancel. Set Cancel = True and the print is called off — so you can validate first (block a print with empty required fields), stamp the print date, or log who printed. Two things surprise everyone: it fires for Print Preview too — not just a real print — so any heavy work here runs every time someone previews; and it fires once for the whole workbook, not once per sheet. The code lives in ThisWorkbook.

' Lives in ThisWorkbook - NOT a Module, NOT a sheet.
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          ' <-- block the print; nothing goes to the printer
    End If
End Sub

Most macros run when a user asks — a button, a shortcut. Workbook_BeforePrint runs when a user tries to print, and it runs before the job reaches the printer. That timing is exactly what you want for last-second checks and stamps: confirm the sheet is print-ready, write today's date into a header, record who printed and when — or stop the print entirely. It's also an event with two behaviours that catch people out, and both change how you should write the handler.

What you'll learn

  • The mental model — a checkpoint on the print, with a Cancel you can veto with
  • The rule that matters most — Cancel = True blocks the print, and a silent block is a bug
  • The Print-Preview gotcha — the event fires on preview too, so keep it light
  • Why it runs once for the whole workbook, not per sheet — and what to do about headers
  • Where the code has to live, and how it differs from the gesture events

The mental model: a checkpoint on the print, not a print button

Workbook_BeforePrint fires the moment a print is requested — File ▸ Print, Ctrl+P, a macro calling .PrintOut, or opening the Print view — and it fires before Excel sends anything to the printer. It passes a single Boolean by reference, Cancel. You never call this Sub; Excel calls you, waits, then looks at Cancel. Leave it alone and the print proceeds. Set Cancel = True and Excel abandons the print.

So the handler is a checkpoint on the way to the printer, not a print button you press. That's the same shape as its guard-event cousins Workbook_BeforeClose and Workbook_BeforeSave: each hands you a Cancel you can set to veto the action Excel is about to take. Read BeforePrint as "tell me when someone prints" and you miss the veto that makes it worth wiring up. And like both of those, its code lives in the ThisWorkbook module — double-click ThisWorkbook under "Microsoft Excel Objects" in the Project Explorer. In a standard Module it never fires.

The rule that matters most: Cancel = True blocks it — but say why

Blocking a print is the whole reason many people reach for this event — validate before printing, or stop a draft from going out. The mechanism is one line, Cancel = True. The mistake is doing it silently:

Private Sub Workbook_BeforePrint(Cancel As Boolean)
    If Sheets("Invoice").Range("Approved").Value <> "Yes" Then Cancel = True  ' blocks... silently
End Sub

The user presses Ctrl+P, nothing prints, and there's no explanation. They assume the printer is broken, or that Excel is broken, and they file a bug against you. A silent Cancel is a bug, not a feature. When you block a print, tell the user why, so the non-event is obviously your decision:

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

Now the block is legible: the user knows exactly what to fix. The same courtesy applies to Workbook_BeforeSave — any veto a user didn't expect needs a sentence explaining it.

The gotcha that ruins performance: Print Preview fires it too

Here is the behaviour that turns a helpful handler into a sluggish one. Workbook_BeforePrint fires not only on a real print but also when the user opens Print Preview (the Print view in Backstage). So if your handler does something expensive — refresh a data connection, run a web query, recalculate a huge model, rebuild a sheet — that work runs every time someone previews, and preview is something people do casually and often. The sheet crawls, and nobody connects the lag to a print event.

The rule: keep BeforePrint light. Validation, a date stamp, a log line — fine. Heavy refreshes belong on an explicit action (a "Refresh" button) or a scheduled task, not on an event that fires on every glance at the print preview. If you genuinely must do heavy work only for a real print, there's no clean, universal way to distinguish preview from print inside the classic event — which is itself the strongest argument for not putting heavy work here at all.

The rule about scope: it runs once for the whole workbook

Workbook_BeforePrint fires once per print request for the entire workbook — not once per sheet, and it does not tell you which sheet is printing. There is no Worksheet_BeforePrint. So any "set this up before printing" logic has to treat the workbook as the unit. If you want a print stamp on every sheet, loop the sheets yourself, or — better — use a PageSetup header that Excel maintains for you:

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

Prefer PageSetup headers and footers — &D for date, &P for page number, &F for file name — over writing the stamp into a cell. A footer is print-only and self-cleaning; a cell you mutate for printing has to be restored afterwards, and since there's no reliable "AfterPrint" partner event and preview fires this handler too, that restoration is fiddly to get right. Let Excel own the paper; keep your cells for data.

The distinction that frames the cluster: no Target, workbook-only

The two gesture events in this family — Worksheet_BeforeDoubleClick and Worksheet_BeforeRightClick — are per-sheet and hand you a Target cell, because a click happens somewhere specific. Workbook_BeforePrint is the opposite: workbook-level, no Target, because a print is a whole-document action, not a point on a sheet. What all three share is the Cancel flag — the one argument that turns an event from a notification into a veto. That's the spine of the whole interception family: double-click, right-click, print — each one, Excel asks your code "should I go ahead?" and your Cancel is the answer.

How ExcelMaster helps

A print guard is a few small decisions that are easy to get subtly wrong: the Cancel = True that blocks the job, the MsgBox that stops the block being mysterious, the discipline of keeping the handler light so preview stays fast, and using PageSetup instead of mutating cells.

ExcelMaster lets you describe the behaviour instead. Say "before printing, if the invoice isn't approved, block the print and tell the user, and stamp the print date in every sheet's footer," and it writes a Workbook_BeforePrint in ThisWorkbook that sets Cancel, explains the block, and uses PageSetup for the stamp. You keep the workbook and the code; you skip learning the preview gotcha the hard way.

Frequently asked questions

How do I run a macro before printing in Excel?

Put a Workbook_BeforePrint procedure in the ThisWorkbook module: Private Sub Workbook_BeforePrint(Cancel As Boolean). Excel calls it before any print of the workbook. Add your setup or checks there. It does not fire from a standard Module.

How do I stop a workbook from printing in VBA?

Set Cancel = True inside Workbook_BeforePrint. Excel checks Cancel after your handler runs, and True blocks the print. Always show a MsgBox explaining why — a silent block looks like a broken printer to the user.

Does Workbook_BeforePrint fire on Print Preview?

Yes. Opening the Print/Print Preview view triggers it, not just a real print. That's why you should keep the handler light — any heavy refresh or recalculation would run every time someone previews.

Is there a Worksheet_BeforePrint event for a single sheet?

No. BeforePrint is workbook-level only and fires once for the whole workbook, without telling you which sheet prints. To act per sheet, loop ThisWorkbook.Worksheets inside the handler, or set each sheet's PageSetup header/footer.

How do I add the print date to every page?

Use a PageSetup footer with the date, either the built-in &D code or Format(Date, "yyyy-mm-dd"), applied to each sheet in Workbook_BeforePrint. A footer is print-only and doesn't need cleaning up, unlike a value written into a cell.

Tested in

Tested in: Excel 365 (Windows 11), VBA 7.1 — last verified 2026-08-06.

Related guides: VBA Worksheet_BeforeDoubleClick · VBA Worksheet_BeforeRightClick · VBA Workbook_BeforeSave · VBA Workbook_BeforeClose · VBA Workbook_Open