Teknik Makale

HotXLS BIFF8 Selection Kayıtları ve Pane Kaydırma

HotXLS, çalışma sayfası seçimlerini ve kaydırma konumlarını hem TXLSWorksheet hem TXLSXWorksheet üzerinde tek bir pane-farkındalıklı API ile saklar: SelectAreas, GetSelectedAreas, ScrollWindow ve TryGetWindowScroll. Klasik .xls dosyaları için HotXLS, her biri en fazla 1369 alan taşıyan BIFF8 Selection kayıtları (0x001D) yazar, mantıksal pane adlarını formatın tanımladığı pane baytlarına çevirir ve her kaydırma eksenini Excel'in beklediği Window2 ya da Pane kaydında tutar

Sorun genellikle mutabakat ya da denetim aracında ortaya çıkar. Araç bir defter dışa aktarımını açar, kaynak sistemle uyuşmayan her hücreyi bulur ve çalışma kitabını, o hücreler donmuş başlık satırı altında seçili olacak biçimde kaydeder; böylece gözden geçiren farkları aramak için kaydırmak yerine doğrudan farkların üzerine düşer. Kırk farkta sorunsuz çalışır. Ay sonu dosyası 3.000 barındırır ve 3.000 alan taşıyan tek bir Selection kaydı var olamaz: gövdesi 18.009 bayt isterdi, bir BIFF8 kaydının taşıyabileceğinin iki katından fazla. Kaydırma konumunda da benzer bir tuzak var. Donmuş panelli bir sayfada "kullanıcının baktığı yer", iki satır konumunu ve iki sütun konumunu paylaşan dört panedir, tek bir koordinat değil

Büyük bir seçim neden birden fazla Selection kaydı gerektiriyor?

Büyük bir seçim birkaç kayıt gerektirir çünkü bir BIFF8 kayıt gövdesi 8224 baytta sınırlandırılmıştır ve her seçili alan sabit altı bayt tutar. [MS-XLS] §2.4.248, Selection kaydını 9 baytlık sabit bir bölüm (pane baytı, aktif hücre için rwAct ve colAct, aktif alan için irefAct, alan sayısı için cref) ve ardından iki 16-bit satır ile iki 8-bit sütun taşıyan cref adet RefU yapısı olarak düzenler. Sığan en büyük sayı (8224 − 9) / 6'nın aşağı yuvarlanmışı, yani 1369'dur ve bu 8223 baytlık bir gövde üretir; sınırın bir bayt altı. TXLSWorksheet.StoreSelectionGroup bu sabiti MaxAreasPerRecord olarak kullanır ve daha büyük bir grubu aynı pane için ardışık Selection kayıtları olarak, seferde 1369 alan olacak biçimde yazar

Isıran ayrıntı irefAct'tir. Her parça aynı aktif satırı, aktif sütunu ve aktif alan indeksini yineler ve irefAct, onu taşıyan kaydın içindeki alanları değil tüm parçaların birleşik dizisini indeksler. Sınırı bir alan aşan bir seçim bunu somutlaştırır: sonuncusu aktif olan 1370 alan iki kayda dönüşür; ilkinde cref 1369, ikincisinde cref 1 vardır ve ikisi de irefAct 1369 taşır. Bu değer, ikinci kaydın kendi alan sayısından büyüktür. Her kayıtta irefAct'i cref ile karşılaştıran bir okuyucu geçerli bir dosyayı reddeder, her kayıtta durumunu üzerine yazan bir okuyucu ise ilk 1369 alanı düşürür. HotXLS okuyucusu ardışık aynı-pane kayıtlarını tek grupta birleştirir, her parçanın aktif hücre ve indeks üzerinde anlaşmasını ister ve aralık kontrolünü yalnızca tam dizi bilindiğinde çalışma sayfasının EOF kaydında çalıştırır. Bu yüzden pane-öncelikli SelectAreas overload'unun 1369 alanlık tavanı yoktur. Her A1 referansını ve aktif indeksi, çalışma sayfası yazma kilidini almadan önce doğrular ve herhangi bir şey bozuksa önceki seçim değişmeden False döner

