Teknik Makale

HotXLS Açılır Listeleri ve Delphi'de Girdi Doğrulaması

HotXLS, Excel veri doğrulama açılır listelerini doğrudan Delphi kodundan okur. TryGetCellDropdownItems bir hücre için aday dizgeleri döndürür, SelectCellDropdownItem bunlardan birini indeksle ya da metinle yazar, ValidateCellInput ise yazılan bir değerin hücrenin kuralından geçip geçmeyeceğini hiçbir şey saklanmadan önce raporlar. Aynı metotlar Classic XLS için TXLSWorksheet üzerinde ve XLSX için TXLSXWorksheet üzerinde bulunur; ikisi de 1 tabanlı satır ve sütun numaraları alır

Buna ihtiyaç duyulmasının klasik tetikleyicisi, sizin tasarlamadığınız bir şablondur. İş tarafındaki biri, Draft, Review ve Final ile kilitli bir Status sütunu, gizli bir sayfadaki aralıktan beslenen bir Channel sütunu ve yalnızca 1 ile 100 kabul eden bir adet sütunu olan bir çalışma kitabı gönderir. İçe aktarma işiniz, web formunuz ya da toplu doldurucunuz artık o kurallara uymak zorundadır ve kurallar çalışma kitabının içinde yaşar, sizin kodunuzda değil. İzinli değerleri şablonun yanına kodlamak, biri Excel'de listeyi ilk düzenlediğinde ikisinin ayrışmaya başlamasının ta kendisidir

Kod yazımları Excel veri doğrulamasını neden yok sayıyor?

Veri doğrulama, bir aralığa eklenmiş meta veridir ve onu zorunlu kılmak, kullanıcının yazdığı düzenleyicinin işidir. XLSX'te kural, türü, operatörü, formula1 ile formula2'yi, istemi ve hata uyarısını taşıyan bir dataValidation öğesidir (ECMA-376 Part 1, §18.3.1.32). Classic XLS dosyasında aynı bilgi, [MS-XLS] içinde tanımlanan DV kaydında durur. İki biçim de hücrenin kendisini işaretlemez; hiçbir şey bir programın kuralı bozan bir değer saklamasını engellemez. Excel de aynı davranır: yazılan girişleri kontrol eder ve başka yollardan gelen aralık dışı bir değer, ancak biri Circle Invalid Data çalıştırdığında görünür

HotXLS o modeli bilinçli olarak izler. Sheet.Cells[3, 2].Value := 250 gibi bir atama, 1 ile 100 kuralı altında 250'yi şikayetsiz saklar; tam olarak, amacı kanıtlamak için bunu yapan paketteki DataValidation demo gibi. Kontrol gönüllülük esasıdır ve bu yazının geri kalanı, doğru yerde gönüllü olmaktan bahsediyor

Hücrenin arkasındaki açılır liste öğelerini okumak

TryGetCellDropdownItems(ARow, ACol, AItems, AShowDropDown) yalnızca hücre bir liste türü kural tarafından kapsanıyorsa True döndürür ve AItems değerini adaylardan oluşan bir TArray<WideString> ile doldurur. HotXLS, liste kaynağının pratikte yazıldığı dört yolu çözümler; çağıran formula1'i elle hiç çözümlemez:

  • Tırnaklı satır içi liste, örneğin "Draft,Review,Final"; virgüllerden bölünür
  • Öğeleri #0 karakterleriyle ayrılmış satır içi liste; Classic XLS kurallarında bulunan bir biçim
  • Aynı sayfada ya da başka bir sayfada bir aralık, örneğin =$G$2:$G$10 ya da =Lists!$A$2:$A$7; güncel hücre değerlerine çözümlenerek
  • O aralıklardan birine işaret eden =ProductList gibi bir tanımlı ad

Birleştirilmiş hücreler de halledilir. Bir birleştirmenin içindeki herhangi bir hücreyi sorun ve HotXLS kuralı, Excel'in hem kuralı hem değeri tuttuğu birleştirme çapasında arar

Delphi'de HotXLS açılır liste çözümlemesi: TryGetCellDropdownItems, bir hücreyi ya da birleştirme çapasını, satır içi liste, aralık ya da tanımlı adla beslenen bir liste kuralına eşler
çağıran 1 tabanlı satır ve sütun numaraları geçirir, HotXLS dört liste kaynağı biçimini de çözümler; hiçbir kod formula1'i elle çözümlemez

