Teknik Makale

Delphi'de HotXLS ile Koşullu Biçimlendirme, Zengin Metin ve Hücre Stilleri

OOXML'deki bir koşullu biçimlendirme kuralı, tek bir adı taşıyan iki ayrı şeydir. Koşul (bir karşılaştırma, bir formül, bir metin eşleşmesi) hangi hücrelerin nitelendirileceğine karar verir. Görünüm (ECMA-376 terimleriyle diferansiyel biçim kaydı, dxf) bu hücrelerin nasıl görüneceğine karar verir. Excel'in iletişim kutusu, her ikisini de aynı anda doldurmanızı sağlayarak dikiş yerini gizler. HotXLS ise bunu yapmaz. Delphi'den bir cellIs kuralı oluşturun ve stili atlayın; kural geçerlidir, aralık doğrudur, formül tam olarak doğru hücrelerde doğru (true) olarak değerlendirilir ve hiçbir şey renk değiştirmez, çünkü kuralın talimatı "doğruysa, hiçbir şey boyama" olmuştur. Koşul ile sonuç arasındaki bu boşluk, ilk olarak düzeltilmesi gereken şeydir ve Kuralları Yönet ekranında doğru göründüğü halde hiçbir şeyi vurgulamayan kuralların çoğundan sorumludur

HotXLS, koşullu biçimlendirmeyi hem BIFF8 .xls hem de OOXML .xlsx dosyalarına yerel olarak yazar ve aynısını zengin metin çalıştırmaları ve havuzlu hücre stili modeli için de yapar. Bu üç özellik, düz API yüzeyinin gösterdiğinden daha fazla ortak altyapı paylaşır ve çıktının niyetten saptığı yerler genellikle aralarındaki eklemlerdir

Bir koşulun bir sonuca ihtiyacı vardır: dxf stili

XLSX çalışma sayfasında, karşılaştırma kuralları bir aralık, TXLSXCfOperator'den bir operatör ve bir formül veya sabit alan ve ardından sayfanın ConditionalFormats koleksiyonundaki yeni kuralın dizinini döndürür olan AddConditionalFormat'ten gelir. Bu dizindeki kural nesnesi bir Style özelliği açığa çıkarır ve vurgulama burada yaşar. Üzerine bir dolgu ayarlayın ve eşleşen hücreler dolguyu alsın. Dokunmadan bırakırsanız, yukarıda açıklanan görünmez kuralı oluşturmuş olursunuz

var
  Book: TXLSXWorkbook;
  Sheet: TXLSXWorksheet;
  Idx: Integer;
begin
  Book := TXLSXWorkbook.Create;
  try
    Book.Open('kpi.xlsx');
    Sheet := Book.Sheets[0];

    // Negatif sapma: açık kırmızı dolgu
    Idx := Sheet.AddConditionalFormat('D2:D200', xlsxCfOpLessThan, '0');
    Sheet.ConditionalFormats[Idx].Style.SetFillBgColor($FFFFC7CE);

    // Yinelenen sipariş kimlikleri de aynı şekilde işaretlenir
    Idx := Sheet.AddCondFormatDuplicateValues('A2:A200');
    Sheet.ConditionalFormats[Idx].Style.SetFillBgColor($FFFFEB9C);

    // Özel formül kuralı: fiili değerin hedefin %90'ını kaçırdığı satırları vurgular
    Idx := Sheet.AddCondFormatExpression('B2:B200', '$C2<$B2*0.9');
    Sheet.ConditionalFormats[Idx].Style.SetFillBgColor($FFFFC7CE);

    Book.SaveAs('kpi-flagged.xlsx');
  finally
    Book.Free;
  end;
end;

