الطريقة الموثوقة لإنتاج تقرير Excel منسّق من Delphi هي البدء من مصنف بناه مصمم بالفعل. شخص في قسم المالية يخطط الفاتورة في Excel: الشعار، وعناوين الأعمدة، والحدود على شريط التفاصيل، وصف المجاميع بخط عريض، وتنسيقات العملة. كودك يفتح ذلك الملف، ويسقط بيانات حية في الخلايا التي حجزها المصمم لها، ويحفظ النتيجة. المظهر ملكهم؛ والأرقام ملكك. HotXLS، مكتبة Delphi وC++Builder أصلية تقرأ وتكتب مصنفات XLS وXLSX دون تشغيل Excel، تمنحك العمليات الثلاث التي يحتاجها هذا النهج: البحث عن خلية بنصها، ونسخ نطاق مع بقاء أنماطه وصيغه سليمة، وإدراج صفوف حتى ينزاح كل ما هو أسفلها مع البيانات
القاعدة الوحيدة التي تفصل بين مولِّد ينجو من تعديلات القالب وآخر ينكسر عند أول تعديل هي ألا تخاطب الخلايا أبدًا بأرقام صف وعمود حرفية. القالب مستند يحرره أشخاص آخرون. فريق المالية يضيف سطر ضريبة، ويرفع ارتفاع صف الشعار، ويعيد ترتيب كتلة العنوان، وصيغة الملف لا تساعدك في شيء: حفظ BIFF أو OOXML ينجح سواء كان الصف 10 لا يزال يعني ما كان يعنيه الربع الماضي أم لا. مولِّد يكتب أول سطر تفاصيل إلى صف 10 مُرمَّز بشكل ثابت سيقوم، أول مرة يدرج فيها أحدهم كتلة فوق قسم التفاصيل، بختم بنود على الخلايا الخاطئة وجمع نطاق مجاميع لم يعد يغطي البيانات. لا شيء يثير خطأً، وكل حفظ يعيد نجاحًا، والإشارة الوحيدة هي ملاحظة عميل لفاتورة خاطئة
ثبّت كل إحداثي على رمز عنصر نائب
الحل هو جعل القالب يحمل إحداثياته الخاصة. يكتب المصمم رموزًا مثل {{CUSTOMER}} و{{DATE}} و{{DETAIL_START}} في الخلايا التي يجب أن يلمسها المولِّد، ويحسب المولِّد كل موضع وقت التشغيل من مكان عثوره على تلك الرموز. تعديلات التخطيط لم تعد مهمة، لأن الرمز يتحرك مع الخلية التي يجلس فيها. النصف الثاني من العقد هو قاعدة الفشل: إذا كان رمز مطلوب مفقودًا، تتوقف المهمة قبل أن تصل أي بيانات عميل إلى الملف. قالب انحرف عن مساره ينبغي أن ينتج تذكرة مهمة فاشلة، لا مستندًا مُسلَّمًا
العثور على الرموز: FindText وReplaceText
كلتا عائلتَي الأصناف في HotXLS تكشفان بحثًا على مستوى ورقة العمل. FindText تعيد صف وعمود أول خلية يطابق نصها، مع حمولة زائدة تضيف حساسية حالة الأحرف. ReplaceText تستبدل كل تكرار وتعيد كم عددًا غيّرت. الاثنتان تغطيان نوعي الرموز اللذين تميل لامتلاكهما. مرساة واحدة كاسم العميل تحددها مرة واحدة وتكتب بجانبها؛ رمز ينبغي أن يظهر مرة واحدة بالضبط، كتاريخ التقرير، تستبدله وتتحقق من العدد. في جهة XLSX، تعبئة تثبّت نفسها بهذه الطريقة تبدو هكذا:
var
Book: TXLSXWorkbook;
Sheet: TXLSXWorksheet;
R, C: Integer;
begin
Book := TXLSXWorkbook.Create;
try
if Book.Open('invoice-template.xlsx') <> 1 then
raise Exception.Create('Cannot open invoice template');
Sheet := Book.Sheets[0]; // TXLSXSheets.Items تبدأ من 0
if not Sheet.FindText('{{CUSTOMER}}', R, C) then
raise Exception.Create('Template drift: {{CUSTOMER}} anchor missing');
Sheet.Cells[R, C].Value := 'ACME Corp';
if Sheet.ReplaceText('{{DATE}}',
FormatDateTime('yyyy-mm-dd', Date)) = 0 then
raise Exception.Create('Template drift: {{DATE}} token missing');
// توسيع التفاصيل والحفظ يتبعان أدناه
finally
Book.Free;
end;
end;
تفصيلان مهمّان. أولًا، FindText وReplaceText تطابقان القيمة النصية لخلية؛ رمز مضمَّن داخل سلسلة صيغة غير مرئي لهما، فرموز العناصر النائبة تنتمي إلى خلايا عادية، لا داخل الصيغ أبدًا. ثانيًا، عدد الاستبدالات هو كاشف انحرافك. قالب ينبغي أن يحتوي على رمز {{DATE}} واحد بالضبط لكنه يبلّغ عن صفر استبدالات قد حُرِّر، وإثارة استثناء في تلك اللحظة هو بالضبط ما يحوّل انحراف تخطيط صامتًا إلى فشل مرئي
استنساخ صف التفاصيل دون فقدان الأنماط أو الصيغ
قسم التفاصيل في فاتورة ينمو مع البيانات. كتابة قيم مباشرة في صفوف فارغة أسفل سطر العينة تُلقي بكل ما أعده المصمم: الحدود، وتنسيقات الأرقام، والصيغ لكل صف. النمط الذي يحافظ على كل ذلك هو ترك صف عينة واحد كامل التنسيق في القالب واستنساخه لكل بند. CopyRange تستنسخ الأنماط والصيغ في استدعاء واحد، وبعدها يكتب المولِّد فوق خلايا القيم فقط
const
DetailRow = 10; // صف العينة المنسّق في القالب
var
I: Integer;
begin
// افتح مساحة قبل كتلة المجاميع أولًا، حتى يتمدد نطاق SUM
// أسفل شريط التفاصيل مع البيانات معًا.
if Length(Items) > 1 then
Sheet.InsertRows(DetailRow + 1, Length(Items) - 1);
for I := 0 to High(Items) do
begin
if I > 0 then // استنسخ الأنماط + الصيغ من صف العينة
Sheet.CopyRange(DetailRow, 1, DetailRow, 5, DetailRow + I, 1);
Sheet.Cells[DetailRow + I, 1].Value := Items[I].Name;
Sheet.Cells[DetailRow + I, 2].Value := Items[I].Qty;
Sheet.Cells[DetailRow + I, 3].Value := Items[I].UnitPrice;
Sheet.Cells[DetailRow + I, 4].Formula :=
Format('B%d*C%d', [DetailRow + I, DetailRow + I]); // بلا بادئة '='
end;
end;
راقب إسناد الصيغة عن كثب. خاصية Formula في XLSX تأخذ التعبير دون علامة يساوي بادئة، بينما تتوقع واجهة XLS '=B10*C10' مُسندة عبر Value. خلط الاصطلاحين هو خطأ الترحيل الأكثر شيوعًا بين عائلتَي الأصناف، وهو يفشل دون شكوى: الخلية تحمل فقط سلسلة حرفية يعرضها Excel كنص. إذا زيّن القالب شريط التفاصيل بصفوف عناوين مدمجة، تذكّر أن الخلية العلوية اليسرى فقط من منطقة مدمجة تحمل قيمة. قواعد التخطيط في المقالة المرافقة عن الخلايا المدمجة في قوالب التقارير المبنية على التخطيط تشرح لماذا تنتمي مناطق الدمج خارج شريط البيانات كليًا
ما الذي تنقله InsertRows، وما الذي تتركه خلفها
إدراج صفوف قبل كتلة المجاميع هو ما يبقي نطاق SUM يتمدد مع نمو قسم التفاصيل. في جهة XLSX، InsertRows تحمل قائمة طويلة من البنى التابعة نازلة مع الخلايا: النطاقات المدمجة، وارتفاعات الصفوف، والروابط التشعبية، والتعليقات، واللوحات المجمّدة، ونطاقات التصفية التلقائية، والتنسيقات الشرطية، والتحقق من صحة البيانات، والجداول، والأسماء المعرَّفة، ومراسي الصور والمخططات. هناك حد واحد في تلك القائمة يستحق حفظه في الذاكرة. إعادة كتابة الصيغ تصل فقط إلى المراجع داخل الورقة نفسها. صيغة في ورقة ملخص تشير إلى المنطقة المنقولة تحتفظ بإحداثياتها القديمة وتقرأ بهدوء الخلايا الخاطئة، وهذا سبب أن المجاميع المسحوبة عبر الأوراق أكثر أمانًا عند التعبير عنها عبر أسماء على مستوى المصنف. المقالة المرافقة عن الأسماء المعرَّفة والصيغ بين الأوراق تشرح ذلك النمط بالتفصيل
صيغة .xls القديمة ترسم الخط في مكان أصعب. يحتفظ HotXLS بالجداول المحورية، وجداول الاستعلام، واتصالات البيانات الخارجية في ملفات BIFF ككتل بايتات خام. تنجو من الفتح والحفظ دون تغيير، لكنها ليست نموذجية، فإدراج الصفوف لا يلمسها أبدًا. قالب يوقف جدولًا محوريًا أسفل كتلة تفاصيل تتمدد يُحفظ دون أي تحذير على الإطلاق بينما ينجرف مستطيل مصدر الجدول المحوري بعيدًا عن البيانات. المخرج بنيوي، لا دفاعي: أبقِ محتوى الجداول المحورية والاستعلامات على أوراق لا يُدرج فيها المولِّد أبدًا، ولن يحدث ذلك التقادم
أعد الحساب قبل التسليم، أو اعرف لماذا تخطيته
لا يقيّم HotXLS الصيغ أثناء SaveAs. عندما يفتح شخص الملف، يعيد Excel حساب كل شيء (تكشف واجهة XLS CalculationMode وRecalcOnSave إن احتجت توجيه ذلك)، فتقرير متجه إلى صندوق وارد إنسان لا يحتاج منك شيئًا أكثر. الصورة تتغير في اللحظة التي يغذّي فيها المصنف برنامجًا آخر. تصدير CSV يكتب الصيغ كنصها الحرفي ولا يحسبها أبدًا، وأي محلل لاحق يثق بالقيم المخبأة سيقرأ أرقامًا قديمة أو فراغات. لتلك المسارات، احسب على الخادم بـCalculate، التي تقيّم تعبيرًا اعتباطيًا مقابل المصنف المحمَّل وتعيد النتيجة:
var
Total: Variant;
LastDetail: Integer;
begin
LastDetail := DetailRow + Length(Items) - 1;
Total := Book.Calculate(Format('SUM(Invoice!D%d:D%d)',
[DetailRow, LastDetail]));
if (not VarIsNumeric(Total)) or
(Abs(Total - ExpectedTotal) > 0.005) then
raise Exception.Create('Invoice total does not match the order record');
if Book.SaveAs('invoice-2026-0611.xlsx') <> 1 then
raise Exception.Create('Save failed: check output path and permissions');
end;
فحص المجموع المحسوب مقابل سجل الطلب قبل الحفظ تأمين رخيص بعائد جيد. إنه يحوّل فاتورة خاطئة إلى مهمة فاشلة. مشغّل يمكنه إعادة محاولة مهمة فاشلة خلال ثوانٍ؛ فاتورة خاطئة موجودة بالفعل في صندوق بريد عميل تكلّف مدير حساب اعتذارًا وتصحيحًا
عائلتا أصناف، خوارزمية واحدة
المنطق نفسه ينتقل بين الصيغتين، لكن ليس نفس الكود. TXLSWorkbook لـ.xls القديم مبني على واجهة ومعدود مرجعيًا، بفهرسة أوراق تبدأ من 1، ولا تحرره أبدًا يدويًا. TXLSXWorkbook لـ.xlsx كائن عادي يجب أن تحرره في try..finally، بفهرسة أوراق تبدأ من 0 واصطلاح الصيغة المبيّن أعلاه. FindText وReplaceText وCopyRange وInsertRows كلها تعيش على الجهتين، فشكل التثبيت-الاستنساخ-إعادة الحساب ينتقل بنظافة. النصيحة العملية هي الالتزام بصيغة واحدة لكل خط أنابيب، أو إخفاء دورتي حياة الكائن خلف مهايئ رقيق خاص بك بدلًا من تشتيت الفرق عبر المولِّد
الحجم نادرًا ما يهم لنوع التقرير الذي ينتجه هذا النمط. استنساخ صف منسّق بضعة آلاف من المرات لا شيء بالنسبة للأجهزة الحالية. مسار الحفظ يصبح عنق الزجاجة فقط عندما يصل شريط تفاصيل إلى ست خانات من الصفوف، وعند تلك النقطة فإن ضبط StreamingWrite يرسل XML ورقة العمل مباشرة إلى حزمة الخرج بدلًا من تخزينها مؤقتًا؛ مقالة الكتابة المتدفقة للمهام الدفعية على الخادم تغطي متى تستحق تلك المقايضة الأخذ بها. المخططات تتصرف بنفس طريقة بقية التخطيط: في جهة XLSX تتحرك كل من مرساة المخطط ومراجع سلاسله عندما تعمل InsertRows فوقها، فيبقى مخطط تحت صف المجاميع مرتبطًا بالبيانات الصحيحة، بينما في جهة XLS تجلس المخططات على أوراق مخططات خاصة بها، ومثل الجداول المحورية، لا تنزاح أبدًا. هذه حجة إضافية للحفاظ على أوراق العرض بعيدة عن الورقة التي يوسّعها المولِّد
هذا النهج القائم على التثبيت-الاستنساخ-إعادة الحساب يتيح لمصمم امتلاك شكل المصنف بينما يملك كودك ما يقوله، وهذا عادة ما يجعل خرج Excel المولَّد يستحق الصيانة. استدعاءات البحث والنسخ والإدراج المعروضة هنا، إلى جانب محرك الصيغ المستخدم لفحص المجموع قبل التسليم، تشحن مع HotXLS Delphi Component لـDelphi وC++Builder