Teknisk artikel

HotXLS: data validation, AutoFilter, and worksheet tables

Tre funktioner i HotXLS delar ett kalkylblad men verkar på helt olika objekt, och problemen börjar när du antar att de gör liknande saker. Datavalidering fäster en regel vid ett område som begränsar vad en användare får skriva i det. Ett AutoFilter fäster en lagrad kriteriedefinition vid ett område och ändrar vilka rader en betraktare visar. En tabell omsluter ett område i en namngiven, typad struktur med bandad styling. En begränsar indata, en registrerar en vy, en påtvingar ett schema. Ingen av dem flyttar ett enda cellvärde på egen hand, och AutoFilter i synnerhet lurar folk, eftersom ordet antyder en handling när det bara lagrar en definition. Att veta vilket objekt varje anrop rör, och när effekten faktiskt materialiseras, är det som skiljer en arbetsbok som beter sig likadant i Excel som i dina tester från en som tyst avviker

Diagram över tre HotXLS-arbetsbladsfunktioner i Delphi där datavalidering begränsar indata, AutoFilter lagrar en visningsdefinition, och en tabell påtvingar ett schema
Datavalidering, AutoFilter och tabeller fäster alla vid samma kalkylbladsintervall i HotXLS, men var och en materialiseras vid olika ögonblick — inmatning, filöppning och sparning

AutoFilter lagrar en definition, det beskär inte rader

Ett AutoFilter i en sparad fil är en kriteriepost. Radgömningen sker senare, när Excel öppnar arbetsboken och utvärderar kriterierna mot datan. HotXLS skriver den posten troget och beskär ingenting: varje rad du filtrerade finns fortfarande fysiskt kvar i filen. En pipeline som applicerar ett filter för att slänga refuserade order och sedan läser tillbaka arbetsboken kommer att se alla, inklusive de refuserade, och koden är korrekt enligt API:et men fel enligt författarens mentala modell. På XLSX-kalkylbladet deklarerar SetAutoFilter det filtrerade området och AddAutoFilterColumn fäster kriterier vid en kolumn i det. När serverkod behöver det faktiska utfallet, för ett radantal i en sammanfattning eller för att vidarebefordra bara matchande rader, utvärderar biblioteket kriterierna åt dig i stället för att låtsas att filen ändrades:

var
  Book: TXLSXWorkbook;
  Sheet: TXLSXWorksheet;
  R, Visible: Integer;
begin
  Book := TXLSXWorkbook.Create;
  try
    Book.Open('orders.xlsx');
    Sheet := Book.Sheets[0];

    Sheet.SetAutoFilter('A1:E500');
    // Kolumn-id 3 = fjärde kolumnen INUTI filterområdet (0-baserad offset)
    Sheet.AddAutoFilterColumn(3, xlsxAfOpGreaterOrEqual, '1000');

    Visible := 0;
    for R := 2 to 500 do
      if Sheet.AutoFilterRowVisible(R) then
        Inc(Visible);
    // Visible matchar nu vad Excel kommer att visa efter att filen öppnats

    Book.SaveAs('orders-filtered.xlsx');
  finally
    Book.Free;
  end;
end;

AutoFilterRowVisible svarar per rad, och PreviewAutoFilterRows går igenom hela området via en callback när du behöver den matchande mängden i ett svep. Det finns ett fall där ingen av dem är rätt svar: om kravet är att uteslutna rader inte alls får finnas i filen, ett integritetssnitt snarare än en vy, ta bort raderna helt och hållet. Ett filter är fel verktyg där, eftersom vilken mottagare som helst kan rensa det med ett klick och datan du menade att undanhålla är tillbaka på skärmen

Kolumn-id:t är en offset, inte ett kolumnnummer

