يقرأ HotXLS قوائم dropdown للتحقق من صحة البيانات في Excel مباشرة من كود Delphi. تعيد TryGetCellDropdownItems السلاسل المرشحة لخلية، وتكتب SelectCellDropdownItem إحداها بالفهرس أو بالنص، ويسجل ValidateCellInput هل ستتجاوز قيمة مكتوبة قاعدة الخلية قبل تخزين أي شيء. والتوابع نفسها موجودة على TXLSWorksheet للـ Classic XLS وعلى TXLSXWorksheet للـ XLSX، وكلاهما يأخذ أرقام صفوف وأعمدة بترقيم من 1
المحفز المعتاد للاحتياج لهذا قالب لم تصممه: شخص من الجهة التجارية يشحن مصنفًا بعمود Status مقفولًا على Draft وReview وFinal، وعمود Channel يتغذى من نطاق في ورقة مخفية، وعمود كمية لا يقبل إلا من 1 إلى 100. ومهمة الاستيراد أو نموذج ويب أو المالئ الدفعي لديك يصبح مضطرًا لاحترام تلك القواعد، والقواعد تسكن المصنف لا كودك. وترميز القيم المسموحة صلبًا بجوار القالب هو الطريقة التي تنشعب بها الاثنتان أول مرة يحرر أحد القائمة في Excel
لماذا تتجاهل كتابات الكود التحقق من صحة البيانات في Excel؟
التحقق من صحة البيانات بيانات وصفية ملحقة بنطاق، وفرضها شأن أي محرر يكتب فيه المستخدم. في XLSX القاعدة عنصر dataValidation (الجزء 1 من ECMA-376، §18.3.1.32) يحمل النوع والمعامل وformula1 وformula2 والطلب وتنبيه الخطأ. وفي ملف Classic XLS تجلس المعلومة نفسها في سجل DV الموصوف في [MS-XLS]. ولا ممن الصيغتين يميز الخلية نفسها، فلا شيء يمنع برنامجًا من تخزين قيمة تكسر القاعدة. ويتصرف Excel بالطريقة نفسها: يفحص المدخلات المكتوبة، والقيمة خارج المدى التي وصلت بوسائل أخرى لا تظهر إلا حين يشغل أحد Circle Invalid Data
يتبع HotXLS ذلك النموذج بقصد: إسناد مثل Sheet.Cells[3, 2].Value := 250 يخزن 250 تحت قاعدة من 1 إلى 100 دون شكوى، تمامًا كما تفعل عرضة DataValidation المرفقة لإثبات النقطة. والفحص اختياري الانضمام، وبقية هذه المقالة عن الانضمام في الموضع الصحيح
قراءة عناصر dropdown خلف خلية
تعيد TryGetCellDropdownItems(ARow, ACol, AItems, AShowDropDown) True فقط حين تغطي القاعدة الخلية من نوع قائمة، وتملا AItems بـ TArray<WideString> من المرشحين. ويحل HotXLS الطرق الأربعة التي تكتب بها مصادر القوائم عمليًا، فلا يحلل المستدعي formula1 يدويًا أبدًا:
- قائمة لاصقة مقتبسة مثل
"Draft,Review,Final"مقسمة على الفواصل - قائمة لاصقة تفصل عناصرها محارف
#0، وهي صيغة توجد في قواعد Classic XLS - نطاق في الورقة نفسها أو في ورقة أخرى، مثل
=$G$2:$G$10أو=Lists!$A$2:$A$7، يقيَّم إلى قيم خلاياه الحالية - اسم معرَّف مثل
=ProductListيشير إلى أحد تلك النطاقات
الخلايا المدمجة يعالجها أيضًا: اطلب أي خلية داخل دمج فيبحث HotXLS القاعدة عند مرساة الدمج، وهي موضع احتفاظ Excel بالقاعدة والقيمة معًا
علم AShowDropDown يستحق ملاحظة، لأن صيغ الملفات تخزنه بالعكس: في XLSX فإن showDropDown="1" يخفي فعليًا السهم داخل الخلية، وفي BIFF8 البت المكافئ علم "كبت combo". يقلب HotXLS الاثنين عند القراءة، فـ AShowDropDown = True تعني ما تقول: كان Excel سيعرض السهم. وقاعدة قائمة والسهم مخفي ما تزال تقيد الإدخال، ولهذا تعيد TryGetCellDropdownItems العناصر في الحالتين
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: صف 2 عمود 6، كلاهما بترقيم من 1
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;
إن كنت تحتاج السلاسل فقط، فتعيد GetCellDropdownItems(ARow, ACol) المصفوفة مباشرة وتعطي مصفوفة فارغة حين لا تنطبق قائمة. ويعيد TryGetCellValidationPrompt عنوان نص رسالة الإدخال ونصه، وهو مفيد حين تريد عرض تلميح مؤلف القالب نفسه بجوار حقل نموذج ويب
اختيار عنصر بالفهرس أو بالنص
تكتب SelectCellDropdownItem إدخال قائمة في الخلية وتعيد False حين لا قاعدة قائمة للخلية أو الإدخال المطلوب غير موجود. والنسخة الصحيحة تأخذ فهرسًا بترقيم من 0 داخل المصفوفة التي أعادت TryGetCellDropdownItems. وتبحث النسخة النصية عن تطابق تام أولًا ثم تعود إلى تطابق غير حساس لحالة الأحرف، فـ 'review' ما زالت تختار Review
وكلا النسختين تكتب عبر WriteAutoValue لا عبر إسناد سلسلة خام. وهذا مهم للقوائم المدعومة بنطاق من الأعداد: العناصر تعود سلاسل مثل '3'، لكن القيمة المخزنة هي العدد 3، وهو ما يخزنه Excel حين يختار المستخدم من السهم وما تتوقعه أي صيغة تشير إلى الخلية. ويفحص عرض DataValidation هذا بالضبط بعد حفظ وإعادة فتح في XLS وXLSX وXLSB
// الاستدعاءات نفسها على TXLSWorksheet (XLS) وTXLSXWorksheet (XLSX)
if not Sheet.SelectCellDropdownItem(2, 1, 'Review') then
Writeln('A2: "Review" is not in the list');
if not Sheet.SelectCellDropdownItem(2, 6, 3) then // العنصر الرابع، بترقيم من 0
Writeln('F2: index 3 is out of range');
كيف تتحقق من إدخال مكتوب قبل كتابته؟
تختبر ValidateCellInput(ARow, ACol, AText) سلسلة مرشحة مقابل القاعدة المغطية للخلية ولا تعدل الخلية أبدًا. وتعيد True حين لا تنطبق قاعدة أو المرشح يجتاز. وتمر السلسلة عبر التحويل نفسه من نص إلى قيمة الذي تستخدمه WriteAutoValue، فيرى الفحص والكتابة النهائية القيمة نفسها. وعلى جانب XLSX يكون ذلك التحويل XlsxAutoTextValue: يتعرف على TRUE/FALSE وliteralات الأخطاء وتواريخ ISO وتواريخ m/d/yyyy وd.m.yyyy والأعداد بالصيغة الثابتة، ويحفظ 0123 نصًا كما يفعل Excel. ومحرك Classic XLS له محوِّله الخاص الذي يحاكي WriteAutoValue الخاصة به
رفيقان يكملان المجموعة: يفحص ValidateCellValue(ARow, ACol) القيمة المخزنة أصلًا في الخلية ويعامل الخلايا الفارغة صالحة. ويعيد TryGetCellValidationError عنوان تنبيه خطأ القاعدة ونصه ونمطه، ويعيد False حين لا تعرض القاعدة تنبيه خطأ أصلًا. ونوع النمط يختلف بحسب المحرك، فطابقه مع صنف ورقة العمل: TXLSDvErrorStyle من lxDataCheck لـ TXLSWorksheet، وTXLSXDvErrorStyle لـ TXLSXWorksheet
سلوك واحد عليك إعادة إنتاجه بنفسك: لا يتحقق Excel من إدخال فارغ أو أي شيء يبدأ بـ =. ويطبق HotXLS ذلك التخطي في مسار تحرير الـ viewer لا داخل ValidateCellInput، فيجب على بوابة على جانب الخادم أن تفعل الشيء نفسه قبل استدعائها:
uses
lxDataCheck, lxHandle;
// تعيد True حين يجوز كتابة AText، محاكية أنماط تنبيه Excel
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 يتخطى التحقق للخلايا الممحوة والصيغ
if (AText = '') or (AText[1] = '=') then
Exit;
// لا تنبيه خطأ مضبوطًا: Excel يمرر أي شيء
if not Sheet.TryGetCellValidationError(ARow, ACol, Title, Text, Style) then
Exit;
if Sheet.ValidateCellInput(ARow, ACol, AText) then
Exit;
AMessage := Title + ': ' + Text;
// Stop يرفض؛ وWarning وInformation تتركان القرار للمستدعي
Result := Style <> xlsDvErrStop;
end;
// المستدعي (Msg: WideString): تحقق، ثم خزّن بالتحويل نفسه
if AcceptTypedValue(Sheet, 2, 2, '42', Msg) then
Sheet.WriteAutoValue(2, 2, '42');
إقران ValidateCellInput مع WriteAutoValue هو مغزى المثال: تحقق بتحويل واخزن بآخر، وعاجلًا أو آجلًا ستجتاز قيمة الفحص كعدد وتهبط في الخلية نصًا
عناصر القوائم المقتبسة ومصيدة التهريب في Classic XLS
عناصر قوائم لاصقة تحوي اقتباسات مزدوجة طريقة سهلة لفقدان قاعدة بصمت: القائمة اللاصقة تخزن كسلسلة صيغة، وسلسلة الصيغة تهرب اقتباسًا مضمَّنًا " بمضاعفته. لف العناصر اقتباسًا دون مضاعفة الداخلي، وقائمة مثل He said "hi",No تنتج صيغة لا تترجم. ولا استثناء: القاعدة ببساطة لا تنشأ، وتذهب القائمة المنسدلة معها. ويضاعف TXLSWorksheet.AddListValidation ونظيره في XLSX كلاهما الاقتباسات المضمَّنة، فتنجو تلك العناصر سليمة
لدى TXLSWorksheet.AddListValidation أيضًا نسخة بـ array of WideString مطابقة للنسخة على TXLSXWorksheet. وهي مريحة، لكن اقرأ ما تفعله: تلحق العناصر بفواصل وتسلم النتيجة للنسخة النصية. فعنصر يحوي بذاته فاصلة يصير عنصرين، وعلى المحرك الكلاسيكي تؤخذ سلسلة مصدر تحوي ! مرجعًا مؤهَّلًا بورقة لا قائمة لاصقة. وللعناوين من ذلك القبيل، ضع القيم في نطاق خلايا ووجه القاعدة إليه
var
Book: IXLSWorkbook;
Sheet: TXLSWorksheet;
begin
Book := TXLSWorkbook.Create;
Sheet := Book.Sheets.Add;
Sheet.AddListValidation('H2:H11', ['He said "hi"', 'Plain option']);
// أكد وجود القاعدة قبل الاعتماد عليها
if Sheet.FindDataValidationAt(2, 8) = nil then
raise Exception.Create('List rule for H2:H11 was not created');
// ...
end;
بعد حفظ وإعادة فتح، تعيد TryGetCellDropdownItems على H2 قيمة He said "hi" باقتباساتها سليمة، في خرج XLS وXLSX وXLSB وODS على السواء
ترك TXLSWorkbookViewer يفرض القواعد
حين يحرر المستخدمون الخلايا على الشاشة، يطبق TXLSWorkbookViewer الفحوص نفسها عنك: مع Editable على True وترك EnforceDataValidation على افتراضه True، يفحص تحرير مكتوب يكسر قاعدة ذات تنبيه خطأ قبل كتابته: Stop يرفض، وWarning يسأل، وInformation يخبر. ويرسم الـ viewer أيضًا سهم القائمة المنسدلة على الخلية النشطة، ويفتح القائمة بنقرة أو Alt+Down، ويعرض الطلب الأصفر للإدخال أسفل الخلية. وتفاصيل الشبكة نفسها مغطاة في مقالة بناء شبكة جداول مخصصة بـ TXLSWorkbookViewer
وضبط CircleInvalidData على True يرسم دوائر حمراء حول القيم المخزنة المخالفة لقاعدتها، مثل أمر Excel بالاسم نفسه. يمسح المساحة المرئية فقط، ويستبعد معظم الخلايا سريعًا عبر FindDataValidationAt، ولا يحيط خلية فارغة أبدًا
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 // يعيد فتح المحرر بالنص المرفوض
else
Action := xvaAccept;
Handled := True; // يكبت الحوار المدمج
end;
اترك Handled على False ويعرض الـ viewer حوار تنبيهه الخاص، وهو يعمل تحت VCL وLCL معًا. ذلك الافتراضي يعض في الاختبارات الآلية: اختبار يقود تحريرات بلا معالج OnValidationError يضبط Handled سينسد على حوار modal. ونمط الـ viewer تعداد من نوع ثالث، TXLSViewerValidationErrorStyle بـ xveStop وxveWarning وxveInformation، متميز عن تعدادي ورقتي العمل
ماذا تعرف قبل الاعتماد على تحقق إدخال HotXLS؟
الفحوص أمينة لقواعد القائمة والعدد والتاريخ والوقت وطول النص، وبضع تفاصيل خاصة بالمحرك تستحق المعرفة:
- على أوراق عمل Classic XLS، يسجل
ValidateCellInputقواعد الصيغ المخصصة صالحة للمرشحين المكتوبين، لأن مرشحًا لا يمكن وضعه في خلية BIFF مؤقتًا دون إزعاج سلسلة الاعتماد. افحص القيمة المخزنة بـValidateCellValueالتي تقيم الصيغة المخصصة، أو استخدم محرك XLSX الذي يقيّم القواعد المخصصة للمرشحين أيضًا - على Classic XLS، يعامل تطابق القائمة القيم المتساوية عدديًا العنصر نفسه، فـ
5و5.0كلتاهما تطابق عنصر 5 - في الـ viewer، المحتوى الملصوق يتخطى التحقق كما في Excel:
EditSelectedCellلا محرر لإعادة فتحه، فنتيجة Retry هناك تتصرف كـ Cancel - إسنادات
Cells[...].Valueالصافية لا تفحص أبدًا، وهذا بالتصميم: التحقق يحدث فقط حيث يطلبه كودك
إنشاء القواعد في المقام الأول، بما فيها أنواع العدد الكامل والتاريخ وطول النص والمخصصة، مغطى في دليل HotXLS للتحقق من صحة البيانات وAutoFilter والجداول. وتحمَل مصنفات XLSB إلى TXLSXWorkbook، فكل استدعاء TXLSXWorksheet معروض هنا يعمل عليها دون تغيير، كما في مقالة قراءة وكتابة مصنفات XLSB ثنائية في Delphi
حل الـ dropdown واختيار العناصر والتحقق من الإدخال يأتي في مكوّن HotXLS لـ Excel في Delphi لـ Delphi وC++Builder وLazarus، مع عرض DataValidation وعرض WorkbookViewer اللذين يمارسان كل استدعاء في هذه المقالة