مقال تقني

تكرار ورقة عمل XLSX في دلفي مع HotXLS

لقد قمت ببناء ورقة عمل واحدة بشكل صحيح تماماً. تم دمج نطاق الرأس، وتناسب عروض الأعمدة البيانات، وتم تجميد الصفين العلويين، وتم تعيين منطقة الطباعة والهوامش لتصدير A4 نظيف، وتم تلوين علامة التبويب بحيث يمكن للقسم المالي العثور عليها. الآن يحتاج التقرير إلى اثني عشر من هذه، واحدة لكل منطقة، تبدأ كل منها من نفس التخطيط. وإعادة بناء تلك الورقة في الكود اثنتي عشرة مرة هي الطريقة التي يتسلل بها الانحراف الطفيف: تحصل المنطقة 7 على عمود أضيق بنقطة واحدة، وتفقد المنطقة 11 التجميد، ولا يلاحظ أحد ذلك حتى تهبط معاينة PDF على مكتب المدير. ما تريده بالفعل هو النسخة البرمجية لأمر Excel المتمثل في النقر بزر الماوس الأيمن، ونقل أو نسخ، وإنشاء نسخة: خذ الورقة المكتملة واطبع نسخاً مكررة مستقلة

كان محرك XLSX في HotXLS، وهو مكتبة دلفي وC++Builder أصلية تقرأ وتكتب ملفات Excel دون تشغيل Excel تلقائياً، يمكنه بالفعل نقل الأوراق وحذف الأوراق ونسخ نطاقات الخلايا عبر الأوراق. وما لم يكن بإمكانه فعله حتى الإصدار v2.91.0 هو استنساخ ورقة عمل بأكملها في استدعاء واحد. يضيف هذا الإصدار نقطتي دخول: TXLSXWorksheet.CopyFrom، التي تنسخ حالة مستوى الورقة من ورقة عمل إلى أخرى، وTXLSXSheets.Duplicate، التي تضيف ورقة جديدة وتشغل CopyFrom من أجلك. الجزء المثير للاهتمام ليس أنه ينسخ الأشياء. بل هو الخط متعمد المرسوم بين ما يتم نسخه نسخاً عميقاً وما لا يتم نسخه، ولماذا يقع هذا الخط في مكانه

استدعاء واحد لاستنساخ صفحة مكتملة

العملية عالية المستوى هي Duplicate. سلمها الفهرس ذو الأساس 1 للورقة المصدر وستعيد ورقة عمل جديدة تماماً تعكس تخطيط الورقة الأصلية وبياناتها. وتتطابق اتفاقية الفهرس مع Items[] على جانب XLSX، لذا فإن الورقة الأولى هي الفهرس 1، وليس 0; مرر فهراً خارج النطاق وستحصل على nil بدلاً من استثناء، نفس عقد الفشل الذي تستخدمه بقية مجموعة أوراق XLSX

var
  Book: TXLSXWorkbook;
  Template, Copy: TXLSXWorksheet;
begin
  Book := TXLSXWorkbook.Create;
  try
    Template := Book.Sheets.Add('Template');
    Template.Cells[1, 1].Value := 'Quarterly Statement';
    Template.Range['A1:C1'].Merge;
    Template.ColWidth[1] := 18;
    Template.FreezePanes(2, 1);          // freeze top row + first column
    Template.TabColorIsAuto := False;
    Template.TabColor := $FF1F4E79;

    // Clone with an explicit name...
    Copy := Book.Sheets.Duplicate(1, 'Region-North');
    // ...or let it pick the Excel-style default name.
    Copy := Book.Sheets.Duplicate(1);    // -> "Template (2)"

    Book.SaveAs('regions.xlsx');
  finally
    Book.Free;
  end;
end;