Kommentaren i utdraget ovan flaggar fällan som kostar mest felsökningstid i det här API:et. AddAutoFilterColumn identifierar sitt mål via den 0-baserade positionen inom filterområdet, inte via kalkylbladskolumnen. För ett filter på A1:E500 råkar de två numreringssystemen skilja sig åt med ett, vilket är precis den sortens nära-miss som överlever ett snabbt test och går sönder i samma stund en kollega filtrerar en annan kolumn. För ett filter som börjar vid kolumn C betyder id 0 kolumn C, och missmatchningen blir snabbt uppenbar. När filterområdet beräknas vid körning, härled kolumn-id:t från samma variabel som byggde områdessträngen, aldrig från en kolumnkonstant på kalkylbladet. Varje kolumn accepterar ett andra villkor via överlagringen som tar två operatorer, två kriterier och en och/eller-koppling, vilket speglar Excels anpassade filterdialog. XLS-fasaden täcker samma mark med SetAutoFilter plus ApplyAutoFilter, vars kriterie- och operatorparametrar följer de äldre COM-stilkonventionerna och numrerar fältet från 1. Att byta fasad betyder att byta indexbas, så anropsstället förtjänar en kommentar om vilken som är i bruk

Diagram som visar en HotXLS AutoFilter som lagrar varje rad i den sparade Excel-filen, medan Delphi-förhandsvisnings-API:t utvärderar vilka rader Excel kommer att visa, med offseten för det nollbaserade kolumn-id:t
Den sparade filen behåller varje rad och registrerar bara kriterierna, medan Excel döljer rader efter att ha utvärderat dem — och AddAutoFilterColumn siktar på kolumner via nollbaserad offset inuti intervallet

Valideringsregler är kontraktet dina användare redigerar under

Av de tre funktionerna är validering den enda som aktivt begränsar framtida indata, och den förtjänar mest designuppmärksamhet i arbetsböcker som skickas ut för ifyllning och kommer tillbaka för bearbetning. Listvarianten bär det mesta av det arbetet:

var
  Idx: Integer;
begin
  Idx := Sheet.AddListValidation('C2:C500', 'New,Approved,Blocked');
  Sheet.DataValidations[Idx].SetPrompt('Status',
    'Pick one of the listed states');
  Sheet.DataValidations[Idx].SetError('Invalid status',
    'Type or paste only listed values', xlsxDvErrStop);
  Sheet.DataValidations[Idx].AllowBlank := False;

  // Antal: heltal, noll eller mer
  Sheet.AddWholeNumberValidation('D2:D500', xlsxDvOpGreaterOrEqual, '0');
end;

Utöver listor och heltal täcker samma familj decimaler, datum, tider, textlängd och fritt formulerade formler via AddCustomValidation, och den generiska AddDataValidation exponerar hela typ-och-operator-matrisen för regelbyggare som styrs av konfiguration. Felstilen betyder mer än namnet antyder. xlsxDvErrStop avvisar felaktig indata helt; varnings- och informationsstilarna släpper igenom värdet efter ett enda klick. Välj per kolumn baserat på om koden som läser tillbaka arbetsboken kan tolerera ett värde utanför regeln. Två gränsfall hör hemma i prompttexten eller README-filen du levererar med filen. Validering i Excel vaktar skrivning, men att klistra in ett block över ett validerat område smiter förbi regeln, så all kod som läser tillbaka datan måste validera igen i stället för att lita på cellerna. Och en regel täcker det bokstavliga området du gav den, vilket betyder att fästa validering innan du känner till det slutliga radantalet lämnar den tillagda svansen oskyddad. Skriv datan först, storleksanpassa sedan reglerna efter den faktiska omfattningen

Den äldre fasaden erbjuder samma regelfamiljer med en ergonomisk skillnad. XLS-sidans skapare, nämligen AddWholeNumberValidation, AddDecimalValidation, AddDateValidation, AddTimeValidation, AddTextLengthValidation och AddCustomValidation, returnerar TDataValidation-objektet direkt i stället för ett index, så att prompt- och felkonfiguration kedjas från den returnerade referensen i stället för en uppslagning. Operatoruppräkningen (xlsDvBetween, xlsDvGreaterThan och resten) speglar XLSX-uppsättningen, så regelbyggande kod portas mellan fasaderna bortsett från den returstilsskillnaden. Själva prompttexten förtjänar lika mycket eftertanke som regeln. En rullgardinsmeny som avvisar indata med en tom felruta lär användare att mejla IT; en som namnger de tillåtna tillstånden lär dem att fixa cellen och gå vidare

En polaritetsomvändning biblioteket absorberar åt dig