AShowDropDown bayrağı bir nota hak ediyor, çünkü dosya biçimleri onu ters saklıyor. XLSX'te showDropDown="1" aslında hücre içi oku gizler; BIFF8'de eşdeğer bit ise bir "suppress combo" bayrağıdır. HotXLS ikisini de okurken ters çevirir; dolayısıyla AShowDropDown = True dediği gibi demektir: Excel oku gösterecektir. Oku gizli bir liste kuralı yine de girdiyi kısıtlar; TryGetCellDropdownItems öğeleri her iki durumda da döndürür, tam da bu yüzden

Delphi'de HotXLS ile showDropDown bayrağının ters çevrilmesi: XLSX 1 saklar, BIFF8 oku gizleyen bir suppress combo biti saklar, okuma sırasında geri çevrilir
iki biçim de açılır liste bayrağını tersten saklar; dolayısıyla AShowDropDown = True, Excel'in hücre içi oku gerçekten göstereceği anlamına gelir
uses
  SysUtils, lxHandleX;

procedure DumpChannelList(const FileName: WideString);
var
  Book: TXLSXWorkbook;
  Sheet: TXLSXWorksheet;
  Items: TArray<WideString>;
  ShowArrow: Boolean;
  I: Integer;
begin
  Book := TXLSXWorkbook.Create;
  try
    if Book.Open(FileName) <> 1 then
      raise Exception.Create('Cannot open ' + FileName);
    Sheet := Book.Sheets[1];
    // F2: satır 2, sütun 6, ikisi de 1 tabanlı
    if Sheet.TryGetCellDropdownItems(2, 6, Items, ShowArrow) then
    begin
      Writeln(Length(Items), ' items, arrow visible: ', BoolToStr(ShowArrow, True));
      for I := 0 to High(Items) do
        Writeln('  [', I, '] ', Items[I]);
    end
    else
      Writeln('F2 has no list validation');
  finally
    Book.Free;
  end;
end;

Yalnızca dizgeler gerekiyorsa GetCellDropdownItems(ARow, ACol) diziyi doğrudan döndürür ve liste yokken boş bir dizi verir. TryGetCellValidationPrompt giriş mesajı başlığı ile metnini döndürür; web form alanının yanında şablon yazarının kendi ipucunu göstermek istediğinizde kullanışlıdır

Bir öğeyi indeksle ya da metinle seçmek

SelectCellDropdownItem bir liste girişini hücreye yazar ve hücrede liste kuralı yokken ya da istenen girişe rastlanmadığında False döner. Tam sayı aşırı yükü, TryGetCellDropdownItems değerinin döndürdüğü diziye 0 tabanlı bir indeks alır. Metin aşırı yükü önce tam eşleşme arar, sonra büyük küçük harf duyarsız eşleşmeye düşer; dolayısıyla 'review' yine Review seçer

İki aşırı yük de WriteAutoValue üzerinden yazar, ham bir dizge atamasıyla değil. Bu, aralık tabanlı sayı listeleri için önemlidir: öğeler '3' gibi dizgeler olarak geri gelir ama saklanan değer 3 sayısıdır; kullanıcı okudan seçtiğinde Excel'in sakladığı ve hücreye atıf yapan her formülün beklediği budur. DataValidation demosu, XLS, XLSX ve XLSB'de kaydet ve yeniden aç sonrasında tam olarak bunu kontrol eder

// TXLSWorksheet (XLS) ve TXLSXWorksheet (XLSX) üzerinde aynı çağrılar
if not Sheet.SelectCellDropdownItem(2, 1, 'Review') then
  Writeln('A2: "Review" is not in the list');

if not Sheet.SelectCellDropdownItem(2, 6, 3) then  // 4. öğe, 0 tabanlı
  Writeln('F2: index 3 is out of range');

Yazılan girdiyi yazmadan önce nasıl doğrularsınız?

ValidateCellInput(ARow, ACol, AText), bir aday dizgeyi hücreyi kapsayan kurala karşı sınar ve hücreyi asla değiştirmez. Kural uygulanmıyorsa ya da aday geçiyorsa True döner. Dizge, WriteAutoValue kullandığı aynı metin-değer dönüşümünden geçer; kontrol ile sonraki yazım aynı değeri görür. XLSX tarafında o dönüşüm XlsxAutoTextValue'dur: TRUE/FALSE değerlerini, hata literallerini, ISO tarihlerini, m/d/yyyy ile d.m.yyyy tarihlerini ve değişmez biçimli sayıları tanır, 0123 değerini Excel gibi metin tutar. Classic XLS motoru, kendi WriteAutoValue değerini aynalayan kendi dönüştürücüsüne sahiptir

