TL;DR —
ActiveWindow.FreezePanes = Truelocks everything above and to the left of the active cell so it stays put while the rest scrolls. Two things surprise people: it freezes relative to whichever cell is selected, so you must position the active cell first (selectA2to freeze row 1,B2to freeze row 1 and column A); and it lives on the window, not the sheet, which is why it isActiveWindow.FreezePanesand why a second window on the same file has its own freeze. Turn it off with= False, and un-freeze before re-freezing somewhere new.
' FreezePanes freezes everything above and left of the ACTIVE cell.
Application.Goto Range("A2"), Scroll:=True ' freeze the top row: put the active cell BELOW it
ActiveWindow.FreezePanes = True
' Freeze the top row AND the first column: active cell at B2
Worksheets("Report").Activate
Range("B2").Select
ActiveWindow.FreezePanes = True
' Unfreeze:
ActiveWindow.FreezePanes = False
FreezePanes is the code version of View > Freeze Panes — the setting that keeps your header row on
screen when you scroll down a thousand rows of data. It is the one view tweak worth scripting into every
report macro, because it turns a wall of numbers into something someone can actually read. It is also
one of the few VBA properties where you legitimately have to touch the active cell and the active
window, which is exactly why it behaves in ways that catch people out.
What you'll learn
- The mental model — freeze locks a corner of the view, and lives on the window
- The one rule that matters — it freezes relative to the active cell, so position it first
- Why it is
ActiveWindow.FreezePanesand each New Window has its own freeze - Turning it off, and why you must un-freeze before re-freezing
FreezePanesversusSplit, and a select-free path throughSplitRow- Where freeze belongs in a report macro — last, on the sheet you just built
The mental model: locking a corner of the view
Freezing panes does not change the data, the sheet, or a single cell — it changes the window you are
looking through. Picture the grid split into four quadrants by one horizontal and one vertical line. The
top-left quadrant is pinned; the other three scroll under it. FreezePanes draws those lines and pins
the corner. That is the whole idea, and it carries two consequences that explain everything else: the
split has to be positioned somewhere, and it belongs to the window, because a window is what does
the scrolling.
Hold on to "it is a view setting on the window," and the two classic surprises — freezing the wrong rows and the freeze not sticking — stop being mysteries.
The rule that matters most: it freezes at the active cell
Here is the mistake behind almost every "it froze the wrong rows" bug. FreezePanes does not take a
row number or a range. It freezes everything above and to the left of whatever cell is active at the
moment you set it. So the active cell is the control, and if you do not position it, you freeze at
wherever the cursor happened to be:
ActiveWindow.FreezePanes = True ' freezes at the current selection - wherever that is
The rule is to place the active cell just below and to the right of what you want frozen, then set the property:
- Freeze the top row → select
A2(everything above row 2 is frozen). - Freeze the first column → select
B1. - Freeze both the top row and first column → select
B2.
That is why the recorded macro always shows a Range(...).Select right before ActiveWindow.FreezePanes = True. Freezing is one of the rare cases where selecting a cell is not a code smell — the selection
is the argument. Get the active cell right and the freeze lands exactly where you want it.
It lives on the window, not the sheet
Notice the object: ActiveWindow.FreezePanes, never Worksheet.FreezePanes. Freezing is a property of
the window through which you view a sheet — and a sheet can be shown in more than one window. Open
View > New Window on the same workbook and you get a second window with its own freeze state; one
can have the top row frozen and the other not. This is why a freeze can seem "not to stick": you set it
on one window and are looking at another.
For a macro, the practical consequence is that the sheet you want to freeze must be active in the
window you are setting — you cannot reliably freeze the panes of a sheet the user is not looking at.
That makes FreezePanes one of the few places where reaching for ActiveWindow and Activate is
correct rather than lazy: the window is genuinely the thing you are configuring. Activate the sheet,
position the active cell, set the property.
Turning it off and re-freezing
Un-freezing is a plain assignment:
ActiveWindow.FreezePanes = False
The subtlety is re-freezing. Setting FreezePanes = True when panes are already frozen does nothing —
Excel does not move an existing freeze to the new active cell. So to move a freeze, you must clear it
first, reposition the active cell, then set it again:
ActiveWindow.FreezePanes = False ' clear the old freeze
Range("C3").Select ' reposition
ActiveWindow.FreezePanes = True ' freeze at the new corner
Skip the = False line and your "new" freeze is ignored while the old one silently stays. Make
un-freeze the first step whenever a macro re-applies a freeze it may have set on a previous run.
FreezePanes versus Split, and a select-free path
FreezePanes locks the panes solid. ActiveWindow.Split (with SplitRow and SplitColumn) creates
draggable split bars instead — the panes are separated but the user can resize them, and they do not
lock. Most report macros want a freeze, not a split. But the split properties give you a tidy way to
freeze without selecting a cell, which some people prefer for readability:
ActiveWindow.SplitRow = 1 ' split below row 1
ActiveWindow.SplitColumn = 1 ' split after column A
ActiveWindow.FreezePanes = True ' turn the split into a freeze - no Select needed
Setting SplitRow/SplitColumn and then FreezePanes = True freezes at the split position, so you
never touch the active cell. Either style is fine; use whichever reads more clearly to you. The
select-free version avoids leaving the user on a surprise cell after the macro runs.
Where freeze belongs in a report macro
Freeze is a finishing touch, so it goes last — after you have written the data, applied the number
formats, and run AutoFit. Freezing early then inserting or deleting rows can shift
where the freeze lands. The reliable pattern is: build the sheet, size it, activate the sheet you built,
position the active cell (or set the split), then ActiveWindow.FreezePanes = True. Do not trust the
hard-coded Range("A2").Select a recorder wrote on a different sheet layout — decide the freeze corner
from the report you actually generated, and place the active cell there deliberately.
How ExcelMaster helps
Freeze Panes is the setting that makes a generated report usable at a glance and also the one that quietly freezes row 1 through row 40 because the active cell was somewhere unexpected, or seems not to work because it was set on the wrong window, or refuses to move because the old freeze was never cleared.
ExcelMaster lets you say what you
want kept on screen — "freeze the header row," "lock the first two columns and the top row" — and it
writes the freeze as the last step of the routine: it activates the right sheet, positions the active
cell (or sets SplitRow/SplitColumn for a select-free freeze) so the split lands exactly where you
meant, and clears any old freeze before applying a new one. You keep the workbook and the code.
Frequently asked questions
Why does VBA freeze the wrong rows?
Because FreezePanes freezes relative to the active cell, not a row number, and the active cell was
not where you thought. It locks everything above and to the left of the selection. Position the active
cell first — select A2 to freeze the top row, B2 to freeze the top row and first column — then set
ActiveWindow.FreezePanes = True.
How do I freeze the top row in VBA?
Make the active cell A2 (one row below the header), then freeze:
Range("A2").Select followed by ActiveWindow.FreezePanes = True. Or skip the selection entirely with
ActiveWindow.SplitRow = 1 then ActiveWindow.FreezePanes = True. The sheet must be active in the
window you are setting.
How do I unfreeze panes in VBA?
Set the property to False: ActiveWindow.FreezePanes = False. Note that to move a freeze you must
un-freeze first — setting FreezePanes = True while panes are already frozen does nothing, so clear the
old freeze, reposition the active cell, then set it again.
Why is ActiveWindow.FreezePanes not working?
Usually one of three things. The sheet you want to freeze is not active in the current window — activate
it first. Or panes are already frozen, so setting True again is ignored — set False, reposition,
then True. Or you are looking at a different window than the one you set: freeze is a per-window
property, and New Window creates a second window with its own state.
Is FreezePanes on the worksheet or the window?
The window. It is ActiveWindow.FreezePanes, not a worksheet property, because freezing configures how
a window scrolls over a sheet. The same sheet shown in two windows (via View > New Window) can have
different freeze states. That is also why the sheet must be active in the window you are configuring.
Tested in
Tested in: Excel 365 (Windows 11), VBA 7.1 — last verified 2026-09-10.
Related guides: VBA AutoFit · VBA Wrap Text · VBA Worksheets · VBA Column Width · VBA Range
