V4 V5

The sheet-wide state lives in SheetProtection (ws.Protection). Protection has the same two layers as Excel. Every cell is locked by default, and that lock only bites once the sheet is protected. So building an input form goes: unlock the ranges people should fill in, then protect the sheet.

The basic shape

using unvell.ReoGrid.Core;
using unvell.ReoGrid.Core.Protection;

// every cell is locked by default, so open the ones meant for input first
ws.SetRangeLock(RangePosition.Parse("B2:B100"), LockState.Unlocked);

// only now does the lock bite
ws.Protect();

// with a password (it guards Unprotect and nothing else — see the note below)
ws.Protect("template");

Lock states live in a range-scoped side table (ProtectionTable), so unlocking a whole column is one entry and writes nothing to any cell. Opening a column of a million rows costs the same as opening one cell.

Lookups subtract rectangles newest-first, which means the most recent setting wins and no per-cell loop is ever needed.

The permission flags

// what stays permitted while protection is on (Excel's "Protect Sheet" checkboxes)
ws.Protection.AllowSort = true;
ws.Protection.AllowAutoFilter = true;
ws.Protection.AllowFormatCells = true;

// forbid selection itself (both default to true, meaning selection is allowed)
ws.Protection.AllowSelectLockedCells = false;

ws.Protect();
PropertyDefaultWhat it permits while protection is on
AllowSelectLockedCellstrueSelecting locked cells
AllowSelectUnlockedCellstrueSelecting unlocked cells
AllowFormatCellsfalseChanging cell formatting
AllowFormatRows / AllowFormatColumnsfalseChanging row heights and column widths
AllowInsertRows / AllowInsertColumnsfalseInserting rows and columns
AllowDeleteRows / AllowDeleteColumnsfalseDeleting rows and columns
AllowSortfalseSorting
AllowAutoFilterfalseUsing the auto-filter

Note that the two selection flags default the other way round. OOXML states these as prohibitions (insertRows="1" means “inserting rows is blocked”) while V5 states them as permissions, so every flag is inverted on the way in and out. The selection pair starts out “not prohibited”, which inverts to true. Getting this backwards silently produces a sheet nobody can click on.

Asking about the state

bool on = ws.IsProtected;

// whether the cell is marked locked (answered even while protection is off)
bool locked = ws.IsCellLocked(1, 1);

// whether it can actually be edited right now: unprotected, or not locked
bool editable = ws.IsCellEditable(1, 1);

// a whole range, answered with rectangle arithmetic rather than a per-cell loop
bool rangeOk = ws.IsRangeEditable(RangePosition.Parse("B2:B100"));

_ = (on, locked, editable, rangeOk);

IsCellLocked reports the lock marking itself and answers even while protection is off. To know whether a cell can be edited right now, use IsCellEditable.

Catching a refusal

ws.EditRefused += (_, e) =>
{
	// e.Reason is Cells / Axis / Structure
	// e.Message carries the same wording Excel uses
	_ = $"{e.Reason}: {e.Message} ({e.Range?.ToAddress()})";

	// set Handled once you have explained it yourself; the control then skips its own alert
	e.Handled = true;
};

ProtectionRefusedEventArgs.Reason (a ProtectionRefusal) has three values.

ValueWhat was refused
CellsThe target cells are locked
AxisFormatting rows or columns is not permitted
StructureInserting or deleting rows or columns is not permitted

Message carries the same wording Excel uses. Set Handled once you have explained it yourself and the control skips its own alert. The edit is refused either way — protection is not negotiable from here. WinForms and WPF show a message box; Avalonia has no standard modal, so it draws a bubble inside the grid.

Lifting it

// a wrong password returns false and leaves the sheet protected
bool ok = ws.Unprotect("template");
_ = ok;

// drop the lock overrides themselves (independent of the protection switch)
ws.LockStates.Clear();

What protection actually stops

User actions, and only those. The chokepoints are the paths that go through the edit recorder, plus the RequestEdit call a control makes before opening an editor. The model API stays open, so a file loader or your own host code can still write to a protected sheet. That is the same line Excel draws with UserInterfaceOnly.

About the password

The password on Protect(password) is Excel’s 16-bit verifier (<sheetProtection password="…">). It is not encryption — collisions are trivial to produce, and Excel treats it accordingly.

V5 does no more with it than guard Unprotect. Do not use it to withhold data: anyone who can open the file can read the cells. It exists so a workbook authored in Excel round-trips looking the way it was authored.

Undo and I/O

  • Changes to lock states are captured in the sheet snapshot.
  • reogrid-json — saved as the sheet’s protection. enabled and the range overrides match reogrid-web; password and allow are V5 extensions.
  • XLSX — read and written as <sheetProtection>. The per-cell locks live in the cell format in OOXML (<xf><protection locked="0"/>), so they travel through the styles and are folded back into the range table on load.

Try it in Studio

Review ▸ Protect Sheet… / Unprotect Sheet…, and Review ▸ Lock Cells / Unlock Cells.

Was this article helpful?