İki yardımcı seti tamamlar. ValidateCellValue(ARow, ACol) hücrede çoktan saklanmış değeri kontrol eder ve boş hücreleri geçerli sayar. TryGetCellValidationError kuralın hata uyarı başlığı, metni ve stilini döndürür; kural hiç hata uyarısı göstermiyorsa False verir. Stil türü motora göre değişir; çalışma sayfası sınıfıyla eşleyin: TXLSWorksheet için lxDataCheck kaynağından TXLSDvErrorStyle, TXLSXWorksheet için TXLSXDvErrorStyle

Kendinizin yeniden üretmek zorunda olduğu bir davranış: Excel, boş bir girişi ya da = ile başlayan hiçbir şeyi doğrulamaz. HotXLS o atlamayı görüntüleyicinin düzenleme yolunda uygular, ValidateCellInput içinde değil; sunucu tarafı bir kapı, çağırmadan önce aynı atlamayı yapmalıdır:

uses
  lxDataCheck, lxHandle;

// AText yazılabiliyorsa True döner; Excel'in uyarı stillerini aynalar
function AcceptTypedValue(Sheet: TXLSWorksheet; ARow, ACol: Integer;
  const AText: WideString; out AMessage: WideString): Boolean;
var
  Title, Text: WideString;
  Style: TXLSDvErrorStyle;
begin
  AMessage := '';
  Result := True;
  // Excel temizlenmiş hücreler ve formüller için doğrulamayı atlar
  if (AText = '') or (AText[1] = '=') then
    Exit;
  // Hata uyarısı yapılmamış: Excel her şeye izin verir
  if not Sheet.TryGetCellValidationError(ARow, ACol, Title, Text, Style) then
    Exit;
  if Sheet.ValidateCellInput(ARow, ACol, AText) then
    Exit;
  AMessage := Title + ': ' + Text;
  // Stop reddeder; Warning ile Information kararı çağırana bırakır
  Result := Style <> xlsDvErrStop;
end;

// Çağıran (Msg: WideString): önce doğrula, sonra aynı dönüşümle sakla
if AcceptTypedValue(Sheet, 2, 2, '42', Msg) then
  Sheet.WriteAutoValue(2, 2, '42');

ValidateCellInput ile WriteAutoValue eşlemesi, örneğin asıl mesajıdır. Bir dönüşümle doğrulayıp öbürüyle saklayın; er ya da geç bir değer kontrolü sayı olarak geçer ve hücreye metin olarak düşer

Delphi'de HotXLS ile önce doğrula sonra yaz hattı: ValidateCellInput metni tam olarak WriteAutoValue gibi dönüştürür, uyarı stili engelleme ile çağıran seçimi arasında karar verir
yazdığınız dönüşümle doğrulayın; yoksa bir değer, kontrolü sayı olarak geçip hücreye metin olarak düşebilir

Tırnaklı liste öğeleri ve Classic XLS kaçış tuzağı

Çift tırnak içeren satır içi liste öğeleri, bir kuralı sessizce kaybetmenin kolay yoludur. Satır içi liste, bir formül dizgesi olarak saklanır ve formül dizgesi gömülü bir " işaretini ikileerek kaçırır. İçtekileri ikilemeden öğeleri tırnaklara sarın ve He said "hi",No gibi bir liste, derlenmeyen bir formül üretir. İstisna yoktur: kural hiç kurulmamıştır ve açılır liste onunla birlikte gider. TXLSWorksheet.AddListValidation ile XLSX karşılığı, gömülü tırnakları ikisizle; böyle öğeler sağlam kalır

TXLSWorksheet.AddListValidation ayrıca, TXLSXWorksheet üzerindekiyle eşleşen bir array of WideString aşırı yüküne sahiptir. Kullanışlıdır ama ne yaptığını okuyun: öğeleri virgülle birleştirir ve sonucu dizge aşırı yüküne verir. Kendisi virgül içeren bir öğe bu yüzden iki öğeye dönüşür ve Classic motorunda ! içeren bir kaynak dizge, satır içi liste yerine sayfa nitelemeli bir başvuru sanılır. O tür başlıklar için değerleri bir hücre aralığına koyun ve kuralı ona yönlendirin

var
  Book: IXLSWorkbook;
  Sheet: TXLSWorksheet;
begin
  Book := TXLSWorkbook.Create;
  Sheet := Book.Sheets.Add;
  Sheet.AddListValidation('H2:H11', ['He said "hi"', 'Plain option']);
  // Kurala güvenmeden önce var olduğunu teyit et
  if Sheet.FindDataValidationAt(2, 8) = nil then
    raise Exception.Create('List rule for H2:H11 was not created');
  // ...
end;

Kaydet ve yeniden aç sonrasında H2 üzerindeki TryGetCellDropdownItems, He said "hi" değerini tırnakları yerindeyken döndürür; XLS, XLSX, XLSB ve ODS çıktısında aynı biçimde

