Doorloop de cellen van een net geopend rapportsjabloon en een samengevoegde titel gedraagt zich als een lek. Je leest A1 en krijgt "Quarterly Statement"; je leest B1 tot en met F1, die zichtbaar onder dezelfde banner zitten, en je krijgt niets. Schrijf een waarde naar C1 om de kop te patchen en die verschijnt nooit op het scherm. Het raster heeft je data niet verloren. Het doet precies wat een samenvoeging betekent: zowel in XLS als XLSX rendert een samengevoegde rechthoek de inhoud van één cel, het linkerboven-anker, en behandelt de rest als bedekte ruimte die waarden bevat maar nooit toont. Excel-gebruikers nemen dit op via vallen en opstaan. Een rapportgenerator moet het als regel coderen, want in gegenereerde code is het symptoom een lege regio zonder uitzondering om naar te traceren. HotXLS, een native Object Pascal-bibliotheek die beide Excel-formaten leest en schrijft vanuit Delphi en C++Builder, legt de samenvoegtabel expliciet genoeg bloot dat je tegen de regel kunt programmeren in plaats van haar opnieuw te ontdekken in een supportticket
Eén waarde, één anker
Een samenvoeging is een weergave-instructie die over een raster wordt gelegd zonder dat het van vorm verandert. Elke bedekte cel bestaat nog steeds als zijn eigen plek in het bestand; het samenvoegrecord vertelt een consument alleen om de inhoud van het anker over de rechthoek te schilderen. Dat onderscheid stuurt drie gedragingen die de moeite waard zijn om je eigen te maken voordat je lay-outcode schrijft. Het lezen van een bedekte cel geeft zijn eigen opgeslagen waarde terug, wat voor een banner die je hebt gebouwd meestal leeg is, dus alle code die een samengevoegde titel inspecteert moet het anker herleiden en lezen. Schrijven naar een bedekte cel slaagt op bestandsniveau en verschijnt nergens, wat de onzichtbare-kopvalkuil uit de opening is. En het ontkoppelen van een regio legt bloot wat er de hele tijd onder zat, dus een verdwaalde waarde geschreven in bedekte ruimte verandert in een zichtbaar defect op de dag dat iemand de samenvoeging opheft
Aan de XLSX-kant is die tabel een volwaardig object. Sheet.MergedCells draagt Add('A1:C1'), FindAt(Row, Col), DeleteAt en Items, en de aanroep waar je het meest naar grijpt is FindAt: geef het elke coördinaat en het geeft de samengevoegde regio terug die die cel bedekt, of nil wanneer de cel op zichzelf staat. Die ene lookup is de basis voor beide helften van correcte samenvoegafhandeling, het veilige lezen en de schrijfbewaking, en beide komen later terug
Twee facades, twee samenvoegidiomen
HotXLS houdt de klassieke BIFF8-.xls-engine en de OOXML-.xlsx-engine als gescheiden objectmodellen, en ze spellen samenvoegen anders omdat ze van verschillende conventies afstammen. De XLS-facade volgt het Excel-COM-idioom: je neemt een bereik uit een geïndexeerde eigenschap met twee argumenten en roept Merge aan met een OleVariant waarvan de waarde de geometrie bepaalt die je eindigt met
var
Book: IXLSWorkbook; // interface geteld: geen handmatige Free
Sh: IXLSWorksheet;
begin
Book := TXLSWorkbook.Create;
Sh := Book.Sheets[1]; // XLS-bladverzameling is 1-gebaseerd
Sh.Range['A1', 'F1'].Merge(False); // False = één samengevoegd blok
Sh.Cells.Item[1, 1].Value := 'Quarterly Statement';
Sh.Range['A3', 'F4'].Merge(True); // True = merge across: één samenvoeging per rij
Book.SaveAs('layout.xls');
end;
Het argument van Merge is het onderdeel waar mensen het fout doen. Over een bereik van twee rijen produceert Merge(True) twee onafhankelijke samenvoegingen van één rij, wat Excels "Merge Across" is en precies wat je wilt voor een gestapelde kopband die zijn rijen apart houdbaar moet houden. Merge(False) smelt de hele rechthoek samen tot één blok. Het bereik meldt ook MergeCells als statusvlag, geeft de omvattende regio terug via MergeArea, en ontbindt zichzelf met Unmerge. De XLSX-facade biedt dezelfde bewerkingen onder andere namen: Sheet.MergeCells(Row1, Col1, Row2, Col2) neemt gehele grenzen, TXLSXRange.Merge accepteert de equivalente Across-variant, en de MergedCells-verzameling houdt het resultaat vast
Een sjabloon dat meegroeit met zijn data
Een echt rapportsjabloon is geen vast raster. De kop en de totalen liggen vast, maar de detailsectie ertussen rekt zich uit tot wat de query ook teruggeeft. Het patroon dat standhoudt houdt één volledig opgemaakte detailrij in het sjabloon, kloont die eenmaal per record, en opent dan een opening vóór het totalenblok zodat alles wat daaronder is verankerd naar beneden schuift zonder zijn opmaak te verliezen
Sheet.Range['A1:F1'].Merge;
Sheet.Cells[1, 1].Value := 'INVOICE #2026-0611'; // waarde gaat naar het anker, A1
Sheet.RowHeight[1] := 28;
TitleFont := Book.Fonts.Add('Calibri', 16, True, False);
Sheet.Cells[1, 1].FontIndex := TitleFont + 1; // poolindex 0-gebaseerd, celzijde 1-gebaseerd
// rij 5 is de opgemaakte detailsjabloonregel
for I := 0 to ItemCount - 1 do
Sheet.CopyRange(5, 1, 5, 6, 6 + I, 1); // stijlen en formules reizen mee
// open een opening boven het totalenblok; inhoud eronder schuift naar beneden
Sheet.InsertRows(6 + ItemCount, 1);
Sheet.Range['A1:F1'].SetBorders(xlsxEdgeOutline, xlsxBorderMedium);
Twee regels verdienen een tweede blik. De lettertypetoewijzing draagt een off-by-one die stilletjes bijt: Fonts.Add geeft een 0-gebaseerde poolpositie terug, terwijl een cel een 1-gebaseerde lettertypeverwijzing opslaat waarbij 0 het standaardlettertype betekent, dus het weglaten van de + 1 geeft geen fout, het geeft je titel alleen het verkeerde lettertype. De andere regel is CopyRange, die opmaak en formules meeneemt samen met waarden. Dat is de hele reden om een handgebouwde sjabloonrij te klonen in plaats van zijn uiterlijk in code te reconstrueren. Een ontwerper bezit het uiterlijk eenmalig, in het sjabloon; de generator giet alleen ooit data in kopieën ervan
Die scheiding schaalt verder wanneer de herbruikbare lay-out in zijn eigen werkmap leeft, zeg een blad met kop- en voettekstbanden dat wordt gedeeld tussen rapporten. CopyRangeTo voert dezelfde kloon uit over werkbladgrenzen heen, met een doelblad plus doelcoördinaten, zodat een generator één smetteloos sjabloonblad kan bewaren en zijn regio's kan stempelen in zoveel uitvoerbladen als een taak nodig heeft. Het alternatief, het sjabloon ter plekke muteren en het daarna proberen te herstellen, is het soort ding dat werkt totdat op een dag een run halverwege afbreekt
Wat InsertRows verplaatst, en wat niet
Het patroon van een meegroeiend sjabloon werkt alleen omdat XLSX-InsertRows een structurele bewerking is in plaats van een celverschuiving. Wanneer het een opening opent, verplaatst het de samengevoegde regio's, rijhoogtes, hyperlinks, opmerkingen, bevroren deelvensters, autofilterbereiken, voorwaardelijke opmaak, gegevensvalidaties, tabellen, gedefinieerde namen, afbeeldingsankers en grafiekankers die onder het invoegpunt zitten, niet alleen de celwaarden. Dat is wat het totalenblok op zijn nieuwe rij laat aankomen met zijn samenvoegingen en getalnotaties intact in plaats van ontdaan aan te komen
De twee gedocumenteerde grenzen zijn degene om omheen te ontwerpen. Formuleaanpassing is beperkt tot het bewerkte blad: verwijzingen binnen dat blad worden herschreven, en een formule op een ander blad die naar het verschoven gebied wijst wordt ook herschreven, maar de aanpassing volgt alleen verwijzingen die het bewerkte blad als doel hebben, dus elk cross-workbook-verwijzingsschema verdient zijn eigen audit in plaats van blind vertrouwen. De tweede grens is scherper, en ligt aan de XLS-kant. Draaitabellen overleven open-save-cycli als ruwe bewaarde records, niet als gemodelleerde objecten die HotXLS kan verplaatsen, dus het invoegen van rijen verplaatst de voetafdruk van een draaitabel niet. Elk sjabloon dat je bouwt voor het .xls-formaat moet zijn draaitabelregio's ruim uit de buurt houden van elke band die groeit
Weigeren om data in lay-outruimte te schrijven
De samenvoegcelfout die daadwerkelijk productie bereikt is niet de cosmetische. Het is structureel: een detailrij drijft af naar een samengevoegde lay-outband, zijn waarden komen in bedekte cellen terecht en worden onzichtbaar, en de kolomtotalen stoppen stilletjes met overeenkomen met wat iedereen die het blad leest kan zien. Omdat FindAt de bedekkingsvraag voor elke coördinaat beantwoordt, kan de generator die schrijfactie weigeren op het moment dat ze zou gebeuren in plaats van een rapport te leveren dat stilletjes ondertelt
// weiger detaildata te schrijven in een samengevoegde lay-outregio
if Sheet.MergedCells.FindAt(Row, 1) <> nil then
raise Exception.CreateFmt('row %d overlaps a merged layout region', [Row]);
Sheet.Cells[Row, 1].Value := Detail.Description;
Dezelfde grenscontrole hoort overal thuis waar een gebruiker de uitvoer later zal sorteren of filteren. Een bereik met samenvoegingen erin kan niet netjes sorteren, omdat het sorteren rijen onafhankelijk verplaatst en een samenvoeging die rijen overspant geen enkele rij heeft om mee te reizen; Excel reageert met een foutmelding of een verknoeide lay-out. De discipline die rapporten correct houdt is geografisch. Beperk samenvoegingen tot titelbanden, sectiescheidingen en handtekeningblokken, en houd het tabellaire midden van het blad plat. Het artikel over sjabloon-rapportgeneratie werkt deze scheiding tussen lay-out en data uit tot een volledige placeholder-gestuurde workflow, en het artikel over voorwaardelijke opmaak en rich text behandelt het opmaken van die platte databand
Hoe samenvoegingen degraderen bij export
Een samenvoeging is een werkmapconcept, en elk tekstgericht exportformaat respecteert het in verschillende mate. De drie gedragingen vooraf kennen bespaart een QA-cyclus. HTML-export reproduceert samenvoegingen getrouw, met colspan en rowspan in één tabel, zodat een browsergebonden rapport zijn gebande uiterlijk behoudt. RTF-export overspant helemaal geen kolommen: de ankertekst komt in zijn eigen cel terecht en de resterende breedte van de samenvoeging komt eruit als lege cellen, wat een brede titel visueel naar links geduwd achterlaat in een tekstverwerker. CSV kent geen begrip van een samenvoeging, dus de ankerwaarde neemt één veld in en elke bedekte cel komt eruit als een leeg veld. De les voor een werkmap die ook exports met scheidingstekens voedt, is om alles wat structureel belangrijk is buiten samengevoegde geometrie te houden; het artikel over CSV-, TSV- en HTML-export behandelt elk formaat in detail
Eén geruststelling voor iedereen die dit afweegt tegen bestandsgrootte: samenvoegingen kosten vrijwel niets op rapportschaal. De samenvoegtabel is piepklein naast de celdata, en het lezen van een bedekte cel gaat nog steeds via FindAt in plaats van te scannen. De prestatiedruk op grote werkmappen komt ergens anders vandaan, vooral groei van de stijlpool en het geheugen dat het opslagpad vasthoudt, wat het artikel over prestaties van grote werkmappen rechtstreeks behandelt. Beide samenvoeg-API's, de structurele bewerkingen en de sjabloondemo's worden geleverd met de HotXLS Delphi Component