ShowHide
Holds the list of variables one worksheet layer offers the user, and the visible or hidden choice for each of them. The list is built from the dictionary. EntryCount, FieldKey, HeaderText, IsHidden, PositionIndex, ControlType and AuthoredVertical read one entry, IndexOf and HasField find one, SetHidden and SetAllOptionalHidden change the choices, SetHiddenInRange and RangeState do the same for a whole span of positions, Apply writes them through a layout, Adopt reads them back from a saved set and ResetToAuthored puts the choices, the sheet and the printed header directions back where the dictionary started them. The class reads the dictionary alone: the worksheet is ShowHideLayout's work and the saved choices are ShowHideStore's.
A SECTION IS A SPAN OF POSITIONS
Hiding a whole section is SetHiddenInRange over the positions SectionMap recorded for it. Nothing about a section is stored here or anywhere else: the choice lands on the member entries, so it travels through the store and through a migration file with no column of its own, and there is no second record that could disagree with the first.
FOUR KINDS OF ENTRY
A variable the dictionary marks as hidden is EXCLUDED. It never enters the list, because the designer said the user must never see it.
Of the variables that do enter, each is one of three kinds:
mandatory listed, always visible, the user cannot change it. locked listed, always hidden, the user cannot change it. A calculated column on a printed sheet or on a CRF lands here, and so does an automatic list: both are named by their CONTROL, never by the "list auto" column, which is about something else entirely. free listed, starts where the dictionary says, follows the user.
IsFree is the single question a click handler asks before it changes anything.
WHICH DICTIONARY COLUMN SAYS WHAT
HList, VList visibility Printed register book CRF crf status
The position of a variable comes from column index, and from crf index on
the CRF layer. A variable with no position is listed, and the layout leaves it
where it is.
THE STATE IS A BOOLEAN
Hidden is True or False. The display words the form shows ("Shown", "Hidden", "Mandatory") are translated at the form and stored nowhere. A user who changes the interface language between two sessions keeps every choice. THE DICTIONARY IS READ IN BLOCKS
The build reads each column it needs as one whole-column block through dict.DataRange and answers every per-variable question from those arrays. Every block spans the same rows (the data body of the dictionary), so one row index reads across all of them. It used to ask LLVariables.Value one cell at a time, two crossings per call, 2V + 14N per build; twelve block reads cost the same whatever V is.
ONLY WHAT DIFFERS IS WRITTEN
Beside hidden() the list keeps written(), the sheet state as Apply last wrote it or Adopt last read it, and stored(), the flags as the store last loaded or saved them. Apply writes the positions where the wanted state differs from written() and reads a position it has never seen before writing it, so a section press writes that section's columns alone. HasChanges compares hidden() with stored(), so a close where nothing moved can skip its save.
Depends on: LLdictionary, ShowHideLayout, BetterArray, Checking, Dictionary column names, The one register book value that asks for a turned header, The dictionary columns the build reads, one block each, row-aligned. A column, the dictionary does not carry is a block of empty strings and its flag is, False, so the build reads the same shape whatever the dictionary holds., What the list knows about one position on the sheet: nothing yet, or the last, state it wrote there or read from it.
Version: 1.4 (2026-09-01)
Instantiation
Create #
create
Build the entry list of one layer of one sheet
Signature:
Public Function Create(ByVal dict As LLdictionary, _
ByVal layer As Byte, _
ByVal sheetName As String) As ShowHide
The factory and the build it runs.
Walks the dictionary once, keeps the variables of the named sheet whose sheet type suits the layer, and files each one as mandatory, locked or free. The dictionary is prepared when it arrives unprepared, because the four columns this class reads (visibility, crf status, column index, crf index) are all written by Prepare.
Parameters:
dict: LLdictionary. The dictionary to read.layer: Byte. A ShowHideWorksheetLayer value. Which of the four layers to build.sheetName: String. The base sheet name, with no print_ or crf_ prefix.
Returns: ShowHide. The entry list, ready to read.
Throws:
- ProjectError.ObjectNotInitialized When the dictionary is Nothing.
- ProjectError.InvalidArgument When the sheet name is empty or the layer is outside the four values.
Accessors
EntryCount #
entry-count
The number of entries the user is offered
Signature:
Public Property Get EntryCount() As Long
What the list holds, and what each entry is.
TargetLayer #
target-layer
The layer this list was built for
Signature:
Public Property Get TargetLayer() As Byte
SheetName #
sheet-name
The base sheet name the entries belong to
Signature:
Public Property Get SheetName() As String
FieldKey #
field-key
The variable name of one entry
Signature:
Public Function FieldKey(ByVal index As Long) As String
Parameters:
index: Long. A one based entry index.
Returns: String. The variable name as the dictionary spells it.
Throws:
- ProjectError.InvalidArgument When the index is out of range.
HeaderText #
header-text
The label the form shows for one entry
Signature:
Public Function HeaderText(ByVal index As Long) As String
Parameters:
index: Long. A one based entry index.
Returns: String. The main label, carrying its sub section for a geo or hf variable.
Throws:
- ProjectError.InvalidArgument When the index is out of range.
IsHidden #
is-hidden
Whether one entry is hidden right now
Signature:
Public Function IsHidden(ByVal index As Long) As Boolean
A mandatory entry answers False and a locked entry answers True, whatever the user asked for. Every other entry answers the stored choice.
Parameters:
index: Long. A one based entry index.
Returns: Boolean. True when the variable is hidden.
Throws:
- ProjectError.InvalidArgument When the index is out of range.
IsMandatory #
is-mandatory
Whether one entry is always visible
Signature:
Public Function IsMandatory(ByVal index As Long) As Boolean
Parameters:
index: Long. A one based entry index.
Returns: Boolean. True when the user cannot hide the variable.
Throws:
- ProjectError.InvalidArgument When the index is out of range.
IsLocked #
is-locked
Whether one entry is always hidden
Signature:
Public Function IsLocked(ByVal index As Long) As Boolean
Parameters:
index: Long. A one based entry index.
Returns: Boolean. True when the variable is held hidden and the user cannot show it.
Throws:
- ProjectError.InvalidArgument When the index is out of range.
IsFree #
is-free
Whether the user may change one entry
Signature:
Public Function IsFree(ByVal index As Long) As Boolean
The single guard a click handler asks. An entry that is neither mandatory nor locked follows whatever the user picks.
Parameters:
index: Long. A one based entry index.
Returns: Boolean. True when the entry follows the user.
Throws:
- ProjectError.InvalidArgument When the index is out of range.
PositionIndex #
position-index
Where one entry sits on the worksheet
Signature:
Public Function PositionIndex(ByVal index As Long) As Long
Parameters:
index: Long. A one based entry index.
Returns: Long. The column or row number, or 0 when the dictionary gives none.
Throws:
- ProjectError.InvalidArgument When the index is out of range.
ControlType #
control-type
The dictionary control of one entry
Signature:
Public Function ControlType(ByVal index As Long) As String
Parameters:
index: Long. A one based entry index.
Returns: String. The control value in lower case, such as "geo1" or "formula".
Throws:
- ProjectError.InvalidArgument When the index is out of range.
AuthoredVertical #
authored-vertical
The header direction the dictionary asked for
Signature:
Public Function AuthoredVertical(ByVal index As Long) As Boolean
Read only, and true for a variable whose register book reads
"print, vertical header". A reset restores this direction without the entry
list ever owning the live orientation of the sheet.
Parameters:
index: Long. A one based entry index.
Returns: Boolean. True when the printed header should be turned.
Throws:
- ProjectError.InvalidArgument When the index is out of range.
Lookup
IndexOf #
index-of
Find an entry by variable name
Signature:
Public Function IndexOf(ByVal fieldKey As String) As Long
Finding an entry by its variable name.
The name is trimmed and lower cased on the way in and on the way out, so a dictionary name carrying a stray space is still reachable.
Parameters:
fieldKey: String. The variable name to look for.
Returns: Long. The one based entry index, or 0 when the name is absent.
HasField #
has-field
Whether a variable name is in the list
Signature:
Public Function HasField(ByVal fieldKey As String) As Boolean
Parameters:
fieldKey: String. The variable name to look for.
Returns: Boolean. True when the list carries the name.
Choices
SetHidden #
set-hidden
Hide or show one entry
Signature:
Public Sub SetHidden(ByVal index As Long, ByVal hidden As Boolean)
Changing what the user sees.
A mandatory or locked entry keeps its state and the call returns quietly, so a caller that loops the whole list needs no guard of its own.
Parameters:
index: Long. A one based entry index.hidden: Boolean. True to hide the variable.
Throws:
- ProjectError.InvalidArgument When the index is out of range.
SetAllOptionalHidden #
set-all-optional-hidden
Hide or show every entry the user owns
Signature:
Public Sub SetAllOptionalHidden(ByVal hidden As Boolean)
Parameters:
hidden: Boolean. True to hide all the free entries.
SetHiddenInRange #
set-hidden-in-range
Hide or show every entry sitting in a span of positions
Signature:
Public Function SetHiddenInRange(ByVal firstPos As Long, _
ByVal lastPos As Long, _
ByVal hidden As Boolean) As Long
What hiding a whole section comes down to. The span is a range of column numbers on an HList sheet and of row numbers on a VList one, which is what SectionMap records for each section.
Hiding a section stores nothing of its own: the choice lands on each member entry, so the store, the show/hide form and a migration file all carry a collapsed section without knowing the word "section". Nothing can drift out of step with anything else, because there is only one record of the choice.
A mandatory or locked entry inside the span keeps its state, as it does everywhere else. So a section holding a mandatory variable never disappears whole, and the count says how many entries actually moved.
Parameters:
firstPos: Long. One end of the span.lastPos: Long. The other end of the span.hidden: Boolean. True to hide the span.
Returns: Long. How many entries the call changed.
RangeState #
range-state
Where a span of positions stands
Signature:
Public Function RangeState(ByVal firstPos As Long, ByVal lastPos As Long) As Byte
Read from the entries the user owns, so a section whose only visible column is a mandatory one still answers Hidden and a caller toggling it offers Show.
Parameters:
firstPos: Long. One end of the span.lastPos: Long. The other end of the span.
Returns: Byte. A ShowHideRangeState value. Empty when no entry sits in the span, Fixed when none of those that do is free, and otherwise Shown, Hidden or Mixed by what the free entries say.
Worksheet
Apply #
apply
Put the worksheet in the state the list describes
Signature:
Public Function Apply(ByVal layout As ShowHideLayout, _
Optional ByVal force As Boolean = False) As Long
The two calls that put the list and a sheet in step.
The one place the entry list reaches a sheet, and it goes through the layout, so the row versus column question and the protection bracket are answered in one place. Entries with no position are skipped.
Only the positions that differ are written. The list remembers what it last wrote to each position and what Adopt last read there; a position it knows nothing about yet is read off the sheet first, which costs a read where a write used to go. So a section press writes the columns of that section alone, and an Apply on a sheet already in step writes nothing. The force flag writes every position whatever the list remembers, for a caller landing on a sheet whose state it cannot trust, such as the migration import.
Parameters:
layout: ShowHideLayout. The sheet to reconcile.force: Optional Boolean. True writes every position. Default False.
Returns: Long. The number of positions that were written.
Adopt #
adopt
Read the worksheet back into the list
Signature:
Public Function Adopt(ByVal layout As ShowHideLayout) As Long
For the case where the user hid a column by hand rather than through the form. Mandatory and locked entries keep what the dictionary said. Every position is read all the same, so the list knows the whole sheet and the next Apply writes only what differs from it.
Parameters:
layout: ShowHideLayout. The sheet to read.
Returns: Long. The number of free entries that were read.
ResetToAuthored #
reset-to-authored
Put the choices and the sheet back where the dictionary started them
Signature:
Public Function ResetToAuthored(ByVal layout As ShowHideLayout) As Long
The reset button of the advanced form comes through here, once per worksheet.
Every free entry goes back to the hidden or visible state the dictionary
authored, the sheet is put in step through Apply, and on a printed sheet each
header direction goes back to what register book asked. Mandatory and locked
entries never left their authored state, so they are covered by the same walk.
Parameters:
layout: ShowHideLayout. The sheet to reconcile, or Nothing to reset the choices alone.
Returns: Long. The number of positions Apply set.
Store
HasChanges #
has-changes
Whether any choice differs from what the store last loaded or saved
Signature:
Public Property Get HasChanges() As Boolean
What the store last saw, so a close with nothing changed saves nothing.
Read before a save. The baseline is set by the build (the authored state, which is what an empty store means), and moved by ShowHideStore.Load and ShowHideStore.Save through MarkStored. Adopt leaves it alone on purpose: a column the user hid by hand is a change the store has not seen yet, and the next save is where it gets recorded.
MarkStored #
mark-stored
Take the choices as they stand for what the store holds
Signature:
Public Sub MarkStored()
ShowHideStore calls it at the end of a Load and of a Save of the current state, so HasChanges answers False until the next choice moves.
Build
Initialise #
initialise
Fill the entry list from the dictionary
Signature:
Public Sub Initialise(ByVal dict As LLdictionary, _
ByVal layer As Byte, _
ByVal sheetName As String)
The dictionary walk the factory runs.
Public because the factory calls it on a fresh instance. The columns the build needs are read as whole blocks first, then every row is read from the arrays, and the entry arrays are trimmed to what matched.
Parameters:
dict: LLdictionary. The dictionary to read.layer: Byte. A ShowHideWorksheetLayer value. The layer being built.sheetName: String. The base sheet name.
Internal members (not exported)
Helpers
EffectiveHidden #
effective-hidden
Work out the state one entry really has
Signature:
Private Function EffectiveHidden(ByVal index As Long) As Boolean
Private workers of the build and the accessors.
Parameters:
index: Long. A one based entry index.
Returns: Boolean. True when the entry is hidden.
RememberWritten #
remember-written
Record what the sheet holds at one entry's position
Signature:
Private Sub RememberWritten(ByVal index As Long, ByVal hidden As Boolean)
Parameters:
index: Long. A one based entry index.hidden: Boolean. The state written to or read from the sheet.
WrittenHidden #
written-hidden
What the list last saw at one entry's position
Signature:
Private Function WrittenHidden(ByVal index As Long) As Boolean
Parameters:
index: Long. A one based entry index.
Returns: Boolean. True when the last write or read found it hidden.
PositionInRange #
position-in-range
Whether one entry sits inside a span of positions
Signature:
Private Function PositionInRange(ByVal position As Long, _
ByVal firstPos As Long, _
ByVal lastPos As Long) As Boolean
The two bounds are read in either order, so a caller that hands them over the
other way round gets the same answer. An entry with no position, which is what
a variable the dictionary gives no column index looks like, is in no span.
Parameters:
position: Long. The entry position.firstPos: Long. One end of the span.lastPos: Long. The other end of the span.
Returns: Boolean. True when the position falls inside the span.
MatchesSheet #
matches-sheet
Whether a variable belongs to the sheet being built
Signature:
Private Function MatchesSheet(ByVal varSheetName As String) As Boolean
Parameters:
varSheetName: String. The variable'ssheet namevalue.
Returns: Boolean. True when the sheet names agree.
MatchesLayer #
matches-layer
Whether a dictionary sheet type suits the layer being built
Signature:
Private Function MatchesLayer(ByVal sheetType As String) As Boolean
The dictionary vocabulary is "vlist1D" and "hlist2D". A printed sheet and a CRF are both derived from an hlist2D sheet, so all three of HList, Printed and CRF test for "hlist". The CRF case used to test for "crf", a word the vocabulary has never held, so every CRF list came back empty.
Parameters:
sheetType: String. The dictionarysheet typevalue, lower cased.
Returns: Boolean. True when the variable belongs on this layer.
StatusForLayer #
status-for-layer
Read the status column that belongs to the layer
Signature:
Private Function StatusForLayer(ByRef blocks As TDictionaryBlocks, _
ByVal row As Long, _
ByVal registerBook As String) As String
Parameters:
blocks: TDictionaryBlocks. The column blocks of the dictionary.row: Long. The dictionary row of the variable, one based.registerBook: String. Theregister bookvalue, already read.
Returns: String. The status text for this layer.
IsMandatoryStatus #
is-mandatory-status
Whether a status makes an entry always visible
Signature:
Private Function IsMandatoryStatus(ByVal layerStatus As String, _
ByVal controlVal As String, _
ByVal layer As Byte) As Boolean
The first geo level of an HList sheet is mandatory whatever its status says, because the geo cascade of every other level reads it.
Parameters:
layerStatus: String. The status of this layer, lower cased.controlVal: String. The control value, lower cased.layer: Byte. A ShowHideWorksheetLayer value. The layer being built.
Returns: Boolean. True when the user cannot hide the entry.
IsLockedStatus #
is-locked-status
Whether a status holds an entry hidden
Signature:
Private Function IsLockedStatus(ByVal layerStatus As String, _
ByVal controlVal As String, _
ByVal layer As Byte, _
ByVal isMandatoryEntry As Boolean) As Boolean
A calculated column and an automatic list have nothing to give on a printed sheet or a CRF, so both layers hold them hidden. A mandatory entry is never locked as well.
Parameters:
layerStatus: String. The status of this layer, lower cased.controlVal: String. The control value, lower cased.layer: Byte. A ShowHideWorksheetLayer value. The layer being built.isMandatoryEntry: Boolean. Whether the entry is already mandatory.
Returns: Boolean. True when the entry is held hidden.
InitialHidden #
initial-hidden
The state a free entry starts in
Signature:
Private Function InitialHidden(ByVal layerStatus As String) As Boolean
The register book vocabulary of a printed sheet is "print, horizontal
header", "print, vertical header" and "hidden". An empty cell means the column
prints, which is why the default answer is visible.
Parameters:
layerStatus: String. The status of this layer, lower cased.
Returns: Boolean. True when the entry starts hidden.
ReadBlocks #
read-blocks
Read every column the build needs as one block each
Signature:
Private Sub ReadBlocks(ByVal dict As LLdictionary, ByRef blocks As TDictionaryBlocks)
The row count comes from the variable name column, and every other block is
read over the same rows, because dict.DataRange spans the data body of the
dictionary whichever column is asked for. A column the dictionary lacks is
read as a block of empty strings with its flag False. variable name is a
required column, so a dictionary that has no such column has no rows to give.
Parameters:
dict: LLdictionary. The dictionary to read.blocks: TDictionaryBlocks. Filled here.
ColumnBlock #
column-block
One dictionary column as a one based array of strings
Signature:
Private Function ColumnBlock(ByVal dict As LLdictionary, _
ByVal colName As String, _
ByVal expectedRows As Long, _
Optional ByRef rowsRead As Long, _
Optional ByVal present As Boolean = True) As Variant
One crossing per column: the range's Value comes back as a 2D array, or as a
single value when the dictionary holds one row, and both are flattened to the
same shape. A column that is absent, or that the read refuses, answers a
block of expectedRows empty strings, so a caller reads it like any other.
Parameters:
dict: LLdictionary. The dictionary to read.colName: String. The column to read.expectedRows: Long. The rows every block has to hold; 0 lets the column decide, which is how the first block sets the count.rowsRead: Long. Set to the number of rows the block holds.present: Optional Boolean. False skips the read and answers the empty block.
Returns: Variant. A one based array of strings.
BlockText #
block-text
One row of a block, empty when the block holds nothing
Signature:
Private Function BlockText(ByRef block As Variant, ByVal row As Long) As String
Parameters:
block: Variant. A block ColumnBlock built.row: Long. The row wanted, one based.
Returns: String. The text at that row.
CellText #
cell-text
A cell value as text, with an error value or an empty read as nothing
Signature:
Private Function CellText(ByVal value As Variant) As String
Parameters:
value: Variant. What the cell answered.
Returns: String. The text.
Normalise #
normalise
Trim and lower case a value for comparison
Signature:
Private Function Normalise(ByVal value As String) As String
Parameters:
value: String. The text to normalise.
Returns: String. The trimmed, lower cased text.
NormaliseKey #
normalise-key
Build the lookup key of a variable name
Signature:
Private Function NormaliseKey(ByVal value As String) As String
The same shape on the way in and on the way out. A dictionary name carrying a trailing space used to be stored untrimmed and looked up trimmed, so its saved state was dropped on every load.
Parameters:
value: String. The variable name.
Returns: String. The lookup key.
EnsureValidIndex #
ensure-valid-index
Refuse an entry index outside the list
Signature:
Private Sub EnsureValidIndex(ByVal index As Long)
Parameters:
index: Long. The index to check.
Throws:
- ProjectError.InvalidArgument When the index is out of range.
ValidateLayer #
validate-layer
Refuse a layer outside the four values
Signature:
Private Sub ValidateLayer(ByVal layer As Byte)
Parameters:
layer: Byte. A ShowHideWorksheetLayer value. The layer to check.
Throws:
- ProjectError.InvalidArgument When the layer is unknown.
ThrowError #
throw-error
Raise a project error naming this class
Signature:
Private Sub ThrowError(ByVal errNumber As ProjectError, ByVal message As String)
Parameters:
errNumber: ProjectError. The error to raise.message: String. What went wrong.
Used in (16 file(s))
- LLExporter.cls
- LLImporter.cls
- LLdictionary.cls
- LLGeo.cls
- Linelist.cls
- SectionMap.cls
- VarWriter.cls
- SectionShowHide.cls
- ShowHideLayout.cls
- ShowHideStore.cls
- EventsLinelistButtons.bas
- FormLogicAdvanced.bas
- TestSectionShowHide.bas
- TestShowHide.bas
- TestShowHideTiming.bas
- TestShowHideTotal.bas