Iterera cellerna i en nyöppnad rapportmall och en sammanslagen rubrik beter sig som en läcka. Du läser A1 och får "Quarterly Statement"; du läser B1 till F1, som synligt sitter under samma banderoll, och du får ingenting. Skriv ett värde i C1 för att lappa rubriken och det syns aldrig på skärmen. Rutnätet har inte tappat din data. Det gör precis vad en sammanslagning innebär: i både XLS och XLSX renderar en sammanslagen rektangel innehållet i en cell, ankaret uppe till vänster, och behandlar resten som täckt utrymme som håller värden men aldrig visar dem. Excel-användare tar sig an detta genom försök och misstag. En rapportgenerator måste koda in det som en regel, för i genererad kod är symptomet ett tomt område utan något undantag att spåra till. HotXLS, ett nativt Object Pascal-bibliotek som läser och skriver båda Excel-formaten från Delphi och C++Builder, exponerar sammanslagningstabellen tillräckligt tydligt för att du ska kunna programmera mot regeln i stället för att återupptäcka den i ett supportärende
Ett värde, ett ankare
En sammanslagning är en visningsinstruktion lagd ovanpå ett rutnät som inte ändrar form. Varje täckt cell existerar fortfarande i filen som sin egen plats; sammanslagningsposten talar bara om för en mottagare att måla ankarcellens innehåll över rektangeln. Den distinktionen driver tre beteenden värda att internalisera innan du skriver någon layoutkod. Att läsa en täckt cell returnerar dess eget lagrade värde, vilket för en banderoll du byggt oftast är tomt, så all kod som inspekterar en sammanslagen rubrik måste slå upp och läsa ankaret. Att skriva till en täckt cell lyckas på filnivå och syns ingenstans, vilket är den osynliga-rubrik-fällan från inledningen. Och att dela upp ett område igen exponerar vad som än legat under det hela tiden, så ett kringspritt värde skrivet i täckt utrymme förvandlas till en synlig defekt den dag någon löser upp sammanslagningen
På XLSX-sidan är den tabellen ett förstklassigt objekt. Sheet.MergedCells bär Add('A1:C1'), FindAt(Row, Col), DeleteAt och Items, och det anrop du oftast tar till är FindAt: mata den med vilken koordinat som helst och den returnerar det sammanslagna området som täcker den cellen, eller nil när cellen står för sig själv. Den enda uppslagningen är grunden för båda halvorna av korrekt sammanslagningshantering, den säkra läsningen och skrivvakten, och båda dyker upp senare
Två fasader, två sammanslagningsidiom
HotXLS behåller den klassiska BIFF8-motorn för .xls och OOXML-motorn för .xlsx som separata objektmodeller, och de stavar sammanslagning olika eftersom de härstammar från olika konventioner. XLS-fasaden följer Excels COM-idiom: du tar ett område från en tvåargumentsindexerad egenskap och anropar Merge med en OleVariant vars värde avgör vilken geometri du hamnar med
var
Book: IXLSWorkbook; // gränssnittsräknat: inget manuellt Free
Sh: IXLSWorksheet;
begin
Book := TXLSWorkbook.Create;
Sh := Book.Sheets[1]; // XLS-bladsamlingen är 1-baserad
Sh.Range['A1', 'F1'].Merge(False); // False = ett sammanslaget block
Sh.Cells.Item[1, 1].Value := 'Quarterly Statement';
Sh.Range['A3', 'F4'].Merge(True); // True = slå samman rad för rad: en sammanslagning per rad
Book.SaveAs('layout.xls');
end;
Argumentet till Merge är det folk gör fel. Över ett tvåradsområde ger Merge(True) två oberoende enradiga sammanslagningar, vilket är Excels "Merge Across" och precis vad du vill ha för ett staplat rubrikband som ska hålla sina rader separerbara. Merge(False) smälter samman hela rektangeln till ett enda block. Området rapporterar också MergeCells som en tillståndsflagga, returnerar det omslutande området via MergeArea, och löser upp sig själv med Unmerge. XLSX-fasaden exponerar samma operationer under andra namn: Sheet.MergeCells(Row1, Col1, Row2, Col2) tar heltalsgränser, TXLSXRange.Merge accepterar motsvarande Across-variant, och samlingen MergedCells håller resultatet
En mall som växer med sin data
En verklig rapportmall är inte ett fast rutnät. Rubriken och summeringarna är fasta, men detaljsektionen mellan dem sträcker ut sig till vad frågan än returnerar. Mönstret som håller behåller en fullt stiliserad detaljrad i mallen, klonar den en gång per post, och öppnar sedan ett mellanrum framför summeringsblocket så att allt förankrat under glider ner utan att förlora sin formatering
Sheet.Range['A1:F1'].Merge;
Sheet.Cells[1, 1].Value := 'INVOICE #2026-0611'; // värdet går till ankaret, A1
Sheet.RowHeight[1] := 28;
TitleFont := Book.Fonts.Add('Calibri', 16, True, False);
Sheet.Cells[1, 1].FontIndex := TitleFont + 1; // poolindex 0-baserat, cellsidan 1-baserad
// rad 5 är den stiliserade detaljmallraden
for I := 0 to ItemCount - 1 do
Sheet.CopyRange(5, 1, 5, 6, 6 + I, 1); // stilar och formler följer med
// öppna ett mellanrum ovanför summeringsblocket; innehåll nedanför flyttas ner
Sheet.InsertRows(6 + ItemCount, 1);
Sheet.Range['A1:F1'].SetBorders(xlsxEdgeOutline, xlsxBorderMedium);
Två rader förtjänar en andra titt. Typsnittstilldelningen bär ett fel-med-en-fel som biter tyst: Fonts.Add lämnar tillbaka en 0-baserad poolposition, medan en cell lagrar en 1-baserad typsnittsreferens där 0 betyder standardtypsnittet, så att utelämna + 1 ger inget fel, det stilar bara din rubrik i fel typsnitt. Den andra raden är CopyRange, som flyttar formatering och formler tillsammans med värdena. Det är hela anledningen till att klona en handbyggd mallrad i stället för att återskapa dess utseende i kod. En designer äger utseendet en gång, i mallen; generatorn häller bara data i kopior av den
Den uppdelningen skalar vidare när den återanvändbara layouten bor i sin egen arbetsbok, säg ett blad med rubrik- och sidfotsband delade mellan rapporter. CopyRangeTo utför samma klon över bladgränser, och tar ett målblad plus destinationskoordinater, så att en generator kan behålla ett orört mallblad och stämpla in dess områden i så många utdatablad som ett jobb behöver. Alternativet, att mutera mallen på plats och försöka återställa den efteråt, är det slags sak som fungerar tills dagen en körning avbryts halvvägs
Vad InsertRows flyttar, och vad den inte gör
Mönstret att låta en mall växa fungerar bara för att XLSX InsertRows är en strukturell redigering snarare än en cellomskakning. När den öppnar ett mellanrum flyttar den de sammanslagna områdena, radhöjder, hyperlänkar, kommentarer, frysta rutor, autofilterintervall, villkorlig formatering, datavalideringar, tabeller, definierade namn, bildankare och diagramankare som sitter under infogningspunkten, inte bara cellvärdena. Det är vad som låter summeringsblocket anlända till sin nya rad med sina sammanslagningar och talformat intakta i stället för att anlända avskalat
Dess två dokumenterade gränser är de att designa kring. Formeljustering är begränsad till bladet som redigeras: referenser inom det bladet skrivs om, och en formel på ett annat blad som pekar in i det förskjutna området skrivs också om, men justeringen följer bara referenser som pekar mot det redigerade bladet, så alla scheman för referenser mellan arbetsböcker förtjänar en egen granskning snarare än blint förtroende. Den andra gränsen är skarpare, och den finns på XLS-sidan. Pivottabeller överlever öppna-spara-cykler som råa bevarade poster, inte som modellerade objekt HotXLS kan flytta, så att infoga rader flyttar inte en pivottabells fotavtryck. Alla mallar du bygger för formatet .xls bör parkera sina pivotområden väl bortom alla band som växer
Att vägra skriva data i layoututrymme
Det sammanslagningsfel som faktiskt når produktion är inte det kosmetiska. Det är strukturellt: en detaljrad driver in i ett sammanslaget layoutband, dess värden hamnar i täckta celler och blir osynliga, och kolumnsummorna slutar tyst stämma med vad någon som läser bladet kan se. Eftersom FindAt besvarar frågan om täckande område för vilken koordinat som helst, kan generatorn vägra den skrivningen i det ögonblick den skulle inträffa i stället för att leverera en rapport som tyst underräknar
// vägra skriva detaljdata i ett sammanslaget layoutområde
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;
Samma gränskontroll hör hemma överallt där en användare senare kommer att sortera eller filtrera utdatan. Ett område med sammanslagningar inuti kan inte sorteras rent, eftersom sorteringen flyttar rader oberoende av varandra och en sammanslagning som spänner över rader inte har någon enskild rad att följa med. Excel svarar med ett fel eller en förvrängd layout. Disciplinen som håller rapporter korrekta är geografisk. Begränsa sammanslagningar till rubrikband, sektionsavdelare och signaturblock, och håll den tabellariska mitten av bladet platt. Artikeln om mallbaserad rapportgenerering utvecklar den här layout-mot-data-uppdelningen till ett fullständigt platshållardrivet arbetsflöde, och artikeln om villkorlig formatering och rich text täcker stilarna för det platta databandet
Hur sammanslagningar degraderas på vägen ut
En sammanslagning är ett arbetsboksbegrepp, och varje textorienterat exportformat respekterar det i olika grad. Att känna till de tre beteendena i förväg sparar en QA-cykel. HTML-export återger sammanslagningar troget, och skriver ut colspan och rowspan på en enda tabell, så en webbläsarbunden rapport behåller sitt bandade utseende. RTF-export spänner inte över kolumner alls: ankartexten hamnar i sin egen cell och den återstående bredden av sammanslagningen kommer ut som tomma celler, vilket lämnar en bred rubrik visuellt tryckt åt vänster i en ordbehandlare. CSV har inget begrepp om sammanslagning, så ankarvärdet upptar ett fält och varje täckt cell skrivs ut som ett tomt fält. Slutsatsen för en arbetsbok som också matar avgränsade exporter är att hålla allt bärande utanför sammanslagen geometri; artikeln om CSV-, TSV- och HTML-export går igenom varje format i detalj
En försäkran för den som väger detta mot filstorlek: sammanslagningar kostar nästan ingenting i rapportskala. Sammanslagningstabellen är liten jämfört med celldatan, och att läsa en täckt cell går fortfarande via FindAt i stället för att skanna. Prestandapressen på stora arbetsböcker kommer från annat håll, huvudsakligen tillväxten av stilpoolen och minnet som sparvägen håller, vilket artikeln om prestanda för stora arbetsböcker tar upp direkt. Båda sammanslagnings-API:erna, de strukturella redigeringsoperationerna och malldemonstrationerna levereras med HotXLS Delphi Component