Kuralları TXLSWorkbookViewer'a zorlatmak

Kullanıcılar hücreleri ekranda düzenlerken TXLSWorkbookViewer aynı kontrolleri sizin yerinize uygular. Editable değeri True ve EnforceDataValidation varsayılan True iken, hata uyarılı bir kuralı bozan yazılan düzenleme, yazılmadan önce kontrol edilir: Stop reddeder, Warning sorar, Information bilgilendirir. Görüntüleyici ayrıca etkin hücrede açılır liste okunu çizer, listeyi tıklamayla ya da Alt+Down ile açar ve sarı giriş istemini hücrenin altında gösterir. Izgaranın kendi ayrıntıları, TXLSWorkbookViewer ile özel bir elektronik tablo ızgarası kurma yazısında kapsanıyor

CircleInvalidData değeri True yapılınca, kuralını ihlal eden saklanmış değerlerin çevresine Excel'in aynı adlı komutu gibi kırmızı daireler çizilir. Yalnızca görünür alanı tarar, çoğu hücreyi FindDataValidationAt üzerinden hızla eler ve hiçbir zaman boş hücreyi daire içine almaz

procedure TMainForm.FormCreate(Sender: TObject);
begin
  Viewer.Editable := True;
  Viewer.EnforceDataValidation := True;
  Viewer.CircleInvalidData := True;
  Viewer.OnValidationError := ViewerValidationError;
end;

procedure TMainForm.ViewerValidationError(Sender: TObject; Row, Col: Integer;
  const Text: WideString; Style: TXLSViewerValidationErrorStyle;
  const AlertTitle, AlertText: WideString;
  var Action: TXLSViewerValidationAction; var Handled: Boolean);
begin
  StatusBar.SimpleText := AlertTitle + ': ' + AlertText;
  if Style = xveStop then
    Action := xvaRetry    // reddedilen metinle düzenleyiciyi yeniden aç
  else
    Action := xvaAccept;
  Handled := True;        // yerleşik pencereyi bastır
end;

Handled değeri False kalırsa görüntüleyici kendi uyarı penceresini gösterir; bu, hem VCL hem LCL altında çalışır. O varsayılan otomatik testlerde ısırır: Handled kurmayan bir OnValidationError işleyicisi olmadan düzenlemeleri süren bir test, kalıcı bir pencerede bloke olur. Görüntüleyicinin stil enum'u üçüncü bir türdür: xveStop, xveWarning ve xveInformation taşıyan TXLSViewerValidationErrorStyle; iki çalışma sayfası enum'undan ayrıdır

HotXLS girdi doğrulamasına güvenmeden önce neyi bilmelisiniz?

Kontroller liste, sayı, tarih, saat ve metin uzunluğu kuralları için sadıktır; motora özel birkaç ayrıntıyı bilmekte fayda var:

  • Classic XLS çalışma sayfalarında ValidateCellInput, özel formüllü kuralları yazılan adaylar için geçerli raporlar; çünkü bir aday, bağımlılık zincirini bozmadan geçici olarak bir BIFF hücresine konulamaz. Saklanan değeri, özel formülü değerlendiren ValidateCellValue ile kontrol edin ya da adaylar için özel kuralları da değerlendiren XLSX motorunu kullanın
  • Classic XLS'te liste eşleme, sayısal olarak eşit değerleri aynı öğe sayar; dolayısıyla 5 ile 5.0 ikisi de 5 öğeli bir öğeyle eşleşir
  • Görüntüleyicide yapıştırılan içerik, Excel'de olduğu gibi doğrulamayı atlar. EditSelectedCell yeniden açılacak bir düzenleyici bulamaz; orada bir Retry sonucu Cancel gibi davranır
  • Sade Cells[...].Value atamaları tasarımdan ötürü hiç kontrol edilmez. Doğrulama yalnızca kodunuzun istediği yerde olur

Kuralları en baştan kurmak; tam sayı, tarih, metin uzunluğu ve özel türler dahil, HotXLS veri doğrulama, AutoFilter ve tablolar rehberinde kapsanıyor. XLSB çalışma kitapları TXLSXWorkbook içine yüklenir; dolayısıyla burada gösterilen her TXLSXWorksheet çağrısı onların üzerinde değişmeden çalışır, bkz. Delphi'de XLSB ikili çalışma kitaplarını okuma ve yazma yazısı

Açılır liste çözümlemesi, öğe seçimi ve girdi doğrulaması; bu yazıdaki her çağrıyı çalıştıran DataValidation ile WorkbookViewer demolarıyla birlikte, Delphi, C++Builder ve Lazarus için HotXLS Delphi Excel bileşeninde gelir