هناك شيئان في هذا المقتطف يستحقان التباطؤ عنده. أولاً، تأخذ FreezePanes معاملاتها الخاصة بالصفوف أولاً، FreezePanes(ARow, ACol)، بحيث تتماشى مع فهرسة Cells[Row, Col]؛ وترث النسخة المكررة انقسام التجميد الدقيق. ثانياً، تم تسمية الطريقة Duplicate وليس الاسم الأكثر وضوحاً Copy، وهذا ليس تفضيلاً للأسلوب. حيث إن Copy هي روتين قياسي في وحدة System، وتُستخدم باستمرار للسلاسل النصية والمصفوفات الديناميكية. والطريقة المسماة Copy على فئة (class) من شأنها أن تظللها داخل أجسام الأساليب وتخلق بالضبط هذا النوع من غموض القرار الذي يعيقك بعد ستة أشهر. وتتجنب Duplicate المشكلة برمتها وتقرأ بشكل صحيح في موقع الاستدعاء

يتبع الاسم الافتراضي قاعدة Excel الخاصة

عندما تستدعي الحمولة الزائدة (overload) ذات المعامل الواحد، أو تمرر سلسلة اسم فارغة، يتم تسمية الورقة الجديدة باسم المصدر مع ملحق (2)، ويرتفع الملحق حتى يصبح الاسم فريداً. كرر ورقة Template مرة واحدة وستحصل على Template (2)؛ كررها مرة أخرى وستحصل على Template (3)، لأن Template (2) محجوز بالفعل. يعكس هذا الأسماء التي يولدها Excel من أمر إنشاء نسخة الخاص به، بحيث يبدو مصنف العمل الذي ينتجه كودك بالشكل الذي يتوقعه المستخدم للمصنف المكرر يدوياً. ويتم تشغيل فحص الفرادة ضد مجموعة الأوراق الحية، مما يعني أنه يتخطى أيضاً الأسماء التي أنشأتها يدوياً، وليس فقط الأسماء الناتجة عن عمليات التكرار السابقة

إذا كنت تقوم بتوليد ورقة عمل واحدة لكل منطقة أو لكل شهر، فاعتمد على الحمولة الزائدة ذات الاسم الصريح بدلاً من ذلك. مخطط Region-North، وRegion-South المتوقع أسهل في معالجته لاحقاً من سلسلة من الملحقات (2) و(3)، ويحافظ على الأسماء المحددة والصيغ عبر الأوراق مقروءة

ما الذي تنسخه CopyFrom نسخاً عميقاً

تحت الغطاء، تضيف Duplicate الورقة ثم تستدعي CopyFrom(ASource)، والتي يمكنك أيضاً استدعاؤها مباشرة عندما تريد الاستنساخ على ورقة قمت بإنشائها بالفعل. وتحرس CopyFrom ضد الحالتين المنحلتين مقدماً: النسخ من nil، أو نسخ ورقة عمل على نفسها، حيث يعود كلاهما على الفور ولا يفعلان شيئاً. وكل شيء بعد ذلك هو النسخة نفسها، وهو واسع عمداً

تأتي بيانات الخلية أولاً. تطلب CopyFrom من المصدر UsedRange الخاص به، وهو مربع الإحاطة الضيق للخلايا المأهولة والمناطق المدمجة، وتعيد استخدام آلية CopyRangeTo الموجودة لنقل كل قيمة وصيغة ومؤشر نمط لكل خلية إلى الهدف بدءاً من A1. وعلاوة على الخلايا، فإنها تعيد تشغيل الطبقة الكاملة لحالة مستوى الورقة التي تجعل القالب يبدو مكتملاً:

  • المناطق المدمجة، المعاد إنشاؤها حسب الإحداثيات بحيث يمتد الشعار على المستطيل نفسه
  • عروض الأعمدة وارتفاعات الصفوف، بالإضافة إلى القوائم المخفية والمنهارة ومستويات المخطط التفصيلي، المنسوخة حرفياً بحيث تتماشى الصفوف والأعمدة تماماً
  • أجزاء التجميد وحالة العرض: مستوى التكبير، وعرض خطوط الشبكة والقيم الصفرية، والاتجاه من اليمين إلى اليسار، ونوع العرض
  • حالة الحماية مع خيارات أذونات كل إجراء الخاصة بها، بحيث يظل القالب المقفل مقفلاً بالطريقة نفسها
  • كتلة إعداد الصفحة بأكملها: الهوامش، والاتجاه، وحجم الورق، والتحجيم والملاءمة مع الصفحة، ومنطقة الطباعة، وعناوين الطباعة، والرؤوس والتذييلات، وعلامات خطوط شبكة الطباعة وعناوين الطباعة
  • نطاق التصفية التلقائية، ولون علامة التبويب، ورؤية الورقة

