HotXLS lagrar kalkylbladsurval och rullningspositioner per fönsterruta genom ett gemensamt fönsterrute-API på både TXLSWorksheet och TXLSXWorksheet: SelectAreas, GetSelectedAreas, ScrollWindow och TryGetWindowScroll. För klassiska .xls-filer skriver HotXLS BIFF8 Selection-poster (0x001D) med högst 1369 områden var, översätter logiska fönsterrutenamn till de pane-byte formatet definierar, och lägger varje rullningsaxel på den Window2- eller Pane-post där Excel förväntar sig den
Problemet dyker oftast upp i ett avstämnings- eller granskningsverktyg. Verktyget öppnar en redovisningsexport, hittar varje cell som inte stämmer med källsystemet, och sparar arbetsboken med de cellerna redan markerade under en frusen rubrikrad, så att granskaren landar på avvikelserna i stället för att leta upp dem med rullningen. Med fyrtio avvikelser fungerar det fint. Månadsslutfilen har 3 000, och en enda Selection-post med 3 000 områden kan inte existera: dess kropp skulle behöva 18 009 byte, mer än dubbelt så mycket som en BIFF8-post kan bära. Rullningspositionen har en liknande fälla. På ett blad med frusna rutor är "där användaren tittade" fyra fönsterrutor som delar två radpositioner och två kolumnpositioner, inte en enda koordinat
Varför behöver ett stort urval mer än en Selection-post?
Ett stort urval behöver flera poster för att en BIFF8-postkropp är taklagd vid 8224 byte och varje markerat område kostar fasta sex byte. [MS-XLS] §2.4.248 beskriver Selection-posten som en fast del på 9 byte (pane-byten, rwAct och colAct för den aktiva cellen, irefAct för det aktiva området och cref för områdesantalet) följd av cref RefU-strukturer, var och en med två 16-bitarsrader och två 8-bitarskolumner. Största antal som ryms är (8224 − 9) / 6 avrundat nedåt, vilket är 1369, och det ger en kropp på 8223 byte, en byte under gränsen. TXLSWorksheet.StoreSelectionGroup använder den konstanten som MaxAreasPerRecord och skriver en större grupp som på varandra följande Selection-poster för samma fönsterruta, 1369 områden i taget
Detaljen som biter är irefAct. Varje bit upprepar samma aktiva rad, aktiva kolumn och aktiva områdesindex, och irefAct indexerar den aggregerade sekvensen av alla bitar, inte områdena inuti posten som bär den. Ett urval ett område över gränsen gör det konkret: 1370 områden med det sista aktiva blir två poster, den första med cref 1369 och den andra med cref 1, och båda bär irefAct 1369. Det värdet är större än den andra postens eget områdesantal. En läsare som kontrollerar irefAct mot cref i varje post avvisar en giltig fil, och en läsare som ersätter sitt tillstånd vid varje post tappar de första 1369 områdena. HotXLS-läsaren samlar på varandra följande poster för samma fönsterruta i en gemensam grupp, kräver att varje bit är överens om den aktiva cellen och indexet, och kör intervallkontrollen först vid bladets EOF-post, när hela sekvensen är känd. Fönsterruteversionen av SelectAreas-överlagringen har därför inget tak på 1369 områden. Den validerar varje A1-referens och det aktiva indexet innan den tar skrivlåset för kalkylbladet, och den returnerar False med det tidigare urvalet orört om något är felformat
var
Book: TXLSWorkbook;
Sheet: TXLSWorksheet;
Diffs: TXLSSelectedAreas;
I: Integer;
begin
Book := TXLSWorkbook.Create;
try
Sheet := Book.Sheets.Add;
Sheet.FreezePanes(1, 1); // rubrikraden och kolumn A stannar på plats
SetLength(Diffs, 3000);
for I := 0 to High(Diffs) do
Diffs[I] := Format('C%d', [I + 2]);
// Frysning nollställer det lagrade urvalet, så markera efter frysningen.
// 3000 områden sparas som tre Selection-poster: 1369 + 1369 + 262
if not Sheet.SelectAreas(xlspBottomRight, Diffs, 0) then
raise Exception.Create('Selection rejected');
Book.SaveAs('reconciliation.xls');
finally
Book.Free;
end;
end;
Vilket pane-byte använder en Selection-post?
En Selection-post identifierar sin fönsterruta med den numeriska kod formatet definierar: 0 för nedre högra, 1 för övre högra, 2 för nedre vänstra och 3 för övre vänstra. Den publika enumerationen TXLSPanePosition är deklarerad i läsordning, xlspTopLeft, xlspTopRight, xlspBottomLeft, xlspBottomRight, så Ord(xlspTopLeft) är 0, vilket är den nedre högra fönsterrutan i filen. En rak typkonvertering av enumen till pane-byten skulle skriva varje övre vänstra urval till den nedre högra rutan utan något fel. Varje fönsterrutekänslig ingångspunkt i HotXLS konverterar enumen genom en explicit case-sats i stället, så anropare behöver aldrig bry sig om de numeriska koderna. Fönsterrutans existens kontrolleras också: den övre högra rutan finns bara med en vertikal delning, den nedre vänstra bara med en horisontell, och den nedre högra bara med båda. För en fönsterruta som den aktuella delnings- eller frysningsgeometrin inte har returnerar SelectAreas False, och GetSelectedAreas returnerar en tom array med ActiveAreaIndex satt till -1, utan att skapa någon fönsterruta, något urvalsobjekt eller någon cell i arbetsboken
Var ligger varje fönsterrutas rullningsposition?
Varje fönsterrutas rullningsposition är delad på två poster, för fyra fönsterrutor delar bara två radpositioner och två kolumnpositioner. I en klassisk arbetsbok är de övre rutornas första synliga rad och de vänstra rutornas första synliga kolumn Window2.rwTop och Window2.colLeft, medan de nedre rutornas rad och de högra rutornas kolumn är Pane.rwTop och Pane.colLeft. ScrollWindow(xlspTopRight, R, C) skriver alltså Window2.rwTop och Pane.colLeft, och sätter man kolumnen för den övre högra rutan flyttas den nedre högra med, precis som de två delar en horisontell rullningslist i Excel. De publika metoderna använder rad- och kolumnnummer med bas ett. En saknad fönsterruta returnerar False och sätter båda frågeutgångarna till noll, och en koordinat utanför intervallet avvisas innan någon axel ändras. Ingenting här beror på hur en visare ritar rutnätet. En renderingskontroll håller sina egna TopRow och LeftCol, som artikeln om att rendera arbetsböcker i ett eget VCL-rutnät beskriver, och det är körtidstillstånd, inte det som sparas
XLSX sprider samma data över två element: sheetView/@topLeftCell (ECMA-376 Part 1, §18.3.1.87) för fönstret som helhet och barnet pane/@topLeftCell (§18.3.1.66) för den nedre högra sidan av en delning. Båda attributen kan finnas samtidigt. HotXLS läser det yttre attributet först till fönsternivåfälten, låter pane-barnet åsidosätta bara fälten på fönsterrutenivå, och skriver tillbaka båda separat. Att kollapsa de två lagren till ett är exakt hur en övre eller vänster rullningsposition tyst försvinner vid inläsning. Bladkopior bär båda lagren i båda motorerna. De äldre ingångspunkterna behåller sitt ursprungliga beteende: de klassiska egenskaperna ScrollRow och ScrollColumn, samt de nollbaserade XLSX-metoderna SetPaneScroll och GetPaneScroll. Frysnings- och delningsgeometrin i sig konfigureras med bladnivåinställningarna som bladskydd, sidinställning och utskrift tar upp
var
Row, Col: Integer;
begin
Sheet.FreezePanes(1, 1);
// Nedre högra: nedre radaxeln (Pane.rwTop) och högra kolumnaxeln (Pane.colLeft)
Sheet.ScrollWindow(xlspBottomRight, 500, 3);
// Övre högra delar högra kolumnaxeln, så detta flyttar också nedre högra till kolumn 6
Sheet.ScrollWindow(xlspTopRight, 1, 6);
if Sheet.TryGetWindowScroll(xlspBottomRight, Row, Col) then
Memo1.Lines.Add(Format('Bottom-right starts at row %d, column %d', [Row, Col]));
// Nedre högra börjar på rad 500, kolumn 6
end;
Vad händer när en Selection-post är korrupt?
När en Selection-post är korrupt behåller HotXLS den som opaka byte, rapporterar diagnostikkod 1304 (xlsDiagnosticSelectionRecordInvalid) och skriver tillbaka den ursprungliga kroppen byte för byte vid sparning. Innan en post ansluter till sin fönsterrutas grupp kontrollerar läsaren den i ordning. Pane-byten måste vara 3 eller mindre. Poster för en och samma fönsterruta måste ligga sammanhängande i strömmen. De 9 fasta bytena måste finnas. cref måste ligga mellan 1 och 1369, och kroppen måste vara exakt 9 + cref × 6 byte lång. Varje bit i en grupp måste vara överens om den aktiva cellen och irefAct, irefAct får inte ha sin teckenbit satt, den aktiva kolumnen måste ligga på rutnätet, och inget område får ha omvända gränser. Problem i en enstaka fysisk post rapporteras en gång per post. Motsägelser som bara dyker upp efter aggregering, som irefAct som pekar förbi det totala områdesantalet eller en aktiv cell utanför det indexerade området, rapporteras en gång per grupp vid EOF. En ogiltig grupp förblir osynlig för det typade API:et: GetSelectedAreas returnerar en tom array med index -1 för den fönsterrutan, medan alla andra rutor fortsätter fungera
var
I: Integer;
D: TXLSDiagnostic;
begin
if Book.Open('supplier-upload.xls') <> 1 then
Exit;
for I := 0 to Book.Diagnostics.Count - 1 do
begin
D := Book.Diagnostics[I];
if D.Code = xlsDiagnosticSelectionRecordInvalid then
Log.Add(Format('%s: record $%.4x kept opaque (%s)',
[D.SheetName, D.RecordId, D.Message]));
end;
end;
Hur överlever urval rad- och kolumninsättningar?
Urval överlever strukturella redigeringar för att infogning eller borttagning av hela rader eller kolumner mappar om varje representerad fönsterrutegrupp i både Classic- och XLSX-motorn genom en gemensam ommappare. Överlevande områden behåller sin ordning och det aktiva området behåller sin identitet. Raderas det aktiva området blir den första överlevande efterträdaren aktiv, sedan den sista överlevande föregångaren om inget följer den. Raderas alla områden kollapsar gruppen till en cell vid borttagningsgränsen, och en aktiv cell som inte längre ligger inuti det valda området flyttas till områdets övre vänstra hörn, så att index och koordinat aldrig motsäger varandra. Gränserna är medvetna. Ogiltiga klassiska grupper hoppas över av ommapparen i stället för att skrivas om till ett påhittat urval, så deras ursprungliga byte kommer fortfarande ut exakt som de gick in. Redigering av en fönsterruta ersätter bara den rutans poster och lämnar de andra byteidentiska. ODS får inget tillstånd för fönsterruteurval alls, eftersom ODF saknar en motsvarande struktur för kalkylbladsvy att bära det
Skriver din applikation .xls-filer som användare öppnar och behöver navigera i, oavsett om det gäller att granska flaggade celler, fortsätta där de slutade eller dela en frusen dashboard, är urvals- och rullnings-API:et som förstår fönsterrutor en del av HotXLS Delphi-kalkylbladskomponenten, och det fungerar likadant för XLS och XLSX