HotXLS büyük bir çalışma sayfası seçimini neden birden fazla BIFF8 Selection kaydı olarak yazıyor: 8.224 baytlık gövde sınırı 9 sabit bayt artı 1369 adet altı baytlık RefU alanına yer açar, böylece 3.000 alan 1369, 1369 ve 262'lik üç aynı-pane kaydına dönüşür ve irefAct birleşik diziyi indekslediği için son alanı aktif olan 1370 alan her iki kayda da irefAct 1369 verir
Her parça aynı aktif hücre ve indeksi yineler, HotXLS okuyucusu ardışık aynı-pane kayıtlarını tek grupta birleştirir ve aralık kontrolü, tam dizi bilindiğinde yalnızca EOF kaydında çalışır
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ı ve A sütunu yerinde kalır

    SetLength(Diffs, 3000);
    for I := 0 to High(Diffs) do
      Diffs[I] := Format('C%d', [I + 2]);

    // Dondurma saklanan seçimi sıfırlar; seçimi dondurmadan sonra yapın.
    // 3000 alan üç Selection kaydı olarak kaydedilir: 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;

Selection kaydı hangi pane baytını kullanıyor?

Bir Selection kaydı panesini, formatın tanımladığı sayısal kodla tanımlar: sağ-alt için 0, sağ-üst için 1, sol-alt için 2 ve sol-üst için 3. Genel TXLSPanePosition numaralandırması okuma sırasında bildirilir: xlspTopLeft, xlspTopRight, xlspBottomLeft, xlspBottomRight; böylece Ord(xlspTopLeft) 0'dır, ki bu dosyada sağ-alt panedir. Enum'u doğrudan pane baytına cast etmek, her sol-üst seçimini hiçbir hata vermeden sağ-alt pane yazardı. Pane-farkındalıklı her HotXLS giriş noktası bunun yerine enum'u açık bir case ifadesiyle çevirir; çağıranlar sayısal kodlarla hiç uğraşmaz. Pane varlığı da denetlenir: sağ-üst pane yalnızca dikey bölünmeyle, sol-alt yalnızca yatay bölünmeyle, sağ-alt ise yalnızca ikisiyle birden var olur. Mevcut bölünme ya da dondurma geometrisinin olmadığı bir pane için SelectAreas False döner, GetSelectedAreas ise ActiveAreaIndex -1'e ayarlı boş bir dizi döndürür; çalışma kitabında pane, seçim nesnesi ya da hücre oluşturulmaz

HotXLS TXLSPanePosition'ı BIFF8 Selection pane baytına nasıl eşliyor: enum okuma sırasında bildirildiği için Ord(xlspTopLeft) 0'dır, dosya ise sağ-alt için 0, sağ-üst için 1, sol-alt için 2 ve sol-üst için 3 tanımlar; bu yüzden pane-farkındalıklı her giriş noktası açık bir case ifadesiyle çevirir
Enum'u doğrudan pane baytına cast etmek her sol-üst seçimini sağ-alt pane yazardı, bu yüzden HotXLS yazmadan önce pane varlığını mevcut bölünme ya da dondurma geometrisine karşı da denetliyor

Her panenin kaydırma konumu nerede durur?

Her panenin kaydırma konumu iki kayda bölünür, çünkü dört pane yalnızca iki satır ve iki sütun konumu 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 ve Window2.colLeft'tir; alt panelerin satırı ve sağ panelerin sütunu ise Pane.rwTop ve Pane.colLeft'tir. Bu yüzden ScrollWindow(xlspTopRight, R, C) hem Window2.rwTop hem Pane.colLeft yazar ve sağ-üst panenin sütununu ayarlamak sağ-alt paneyi de oynatır — tıpkı Excel'de ikisinin tek bir yatay kaydırma çubuğu paylaşması gibi. Genel yöntemler 1 tabanlı satır ve sütun numaraları kullanır. Olmayan bir pane False döner ve iki sorgu çıktısını da sıfıra çeker, aralık dışı bir koordinat ise herhangi bir eksen değişmeden reddedilir. Buradaki hiçbir şey bir görüntüleyicinin ızgarayı nasıl çizdiğine bağlı değildir. Bir render kontrolü kendi TopRow ve LeftCol değerini taşır — özel VCL grid'de çalışma kitabı render etme makalesinin anlattığı gibi — ve bunlar çalışma zamanı durumudur, kaydedilen şey değildir

Her HotXLS pane kaydırma ekseninin yeri: dört pane iki satır ve iki sütun konumu paylaşır, üst satır ve sol sütun Window2.rwTop ile Window2.colLeft iken alt satır ve sağ sütun Pane.rwTop ile Pane.colLeft'tir ve ScrollWindow(xlspTopRight, 1, 6) bir Window2 alanı artı bir Pane alanı yazar, sağ-alt böylece takip eder
XLSX aynı veriyi sheetView ve pane topLeftCell özniteliklerine yayar ve iki katmanı tek katmana indirmek, bir üst ya da sol kaydırma konumunun yüklemekte sessizce yok olmasının tam nedenidir