النتيجة هي ورقة تطبع وتصفي وتعرض بشكل مطابق لمصدرها. ولأن الخلايا وعمليات الدمج وقوائم الأبعاد يتم إعادة إنشائها مادياً على الورقة الجديدة بدلاً من استعارتها، فإن النسخة المكررة مستقلة تماماً. اكتب 999 في خلية في النسخة وسيحافظ المصدر على قيمته الأصلية؛ هذا الاستقلال هو السمة الأكثر أهمية للمستنسخ المخصص للتقارير الإقليمية الموازية، ويؤكد عرض SheetCopy المشحون ذلك صراحة

ما الذي تتركه ضحلاً، ولماذا

الآن الجزء الصادق. المخططات، والصور المضمنة، وجداول XLSX، وعمليات التحقق من البيانات، وقواعد التنسيق الشرطي لا يتم نسخها. هذا حد موثق ومتعمد، وليس سهواً، ويجدر فهم الأسباب حتى تتمكن من التخطيط حوله بدلاً من أن تفاجأ به

تحمل كل من تلك المجموعات هوية ومراجع لا تنجو بنسخة حقل ساذجة. يشير المخطط إلى نطاق بيانات المصدر ويمتلك علاقة رسم في حزمة OOXML؛ وإن استنساخ الكائن دون إعادة تعيين العلاقة ومراجع السلاسل ينتج مخططاً يعرض بيانات خاطئة، أو حزمة يعلمها Excel كحاجة إلى إصلاح. والجدول له اسم يجب أن يكون فريداً داخل مصنف العمل، وصف رأس مرتبط بأعمدة محددة، وعلاقة توليد تلقائي خاصة به. وترتبط التنسيقات الشرطية والتحقق من صحة البيانات بنطاقات الإحداثيات، وفي حالة التحقق، يمكن الإشارة إلى نطاقات أخرى بواسطة صيغة. ويعني نسخ أي من هذه نسخاً عميقاً بشكل صحيح إعادة كتابة المراجع وصياغة هويات جديدة، وهو عمل حقيقي بوضع فشل حقيقي. وفعله في منتصف الطريق، عن طريق نسخ الكائن ولكن ليس مراجع، أسوأ من عدم النسخ على الإطلاق: فهو ينتج ملفاً يفتح بمطالبة إصلاح ويسقط المحتوى بصمت. لذا ينسخ المحرك الأشياء التي يمكنه نسخها بشكل نظيف ويترك المجموعات الحاملة للمراجع للمستدعي، الذي يعرف ما يجب أن يشير إليه الهدف

عملياً، يعني ذلك أن تدفق العمل لقالب أغنى هو: تكرار الورقة للحصول على الخلايا والتخطيط وإعداد الطباعة، ثم إعادة بناء المخطط أو الجدول أو عمليات التحقق أو التنسيقات الشرطية على النسخة باستخدام واجهة برمجة التطبيقات نفسها التي استخدمتها لإنشائها في المرة الأولى. ولأنك تعيد إنشائها مقابل نطاقات النسخة المكررة الخاصة بها، فإن المراجع تخرج صحيحة بالبناء. بالنسبة للمخطط الذي يقرأ A1:C10، أضف مخططاً جديداً على النسخة يشير إلى A1:C10 للنسخة؛ وبالنسبة لتصفية تلقائية تريدها نشطة، لاحظ أن نطاق التصفية ينتقل بالفعل، لذا تقوم فقط بإعادة تطبيق معايير العمود. وقواعد التنسيق الشرطي والتحقق من صحة البيانات التي ستعيد إضافتها من خلال الاستدعاءات نفسها الموصوفة في المقالة حول الخلايا المدمجة وتخطيط قالب التقرير، والتي تتصفح جدول الدمج ونموذج النطاق الذي ترثه النسخة

