Pivot table API
HotXLS ondersteunt getypeerde inspectie, authoring en resultaatmaterialisatie van draaitabellen voor XLS- en XLSX-werkmappen. Relatiegedreven XLSX-lading herleidt willekeurige package-part-namen en herstelt cacherecords, gedeelde items, veldmetadata, filters, groepering, berekende definities, indelingen en extensies in het publieke model. Klassieke XLS-invoer houdt zijn oorspronkelijke draaitabelrecords beschikbaar voor getrouwe save-round-trips
Round-trip en getypeerd model
Wanneer een klassieke werkmap al draaitabellen bevat, bewaart de reader de raw BIFF-records en stelt daarnaast een getypeerd model bloot. Tabellen en caches die uit een klassiek bestand zijn geladen, houden FromRawBlobs = True, zodat de save-uitvoer voor getrouwheid de oorspronkelijke bytes herspeelt. XLSX-draaitabellen worden geladen en opgeslagen via hun eigen package-relaties, terwijl programmatisch aangemaakte draaitabellen de getypeerde writer gebruiken
const
xlPivotBaseItemPrevious = $7FFB;
xlPivotBaseItemNext = $7FFC;
type
TXLSPivotFieldAxis = (xlpfaNone, xlpfaRowField, xlpfaColumnField, xlpfaPageField, xlpfaDataField);
TXLSPivotAggregation = (xlpaSum, xlpaCount, xlpaAverage, xlpaMax, xlpaMin, xlpaProduct, xlpaCountNums, xlpaStdDev, xlpaStdDevP, xlpaVar, xlpaVarP);
TXLSPivotShowDataAs = (xlpsdaNormal, xlpsdaDifference, xlpsdaPercent, xlpsdaPercentDiff, xlpsdaRunTotal, xlpsdaPercentOfRow, xlpsdaPercentOfCol, xlpsdaPercentOfTotal, xlpsdaIndex);
TXLSPivotExtendedShowDataAs = (xlpesdaNone, xlpesdaPercentOfParent, xlpesdaPercentOfParentRow, xlpesdaPercentOfParentColumn, xlpesdaPercentOfRunningTotal, xlpesdaRankAscending, xlpesdaRankDescending);
TXLSPivotSubtotal = (xlpsDefault, xlpsSum, xlpsCount, xlpsAverage, xlpsMax, xlpsMin, xlpsProduct, xlpsCountNums, xlpsStdDev, xlpsStdDevP, xlpsVar, xlpsVarP);
TXLSPivotSubtotals = set of TXLSPivotSubtotal;
TXLSPivotDesignerZone = (xlpdzAvailable, xlpdzRows, xlpdzColumns, xlpdzFilters, xlpdzValues);
TXLSPivotCacheValue = record
ValueType: TXLSPivotCacheFieldType;
end;
TXLSPivotCacheField = class
property Name: WideString;
property FieldType: TXLSPivotCacheFieldType;
property ItemCount: Integer;
property Items[Index: Integer]: TXLSPivotCacheItem;
end;
TXLSPivotCache = class
property CacheId: Integer;
property SourceRangeSheet: WideString;
property SourceFirstRow: Integer;
property SourceFirstCol: Integer;
property SourceLastRow: Integer;
property SourceLastCol: Integer;
property RecordCount: Integer;
property FieldCount: Integer;
property Fields[Index: Integer]: TXLSPivotCacheField;
property RecordIndices[RecordIdx, FieldIdx: Integer]: Integer;
property FromRawBlobs: Boolean;
end;
TXLSPivotCaches = class
function Add: TXLSPivotCache;
function FindByCacheId(ACacheId: Integer): TXLSPivotCache;
property Count: Integer;
property Items[Index: Integer]: TXLSPivotCache;
end;
TXLSPivotField = class
function AddItem: TXLSPivotItem;
function AddCalculatedItem(const AName, AFormula: WideString): TXLSPivotCalculatedItem;
property Name: WideString;
property Axis: TXLSPivotFieldAxis;
property CacheFieldIndex: Integer;
property ItemCount: Integer;
property Items[Index: Integer]: TXLSPivotItem;
property SubtotalMask: Word;
property Subtotals: TXLSPivotSubtotals;
property PageItemIndex: Integer;
property SortType: TXLSPivotFieldSortType;
property SortDataField: Integer;
property Filters: TXLSPivotFilters;
end;
TXLSPivotDataField = class
property Field: TXLSPivotField;
property Aggregation: TXLSPivotAggregation;
property NumberFormat: Word;
property DisplayName: WideString;
property ShowDataAs: TXLSPivotShowDataAs;
property ExtendedShowDataAs: TXLSPivotExtendedShowDataAs;
property BaseField: Integer;
property BaseItem: Integer;
end;
TXLSPivotSupplementalRecord = class
property RecID: Word;
property BodyLength: Integer;
property Body[Index: Integer]: Byte;
end;
TXLSPivotTable = class
function AddField(const AName: WideString; ACacheFieldIndex: Integer): TXLSPivotField;
function FindFieldByName(const AName: WideString): TXLSPivotField;
function SetFieldAxis(const AName: WideString; AAxis: TXLSPivotFieldAxis): TXLSPivotField;
function AddRowField(const AName: WideString): TXLSPivotField;
function AddColumnField(const AName: WideString): TXLSPivotField;
function AddPageField(const AName: WideString): TXLSPivotField;
function AddDataField(AField: TXLSPivotField; AAggregation: TXLSPivotAggregation): TXLSPivotDataField;
function AddDataFieldByName(const AName: WideString; AAggregation: TXLSPivotAggregation = xlpaSum): TXLSPivotDataField;
function AddCalculatedMember(const AName, AFormula: WideString): TXLSPivotCalculatedMember;
function AddSupplementalRecord(ARecID: Word; const ABody: TBytes): TXLSPivotSupplementalRecord;
function Make(AWriter: TlxPivotResultWriter): Integer;
property Name: WideString;
property DataCaption: WideString;
property CacheId: Integer;
property Cache: TXLSPivotCache;
property FieldCount: Integer;
property Fields[Index: Integer]: TXLSPivotField;
property DataFieldCount: Integer;
property DataFields[Index: Integer]: TXLSPivotDataField;
property RowGrandTotals: Boolean;
property ColumnGrandTotals: Boolean;
property MaterializeGrandTotalRow: Boolean;
property MaterializeGrandTotalColumn: Boolean;
property SupplementalRecordCount: Integer;
property SupplementalRecords[Index: Integer]: TXLSPivotSupplementalRecord;
property FromRawBlobs: Boolean;
end;
TXLSPivotDesigner = class
constructor Create(APivotTable: TXLSPivotTable = nil);
procedure ClearLayout;
function MoveField(const AName: WideString; AZone: TXLSPivotDesignerZone; APosition: Integer = -1): Boolean;
function SetAggregation(const AName: WideString; AAggregation: TXLSPivotAggregation): Boolean;
function Validate(out ErrorText: WideString): Boolean;
function Apply(AWriter: TlxPivotResultWriter): Integer;
property PivotTable: TXLSPivotTable;
property FieldCount: Integer;
property Fields[Index: Integer]: TXLSPivotDesignerField;
end;
TXLSPivotTables = class
function Add: TXLSPivotTable;
function FindByName(const AName: WideString): TXLSPivotTable;
property Count: Integer;
property Items[Index: Integer]: TXLSPivotTable;
end;
Worksheet- en werkmap-toegangspunten
type
TXLSWorksheet = class
function AddPivotTable(const SourceRangeA1: WideString; DestRow, DestCol: Integer; const Name: WideString): TXLSPivotTable;
function PivotSetDataFieldFormat(DataField: TXLSPivotDataField; const FormatStr: WideString): Word;
property PivotTables: TXLSPivotTables;
end;
TXLSWorkbook = class
property PivotCaches: TXLSPivotCaches;
function RegisterNumFormat(const FormatStr: WideString): Word;
end;
RegisterNumFormat registreert een getalnotatiestring en geeft zijn BIFF8-ifmt-index terug, precies wat TXLSPivotDataField.NumberFormat en TXLSPivotCacheField.NumberFormat verwachten; dezelfde string tweemaal registreren geeft dezelfde index terug
DestRow en DestCol zijn 1-based, net als Cells[Row, Col], dus AddPivotTable('Data!A1:C9', 3, 1, 'SalesPivot') verankert de draaitabel op A3. Ook de posities op TXLSPivotTable (FirstRow .. FirstDataCol) en het bronbereik op TXLSPivotCache zijn 1-based, net als in de XLSX-engine; ligt het anker buiten het werkblad van 65,536 x 256, dan geeft de functie nil terug
Bouw een draaitabelindeling met slepen en neerzetten
TXLSPivotDesigner is een niet-visueel lay-outmodel dat veldlijstinterfaces voor VCL, FMX, web of de commandoregel kan ondersteunen, zonder de draaitabellogica aan één UI-toolkit te koppelen
Elk veld meldt een beschikbare rij-, kolom-, filter- of waardezone plus zijn deterministische positie; MoveField verwerkt een drop, SetAggregation wijzigt een waardeveld, Validate rapporteert onvolledige indelingen en Apply valideert en materialiseert het resultaat via de normale pivot-writer
Nieuwe numerieke waardevelden en datum-waardevelden staan standaard op xlpaSum, terwijl tekst-, boolean-, fout-, lege en gemengde velden standaard op xlpaCount staan
var
Designer: TXLSPivotDesigner;
ErrorText: WideString;
begin
Designer := TXLSPivotDesigner.Create(Pivot);
try
Designer.MoveField('Region', xlpdzRows);
Designer.MoveField('Quarter', xlpdzColumns);
Designer.MoveField('Closed', xlpdzFilters);
Designer.MoveField('Revenue', xlpdzValues);
Designer.SetAggregation('Revenue', xlpaAverage);
if Designer.Validate(ErrorText) then
Designer.Apply(ResultWriter.WriteCell);
finally
Designer.Free;
end;
end;
Maak een draaitabel
uses lxHandle;
var
Workbook: TXLSWorkbook;
SourceSheet, PivotSheet: TXLSWorksheet;
Pivot: TXLSPivotTable;
RowField: TXLSPivotField;
DataField: TXLSPivotDataField;
begin
Workbook := TXLSWorkbook.Create;
try
SourceSheet := Workbook.Sheets[1];
SourceSheet.Name := 'SalesData';
SourceSheet.Cells[1, 1].Value := 'Region';
SourceSheet.Cells[1, 2].Value := 'Product';
SourceSheet.Cells[1, 3].Value := 'Revenue';
PivotSheet := Workbook.Sheets.Add;
PivotSheet.Name := 'PivotReport';
Pivot := PivotSheet.AddPivotTable('SalesData!A1:C20', 4, 1, 'SalesPivot');
RowField := Pivot.AddRowField('Region');
Pivot.AddColumnField('Product');
DataField := Pivot.AddDataFieldByName('Revenue', xlpaSum);
RowField.Subtotals := [xlpsSum];
PivotSheet.PivotSetDataFieldFormat(DataField, '$#,##0.00');
Workbook.SaveAs('pivot-report.xls');
finally
Workbook.Free;
end;
end;
Toon waarden als
Zet ShowDataAs op elk gegevensveld voordat je Make aanroept of de werkmap opslaat. Difference, Percent en PercentDiff vereisen een op 0 gebaseerde BaseField-draaitabelveldindex plus een BaseItem. RunTotal vereist alleen BaseField. De modi row, column, total en index leiden hun noemers uit de bronrecords af, met de geconfigureerde aggregatie
Een expliciete BaseItem is een op 0 gebaseerde itemindex binnen het basisveld van de draaitabel. Gebruik xlPivotBaseItemPrevious of xlPivotBaseItemNext voor vergelijkingen met aangrenzende items. Ontbrekende vergelijkingsintersecties blijven leeg, terwijl een aanwezige noemer nul #DIV/0! oplevert
DataField := Pivot.AddDataFieldByName('Revenue', xlpaSum);
DataField.ShowDataAs := xlpsdaPercentDiff;
DataField.BaseField := 0;
DataField.BaseItem := xlPivotBaseItemPrevious;
CellsWritten := Pivot.Make(ResultWriter.WriteCell);
Filteren, groeperen en sorteren
Make past verborgen items en paginaselecties toe vóór de domeinconstructie en evalueert daarna caption-, datum-, count-, percent-, sum- en value-filters. Numerieke bereiken, kalendergroepen en discrete groepen gebruiken de grouping-metadata van cachevelden vóór de aggregatie. SortType kiest stabiele labelvolgorde, terwijl SortDataField het op 0 gebaseerde waardeveld kiest voor stabiele sortering op gegevenswaarden
Berekende expressies
Berekende cachevelden worden één keer per bronrecord uitgevoerd, berekende items over de aggregaten van zusterelementen heen, en berekende members met MemberType = 'data' voegen virtuele waardevelden toe na de bronaggregatie. Formulenamen zijn case-insensitive en mogen tussen rechte haken of aanhalingstekens staan. Rekenkunde, vergelijkingen, percentages, haakjes en gangbare numerieke en logische functies worden ondersteund, met herhaalde dependency-passes voor acyclische berekende definities
ProfitField := Pivot.Cache.AddField('Profit', xlpcftNumber);
ProfitField.Formula := '=Revenue-Cost';
ProfitField.DatabaseField := False;
Pivot.AddDataField(Pivot.AddField('Profit', 4), xlpaSum);
RegionField.AddCalculatedItem('Combined', '=North+South');
Pivot.AddCalculatedMember('Margin', '=Profit/Revenue').MemberType := 'data';
Subtotalen en grand totalen
Elk rij- of kolomveld kan elke geconfigureerde subtotalfunctie boven of onder zijn detailitems materialiseren via Subtotals en SubtotalTop. RowGrandTotals en ColumnGrandTotals sturen het opslagbeleid van de werkmap, terwijl MaterializeGrandTotalRow en MaterializeGrandTotalColumn resultaatcallbacks expliciete grand-total-uitvoer mee laten leveren zonder de bronrecords opnieuw te scannen
Uitgebreide show-value-modi
ExtendedShowDataAs biedt percent-of-parent, percent-of-parent-row, percent-of-parent-column, percent-of-running-total en oplopende of aflopende rank. Deze modi gebruiken het XLSX-extensiemodel en schrijven geen niet-ondersteunde gegevensveldwaarden naar klassieke BIFF
Inspecteer een bestaande draaitabelwerkmap
Workbook.Open('excel-authored-pivot.xls');
for SheetIndex := 1 to Workbook.Sheets.Count do
begin
Sheet := Workbook.Sheets[SheetIndex];
for PivotIndex := 0 to Sheet.PivotTables.Count - 1 do
begin
Pivot := Sheet.PivotTables[PivotIndex];
Writeln(Pivot.Name);
Writeln(Pivot.FieldCount);
if Assigned(Pivot.Cache) then
Writeln(Pivot.Cache.SourceRangeSheet);
end;
end;