Buradaki renkler 32 bitlik ARGB değerleridir, bu nedenle $FFFFC7CE, RGB'nin önünde tamamen opak bir alfa baytı bulunan, iletişim kutusundan bildiğiniz Excel "açık kırmızı"sıdır. Hücre başına bir koşulda tetiklenen her kural türü aynı oluştur-ve-sonra-biçimlendir şeklini takip eder. Metin eşleştiriciler (AddCondFormatContainsText, AddCondFormatBeginsWith, AddCondFormatEndsWith) sonradan biçimlendireceğiniz bir dizin döndürür; AddCondFormatTop10, AddCondFormatAboveAverage ile boşluk ve hata dedektörleri de öyle. Kalıbı bir kez öğrendiğinizde, tüm metin ve karşılaştırma ailesi aynı şekilde davranır

Veri çubukları, renk ölçekleri ve simge kümeleri kendilerini boyar

Görsel kural türleri ise tam tersi şekilde çalışır. Görünümlerini kural tanımının içinde taşırlar ve Style özelliğini tamamen görmezden gelirler. Veri çubuğu kuralına bir dolgu atayın ve hiçbir şey olmaz; bu durum sınıflandırma oturana kadar bir hata gibi görünür: AddCondFormatDataBar çubuk rengini doğrudan bir bağımsız değişken olarak alır, iki ve üç noktalı renk ölçekleri uç nokta renklerini aynı şekilde alır ve AddCondFormatIconSet, icsTrafficLights3 gibi 26 simge kümesi türünden birini seçer. Burada unutulacak ayrı bir stil kaydı yoktur çünkü zaten ayrı bir stil kaydı bulunmamaktadır

Bu çağrılarda üzerinde düşünülmeye değer parametreler, TXLSCfValueKind tipindeki değer çıpalarıdır. Bir çubuk veya ölçek uç noktası aralık minimumunda veya maksimumunda, sabit bir sayıda, bir yüzde veya yüzde birlik dilimde (percentile) ya da bir formülün sonucunda yer alabilir. Varsayılan olan aralık-minimumu ve aralık-maksimumu, düzenli demo verilerinde iyi davranır ve ardından aykırı değerler (outliers) içeren gerçek verilerde sizi ele verir: Kontrolden çıkmış tek bir değer ölçeği esnetir ve diğer her çubuğu bir güdük şeklinde düzleştirir. Gösterge paneli (dashboard) dönemler boyunca okunmak üzere tasarlandığında, uç noktaları sabit sayılara veya yüzdelik dilimlere sabitleyin, böylece Mart ayındaki yarım çubuk, Nisan ayındaki yarım çubukla aynı miktarı ifade etsin. Otomatik ölçeklendirilmiş bir çubuk yalnızca kendisiyle karşılaştırılabilir

XLS yazıcısı dört kural türünü kapsar, daha fazlasını değil

Eski BIFF8 tarafı, XLSX tarafının daha küçük bir aynası değildir; kasıtlı bir alt kümesidir. XLS arayüzü; akışa CF12 kayıtları olarak yayılan tam olarak dört koşullu kural şeklini (veri çubukları, iki renkli ölçekler, üç renkli ölçekler ve simge kümeleri) oluşturabilir. cellIs, ifade veya metin kuralları için bir oluşturma API'si yoktur. Açtığınız bir dosyada halihazırda bulunan bu tür kurallar okunur, tutulur ve değiştirilmeden geri yazılır, bu nedenle bir müşterinin .xls dosyasını açıp yeniden kaydetmek, taşıdığı biçimlendirmeye asla zarar vermez. Yapamayacağınız şey, bir .xls dosyasında sıfırdan eşik vurgulaması oluşturmaktır. Buradaki seçenekler, kodda hesaplanan sıradan hücre dolgularıyla bunu taklit etmek veya tüm kural ailesinin masada olduğu bir .xlsx çıktısı yapmaktır

Bu, veri katmanı var olmadan önce çözülmesi gereken bir kısıtlamadır, sonrasında değil; çünkü gösterge paneli şeklindeki her şey için dosya biçimi kararını değiştirir. Uyumluluk için .xls seçip ardından cellIs eşikleri olan bir KPI raporu belirleyen bir ekip, birbiriyle uyuşmayan iki şey seçmiştir ve bunu fark etmek için en ucuz zaman, inşaata başladıktan üç hafta sonra değil, biçim kararının verildiği andır

