اجتز خلايا قالب تقرير فُتح للتو وسيتصرف عنوان مدمج وكأنه تسريب. تقرأ A1 فتحصل على "Quarterly Statement"؛ تقرأ من B1 إلى F1، التي تجلس مرئيًا تحت اللافتة نفسها، فلا تحصل على شيء. تكتب قيمة في C1 لترقيع الترويسة ولا تظهر أبدًا على الشاشة. الشبكة لم تفقد بياناتك. إنها تفعل بالضبط ما يعنيه الدمج: في كل من XLS وXLSX، يعرض مستطيل مدمج محتوى خلية واحدة، هي المرساة العلوية اليسرى، ويعامل الباقي كمساحة مغطاة تحمل قيمًا لكنها لا تعرضها أبدًا. مستخدمو Excel يستوعبون هذا بالتجربة والخطأ. أما مولّد التقارير فيجب أن يرمّزه كقاعدة، لأن العرَض في الكود المولَّد منطقة فارغة دون استثناء يقودك إليها. HotXLS، مكتبة Object Pascal أصلية تقرأ وتكتب كلا صيغتَي Excel من Delphi وC++Builder، تكشف جدول الدمج بوضوح كافٍ يتيح لك البرمجة وفق القاعدة بدلًا من إعادة اكتشافها في تذكرة دعم
قيمة واحدة، مرساة واحدة
الدمج تعليمة عرض تُطبَّق فوق شبكة لا تغيّر شكلها. كل خلية مغطاة لا تزال موجودة في الملف كفتحتها الخاصة؛ سجل الدمج فقط يخبر المستهلك برسم محتوى المرساة عبر المستطيل. هذا التمييز يقود ثلاثة سلوكيات تستحق الاستيعاب قبل كتابة أي كود تخطيط. قراءة خلية مغطاة تعيد قيمتها المخزَّنة الخاصة، وهي عادة فارغة لعنوان بنيته، فأي كود يفحص عنوانًا مدمجًا يجب أن يحل المرساة ويقرأها. الكتابة إلى خلية مغطاة تنجح على مستوى الملف ولا تظهر في أي مكان، وهذا هو فخ الترويسة غير المرئية من الافتتاحية. وفك دمج منطقة يكشف كل ما كان جالسًا تحتها طوال الوقت، فقيمة شاردة كُتبت في مساحة مغطاة تتحول إلى عيب مرئي يوم يحل أحدهم الدمج
في جهة XLSX، ذلك الجدول كائن كامل المرتبة. Sheet.MergedCells تحمل Add('A1:C1') وFindAt(Row, Col) وDeleteAt وItems، والاستدعاء الذي تلجأ إليه أكثر من غيره هو FindAt: سلّمها أي إحداثي وتعيد المنطقة المدمجة التي تغطي تلك الخلية، أو nil عندما تقف الخلية بمفردها. ذلك البحث الواحد هو الأساس لكلا نصفي المعالجة الصحيحة للدمج، القراءة الآمنة وحارس الكتابة، وكلاهما يظهر لاحقًا
واجهتان، اصطلاحا دمج
يحتفظ HotXLS بمحرك BIFF8 .xls الكلاسيكي ومحرك OOXML .xlsx كنموذجَي كائن منفصلين، ويهجّئان الدمج بشكل مختلف لأنهما ينحدران من اصطلاحين مختلفين. واجهة XLS تتبع اصطلاح Excel COM: تأخذ نطاقًا من خاصية مفهرسة بمعاملين وتستدعي Merge بـOleVariant تقرر قيمته الهندسة التي ستنتهي إليها
var
Book: IXLSWorkbook; // معدود بالواجهة: لا تحرير يدوي
Sh: IXLSWorksheet;
begin
Book := TXLSWorkbook.Create;
Sh := Book.Sheets[1]; // مجموعة أوراق XLS تبدأ من 1
Sh.Range['A1', 'F1'].Merge(False); // False = كتلة مدمجة واحدة
Sh.Cells.Item[1, 1].Value := 'Quarterly Statement';
Sh.Range['A3', 'F4'].Merge(True); // True = دمج عبر: دمج واحد لكل صف
Book.SaveAs('layout.xls');
end;
معامل Merge هو الجزء الذي يخطئ فيه الناس. عبر نطاق من صفين، Merge(True) تنتج دمجين مستقلين كل منهما بصف واحد، وهذا ما يسميه Excel "Merge Across" وهو بالضبط ما تريده لشريط ترويسة مكدّس ينبغي أن تبقى صفوفه قابلة للفصل. أما Merge(False) فتدمج المستطيل بأكمله في كتلة واحدة. النطاق يُبلغ أيضًا عن MergeCells كعلامة حالة، ويعيد المنطقة الحاوية عبر MergeArea، ويحل نفسه بـUnmerge. واجهة XLSX تكشف العمليات نفسها بأسماء مختلفة: Sheet.MergeCells(Row1, Col1, Row2, Col2) تأخذ حدودًا صحيحة، وTXLSXRange.Merge تقبل النسخة المكافئة Across، ومجموعة MergedCells تحمل النتيجة
قالب ينمو مع بياناته
قالب تقرير حقيقي ليس شبكة ثابتة. الترويسة والمجاميع ثابتتان، لكن قسم التفاصيل بينهما يتمدد بقدر ما يعيده الاستعلام. النمط الذي يصمد يبقي صف تفاصيل واحدًا كامل التنسيق في القالب، ويستنسخه مرة لكل سجل، ثم يفتح فجوة قبل كتلة المجاميع حتى ينزلق كل ما يرتبط بها من الأسفل دون أن يفقد تنسيقه
Sheet.Range['A1:F1'].Merge;
Sheet.Cells[1, 1].Value := 'INVOICE #2026-0611'; // القيمة تذهب إلى المرساة، A1
Sheet.RowHeight[1] := 28;
TitleFont := Book.Fonts.Add('Calibri', 16, True, False);
Sheet.Cells[1, 1].FontIndex := TitleFont + 1; // فهرس التجمع يبدأ من 0، جهة الخلية تبدأ من 1
// الصف 5 هو سطر قالب التفاصيل المنسّق
for I := 0 to ItemCount - 1 do
Sheet.CopyRange(5, 1, 5, 6, 6 + I, 1); // الأنماط والصيغ تسافر معه
// افتح فجوة فوق كتلة المجاميع؛ المحتوى أسفلها ينزاح للأسفل
Sheet.InsertRows(6 + ItemCount, 1);
Sheet.Range['A1:F1'].SetBorders(xlsxEdgeOutline, xlsxBorderMedium);
سطران يستحقان نظرة ثانية. إسناد الخط يحمل خطأ إزاحة بواحد يعضّ بصمت: Fonts.Add تعيد موضع تجمّع يبدأ من 0، بينما تخزّن الخلية مرجع خط يبدأ من 1 حيث تعني 0 الخط الافتراضي، فإسقاط + 1 لا يثير شيئًا، بل ينسّق عنوانك ببساطة بخط الطباعة الخاطئ. السطر الآخر هو CopyRange، التي تنقل التنسيق والصيغ مع القيم. هذا هو السبب الكامل لاستنساخ صف قالب مبني يدويًا بدلًا من إعادة بناء مظهره في الكود. المصمم يملك المظهر مرة واحدة، في القالب؛ والمولِّد لا يفعل شيئًا سوى صبّ البيانات في نسخ منه
ذلك الانقسام يتوسع أكثر عندما يعيش التخطيط القابل لإعادة الاستخدام في مصنفه الخاص، لنقل ورقة من أشرطة ترويسة وتذييل مشتركة عبر التقارير. CopyRangeTo تنفذ نفس الاستنساخ عبر حدود ورقة العمل، آخذة ورقة هدف بالإضافة إلى إحداثيات الوجهة، فيمكن لمولِّد أن يبقي ورقة قالب نقية واحدة ويطبع مناطقها في عدد ما تحتاجه مهمة من أوراق الخرج. البديل، تعديل القالب في مكانه ومحاولة استعادته لاحقًا، هو من ذلك النوع من الأمور التي تعمل حتى اليوم الذي تُجهض فيه تشغيلة في منتصفها
ما الذي تنقله InsertRows، وما الذي لا تنقله
نمط نمو القالب لا يعمل إلا لأن InsertRows في XLSX تعديل بنيوي لا خلط خلايا. عندما تفتح فجوة، تنقل المناطق المدمجة، وارتفاعات الصفوف، والروابط التشعبية، والتعليقات، واللوحات المجمّدة، ونطاقات التصفية التلقائية، والتنسيقات الشرطية، والتحقق من صحة البيانات، والجداول، والأسماء المعرَّفة، ومراسي الصور، ومراسي المخططات التي تجلس تحت نقطة الإدراج، لا قيم الخلايا وحدها. هذا ما يتيح لكتلة المجاميع الوصول إلى صفها الجديد مع بقاء دمجها وتنسيقات أرقامها سليمة بدلًا من الوصول مجرَّدة منها
حدّاها الموثّقان هما ما ينبغي التصميم حوله. تعديل الصيغ محصور بالورقة قيد التحرير: المراجع داخل تلك الورقة تُعاد كتابتها، وصيغة في ورقة أخرى تشير إلى المنطقة المزاحة تُعاد كتابتها أيضًا، لكن التعديل يتبع فقط المراجع التي تستهدف الورقة المحررة، فأي مخطط مراجع بين المصنفات يستحق تدقيقه الخاص بدلًا من الثقة العمياء. الحد الثاني أحدّ، وهو في جهة XLS. الجداول المحورية تنجو من دورات فتح-حفظ كسجلات خام محفوظة، لا ككائنات نموذجية يستطيع HotXLS تحريكها، فإدراج صفوف لا ينقل بصمة جدول محوري. أي قالب تبنيه لصيغة .xls ينبغي أن يوقف مناطق جداوله المحورية بعيدًا تمامًا عن أي شريط ينمو
رفض كتابة البيانات في مساحة التخطيط
إخفاق الخلية المدمجة الذي يصل فعلًا إلى الإنتاج ليس التجميلي. إنه بنيوي: صف تفاصيل ينجرف إلى شريط تخطيط مدمج، فتهبط قيمه في خلايا مغطاة وتصبح غير مرئية، ومجاميع الأعمدة تتوقف بهدوء عن مطابقة ما يستطيع أي قارئ للورقة رؤيته. ولأن FindAt تجيب عن سؤال المنطقة المغطية لأي إحداثي، يستطيع المولِّد رفض تلك الكتابة في اللحظة التي كانت ستحدث فيها بدلًا من تسليم تقرير يُنقص العد بصمت
// ارفض كتابة بيانات تفاصيل في منطقة تخطيط مدمجة
if Sheet.MergedCells.FindAt(Row, 1) <> nil then
raise Exception.CreateFmt('row %d overlaps a merged layout region', [Row]);
Sheet.Cells[Row, 1].Value := Detail.Description;
نفس فحص الحدود ينتمي إلى أي مكان سيقوم فيه مستخدم لاحقًا بترتيب الخرج أو تصفيته. نطاق يحتوي دمجًا بداخله لا يستطيع الترتيب بنظافة، لأن الترتيب ينقل الصفوف باستقلالية ودمج يمتد عبر صفوف ليس له صف واحد يسافر معه؛ يستجيب Excel بخطأ أو بتخطيط مشوَّش. الانضباط الذي يبقي التقارير صحيحة جغرافي. احصر الدمج في أشرطة العناوين، وفواصل الأقسام، وكتل التوقيع، وأبقِ الوسط الجدولي للورقة مسطّحًا. مقالة توليد التقارير من قالب تطوّر هذا الانفصال بين التخطيط والبيانات إلى سير عمل كامل مبني على العناصر النائبة، ومقالة التنسيق الشرطي والنص المنسّق تغطي تنسيق ذلك الشريط المسطّح من البيانات
كيف يتدهور الدمج في طريقه للخارج
الدمج مفهوم خاص بالمصنف، وكل صيغة تصدير موجّهة للنص تحترمه بدرجة مختلفة. معرفة السلوكيات الثلاثة مسبقًا توفر دورة ضمان جودة كاملة. تصدير HTML يستنسخ الدمج بأمانة، مصدرًا colspan وrowspan في جدول واحد، فيحافظ تقرير موجَّه للمتصفح على مظهره المُشرَّط. تصدير RTF لا يمتد عبر الأعمدة على الإطلاق: نص المرساة يهبط في خليته الخاصة والعرض المتبقي من الدمج يخرج كخلايا فارغة، وهذا يترك عنوانًا عريضًا مدفوعًا بصريًا نحو اليسار في معالج نصوص. أما CSV فليس لديه مفهوم للدمج على الإطلاق، فقيمة المرساة تشغل حقلًا واحدًا وكل خلية مغطاة تُصدَّر كحقل فارغ. الخلاصة لمصنف يغذّي أيضًا تصديرات محدَّدة بفاصل هي إبقاء أي شيء حامل للوزن خارج هندسة الدمج؛ مقالة تصدير CSV وTSV وHTML تشرح كل صيغة بالتفصيل
طمأنة واحدة لمن يزن هذا مقابل حجم الملف: الدمج لا يكلف شيئًا يُذكر تقريبًا على نطاق التقارير. جدول الدمج ضئيل بجانب بيانات الخلايا، وقراءة خلية مغطاة لا تزال تمر عبر FindAt بدلًا من المسح. الضغط على الأداء في المصنفات الكبيرة يأتي من مكان آخر، أساسًا نمو تجمّع الأنماط والذاكرة التي يحملها مسار الحفظ، وهو ما تتناوله مقالة أداء المصنفات الكبيرة مباشرة. كلتا واجهتَي الدمج، وعمليات التحرير البنيوية، وعروض القوالب التجريبية تشحن مع HotXLS Delphi Component