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:

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:


WrittenHidden #

written-hidden

What the list last saw at one entry's position

Signature:

Private Function WrittenHidden(ByVal index As Long) As Boolean

Parameters:

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:

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:

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:

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:

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:

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:

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:

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:


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:

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:

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:

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:

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:

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:

Throws:


ValidateLayer #

validate-layer

Refuse a layer outside the four values

Signature:

Private Sub ValidateLayer(ByVal layer As Byte)

Parameters:

Throws:


ThrowError #

throw-error

Raise a project error naming this class

Signature:

Private Sub ThrowError(ByVal errNumber As ProjectError, ByVal message As String)

Parameters:


Used in (16 file(s))