Kural yığınlama, öncelik ve çakışan aralıklar

Gerçek gösterge panelleri nadiren aralık başına tek bir kural çalıştırır. Bir sapma sütunu, büyüklük için bir veri çubuğu, sert eşik için bir cellIs kuralı ve bunların her ikisinin üzerinde tırmanmalar (escalations) için satır düzeyinde bir ifade kuralı taşıyabilir. Her bir TXLSXConditionalFormat bir Priority (öncelik) değeri açığa çıkarır ve Excel rekabet eden kuralları öncelik sırasına göre çözer. İki kural aynı hücreyi boyamak istediğinde kazanan, bir inceleyenin Kuralları Yönet iletişim kutusunda kaydırdığı sıraya göre değil, ayarladığınız bir sayıya göre belirlenir

Önceliği, bir çizim programının z-sırasını ele aldığı şekilde ele alın. İki kuralın aynı hücrelere ulaşabileceği her yerde bunu kasıtlı olarak atayın ve değerler arasında boşluklar bırakın, böylece daha sonraki bir kural diğerlerini yeniden numaralandırmadan araya girebilir. Kuralların çakışamayacağı durumlarda (örneğin E sütunuyla sınırlı bir veri çubuğu ve G sütunuyla sınırlı bir metin kuralı), oluşturma sırası iyidir ve öncelik ilgiyi hak etmez. Bu ilgiyi bunun yerine aralık sınırlarına harcayın, çünkü buradaki pahalı hatalar neredeyse hiçbir zaman öncelik tersine dönmeleri değildir. Bunlar, 350 satıra kadar büyüyen bir rapordaki B2:B200 gibi, kapsanmayan kuyruk kısmının tam olarak sağlıklı veri gibi görünen düz hücreler olarak işlendiği aralıklardır. Her kural aralığını, çalışma kitabının başka yerlerindeki grafik serilerini ve doğrulama aralıklarını yönlendiren aynı son satır sayısı değerinden türetin; böylece kuyruk düşmeyi bırakacaktır

Bir doğrulama alışkanlığı değerini kanıtlar. Oluşturduktan sonra dosyayı Excel'de açın, biçimlendirilmiş aralığı seçin ve her şablon değişikliği için Kuralları Yönet ekranını bir kez gezin. Koşullu biçimlendirme, tek yetkili işleyicinin (renderer) dosyayı tüketen uygulama olduğu birkaç alandan biridir, bu nedenle XML üzerindeki bir birim test kuralın yazıldığını kanıtlar, Excel'in bunu kastettiğiniz şekilde boyadığını değil. Bir dakikalık göz atma bu boşluğu kapatır

Zengin metin: Tek bir hücre içinde birçok biçim

XLSX modelindeki bir zengin metin (rich text) hücresi, her biri bir metin aralığı artı kendi yazı tipi özniteliklerinden oluşan bir çalıştırmalar (runs) listesi tutar. Listeyi kenarda bir TXLSXRichText nesnesi olarak oluşturur, ona çalıştırmalar ekler ve ardından her şeyi bir hücreye bağlarsınız. Sahiplik kuralı ısıran kısımdır. Cell.RichText özelliğine atama yapmak o nesnenin sahipliğini hücreye devreder ve hücre kendi yıkımı (destruction) sırasında onu serbest bırakır. Kendiniz de serbest bırakırsanız bir double-free hatası alırsınız; bu hata, buna neden olan çalışma boyunca sessiz kalan ve çok sonra alakasız bir yerde çökme olarak yüzeye çıkan türdendir

var
  Rich: TXLSXRichText;
  Run: TXLSXRichTextRun;