XLSX aynı veriyi iki öğeye yayar: pencerenin bütünü için sheetView/@topLeftCell (ECMA-376 Part 1, §18.3.1.87) ve bir bölünmenin sağ-alt tarafı için çocuk pane/@topLeftCell (§18.3.1.66). İki öznitelik de aynı anda bulunabilir. HotXLS dıştaki özniteliği önce pencere düzeyindeki alanlara okur, pane çocuğunun yalnızca pane düzeyindeki alanları ezmesine izin verir ve ikisini ayrı ayrı geri yazar. İki katmanı tek katmana indirmek, bir üst ya da sol kaydırma konumunun yüklemekte sessizce yok olmasının tam nedenidir. Çalışma sayfası kopyaları her iki motorda da iki katmanı taşır. Daha eski giriş noktaları özgün davranışlarını korur: klasik ScrollRow ve ScrollColumn özellikleri ve sıfır tabanlı XLSX SetPaneScroll ile GetPaneScroll. Dondurma ve bölünme geometrisinin kendisi, sayfa koruması, sayfa yapısı ve yazdırma başlığında ele alınan sayfa düzeyi ayarlarla yapılandırılır

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 ç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;

Bir Selection kaydı bozuk olduğunda ne olur?

Bir Selection kaydı bozuk olduğunda HotXLS onu opak baytlar olarak tutar, 1304 tanı kodunu raporlar (xlsDiagnosticSelectionRecordInvalid) ve kaydederken özgün gövdeyi bayt bayt geri yazar. Bir kayıt panesinin grubuna katılmadan önce okuyucu onu sırayla denetler. Pane baytı 3 ya da daha küçük olmalı. Aynı pane'in kayıtları akışta ardışık olmalı. 9 sabit bayt mevcut olmalı. cref 1 ile 1369 arasında olmalı ve gövde tam olarak 9 + cref × 6 bayt uzunluğunda olmalı. Gruptaki her parça aktif hücre ve irefAct üzerinde anlaşmalı, irefAct'in işaret biti set edilmemiş olmalı, aktif sütun ızgara üzerinde olmalı ve hiçbir alan ters sınırlar taşımamalı. Tek bir fiziksel kayıttaki sorunlar kayıt başına bir kez raporlanır. Yalnızca birleştirmeden sonra görünen çelişkiler — irefAct'in toplam alan sayısını aşması ya da aktif hücrenin indekslenen alanın dışında kalması gibi — grup başına bir kez, EOF'ta raporlanır. Geçersiz bir grup tipli API'ye görünmez kalır: GetSelectedAreas o pane için indeks -1'li boş bir dizi döndürür, diğer tüm paneler ç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.Add(Format('%s: record $%.4x kept opaque (%s)',
        [D.SheetName, D.RecordId, D.Message]));
  end;
end;

Seçimler satır ve sütun eklemelerinden nasıl sağ çıkar?

Seçimler yapısal düzenlemelerden sağ çıkar çünkü bütün satır ya da sütun eklemek ya da silmek, hem klasik hem XLSX motorundaki temsil edilen her pane grubunu tek bir ortak remapper üzerinden yeniden haritalar. Sağ kalan alanlar sıralarını korur, aktif alan kimliğini korur. Aktif alan silinirse ilk sağ kalan halef aktif olur; onu takip eden bir şey yoksa son sağ kalan selef olur. Tüm alanlar silinirse grup silme sınırındaki tek bir hücreye büzülür ve seçilen alanın artık içinde düşmeyen bir aktif hücre o alanın sol-üst köşesine taşınır; böylece indeks ile koordinat asla çelişmez. Sınırlar bilinçli. Geçersiz klasik gruplar remapper tarafından uydurma bir seçim haline getirilmek yerine atlanır, böylece özgün baytları round-trip'e devam eder. Tek bir pane'i düzenlemek yalnızca o pane'in kayıtlarını değiştirir, diğerlerini bayt-bayt özdeş bırakır. ODS hiç pane seçim durumu almaz, çünkü ODF'in onu taşıyacak eşdeğer bir çalışma sayfası görünüm yapısı yoktur

Uygulamanız kullanıcıların açıp gezinmesi gereken .xls dosyaları yazıyorsa — işaretli hücreleri gözden geçirmek, kaldıkları yerden devam etmek ya da donmuş bir panoyu paylaşmak olsun — pane-farkındalıklı seçim ve kaydırma API'si HotXLS Delphi spreadsheet component'in bir parçasıdır ve XLS ile XLSX için aynı şekilde çalışır