AnaTabIds
Records the charts of the analysis sheets so an export can draw them again on the sheets it creates, and copies the named ranges those charts read. PrepareSheet builds the tracking worksheet, AddGraphInfo records one chart series and AddGraphFormat the look of one chart, TransferNames copies the named ranges onto an output worksheet, and WriteGraphs draws the charts there.
ONE REGISTRY TABLE
The tracking worksheet holds a single ListObject. Every row carries the scope it belongs to and a kind: a series row describes one chart series, a format row describes the look of one chart. The rows of one chart stay together because they are appended in call order, and the reader starts a new chart when the graph identifier changes.
THE TABLE NAMES COME FROM THE WORKBOOK
Every named range of an analysis table was created by CrossTable through Range.Name, so the workbook already holds it. TransferNames reads the workbook name collection, keeps the names that resolve to a range on the analysis sheet, and creates each one at the same address on the output sheet. The collection is walked once per export and sorted by owning worksheet, because the export asks for four sheets in turn.
Analysis scopes. Each one is a sheet of the generated linelist. This class is the leaf that reads the value, so it owns the enum: AnalysisOutput and LLExporter both name it, and the generated linelist carries this class without carrying AnalysisOutput.
A fifth member used to sit here, standing for the tables-only pass. It was a build stage wearing the type of a scope, and four Select Case blocks that route on a scope had no branch for it. AnalysisOutput owns that enum now, under the name AnalysisBuildStage.
Column positions of the registry table. The headers are written from this enum by PrepareSheet, so one place decides both the layout and every read.
Depends on: Graphs
Version: 2026-07-30
Instantiation
Create #
create
Create a new AnaTabIds instance bound to a tracking worksheet
Signature:
Public Function Create(ByVal idsh As Worksheet, _
Optional ByVal check As Boolean = True) As AnaTabIds
Factory method on the predeclared instance. Validates the worksheet reference and, when asked, that the registry table is on the sheet. Pass check as False while the linelist is still being built.
Parameters:
idsh: Worksheet. The worksheet hosting the registry table.check: Optional Boolean. When True, calls CheckRequirements. Defaults to True.
Returns: AnaTabIds. A fully initialised tracking instance.
Throws:
- ProjectError.InvalidArgument When idsh is Nothing.
- ProjectError.InvalidArgument When check is True and the registry table is missing.
PrepareSheet #
prepare-sheet
Build the registry table on a tracking worksheet
Signature:
Public Sub PrepareSheet(ByVal idsh As Worksheet)
Called on the predeclared instance while the linelist is being built. Writes the header row from RegistryColumnIndex and lists it as REGISTRY_TABLE. A sheet that already carries the table is left as it is, so a second build over the same workbook changes nothing.
Parameters:
idsh: Worksheet. The worksheet to prepare.
Throws:
- ProjectError.InvalidArgument When idsh is Nothing.
Graph Tracking
AddGraphInfo #
add-graph-info
Register a graph series row in the registry
Signature:
Public Sub AddGraphInfo(ByVal scope As Byte, _
ByVal graphId As String, _
ByVal seriesName As String, _
ByVal seriesType As String, _
ByVal seriesPos As String, _
ByVal seriesLabel As String, _
ByVal seriesColumnLabel As String, _
ByVal hardCodeLabels As Boolean, _
ByVal outRangeAddress As String, _
Optional ByVal prefix As String = vbNullString, _
Optional ByVal prefixOnly As Boolean = False)
Methods for registering graph series and formatting metadata.
Writes one series row. The rows of one chart have to stay together, and they do because every writer registers the series of a chart one after the other. The two flags are stored as 0 or 1 so they survive a save and reopen.
Parameters:
scope: Byte. AnalysisScope value the chart belongs to.graphId: String. Identifier of the chart receiving this series.seriesName: String. Named range name for the series data.seriesType: String. Chart series type (e.g. "line").seriesPos: String. Axis position indicator for the series.seriesLabel: String. Named range name for category labels.seriesColumnLabel: String. Named range name for column header labels.hardCodeLabels: Boolean. When True, labels are written as literals.outRangeAddress: String. Cell address for chart placement on output.prefix: Optional String. Admin-level prefix for spatial graphs. Defaults to vbNullString.prefixOnly: Optional Boolean. When True, only the prefix is used as the label. Defaults to False.
Throws:
- ProjectError.InvalidArgument When the scope is unknown.
AddGraphFormat #
add-graph-format
Register the look of one chart in the registry
Signature:
Public Sub AddGraphFormat(ByVal scope As Byte, _
ByVal graphId As String, _
ByVal catTitle As String, _
ByVal valuesTitle As String, _
ByVal hardCodeLabels As Boolean, _
Optional ByVal heightFactor As Long = 1, _
Optional ByVal plotTitle As String = vbNullString)
Writes one format row, found again by the graph identifier when the chart has all its series.
Parameters:
scope: Byte. AnalysisScope value the chart belongs to.graphId: String. Identifier of the chart to format.catTitle: String. Category (X) axis title.valuesTitle: String. Value (Y) axis title.hardCodeLabels: Boolean. When True, axis labels are written as literals.heightFactor: Optional Long. Multiplier for chart height. Defaults to 1.plotTitle: Optional String. Chart title. Defaults to vbNullString.
Throws:
- ProjectError.InvalidArgument When the scope is unknown.
Name Transfer
TransferNames #
transfer-names
Recreate every named range of an analysis sheet on the output sheet
Signature:
Public Sub TransferNames(ByVal inpsh As Worksheet, ByVal outsh As Worksheet)
Copying the named ranges of an analysis sheet to the output sheet.
A chart reads its values and its labels through named ranges, so the output sheet needs the same names at the same addresses. The names are read from the workbook collection: every one of them was created on the analysis sheet by the table builder, which is why nothing has to be stored to find them again. A name holding a value has no range behind it, so it is skipped, and so is a name that no longer resolves.
Parameters:
inpsh: Worksheet. The analysis sheet the names live on.outsh: Worksheet. The sheet the names are recreated on.
Throws:
- ProjectError.InvalidArgument When either worksheet is Nothing.
Graph Writing
WriteGraphs #
write-graphs
Recreate the named ranges and the charts of one scope
Signature:
Public Sub WriteGraphs(ByVal outsh As Worksheet, ByVal scope As Byte, _
ByVal inpsh As Worksheet)
Drawing the tracked charts on an output worksheet.
Copies the named ranges of the analysis sheet first, because a chart cannot read a series before its name exists. Then walks the series rows of the scope in registry order and starts a new chart every time the graph identifier changes. Each chart is formatted once it holds all its series.
Parameters:
outsh: Worksheet. The output worksheet receiving the charts.scope: Byte. AnalysisScope value selecting the registry rows.inpsh: Worksheet. The analysis sheet the names are copied from. The caller holds it already, which is why it is passed in.
Remarks:
- Application.GoTo scrolls to the chart position, which some Excel versions need for correct chart placement. It also means the output sheet has to be selectable.
Throws:
- ProjectError.InvalidArgument When a worksheet is Nothing or the scope is unknown.
Internal members (not exported)
Internal Properties
Seal #
seal
Seal the instance against further setup writes.
Signature:
Public Sub Seal()
Factory-support properties for construction and worksheet access.
GuardNotSealed #
guard-not-sealed
Guard a setup setter against post-creation writes.
Signature:
Private Sub GuardNotSealed(ByVal propName As String)
Parameters:
propName: String. The property name for the error message.
Wksh #
wksh-set
Assign the tracking worksheet
Signature:
Public Property Set Wksh(ByVal sh As Worksheet)
Parameters:
sh: Worksheet. The worksheet to bind.
Wksh #
wksh
Tracking worksheet reference
Signature:
Public Property Get Wksh() As Worksheet
Returns: Worksheet. The bound tracking worksheet.
Registry Access
RegistryHeaders #
registry-headers
The header row of the registry table, in column order
Signature:
Private Function RegistryHeaders() As Variant
The one table every read and every write goes through.
Returns: Variant. A zero-based array of REGISTRY_COLUMN_COUNT header names.
Registry #
registry
Resolve the registry table, held after the first read
Signature:
Private Property Get Registry() As ListObject
Returns: ListObject. The registry table of the tracking worksheet.
Throws:
- ProjectError.InvalidArgument When the table is missing.
RegistryValues #
registry-values
Read the whole registry table into memory
Signature:
Private Function RegistryValues() As Variant
One read serves the whole walk of one scope. A table with no rows answers Empty, which the callers test with IsEmpty.
Returns: Variant. A two-dimensional array of the data rows, or Empty.
AppendRow #
append-row
Append one row to the registry table in a single write
Signature:
Private Sub AppendRow(ByRef fields As Variant)
The tracking worksheet holds this one table and nothing else, so growing the table over the cells below it is safe. A freshly built table carries one blank row and the first entry goes into it.
Parameters:
fields: Variant. A one-dimensional array of REGISTRY_COLUMN_COUNT values, in RegistryColumnIndex order.
ValidateScope #
validate-scope
Refuse a scope this class cannot route
Signature:
Private Sub ValidateScope(ByVal scope As Byte)
Parameters:
scope: Byte. AnalysisScope value to test.
Throws:
- ProjectError.InvalidArgument When the scope is outside the four storage scopes.
Graph Tracking
EmptyRow #
empty-row
A registry row with every column blank
Signature:
Private Function EmptyRow() As Variant
Returns: Variant. A one-based array of REGISTRY_COLUMN_COUNT empty strings.
Name Transfer
SheetNames #
sheet-names
The tracked names of one worksheet, name and address
Signature:
Private Function SheetNames(ByVal inpsh As Worksheet) As Collection
Answers from the bucket built on the first call. The export asks for four sheets in turn and the workbook name collection holds thousands of entries, so it is walked once.
Parameters:
inpsh: Worksheet. The worksheet to answer for.
Returns: Collection. Array(simple name, address) entries, empty when the sheet carries no name.
BuildNameBuckets #
build-name-buckets
Walk the workbook names once and bucket them by owning worksheet
Signature:
Private Sub BuildNameBuckets(ByVal wkb As Workbook)
The owning sheet is read off RefersToRange. An analysis sheet name is translated and can carry spaces and apostrophes, which makes the RefersTo text a poor thing to match on. A name that holds a value has no range behind it and raises, which is how the two are told apart.
Parameters:
wkb: Workbook. The workbook whose names are read.
SimpleName #
simple-name
The name without its worksheet qualifier
Signature:
Private Function SimpleName(ByVal fullName As String) As String
A worksheet-scoped name reads as "Sheet name!LOCAL_NAME". The output sheet gets the part after the separator.
Parameters:
fullName: String. The name as the Names collection reports it.
Returns: String. The name with no qualifier.
Graph Writing
IsSeriesRow #
is-series-row
Test whether a registry row describes a series of this scope
Signature:
Private Function IsSeriesRow(ByRef values As Variant, ByVal rowIndex As Long, _
ByVal scope As Byte) As Boolean
Parameters:
values: Variant. The registry rows.rowIndex: Long. The row to test.scope: Byte. The scope being written.
Returns: Boolean. True for a series row of that scope.
AddSeries #
add-series
Read one series row and add it to a chart
Signature:
Private Sub AddSeries(ByVal gr As Graphs, ByRef values As Variant, _
ByVal rowIndex As Long)
Parameters:
gr: Graphs. The chart receiving the series.values: Variant. The registry rows.rowIndex: Long. The series row to read.
ApplyFormat #
apply-format
Apply the format row of one chart
Signature:
Private Sub ApplyFormat(ByVal gr As Graphs, ByRef values As Variant, _
ByVal scope As Byte, ByVal graphId As String)
Finds the format row of the graph identifier inside the scope. A chart with no format row keeps the chart defaults.
Parameters:
gr: Graphs. The chart to format.values: Variant. The registry rows.scope: Byte. The scope being written.graphId: String. The chart whose format row is wanted.
FormatRowIndex #
format-row-index
Locate the format row of one chart
Signature:
Private Function FormatRowIndex(ByRef values As Variant, ByVal scope As Byte, _
ByVal graphId As String) As Long
Parameters:
values: Variant. The registry rows.scope: Byte. The scope being written.graphId: String. The chart to look for.
Returns: Long. The row index, or 0 when the chart has no format row.
Typed Reads
ReadBoolean #
read-boolean
Read a stored flag
Signature:
Private Function ReadBoolean(ByVal value As Variant) As Boolean
Turning a stored cell value back into a typed value, with no silent trap.
The writers store a flag as 0 or 1, so the numeric arm answers every value this class has ever written. The text arms cover a value typed by hand into the sheet, and anything else answers False.
Parameters:
value: Variant. The cell value.
Returns: Boolean. The stored flag.
ReadLong #
read-long
Read a stored whole number
Signature:
Private Function ReadLong(ByVal value As Variant, ByVal defaultValue As Long) As Long
Parameters:
value: Variant. The cell value.defaultValue: Long. What an empty or non-numeric cell answers.
Returns: Long. The stored number, or the default.
Validation
LoExists #
lo-exists
Check whether a ListObject exists on a worksheet
Signature:
Private Function LoExists(ByVal sh As Worksheet, ByVal loName As String) As Boolean
Existence checks and factory validation.
Parameters:
sh: Worksheet. The worksheet to search.loName: String. The ListObject name to look for.
Returns: Boolean. True when the ListObject exists.
CheckRequirements #
check-requirements
Validate the tracking worksheet infrastructure
Signature:
Private Sub CheckRequirements(ByVal sh As Worksheet)
Checks that the registry table is on the sheet. The export passes the sheet it copies from, so no analysis sheet name is looked up here.
Parameters:
sh: Worksheet. The tracking worksheet to validate.
Throws:
- ProjectError.InvalidArgument When the registry table is missing.
Error Handling
ThrowError #
throw-error
Raise a ProjectError-based exception
Signature:
Private Sub ThrowError(ByVal errNumber As Long, ByVal message As String)
Wrapper around Err.Raise that standardises the source to CLASS_NAME.
Parameters:
errNumber: Long. The ProjectError code to raise.message: String. Human-readable description of the failure.
Throws:
- ProjectError. Always raises the specified error.
Used in (6 file(s))
- AnalysisOutput.cls
- LLExporter.cls
- Graphs.cls
- Linelist.cls
- TestAnaTabIds.bas
- TestGraphs.bas