begin
  Rich := TXLSXRichText.Create;
  Rich.AddRunText('Status: ');
  Run := Rich.AddRunText('OVERDUE');
  Run.Bold := True;
  Run.Color := $FFC00000;
  Run.ColorIsAuto := False;
  Run := Rich.AddRunText(' (escalated to regional manager)');
  Run.Italic := True;
  Sheet.Cells[2, 7].RichText := Rich;   // sahiplik hücreye geçer: serbest bırakmayın (do not Free)
end;

Açıkça belirtilen ColorIsAuto := False isteğe bağlı bir süsleme değildir. Bir çalıştırma otomatik renk bayrağı taşır ve bir renk ataması ancak o bayrak temizlendiğinde onurlandırılır. Color ayarlayıp ColorIsAuto'yu unutursanız, çalıştırma kalın ancak inatla siyah çıkar ve nedeni gösterecek hiçbir hata olmaz. Çalıştırmalar ayrıca üstü çizili, altı çizili varyantları ile üst simge ve alt simge için dikey hizalamayı destekler; PlainText ise metin içeriğini dışa aktarmanız veya karşılaştırmanız (diff) gerektiğinde tüm listeyi tek bir dizeye düzleştirir

Hücre düzeyinde zengin metin yalnızca XLSX'e özgüdür. XLS arayüzünde bunu yazmak için genel bir API yoktur, ancak çalıştırmalar orada TextRuns aracılığıyla yorumlar ve metin kutuları üzerinde mevcuttur; mevcut bir .xls dosyasından okunan zengin dizeler ise bir gidiş-dönüşten zarar görmeden sağ çıkar. Yönelim koşullu biçimlendirmedekiyle aynıdır: Bir hücre içindeki biçimleri karıştıran her şey XLSX yazıcısına aittir

Stil havuzu ve indeks kayması hatası

XLSX modelindeki düz hücre biçimlendirmesi, çalışma kitabındaki havuzlu koleksiyonlar aracılığıyla çalışır. Fonts.Add, Fills.AddSolid ve Borders.Add her biri bir tanım kaydeder ve havuzdaki dizini döndürür. Bu dizinler 0 tabanlıdır. Bunları tüketen hücre tarafındaki özellikler (FontIndex gibi), 0'ı "varsayılan" için ayırır, bu nedenle bir hücreye atadığınız değer havuz dizini artı birdir:

HeaderFont := Book.Fonts.Add('Calibri', 11, True, False);  // havuz dizini, 0 tabanlı
for Col := 1 to 6 do
  Sheet.Cells[1, Col].FontIndex := HeaderFont + 1;          // hücre dizini, 1 tabanlı

+ 1'i düşürdüğünüzde her başlık varsayılan yazı tipine geri döner. Hiçbir istisna ve uyarı olmaz, sadece kimsenin biçimlendirmediği gibi görünen bir çalışma kitabı elde edersiniz. İkinci derece hata döngüde gizlidir: Satır başına bir kez Fonts.Add çağırmak. Özdeş yazı tipi tanımları tekilleştirilir, bu nedenle dosya bozulmaz ancak iş boşa harcanır ve özellikle hizalama havuzu, kopyaları katlamak yerine her çağrıda yeni bir nesne döndürür. Döngüden önce bir avuç stili bir kez oluşturun ve dizinlerini yeniden kullanın. Yüz bin satırlık raporlarda bu tek değişiklik, HotXLS için büyük çalışma kitabı performans optimizasyonunda ele alınan manivelalardan biridir. Yalnızca hazır bir anlamsal görünüme ihtiyacınız olduğunda, her iki arayüz de aralıklar üzerinde, havuzlara hiç dokunmadan Excel'in yerleşik İyi, Kötü, Nötr ve vurgu stilleriyle eşleşen ApplyBuiltinStyle özelliğini sunar

Koşullu biçimlendirme, zengin metin ve havuzlu stiller; veri modeli ve düzen oturduktan sonra uygulanan bir raporun son kilometresidir; bu önceki aşamalar, HotXLS ile şablon tabanlı rapor oluşturmanın konusudur. Tam kural, çalıştırma ve stil referansı HotXLS Bileşeni ürün sayfasında yer almaktadır