Bir mutabakat aracı, defterle uyuşmayan 3.000 hücre bulur ve kaydedilen .xls'in, dondurulmuş bir başlık satırının altında bunların tamamı seçili olarak açılmasını ister. Kâğıt üzerinde bu tek bir Selection kaydıdır. Bayt cinsinden 9 + 3.000 × 6 = 18.009 eder ve bir BIFF8 kayıt gövdesi 8.224te durur. Aynı çalışma kitabı, önemsiz görünen ve olmayan ikinci bir soru doğurur: B2de dondurulmuş bir sayfada "kaydırma konumu", iki satır ofsetini ve iki sütun ofsetini paylaşan dört panedir ve o dört sayı iki farklı kayıtta yaşar
İki sorun da format aritmetiğidir: seçim, okuyucunun yeniden dikmesi gereken kayıtlara bölünür; pane, okuma sırasını izlemeyen bir kodla adlandırılır ve her kaydırma ekseni, Excelin onu okuduğu kayda düşer
3.000 seçili alan neden üç Selection kaydı ister?
[MS-XLS] §2.4.248, Selection kaydını (0x001D) alanların izlediği 9 baytlık sabit bir bölüm olarak dizer. Sabit bölüm pane baytını, aktif hücre için rwAct ile colActı, aktif alanın indeksi için irefActi ve alan sayısı için crefi tutar. Her alan, iki 16-bit satır ve iki 8-bit sütundan oluşan bir RefU yapısıdır. Sığan en büyük sayı, aşağı yuvarlanmış (8.224 − 9) / 6dır; bu 1.369 alan ve 8.223 baytlık bir gövde verir, sınırın bir bayt altı
HotXLS kayıt başına tavan olarak 1.369'ı kullanır. TXLSWorksheet, ondan fazla alanlı bir seçim grubunu kaydederken aynı pane için ardışık Selection kayıtları yazar, her seferinde 1.369 alanla, ve her kayıt aynı pane baytını, aktif hücreyi ve aktif indeksi yineler. 3.000 hücrelik mutabakat seçimi 1.369, 1.369 ve 262 alan tutan üç kayıt olarak çıkar. Çağıranlar o bölmeyi asla görmez; onlar A1 referanslı bir dizi verir
var
Book: TXLSWorkbook;
Sheet: TXLSWorksheet;
Diffs: TXLSSelectedAreas;
I: Integer;
begin
Book := TXLSWorkbook.Create;
try
Sheet := Book.Sheets.Add;
Sheet.FreezePanes(1, 1); // başlık satırı ile A sütunu yerinde kalır
SetLength(Diffs, 3000);
for I := 0 to High(Diffs) do
Diffs[I] := Format('C%d', [I + 2]);
// FreezePanes saklanan seçimi temizler; bu yüzden dondurduktan sonra seçin.
// 3000 alan, üç Selection kaydı olarak saklanır: 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;
Çağrı sırası burada önemlidir. FreezePanes, pane düzenini yeniden kurar ve sayfanın taşıdığı her pane seçimini temizler; önceki bir seçim, kayıt anına kadar çoktan gitmiştir. Dondurulmamış bir sayfada sağ alt pane henüz yoktur ve aynı SelectAreas çağrısı düpedüz False döndürür. Önce dondur, sonra seç
Bir seçim birkaç kayda yayıldığında irefAct neyi işaret eder?
Bölünmüş bir seçimde irefAct, tüm parçalardaki alanların bütün birleşik dizisini indeksler ve her parça aynı değeri taşır. Sınırın bir alan ötesindeki bir seçim bunu somutlaştırır: sonuncusu aktif 1.370 alanlı bir seçim iki kayda bölünür; ilki cref 1.369, ikincisi cref 1 taşır ve ikisi de irefAct 1.369 taşır. İkinci kayıtta o indeks, kaydın kendi alan sayısından büyüktür. Tek kaydı tek okuyan biri için kırık görünür ve iki tür okuyucu buraya takılır
- Her kayıt içinde
irefActicrefle karşılaştıran bir okuyucu, kusursuz geçerli bir dosyayı ikinci parçada reddeder - Her Selection kaydında seçim durumunu yeniden kuran bir okuyucu yalnızca son parçayı tutar ve ilk 1.369 alanı düşürür
HotXLS okuyucusu, aynı pane için gelen ardışık kayıtları tek bir grupta birleştirir ve her parçanın aktif satır, aktif sütun ve irefAct üzerinde uyuşmasını şart koşar. irefAct üzerindeki aralık kontrolü ile aktif hücrenin gerçekten işaret ettiği alanın içinde olduğunu denetleyen kontrol, yalnızca çalışma sayfasının EOF kaydı ulaşıp tam dizi belli olduğunda koşar. Daha erken kontrol etmek daha basit kod olurdu ve 1.369 alan üstü her seçim için yanlış cevap olurdu
Yazma tarafında pane önce alanlı SelectAreas overload'unun kendi başına bir 1.369 alan sınırı yoktur. Sayfaya dokunmadan önce her referansı ayrıştırır: $ işaretleri kabul edilip soyulur, C9:A2 gibi ters çevrilmiş bir aralık A2:C9a normalize edilir ve boş ya da ayrıştırılamayan bir girdi tüm çağrıyı başarısız kılar. Aktif indeksin dizi içinde kalması ve 32.767de kalması gerekir; çünkü irefAct işaretli 16-bitlik bir alandır. Herhangi bir başarısızlık False döndürür ve önceki seçimi tam olarak olduğu gibi bırakır
Pane kodları okuma sırasını izlemez
Bir Selection kaydı paneini formatın tanımladığı sayısal kodla adlandırır: sağ alt için 0, sağ üst için 1, sol alt için 2 ve sol üst için 3. Kamusal enumeration TXLSPanePosition okuma sırasında bildirilmiştir: xlspTopLeft, xlspTopRight, xlspBottomLeft, xlspBottomRight. Yani Ord(xlspTopLeft) 0dır ve dosyadaki 0 sağ alt demektir. Enumu doğrudan pane baytına döken bir yazar, her sol üst seçimini sağ alt paneye koyar ve dosya yapısal olarak geçerli kalır; çünkü ikisi de yasal pane kodlarıdır
HotXLS'teki pane bilen her giriş noktası enumu açık bir case ifadesiyle dönüştürür; uygulama kodu sayısal kodlarla asla uğraşmaz. Pane varlığı ayrıca güncel dondurma ya da bölme geometrisiyle de kontrol edilir. Sol üst pane daima vardır. Sağ üst dikey bir bölme, sol alt yatay bir bölme, sağ alt ise ikisini birden ister. Sayfanın olmadığı bir pane için SelectAreas False döndürür, GetSelectedAreas ActiveAreaIndexi -1 kurulmuş boş bir dizi döndürür ve iki çağrı da yolda ne bir pane ne bir seçim ne bir hücre kurar
Çalışma sayfası, daha eski iki argümanlı SelectAreas(Areas, ActiveAreaIndex)'i de tutar; o an aktif olan paneine yazar ve Integer bir durum döndürür. Paneleri dondurulmuş bir sayfada bu, çoğu zaman kendiniz kurmadığınız bir durum üzerine tahmindir. Dondurulmuş ya da bölünmüş sayfalarda koşan her şey için önce TXLSPanePosition alan overloadu kullanın; böylece pane çağrının parçası olur ve False bir sonuç, geometri ile isteğin anlaşmadığını söyler. Sonucu geri okumak aynı şekildedir:
var
Areas: TXLSSelectedAreas;
Active: Integer;
begin
Areas := Sheet.GetSelectedAreas(xlspBottomRight, Active);
if Active < 0 then
Memo1.Lines.Add('No usable selection on the bottom-right pane')
else
Memo1.Lines.Add(Format('%d areas, active area %s',
[Length(Areas), Areas[Active]]));
end;
Her panenin kaydırma konumu nerede durur?
Her panein kaydırma konumu iki kayda bölünür; çünkü dört pane yalnızca iki satır konumunu ve iki sütun konumunu paylaşır. Klasik bir çalışma kitabında üst panelerin ilk görünür satırı ile sol panelerin ilk görünür sütunu Window2.rwTop ile Window2.colLeft'dir. Alt panelerin satırı ile sağ panelerin sütunu Pane.rwTop ile Pane.colLeft'dir. Sağ üst bu yüzden satırını Window2den, sütununu Paneden alır; sol alt tersini yapar
ScrollWindow ile TryGetWindowScroll o yerleşimi birebir izler ve 1 tabanlı satır ve sütun numaraları kullanır. ScrollWindow(xlspTopRight, R, C), Window2.rwTop ile Pane.colLeft yazar; yani sağ üst sütununu kurmak sağ alt panei de taşır. Bu, iki sağ panein tek bir yatay kaydırma çubuğunu paylaştığı Excel'le uyuşur. Eksik bir pane, ScrollWindow'u False döndürür ve TryGetWindowScroll'u her iki çıktıyı sıfıra kurar. Aralık dışı bir satır ya da sütun, iki eksenden herhangi biri değişmeden reddedilir; kötü bir sütun yarım uygulanmış bir satır bırakamaz
var
Row, Col: Integer;
begin
Sheet.FreezePanes(1, 1);
// Sağ alt: alt satır ekseni (Pane.rwTop) ve sağ sütun ekseni (Pane.colLeft)
Sheet.ScrollWindow(xlspBottomRight, 500, 3);
// Sağ üst, sağ sütun eksenini paylaşır; bu yüzden bu çağrı sağ altı da 6. sütuna taşır
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]));
// Sağ alt, 500. satır 6. sütundan başlar
end;
XLSX aynı iki katmanı iki ögede saklar: pencerenin bütünü için sheetView/@topLeftCell (ECMA-376 Kısım 1, §18.3.1.87) ve bir bölmenin sağ alt tarafı için çocuk pane/@topLeftCell (§18.3.1.66). İki nitelik de aynı anda bulunabilir. Yalnızca birini tutan bir yükleyici, dokunduğu her dondurulmuş sayfanın ya üst satırlarını ya sol sütunlarını kaybeder. TXLSXWorksheet, dış niteliği önce pencere düzeyi alanlara okur, pane çocuğunun yalnızca pane düzeyi alanları override etmesine izin verir ve ikisini ayrı ayrı geri yazar. ScrollWindow ile TryGetWindowScroll, TXLSXWorksheet üzerinde aynı imzalarla ve aynı anlamla durur ve bir çalışma sayfasını kopyalamak iki katmanı da her iki formatta taşır
Dar özellikler, yalnızca tek eksen çifti gerektiren kod için hâllâ yerindedir: klasik çalışma sayfasında ScrollRow ile ScrollColumn ve XLSXte sağ alt pane için sıfır tabanlı SetPaneScroll ile GetPaneScroll. Bir render kontrolü kendi TopRowu ile LeftColunu tutar — özel bir VCL ızgarasında çalışma kitaplarını render etme yazısının anlattığı gibi — ve o çalışma anı durumu saklanan şeyden ayrıdır. Dondurma ve bölme geometrisinin kendisi, sayfa koruması, sayfa düzeni ve yazdırma yazısının kapsadığı sayfa düzeyi ayarlarla kurulur
Bozuk Selection kayıtları bayt bayt tutulur
Bir Selection kaydı bozuk olduğunda HotXLS gövdesini opak baytlar olarak tutar, 1304 tanı kodunu (xlsDiagnosticSelectionRecordInvalid) uyarı olarak bildirir ve kaydederken özgün gövdeyi değişmeden geri yazar. Bilinmeyen üreticilerden gelen ya da elle yazılmış BIFF yazıcılarının dosyaları böyle kayıtlar taşıyabilir. Bir kayıt paneinin grubuna katılmadan önce okuyucu şunları kontrol eder: 3 ya da altında bir pane baytı, pane başına bitişik kayıtlar, gövdesi tam olarak 9 + cref × 6 bayt olan 1 ile 1.369 arası bir cref, aktif hücre üzerinde uyuşan ve işaret biti olmayan bir irefAct taşıyan parçalar, ızgarada bir aktif sütun ve ters çevrilmiş alan olmayışı. Tek bir fiziksel kaydın içindeki sorunlar kayıt başına bir kez bildirilir; yalnızca birleştirmeden sonra ortaya çıkan çelişkiler — toplam alan sayısını aşan bir irefAct ya da işaret edilen alanın dışında bir aktif hücre gibi — EOFta grup başına bir kez bildirilir
Geçersiz işaretlenmiş bir grup yazılan APIden dışarıda kalır: GetSelectedAreas o pane için indeksi -1 olan boş bir dizi döndürür ve diğer paneler normal çalışmaya devam eder
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 sizin TStringsiniz; HotXLS yalnızca Diagnosticsi doldurur
Log.Add(Format('%s: record $%.4x kept opaque (%s)',
[D.SheetName, D.RecordId, D.Message]));
end;
end;
Kaydı tamir etmek — irefActi kelepçelemek ya da ters çevrilmiş alanı düşürmek — diğer seçenekti. Saklamak daha emniyetli öntanımlıdır: tamir edilmiş bir seçim kimsenin seçmediği bir seçimdir ve özgün baytlar çoktan gitmiş olurdu. Tanı mesajı, başarısız olan kontrolü adlandırır — "alan sayısı kayıt uzunluğuyla uyuşmuyor" ya da "aynı panein kayıtları bitişik değil" gibi — ve bu kod için RecordId daima $001D'dir
Satır ve sütun eklemeleri her panein seçimini yeniden haritalar
Tam satır ya da sütun eklemek ve silmek, her panein seçimini tek bir paylaşılan rutin üzerinden yeniden haritalar; klasik ve XLSX motorunda da. Hayatta kalan alanlar sıralarını korur ve kapsadıkları hücrelerle kayar; aktif alan kimliğini korur. Aktif alan silinirse ondan sonraki ilk hayatta kalan alan aktif olur; arkasından hiçbir şey gelmiyorsa önündeki son hayatta kalan alan olur. Her alan silinmişse grup, silme sınırında tek bir hücreye çöker. Artık seçilen alanın içine düşmeyen bir aktif hücre, o alanın sol üst köşesine taşınır; indeks ile koordinat, kaydedilen dosyada asla birbirini yalan söyleyemez
Yüklemekte geçersiz işaretlenmiş gruplar, yeniden haritalayıcıya dokunulmaz; özgün baytları düzenlemeden sonra da gidiş-dönüş yapar. Bir panede seçimi değiştirmek yalnızca o panein kayıtlarını yeniden yazar ve diğer panelerin kayıtlarını bayt bayt aynı bırakır. ODS dosyaları hiç pane seçim durumu taşımaz; çünkü OpenDocument'in pane başına çalışma sayfası görünümü karşılığı yoktur. Seçimler ve pane kaydırma konumları XLS ile XLSXe uygulanır
Uygulamanız kaydettiği çalışma kitaplarının, işaretlenmiş hücreleri incelemek, kaldıkları yerden devam etmek ya da dondurulmuş bir panoyu okumak için açıldığı durumlardaysa pane bilen seçim ve kaydırma çağrıları, HotXLS Delphi spreadsheet bileşeniyle gelir ve .xls ile .xlsx çıktısında aynı şekilde davranır