Alla som handläst OOXML-valideringens XML har stött på det inverterade attributet showDropDown: i ISO/IEC 29500 betyder ett sant värde "dölj rullgardinspilen," motsatsen till vad namnet antyder. HotXLS vänder på det här internt, så egenskapen ShowDropDown på en valideringsregel betyder vad den säger, med true som visar rullgardinsmenyn. Det enda sättet att bränna sig är att blanda sanningsnivåer, sätta egenskapen från kod medan en kollega granskar den sparade XML:en och "rättar" attributet som ser bakvänt ut för dem. Bestäm om egenskapen eller den råa XML:en är auktoritativ för granskningsverktyg, och skriv ner inverteringen där det beslutet bor

Tabeller ger ett område ett schema och ett namn

En kalkylbladstabell, ListObject i Excels termer, omsluter ett område i ett namn, typade kolumner, bandad styling och stöd för strukturerade referenser. Det är funktionen som får en genererad arbetsbok att kännas färdig så fort användare börjar sortera och utöka den. Skapandet är symmetriskt mellan fasaderna, med AddTable som tar ett namn, ett område och en kolumnlista:

Diagram över en HotXLS-arbetsbladstabell i Delphi med typade kolumner, strukturerade referenser, arbetsboksunika namn, och fällan med tillägg i totals-rad
En HotXLS-tabell sveper in sitt intervall i ett namn, typade kolumner och randig stil, medan summanraden ligger direkt under datan där ett naivt tillägg på sista raden landar
var
  Cols: TStringList;
begin
  Cols := TStringList.Create;
  try
    Cols.CommaText := 'OrderId,Customer,Status,Amount,Owner';
    Sheet.AddTable('Orders', 'A1:E500', Cols);
  finally
    Cols.Free;
  end;
end;

På XLSX-sidan exponerar det resulterande tabellobjektet StyleName (den inbyggda familjen TableStyleMedium2 och dess syskon), randväxlare och en summeringsradsflagga, så att applicera husstil är en egenskapstilldelning i stället för en manuell formateringsomgång. I äldre .xls-filer skriver samma anrop BIFF8-tabellposterna, och fasaden erbjuder också AddPivotTable för sammanfattningsvyer byggda från rad-, kolumn- och datafält, en påminnelse om att "tabeller" i det äldre formatet sträcker sig längre än OOXML:s ListObject gör. Namnge tabeller på samma sätt du namnger databasvyer. Nedströmskod som läser Orders[Amount] via strukturerad referens överlever den kolumnomordning som slår sönder positionsbaserad kod

Två konventioner sparar uppstädning senare. Excel kräver att tabellnamn är unika över hela arbetsboken, så en generator som skriver ut ett blad per region behöver ett schema som Orders_EMEA i stället för att återanvända Orders. En dublett misslyckas inte vid skrivtillfället; den dyker upp som en reparationsdialog när användaren öppnar filen, vilket är den värsta platsen att upptäcka den på. Den andra konventionen gäller summeringsraden: när den är aktiverad sitter den direkt under dataområdet, så all kod som senare lägger till via "sista använda rad plus ett" skriver in i summeringsbandet i stället för efter det. Håll reda på dataomfattningen separat från tabellomfattningen så hamnar tilläggen där du förväntar dig

De tre funktionerna kombineras naturligt i leveranser för datainmatning. En tabell definierar det redigerbara området, validering begränsar kolumnerna användare skriver i, och ett förinställt filter besparar mottagaren de första klicken. Det finns ett rimligt argument för att leverera med ett filter redan applicerat så att arbetsboken öppnas fokuserad på de rader som spelar roll, så länge du kommer ihåg att de uteslutna raderna fortfarande finns i filen och en nyfiken mottagare kan avslöja dem. Att få frågeresultat in i bladet effektivt, den uppströms halvan av den här pipelinen, täcks i artikeln om att exportera databasresultat till Excel från Delphi, och arbetsböcker där formler sammanfattar den validerade datan drar nytta av definierade namn för stabila korsbladsreferenser

Validering, filter och tabeller är skillnaden mellan att leverera ett rutnät av värden och att leverera en liten applikation. Den fullständiga referensen för regler, filter och tabeller finns på produktsidan för HotXLS Delphi Component