أين يتلاءم التكرار مع تدفق عمل التقارير

يعتبر تكرار ورقة العمل الرفيق الطبيعي للتوليد القائم على العناصر النائبة. يحل النهج المعتمد على الرموز في دليل توليد التقارير المستندة إلى القوالب في دلفي مشكلة كتابة البيانات في تخطيط يحرره أشخاص آخرون؛ ويحل التكرار مشكلة الحاجة إلى هذا التخطيط مرات عديدة في مصنف عمل واحد. ادمجهما معاً وسيصبح النمط نظيفاً: احتفظ بورقة Template أصلية واحدة برموزها وعمليات الدمج وإعداد الطباعة، ثم لكل منطقة أو فترة استدعي Duplicate، وملأ رموز المستنسخ بشريحة البيانات هذه، واحرص على المتابعة. القالب الأصلي لا يتحول أبداً، لذا يظل مصدراً موثوقاً للمستنسخ التالي، وتبدأ كل ورقة إخراج من تخطيط متطابق بايت ببايت

ملاحظة تسلسلية واحدة توفر فئة من الارتباك. كرر الورقة قبل صب البيانات فيها، وليس بعده. يجب أن يحتوي القالب على الهيكل والتنسيق، وليس أرقام الربع الأخير، ويعني استنساخ صفحة منسقة فارغة أن كل نسخة مكررة تبدأ نظيفة. وإذا قمت بتكرار ورقة تحمل بيانات بالفعل، فإن تلك البيانات تأتي معها، لأن CopyFrom تنسخ النطاق المستخدم بأمانة؛ وهذا أحياناً ما تريده، ولكن بالنسبة لتقرير توزيع، فعادة ما لا يكون كذلك

عادة تحقق سريعة

نظراً لأن انقسام النسخ العميق مقابل النسخ الضحل غير مرئي حتى تبحث عنه، فابنِ فحصاً من خمسة أسطر في المهمة بدلاً من الثقة في أن كل شيء قد انتقل. وبعد التكرار، اقرأ الإشارات الهيكلية التي من المفترض أن ترثها النسخة وتأكد من مطابقتها للمصدر

Copy := Book.Sheets.Duplicate(1, 'Region-North');
WriteLn(Format('merged=%d  colA=%.1f  freezeRow=%d  tabAuto=%d',
  [Copy.MergedCells.Count, Copy.ColWidth[1],
   Copy.FreezeRow, Integer(Copy.TabColorIsAuto)]));
// Prove independence: mutate the copy, confirm the source is untouched.
Copy.Cells[2, 2].Value := 999;
// Template.Cells[2, 2].Value is still whatever it was.

تخبرك أعداد الدمج، وعرض العمود، وصف التجميد، وعلامة لون علامة التبويب أن الطبقة التي يتم نسخها قد وصلت بالفعل. وبشكل منفصل، في أي ورقة كانت تحمل مخططاً أو جدولاً أو تحققاً أو تنسيقات شرطية، عاملها كقائمة إعادة بناء على النسخة: غيابها مقصود، والإصلاح عبارة عن بضع استدعاءات، وليس تقرير خطأ. هذا نموذج الذهني، عميق حيث يكون آمناً وضحل حيث تنكسر المراجع، هو القصة بأكملها لكيفية استخدام هذه الميزة بشكل جيد

يُشحن تكرار ورقة العمل ونسخ حالة الورقة CopyFrom الموصوف هنا في الإصدار v2.91.0 من مكون HotXLS Delphi spreadsheet component الأصلي، جنباً إلى جنب مع عينة SheetCopy قابلة للتشغيل وتدرب دورة الاستنساخ والتحول من البداية إلى النهاية