Skip to content

11. تطبيق [SimuPaie] – الإصدار 7 – ASP.NET / متعدد العروض / متعدد الصفحات


قراءات موصى بها: مرجع [1]، تطوير WEB مع ASP.NET 1.1 الفقرة: أمثلة


نحن ندرس الآن إصدارًا مطابقًا وظيفيًا لتطبيق ASP.NET ثلاثي الطبقات [pam-v4-3tier-nhibernate-multivues-monopage] الذي تمت دراسته سابقًا، لكننا نعدل بنية هذا الأخير بالطريقة التالية: حيث في الإصدار السابق، تم تنفيذ العروض بواسطة صفحة واحدة ASPX، هنا سيتم تنفيذها بواسطة ثلاث صفحات ASPX.

كانت بنية التطبيق السابق كما يلي:

لدينا هنا بنية MVC (نموذج – عرض – وحدة تحكم):

  • تحتوي [Default.aspx.cs] على كود وحدة التحكم. الصفحة [Default.aspx] هي الوسيط الوحيد للعميل. وهي تتلقى جميع طلبات العميل.
  • [Saisies, Simulation, Simulations, ...] هي العروض. يتم تنفيذ هذه العروض هنا، بواسطة مكونات [View] للصفحة [Default.aspx].

وستكون بنية الإصدار الجديد كما يلي:

  • تتطور طبقة [web] فقط
  • لا تتغير طرق العرض (ما يظهر للمستخدم).
  • أصبح كود وحدة التحكم، الذي كان موجودًا بالكامل في الإصدار السابق في [Default.aspx.cs]، موزعًا الآن على عدة صفحات:
    • [MasterPage.master]: صفحة تجمع العناصر المشتركة بين العروض المختلفة: الشريط العلوي مع خيارات القائمة
    • [Formulaire.aspx]: الصفحة التي تعرض نموذج المحاكاة وتدير الإجراءات التي تتم على هذا النموذج
    • [Simulations.aspx]: الصفحة التي تعرض قائمة عمليات المحاكاة وتدير الإجراءات التي تتم على هذه الصفحة نفسها
    • [Erreurs.aspx]: الصفحة التي يتم عرضها عند حدوث خطأ في تهيئة التطبيق. لا توجد إجراءات ممكنة على هذه الصفحة.

يمكن اعتبار أن لدينا هنا بنية MVC ذات وحدات تحكم متعددة، في حين أن بنية الإصدار السابق كانت بنية MVC ذات وحدة تحكم واحدة.

تتم معالجة طلب العميل وفقًا للخطوات التالية:

  1. يقوم العميل بتقديم طلب إلى التطبيق. ويقوم بذلك على إحدى الصفحتين [Formulaire.aspx, Simulations.aspx].
  2. تقوم الصفحة المطلوبة بمعالجة هذا الطلب. وللقيام بذلك، قد تحتاج إلى مساعدة الطبقة [métier] التي قد تحتاج بدورها إلى الطبقة [dao] إذا كان من الضروري تبادل البيانات مع قاعدة البيانات. يتلقى التطبيق ردًا من الطبقة [métier].
  3. وبناءً على ذلك، تختار (3) العرض (= الرد) الذي سيتم إرساله إلى العميل وتزوده (4) بالمعلومات (القالب) التي يحتاجها.
  4. يتم إرسال الرد إلى العميل (5)

11.1. طرق عرض التطبيق

فيما يلي العروض المختلفة المعروضة على المستخدم:

  • - العرض [VueSaisies] الذي يعرض نموذج المحاكاة
  • - عرض [VueSimulation] المستخدم لعرض النتيجة التفصيلية للمحاكاة:
  • - عرض [VueSimulations] الذي يعرض قائمة بالمحاكاة التي أجراها العميل
  • - عرض [VueSimulationsVides] الذي يشير إلى أن العميل ليس لديه محاكاة أو لم يعد لديه محاكاة:
  • عرض [VueErreurs] الذي يشير إلى خطأ في تهيئة التطبيق:

11.2. إنشاء العروض في سياق متعدد المتحكمات

في الإصدار السابق، تم إنشاء جميع العروض من الصفحة الوحيدة [Default.aspx]. كانت هذه الصفحة تحتوي على مكونين [MultiView] وكانت العروض تتكون من تجميع مكون أو مكونين [View] ينتميان إلى هذين المكونين [MultiView].

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

يتمثل أحد الحلول في توزيع العروض على صفحات مختلفة. وهذا ما نقوم به هنا. دعونا ندرس حالتين مختلفتين لتوليد العروض:

  1. يتم إرسال الطلب إلى صفحة P1 وتقوم هذه الصفحة بإنشاء الرد
  2. يتم إرسال الطلب إلى صفحة P1 وتطلب هذه الصفحة من صفحة P2 إنشاء الرد

11.2.1. الحالة 1: صفحة تحكم / عرض

في الحالة 1، نعود إلى بنية التحكم الأحادي في الإصدار السابق، حيث تكون الصفحة [Default.aspx] هي الصفحة P1:

  1. يقوم العميل بتوجيه طلب إلى الصفحة P1 (1)
  2. تقوم الصفحة P1 بمعالجة هذا الطلب. وللقيام بذلك، قد تحتاج إلى مساعدة الطبقة [métier] (2) التي قد تحتاج بدورها إلى الطبقة [dao] إذا كان من الضروري تبادل البيانات مع قاعدة البيانات. يتلقى التطبيق ردًا من الطبقة [métier].
  3. وبناءً على ذلك، تختار (3) العرض (= الرد) الذي سيتم إرساله إلى العميل وتزوده (4) بالمعلومات (القالب) التي يحتاجها. يتعلق الأمر هنا باختيار المكونات [Panel] أو [View] المراد عرضها في الصفحة P1 وتهيئة المكونات التي تحتوي عليها.
  4. يتم إرسال الرد إلى العميل (5)

فيما يلي مثالان مأخوذان من التطبيق قيد الدراسة:

[page Formulaire.aspx]

  • في [1]: بعد أن طلب المستخدم الصفحة [Formulaire.aspx]، يطلب محاكاة
  • في [2]: عالجت الصفحة [Formulaire.aspx] هذا الطلب وقامت بنفسها بتوليد الرد من خلال عرض مكون [View] الذي لم يتم عرضه في [1]

[page Simulations.aspx]

  • في [1]: يريد المستخدم، بعد طلب الصفحة [Simulations.aspx]، إزالة محاكاة
  • في [2]: عالجت الصفحة [Simulations.aspx] هذا الطلب وقامت بنفسها بتوليد الرد من خلال إعادة عرض قائمة المحاكاة الجديدة.

11.2.2. الحالة 2: صفحة 1 وحدة تحكم، صفحة 2 وحدة تحكم / عرض

يمكن أن يشمل الحالة 2 العديد من البنى. سنختار ما يلي:

  1. يقوم العميل بتقديم طلب إلى الصفحة P1 (1)
  2. تقوم الصفحة P1 بمعالجة هذا الطلب. وللقيام بذلك، قد تحتاج إلى مساعدة الطبقة [métier] (2) التي قد تحتاج بدورها إلى الطبقة [dao] إذا كان من الضروري تبادل البيانات مع قاعدة البيانات. يتلقى التطبيق ردًا من الطبقة [métier].
  3. وبناءً على ذلك، تختار (3) العرض (= الرد) الذي سيتم إرساله إلى العميل وتزوده (4) بالمعلومات (القالب) التي يحتاجها. ويحدث هنا أن العرض المطلوب إنشاؤه يجب أن يتم بواسطة صفحة أخرى غير P1، وهي الصفحة P2. ولإجراء العمليتين (3) و(4)، تتوفر للصفحة P1 خياران:
    • إجراء تحويل للتنفيذ إلى الصفحة P2 باستخدام العملية [Server.Transfer(" P2.aspx ")]. في هذه الحالة، يمكنها وضع النموذج المخصص للصفحة P2 في سياق الطلب [Context.Items[" clé "]=valeur] أو في جلسة عمل المستخدم [Session.[" clé "]=valeur]. سيتم عندئذٍ إنشاء مثيل للصفحة P2، وعند معالجة حدثها Load على سبيل المثال، ستتمكن من استرداد المعلومات التي أرسلتها الصفحة P1 من خلال العمليات [valeur=(Type)Context.Items[" clé "]] أو [valeur=(Type)Session[" clé "]] حسب الحالة، حيث Type هو نوع القيمة المرتبطة بالمفتاح. يعد نقل القيم عبر السياق Context هو الأنسب إذا لم تكن هناك حاجة للاحتفاظ بقيم النموذج لطلب مستقبلي من العميل.
    • اطلب من العميل إعادة التوجيه إلى الصفحة P2 من خلال العملية [Response.Redirect(" P2.aspx ")]. في هذه الحالة، ستضع الصفحة P1 النموذج المخصص للصفحة P2 في الجلسة، لأن سياق الطلب Context يتم حذفه في نهاية كل طلب. ولكن هنا، ستؤدي إعادة التوجيه إلى إنهاء الطلب الأول للعميل إلى P1 وإرسال طلب ثانٍ من نفس العميل، إلى P2 هذه المرة. هناك طلبان متتاليان. نعلم أن الجلسة هي إحدى الوسائل للحفاظ على "الذاكرة" بين الطلبات. هناك حلول أخرى غير الجلسة.
  4. بغض النظر عن الطريقة التي تتولى بها P2 زمام الأمور، فإننا نعود بعد ذلك إلى الحالة 1: تلقت P2 طلبًا ستقوم بمعالجته (5) وستقوم هي نفسها بتوليد الرد (6، 7). يمكننا أيضًا أن نتخيل أن الصفحة P2 ستقوم، بعد معالجة الطلب، بتمرير زمام الأمور إلى صفحة P3، وهكذا دواليك.

فيما يلي مثال مأخوذ من التطبيق قيد الدراسة:

  • إلى [1]: يطلب المستخدم الذي طلب الصفحة [Formulaire.aspx] عرض قائمة المحاكاة
  • في [2]: تعالج الصفحة [Formulaire.aspx] هذا الطلب وتعيد توجيه العميل إلى الصفحة [Simulations.aspx]. وهذه الأخيرة هي التي تقدم الرد للمستخدم. بدلاً من مطالبة العميل بإعادة التوجيه، كان بإمكان الصفحة [Formulaire.aspx] تحويل طلب العميل إلى الصفحة [Simulations.aspx]. في هذه الحالة في [2]، كنا سنرى نفس عنوان URL الموجود في [1]. في الواقع، يعرض المتصفح دائمًا آخر عنوان URL تم طلبه:
    • الإجراء المطلوب في [1] موجه إلى الصفحة [Formulaire.aspx]. يقوم المتصفح بإجراء POST إلى هذه الصفحة.
    • إذا عالجت الصفحة [Formulaire.aspx] الطلب ثم نقلته عبر [Server.Transfer(" Simulations.aspx ")] إلى الصفحة [Simulations.aspx]، فإننا نبقى في نفس الطلب. سيعرض المتصفح عندئذٍ في [2]، URL من [Formulaire.aspx] الذي تم فيه POST.
    • إذا عالجت الصفحة [Formulaire.aspx] الطلب ثم أعاد توجيهه عبر [Response.Redirect(" Simulations.aspx ")] إلى الصفحة [Simulations.aspx]، فسيقوم المتصفح بإجراء طلب ثانٍ، وهو GET إلى [Simulations.aspx]. سيعرض المتصفح بعد ذلك في [2]، URL من [Simulations.aspx] الذي تم توجيه GET إليه. وهذا ما تظهره لنا لقطة الشاشة [2] أعلاه.

11.3. مشروع Visual Web Developer للطبقة [web]

مشروع Visual Web Developer للطبقة [web] هو التالي:

  • في [1] نجد:
    • ملف التكوين [Web.config] للتطبيق – وهو مطابق لملف التكوين الخاص بالتطبيق [pam-v4-3tier-nhibernate-multivues-monopage].
    • الصفحة [Default.aspx] – تكتفي بإعادة توجيه العميل إلى الصفحة [Formulaire.aspx]
    • الصفحة [Formulaire.aspx] التي تعرض للمستخدم نموذج المحاكاة وتعالج الإجراءات المتعلقة بهذا النموذج
    • الصفحة [Simulations.aspx] التي تعرض للمستخدم قائمة محاكاته وتعالج الإجراءات المتعلقة بهذه الصفحة
    • الصفحة [Erreurs.aspx] التي تعرض للمستخدم صفحة تشير إلى وجود خطأ عند بدء تشغيل تطبيق الويب.
  • في [2] نرى مراجع المشروع.

لنعد إلى بنية المشروع الجديد:

بالنسبة للمشروع [pam-v4-3tier-nhibernate-multivues-monopage]، لم تتغير سوى طرق العرض. وهكذا يستعيد المشروع الجديد بعض ملفات هذا المشروع:

  • ملف التكوين [Web.config]
  • الملفات DLL المشار إليها في [pam-dao-nhibernate, pam-metier-dao-nhibernate, Spring.Core, NHibernate]
  • فئة التطبيق الشاملة [Global.asax]
  • المجلدات [images, ressources, pam]

للتوافق مع المشروع قيد الإنشاء، سنحرص على أن يكون مساحة اسم العروض وفئة التطبيق الشاملة هي [pam-v7]:

  

11.4. كود عرض الصفحات

11.4.1. الصفحة الرئيسية [MasterPage.master]

تحتوي طرق عرض التطبيق المعروضة في الفقرة 11.1 على أجزاء مشتركة يمكن تجميعها في صفحة رئيسية، تسمى Master Page في Visual Studio. لنأخذ على سبيل المثال، العرضين [VueSaisies] و [VueSimulationsVides] أدناه، اللذين تم إنشاؤهما على التوالي بواسطة الصفحتين [Formulaire.aspx] و [Simulations.aspx]:

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

  • وضع هذا الجزء المشترك في مكون مستخدم. كانت هذه هي التقنية الرئيسية مع ASP.NET 1.1
  • وضع هذا الجزء المشترك في صفحة رئيسية. ظهرت هذه التقنية مع ASP.NET 2.0. وهي التقنية التي نستخدمها هنا.

لإنشاء صفحة رئيسية في تطبيق ويب، يمكن اتباع الخطوات التالية:

  • انقر بزر الماوس الأيمن على المشروع / إضافة عنصر جديد / صفحة رئيسية:

يؤدي إضافة صفحة رئيسية إلى إضافة ثلاثة ملفات إلى تطبيق الويب بشكل افتراضي:

  • [MasterPage.master]: كود عرض الصفحة الرئيسية
  • [MasterPage.master.cs]: كود التحكم في الصفحة الرئيسية
  • [Masterpage.Master.designer.cs]: إعلان مكونات الصفحة الرئيسية

الرمز الذي أنشأه Visual Studio في [MasterPage.master] هو التالي:


<%@ Master Language="C#" AutoEventWireup="true" CodeBehind="MasterPage.master.cs" Inherits="pam_v7.MasterPage" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Untitled Page</title>
    <asp:ContentPlaceHolder id="head" runat="server">
    </asp:ContentPlaceHolder>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:ContentPlaceHolder id="ContentPlaceHolder1" runat="server">
        
        </asp:ContentPlaceHolder>
    </div>
    </form>
</body>
</html>
  • السطر 1: تُستخدم العلامة <%@ Master ... %> لتعريف الصفحة كصفحة رئيسية. سيكون كود التحكم في الصفحة موجودًا في الملف المحدد بواسطة السمة CodeBehind، وسترث الصفحة الفئة المحددة بواسطة السمة Inherits.
  • الأسطر 12-18: نموذج الصفحة الرئيسية
  • الأسطر 14-16: حاوية فارغة ستحتوي في تطبيقنا على إحدى الصفحات [Formulaire.aspx, Simulations.aspx, Erreurs.aspx]. يتلقى العميل كاستجابة، دائمًا نفس الصفحة، الصفحة الرئيسية، حيث سيتلقى الحاوية [ContentPlaceHolder1] تدفقًا HTML مقدمًا من إحدى الصفحات [Formulaire.aspx, Simulations.aspx, Erreurs.aspx]. وبالتالي، لتغيير مظهر الصفحات المرسلة إلى العملاء، يكفي تغيير مظهر الصفحة الرئيسية.
  • السطران 8-9: حاوية فارغة يمكن للصفحات "الفرعية" من خلالها تخصيص العنوان <head>...</head>.

يتم عرض التمثيل المرئي (علامة التبويب "التصميم") لهذا الكود المصدري في (1) أدناه. علاوة على ذلك، يمكن إضافة أي عدد من الحاويات حسب الرغبة، بفضل المكون [ContentPlaceHolder] (2) في شريط الأدوات [Standard].

رمز التحكم الذي تم إنشاؤه بواسطة Visual Studio في [MasterPage.master.cs] هو التالي:


using System;

public partial class MasterPage : System.Web.UI.MasterPage
{
    protected void Page_Load(object sender, EventArgs e)
    {

    }
}
  • السطر 3: الفئة المشار إليها بواسطة السمة [Inherits] للتوجيه <%@ Master ... %> في الصفحة [MasterPage.master] مشتقة من الفئة [System.Web.UI.MasterPage]

فيما سبق، نرى وجود الطريقة Page_Load التي تدير الحدث Load للصفحة الرئيسية. ستحتوي الصفحة الرئيسية في داخلها على صفحة أخرى. بأي ترتيب تحدث الأحداث Load للصفحتين؟ هذه قاعدة عامة: يحدث الحدث Load لمكون ما قبل الحدث الخاص بحاويته. هنا، سيحدث الحدث Load للصفحة المُدرجة في الصفحة الرئيسية قبل الحدث الخاص بالصفحة الرئيسية نفسها.

لإنشاء صفحة التي تكون صفحتها الرئيسية هي الصفحة السابقة [MasterPage.master]، يمكن اتباع الخطوات التالية:

  • في [1]: انقر بزر الماوس الأيمن على الصفحة الرئيسية ثم اختر الخيار [Ajouter une page de contenu]
  • إلى [2]: يتم إنشاء صفحة افتراضية، وهي هنا [WebForm1.aspx].

رمز العرض [WebForm1.aspx] هو التالي:


<%@ Page Title="" Language="C#" MasterPageFile="~/MasterPage.Master" AutoEventWireup="true" CodeBehind="WebForm1.aspx.cs" Inherits="pam_v7.WebForm1" %>
<asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">
</asp:Content>
  • السطر 1: توجيه Page وسماته
    • MasterPageFile: يشير إلى ملف الصفحة الرئيسية للصفحة الموصوفة في التوجيه. يشير الرمز ~ إلى مجلد المشروع.
    • المعلمات الأخرى هي المعلمات المعتادة لصفحة ويب ASP
  • السطران 2-3: ترتبط العلامات <asp:Content> واحدة تلو الأخرى بالتوجيهات <asp:ContentPlaceHolder> للصفحة الرئيسية عبر السمة ContentPlaceHolderID. سيتم وضع المكونات الموجودة بين السطرين 2-3 أعلاه، عند التنفيذ، في حاوية ID ContentPlaceHolder1 في الصفحة الرئيسية.

من خلال إعادة تسمية الصفحة [WebForm1.aspx] التي تم إنشاؤها بهذه الطريقة، يمكن إنشاء الصفحات المختلفة التي تستخدم [MasterPage.master] كصفحة رئيسية.

بالنسبة لتطبيقنا [SimuPaie]، سيكون الشكل المرئي للصفحة الرئيسية كما يلي:

رقم
النوع
الاسم
الدور
A
لوحة (الوردي أعلاه)
رأس
عنوان الصفحة
B
لوحة (باللون الأصفر أعلاه)
المحتوى
محتوى الصفحة
1
LinkButton
LinkButtonFaireSimulation
طلب حساب المحاكاة
2
LinkButton
LinkButtonEffacerSimulation
مسح نموذج الإدخال
3
LinkButton
LinkButtonVoirSimulations
يعرض قائمة المحاكاة التي تمت بالفعل
4
LinkButton
LinkButtonFormulaireSimulation
العودة إلى نموذج الإدخال
5
LinkButton
LinkButtonEnregistrerSimulation
يسجل المحاكاة الحالية في قائمة المحاكاة
6
LinkButton
LinkButtonTerminerSession
إنهاء الجلسة الحالية

الرمز المصدري المقابل هو التالي:


<%@ Master Language="C#" AutoEventWireup="true" CodeBehind="MasterPage.master.cs" Inherits="pam_v7.MasterPage" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
  <title>Application PAM</title>
</head>
<body background="ressources/standard.jpg">
  <form id="form1" runat="server">
  <asp:ScriptManager ID="ScriptManager1" runat="server" EnablePartialRendering="true" />
  <asp:UpdatePanel runat="server" ID="UpdatePanelPam" UpdateMode="Conditional">
    <ContentTemplate>
      <asp:Panel ID="entete" runat="server" BackColor="#FFE0C0">
        <table>
          <tr>
            <td>
              <h2>
                Simulateur de calcul de paie</h2>
            </td>
            <td>
              <label>
                &nbsp;&nbsp;&nbsp</label>
              <asp:UpdateProgress ID="UpdateProgress1" runat="server">
                <ProgressTemplate>
                  <img alt="" src="images/indicator.gif" />
                  <asp:Label ID="Label5" runat="server" BackColor="#FF8000"
                             EnableViewState="False" Text="Calcul en cours. Patientez ....">
                        </asp:Label>
                </ProgressTemplate>
              </asp:UpdateProgress>
            </td>
            <td>
              <asp:LinkButton ID="LinkButtonFaireSimulation" runat="server"
                         CausesValidation="False">| Faire la simulation<br />
                    </asp:LinkButton>
              <asp:LinkButton ID="LinkButtonEffacerSimulation" runat="server"
                         CausesValidation="False">| Effacer la simulation<br />
                    </asp:LinkButton>
              <asp:LinkButton ID="LinkButtonVoirSimulations" runat="server"
                     CausesValidation="False">| Voir les simulations<br />
                    </asp:LinkButton>
              <asp:LinkButton ID="LinkButtonFormulaireSimulation" runat="server"
                         CausesValidation="False">| Retour au formulaire de simulation<br />
                    </asp:LinkButton>
              <asp:LinkButton ID="LinkButtonEnregistrerSimulation" runat="server"
                         CausesValidation="False">| Enregistrer la simulation<br />
                    </asp:LinkButton>
              <asp:LinkButton ID="LinkButtonTerminerSession" runat="server"
                         CausesValidation="False">| Terminer la session<br />
                    </asp:LinkButton>
            </td>
          </tr>
        </table>
        <hr />
      </asp:Panel>
      <div>
        <asp:Panel ID="contenu" runat="server" BackColor="#FFFFC0">
          <asp:ContentPlaceHolder ID="ContentPlaceHolder1" runat="server">
          </asp:ContentPlaceHolder>
        </asp:Panel>
      </div>
    </ContentTemplate>
  </asp:UpdatePanel>
  </form>
</body>
</html>
  • السطر 1: لاحظ اسم فئة الصفحة الرئيسية: MasterPage
  • السطر 8: يتم تعريف صورة خلفية للصفحة.
  • الأسطر 9-64: النموذج
  • السطر 10: المكون ScriptManager الضروري لتأثيرات Ajax
  • الأسطر 11-63: الحاوية AJax
  • الأسطر 12-62: المحتوى المُعالج بواسطة Ajax
  • الأسطر 13-55: المكون Panel [entete]
  • الأسطر 57-60: المكون Panel [contenu]
  • السطور 58-59: مكون ID [ContentPlaceHolder1] الذي سيحتوي على الصفحة المُغلفة [Formulaire.aspx, Simulations.aspx, Erreurs.aspx]

لبناء هذه الصفحة، يمكن إدراج الرمز ASPX من العرض [VueEntete] للصفحة [Default.aspx] من الإصدار [pam-v4-3tier-nhibernate-multivues-monopage]، الموصوف في الفقرة 8.5.2.

11.4.2. الصفحة [Formulaire.aspx]

لإنشاء هذه الصفحة، سنتبع الطريقة الموضحة في الفقرة 11.4.1 وسنقوم بتغيير اسم الصفحة [WebForm1.aspx] التي تم إنشاؤها إلى [Formulaire.aspx]. سيكون الشكل المرئي للصفحة [Formulaire.aspx] قيد الإنشاء كما يلي:

يتكون المظهر المرئي للصفحة [Formulaire.aspx] من عنصرين:

  • في [1] الصفحة الرئيسية مع حاوية [ContentPlaceHolder1] (2)
  • في [2] المكونات الموضوعة في الحاوية [ContentPlaceHolder1]. هذه المكونات مطابقة لتلك الموجودة في التطبيق السابق.

كود المصدر لهذه الصفحة هو التالي:


<%@ Page Language="C#" MasterPageFile="~/MasterPage.master" AutoEventWireup="true"
  CodeBehind="Formulaire.aspx.cs" Inherits="pam_v7.PageFormulaire" Title="Simulation de calcul de paie : formulaire" %>

<%@ MasterType VirtualPath="~/MasterPage.master" %>
<asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" runat="Server">
  <div>
    <table>
      <tr>
        <td>
          Employé
        </td>
        <td>
          Heures travaillées
        </td>
        <td>
          Jours travaillés
        </td>
        <td>
        </td>
      </tr>
...
</asp:Content>
  • السطر 1: التوجيه Page مع سمة MasterPageFile
  • السطر 4: يمكن لفئة التحكم في الصفحة الرئيسية عرض الحقول والخصائص العامة. يمكن الوصول إليها من خلال الصفحات المُغلفة باستخدام صيغة Master.[champ] أو Master.[propriété]. تشير خاصية Master للصفحة إلى الصفحة الرئيسية في شكل مثيل من النوع [System.Web.UI.MasterPage]. لذا في مثالنا، يجب في الواقع كتابة (MasterPage)(Master).[champ] أو (MasterPage)(Master).[propriété]. يمكن تجنب هذا التحويل عن طريق إدراج التوجيه MasterType في الصف 4 من الصفحة. يشير السمة VirtualPath لهذه التوجيهية إلى ملف الصفحة الرئيسية. يمكن للمترجم عندئذ معرفة الحقول والخصائص والطرق العامة التي تعرضها فئة الصفحة الرئيسية، وهي هنا من النوع [MasterPage].
  • الأسطر 5-22: المحتوى الذي سيتم إدراجه في الحاوية [ContentPlaceHolder1] للصفحة الرئيسية.

يمكن إنشاء هذه الصفحة بوضع محتوى (الأسطر 6-21) من العرض [VueSaisies] الموصوف في الفقرة 8.5.3 ومحتوى العرض [VueSimulation] الموصوف في الفقرة 8.5.4.

11.4.3. الصفحة [Simulations.aspx]

لإنشاء هذه الصفحة، سنتبع الطريقة الموضحة في الفقرة 11.4.1 وسنقوم بتغيير اسم الصفحة [WebForm1.aspx] التي تم إنشاؤها إلى [Simulations.aspx]. المظهر المرئي للصفحة [Simulations.aspx] قيد الإنشاء هو كما يلي:

يتكون المظهر المرئي للصفحة [Simulations.aspx] من عنصرين:

  • في [1] الصفحة الرئيسية مع حاوية [ContentPlaceHolder1]
  • في [2] المكونات الموضوعة في الحاوية [ContentPlaceHolder1]. هذه المكونات مطابقة لتلك الموجودة في التطبيق السابق.

كود المصدر لهذه الصفحة هو التالي:


<%@ Page Language="C#" MasterPageFile="~/MasterPage.master" AutoEventWireup="true"
  CodeBehind="Simulations.aspx.cs" Inherits="pam_v7.PageSimulations" Title="Pam : liste des simulations" %>

<%@ MasterType VirtualPath="~/MasterPage.master" %>
<asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" runat="Server">
  <asp:MultiView ID="MultiView1" runat="server">
    <asp:View ID="View1" runat="server">
      <h2>
        Liste de vos simulations</h2>
      <p>
        <asp:GridView ID="GridViewSimulations" runat="server" ...>
...
        </asp:GridView>
      </p>
    </asp:View>
    <asp:View ID="View2" runat="server">
      <h2>
        La liste de vos simulations est vide</h2>
    </asp:View>
  </asp:MultiView><br />
</asp:Content>

يمكن إنشاء هذه الصفحة عن طريق وضع محتوى (الأسطر 5-21) الخاص بالعرض [VueSimulations] الموصوف في الفقرة 8.5.5 ومحتوى العرض [VueSimulationsVides] الموصوف في الفقرة 8.5.6.

11.4.4. الصفحة [Erreurs.aspx]

لإنشاء هذه الصفحة، سنتبع الطريقة الموضحة في الفقرة 11.4.1 وسنقوم بتغيير اسم الصفحة [WebForm1.aspx] التي تم إنشاؤها إلى [Erreurs.aspx]. المظهر المرئي للصفحة [Erreurs.aspx] قيد الإنشاء هو كما يلي:

يتكون المظهر المرئي للصفحة [Erreurs.aspx] من عنصرين:

  • في [1] الصفحة الرئيسية مع حاوية [ContentPlaceHolder1]
  • في [2] المكونات الموضوعة في الحاوية [ContentPlaceHolder1]. هذه المكونات مطابقة لتلك الموجودة في التطبيق السابق.

كود المصدر لهذه الصفحة هو التالي:


<%@ Page Language="C#" MasterPageFile="~/MasterPage.master" AutoEventWireup="true"
  CodeBehind="Erreurs.aspx.cs" Inherits="pam_v7.PageErreurs" Title="Pam : erreurs" %>

<%@ MasterType VirtualPath="~/MasterPage.master" %>
<asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
        <h3>Les erreurs suivantes se sont produites au démarrage de l'application</h3>
        <ul>
            <asp:Repeater id="rptErreurs" runat="server">
                <ItemTemplate>
                    <li>
                        <%# Container.DataItem %>
                    </li>
                </ItemTemplate>
            </asp:Repeater>
        </ul>
</asp:Content>

11.5. رمز التحقق من الصفحات

11.5.1. نظرة عامة

لنعد إلى بنية التطبيق:

  • [Global] هو الكائن من النوع [HttpApplication] الذي يقوم بتهيئة (الخطوة 0) التطبيق. هذه الفئة مطابقة لتلك الموجودة في الإصدار السابق.
  • أصبح كود وحدة التحكم، الذي كان موجودًا بالكامل في الإصدار السابق في [Default.aspx.cs]، موزعًا الآن على عدة صفحات:
    • [MasterPage.master]: الصفحة الرئيسية لصفحات [Formulaire.aspx, Simulations.aspx, Erreurs.aspx]. وهي تحتوي على القائمة.
    • [Formulaire.aspx]: الصفحة التي تعرض نموذج المحاكاة وتدير الإجراءات التي تتم على هذا النموذج
    • [Simulations.aspx]: الصفحة التي تعرض قائمة عمليات المحاكاة وتدير الإجراءات التي تتم على هذه الصفحة نفسها
    • [Erreurs.aspx]: الصفحة التي يتم عرضها عند حدوث خطأ في تهيئة التطبيق. لا توجد إجراءات ممكنة على هذه الصفحة.

تتم معالجة طلب العميل وفقًا للخطوات التالية:

  1. يقدم العميل طلبًا إلى التطبيق. وعادةً ما يقدم هذا الطلب إلى إحدى الصفحتين [Formulaire.aspx, Simulations.aspx]، ولكن لا شيء يمنعه من طلب الصفحة [Erreurs.aspx]. لذا يجب توقع هذه الحالة.
  2. تقوم الصفحة المطلوبة بمعالجة هذا الطلب (الخطوة 1). وللقيام بذلك، قد تحتاج إلى مساعدة الطبقة [métier] (الخطوة 2) التي قد تحتاج بدورها إلى الطبقة [dao] إذا كان من الضروري تبادل البيانات مع قاعدة البيانات. يتلقى التطبيق ردًا من الطبقة [métier].
  3. وبناءً على ذلك، تختار (الخطوة 3) العرض (= الرد) الذي سيتم إرساله إلى العميل وتزوده (الخطوة 4) بالمعلومات (النموذج) التي يحتاجها. لقد رأينا ثلاث احتمالات لتوليد هذا الرد:
    • الصفحة (D) المطلوبة هي أيضًا الصفحة (R) المرسلة كرد. يتكون بناء نموذج الرد (R) إذن من إعطاء بعض مكونات الصفحة (D) القيمة التي يجب أن تكون عليها في الرد.
    • الصفحة (D) المطلوبة ليست هي الصفحة (R) المرسلة كرد. عندئذٍ يمكن للصفحة (D):
      • تحويل تدفق التنفيذ إلى الصفحة (R) بواسطة التعليمات Server.Transfer(" R "). يمكن عندئذ وضع النموذج في السياق بواسطة Context.Items("مفتاح")=قيمة أو، في حالات نادرة، في الجلسة بواسطة Session.Items("مفتاح")=قيمة
      • إعادة توجيه العميل إلى الصفحة (R) بواسطة الأمر Response.redirect(" R "). يمكن عندئذٍ وضع النموذج في الجلسة ولكن ليس في السياق.
  4. يتم إرسال الرد إلى العميل (الخطوة 5)

ستستجيب كل صفحة من صفحات [MasterPage.master, Formulaire.aspx, Simulations.aspx, Erreurs.aspx] لواحد أو أكثر من الأحداث التالية:

  • Init: أول حدث في دورة حياة الصفحة
  • Load: يحدث عند تحميل الصفحة
  • Click: النقر على أحد الروابط في قائمة الصفحة الرئيسية

نقوم بمعالجة الصفحات واحدة تلو الأخرى بدءًا من الصفحة الرئيسية.

11.5.2. كود التحكم في الصفحة [MasterPage.master]

11.5.2.1. هيكل الفئة

يحتوي رمز التحكم في الصفحة الرئيسية على الهيكل التالي:


using System.Web.UI.WebControls;

namespace pam_v7
{
  public partial class MasterPage : System.Web.UI.MasterPage
  {

    // القائمة 
    public LinkButton OptionFaireSimulation
    {
      get { return LinkButtonFaireSimulation; }
    }
...

    // تثبيت القائمة 
    public void SetMenu(bool boolFaireSimulation, bool boolEnregistrerSimulation, bool boolEffacerSimulation, bool boolFormulaireSimulation, bool boolVoirSimulations, bool boolTerminerSession)
    {
....
    }

    // إدارة الخيار [Terminer la session] 
    protected void LinkButtonTerminerSession_Click(object sender, System.EventArgs e)
    {
....
    }

    // تهيئة الصفحة الرئيسية 
    protected void Page_Init(object sender, System.EventArgs e)
    {
....
      }
    }
  }
}
  • السطر 5: تسمى الفئة [MasterPage] وهي مشتقة من فئة النظام [System.Web.UI.MasterPage].
  • الأسطر 9-14: يتم عرض الخيارات الستة في القائمة كخصائص عامة للفئة
  • الأسطر 16-19: ستسمح الطريقة العامة SetMenu للصفحات [Formulaire.aspx, Simulations.aspx, Erreurs.aspx] بتحديد قائمة الصفحة الرئيسية
  • الأسطر 22-25: الإجراء الذي سيدير النقر على الرابط [LinkButtonTerminerSession]
  • الأسطر 28-31: إجراء إدارة الحدث Init للصفحة الرئيسية

11.5.2.2. الخصائص العامة للفئة


using System.Web.UI.WebControls;

namespace pam_v7
{
  public partial class MasterPage : System.Web.UI.MasterPage
  {

    // القائمة 
    public LinkButton OptionFaireSimulation
    {
      get { return LinkButtonFaireSimulation; }
    }

    public LinkButton OptionEffacerSimulation
    {
      get { return LinkButtonEffacerSimulation; }
    }

    public LinkButton OptionEnregistrerSimulation
    {
      get { return LinkButtonEnregistrerSimulation; }
    }

    public LinkButton OptionVoirSimulations
    {
      get { return LinkButtonVoirSimulations; }
    }

    public LinkButton OptionTerminerSession
    {
      get { return LinkButtonTerminerSession; }
    }

    public LinkButton OptionFormulaireSimulation
    {
      get { return LinkButtonFormulaireSimulation; }
    }

...
  }
}

لفهم هذا الرمز، يجب تذكر المكونات التي تشكل الصفحة الرئيسية:

رقم
النوع
الاسم
الدور
A
لوحة (الوردي أعلاه)
رأس
عنوان الصفحة
B
لوحة (باللون الأصفر أعلاه)
المحتوى
محتوى الصفحة
1
LinkButton
LinkButtonFaireSimulation
طلب حساب المحاكاة
2
LinkButton
LinkButtonEffacerSimulation
مسح نموذج الإدخال
3
LinkButton
LinkButtonVoirSimulations
يعرض قائمة المحاكاة التي تمت بالفعل
4
LinkButton
LinkButtonFormulaireSimulation
العودة إلى نموذج الإدخال
5
LinkButton
LinkButtonEnregistrerSimulation
يسجل المحاكاة الحالية في قائمة المحاكاة
6
LinkButton
LinkButtonTerminerSession
إنهاء الجلسة الحالية

لا يمكن الوصول إلى المكونات من 1 إلى 6 خارج الصفحة التي تحتوي عليها. تهدف خصائص الأسطر من 9 إلى 37 إلى جعلها متاحة للفئات الخارجية، وهي في هذه الحالة فئات الصفحات الأخرى في التطبيق.

11.5.2.3. الطريقة SetMenu

تسمح الطريقة العامة SetMenu للصفحات [Formulaire.aspx, Simulations.aspx, Erreurs.aspx] بتحديد قائمة الصفحة الرئيسية. كودها بسيط:


        // تثبيت القائمة 
        public void SetMenu(bool boolFaireSimulation, bool boolEnregistrerSimulation, bool boolEffacerSimulation, bool boolFormulaireSimulation, bool boolVoirSimulations, bool boolTerminerSession)
        {
            // تثبيت خيارات القائمة 
            LinkButtonFaireSimulation.Visible = boolFaireSimulation;
            LinkButtonEnregistrerSimulation.Visible = boolEnregistrerSimulation;
            LinkButtonEffacerSimulation.Visible = boolEffacerSimulation;
            LinkButtonVoirSimulations.Visible = boolVoirSimulations;
            LinkButtonFormulaireSimulation.Visible = boolFormulaireSimulation;
            LinkButtonTerminerSession.Visible = boolTerminerSession;
}

11.5.2.4. إدارة أحداث الصفحة الرئيسية

ستدير الصفحة الرئيسية حدثين:

  • الحدث Init وهو الحدث الأول في دورة حياة الصفحة
  • الحدث Click على الرابط [LinkButtonTerminerSession]

تحتوي الصفحة الرئيسية على خمسة روابط أخرى: [LinkButtonFaireSimulation, LinkButtonEnregistrerSimulation, LinkButtonEffacerSimulation, LinkButtonVoirSimulations, LinkButtonFormulaireSimulation]. على سبيل المثال، دعونا ننظر إلى ما يجب القيام به عند النقر على الرابط [LinkButtonFaireSimulation]:

  1. التحقق من البيانات المدخلة (الساعات، الأيام) في الصفحة [Formulaire.aspx]
  2. إجراء حساب الراتب
  3. عرض النتائج في الصفحة [Formulaire.aspx]

تتطلب العمليتان 1 و 3 الوصول إلى مكونات الصفحة [Formulaire.aspx]. وهذا ليس هو الحال. في الواقع، لا تعرف الصفحة الرئيسية أي شيء عن مكونات الصفحات التي يمكن إدراجها في حاويتها [ContentPlaceHolder1]. في مثالنا، يتعين على الصفحة [Formulaire.aspx] إدارة النقر على الرابط [LinkButtonFaireSimulation] لأنها هي التي يتم عرضها عند حدوث هذا الحدث. كيف يمكن إخطارها بهذا الحدث؟

  • نظرًا لأن الرابط [LinkButtonFaireSimulation] ليس جزءًا من الصفحة [Formulaire.aspx]، لا يمكننا كتابة الإجراء المعتاد في [Formulaire.aspx]:

    private void LinkButtonFaireSimulation_Click(object sender, System.EventArgs e)
    {
...
}

يمكن التغلب على المشكلة باستخدام الكود التالي في [Formulaire.aspx]:


using System.Collections.Generic;
...

namespace pam_v7
{
    public partial class Formulaire : System.Web.UI.Page
    {
// تحميل الصفحة 
    protected void Page_Load(object sender, System.EventArgs e)
    {
        // مدير الأحداث
        Master.OptionFaireSimulation.Click += OptFaireSimulation_Click;
        Master.OptionEffacerSimulation.Click += OptEffacerSimulation_Click;
        Master.OptionVoirSimulations.Click += OptVoirSimulations_Click;
        Master.OptionEnregistrerSimulation.Click += OptEnregistrerSimulation_Click;
...
    }

    // حساب الرواتب 
    private void OptFaireSimulation_Click(object sender, System.EventArgs e)
    {
....
    }

    // مسح المحاكاة 
    private void OptEffacerSimulation_Click(object sender, System.EventArgs e)
    {
...
    }

    protected void OptVoirSimulations_Click(object sender, System.EventArgs e)
    {
...
    }

    protected void OptEnregistrerSimulation_Click(object sender, System.EventArgs e)
    {
...
    }
 }
}
  • الأسطر 12-15: عند حدوث الحدث Load في الصفحة [Formulaire.aspx]، تم إنشاء مثيل للفئة [MasterPage] في الصفحة الرئيسية. يمكن الوصول إلى خصائصها العامة Optionxx وهي من النوع LinkButton، وهو مكون يدعم الحدث Click. نربط بهذه الأحداث Click الطرق:
    • OptFaireSimulation_Click للحدث Click على الرابط LinkButtonFaireSimulation
    • OptEffacerSimulation_Click للحدث Click على الرابط LinkButtonEffacerSimulation
    • OptVoirSimulations_Click للحدث Click على الرابط LinkButtonVoirSimulations
    • OptEnregistrerSimulation_Click للحدث Click على الرابط LinkButtonEnregistrerSimulation

سيتم توزيع إدارة الأحداث Click على الروابط الستة في القائمة على النحو التالي:

  • ستدير الصفحة [Formulaire.aspx] الروابط [LinkButtonFaireSimulation, LinkButtonEnregistrerSimulation, LinkButtonEffacerSimulation, LinkButtonVoirSimulations]
  • ستدير الصفحة [Simulations.aspx] الرابط [LinkButtonFormulaireSimulation]
  • ستدير الصفحة الرئيسية [MasterPage.master] الرابط [LinkButtonTerminerSession]. بالنسبة لهذا الحدث، فهي لا تحتاج في الواقع إلى معرفة الصفحة التي تغلفها.

11.5.2.5. حدث Init للصفحة الرئيسية

الصفحات الثلاث [Formulaire.aspx, Simulations.aspx, Erreurs.aspx] للتطبيق لها [MasterPage.master] كصفحة رئيسية. لنسمي الصفحة الرئيسية M، والصفحة المُغلفة E. عندما يطلب العميل الصفحة E، تحدث الأحداث التالية بالترتيب:

  • E.Init
  • M.Init
  • E.Load
  • M.Load
  • ...

سنستخدم الحدث Init من الصفحة M لتنفيذ كود من المهم تنفيذه في أقرب وقت ممكن، بغض النظر عن الصفحة المستهدفة E. لمعرفة هذا الكود، دعونا نراجع الصورة العامة للتطبيق:

في الأعلى، [Global] هو الكائن من النوع [HttpApplication] الذي يقوم بتهيئة التطبيق. هذه الفئة هي نفسها الموجودة في الإصدار [pam-v4-3tier-nhibernate-multivues-monopage]:


using System;
...

namespace pam_v7
{
  public class Global : System.Web.HttpApplication
  {
    // --- البيانات الثابتة للتطبيق ---
    public static Employe[] Employes;
    public static IPamMetier PamMetier = null;
    public static string Msg;
    public static bool Erreur = false;

    // تشغيل التطبيق
    public void Application_Start(object sender, EventArgs e)
    {
...
    }

    public void Session_Start(object sender, EventArgs e)
    {
...
    }
  }
}

إذا فشلت الفئة [Global] في تهيئة التطبيق بشكل صحيح، فإنها تحدد قيمتين لمتغيرين عامين ثابتين:

  • يتم تعيين المتغير المنطقي Error في السطر 12 إلى vrai
  • تحتوي المتغير Msg في السطر 11 على رسالة توضح تفاصيل الخطأ الذي حدث

عندما يطلب المستخدم إحدى صفحات [Formulaire.aspx, Simulations.aspx] في حين أن التطبيق لم يتم تهيئته بشكل صحيح، يجب تحويل هذا الطلب أو إعادة توجيهه إلى الصفحة [Erreurs.aspx]، التي ستعرض رسالة الخطأ من الفئة [Global]. يمكن إدارة هذه الحالة بعدة طرق:

  • إجراء اختبار خطأ التهيئة في مدير الأحداث Init أو Load لكل صفحة من صفحات [Formulaire.aspx, Simulations.aspx]
  • إجراء اختبار خطأ التهيئة في معالج الأحداث Init أو Load في الصفحة الرئيسية لهاتين الصفحتين. وتتميز هذه الطريقة بوضع اختبار خطأ التهيئة في مكان واحد فقط.

نختار إجراء اختبار خطأ التهيئة في معالج الحدث Init للصفحة الرئيسية:


        protected void Page_Init(object sender, System.EventArgs e)
        {
            // مدير الأحداث 
            LinkButtonTerminerSession.Click += LinkButtonTerminerSession_Click;
            // أخطاء في التهيئة؟ 
            if (Global.Erreur)
            {
                // هل الصفحة المُغلفة هي صفحة الأخطاء؟ 
                bool isPageErreurs =...;
                // إذا كانت صفحة الأخطاء هي التي تظهر، فنتجاهل الأمر، وإلا فإننا نعيد توجيه العميل إلى صفحة الأخطاء 
                if (!isPageErreurs)
                    Response.Redirect("Erreurs.aspx");
                return;
            }
}

سيتم تنفيذ الكود أعلاه بمجرد طلب إحدى الصفحات [Formulaire.aspx, Simulations.aspx, Erreurs.aspx]. في حالة طلب الصفحة [Formulaire.aspx, Simulations.aspx]، نكتفي (السطر 12) بإعادة توجيه العميل إلى الصفحة [Erreurs.aspx]، والتي تتولى عرض رسالة الخطأ من الفئة [Global]. في حالة ما إذا كانت الصفحة المطلوبة هي [Erreurs.aspx]، يجب ألا تتم عملية إعادة التوجيه هذه: يجب السماح بعرض الصفحة [Erreurs.aspx]. لذلك، يجب أن نعرف في الطريقة [Page_Init] للصفحة الرئيسية، ما هي الصفحة التي تغلفها هذه الأخيرة.

لنعد إلى شجرة مكونات الصفحة الرئيسية:


...
<body background="ressources/standard.jpg">
    <form id="form1" runat="server">
        <asp:Panel ID="entete" runat="server" BackColor="#FFE0C0" Width="1239px" >
...
        </asp:Panel>
        <div>
            <asp:Panel ID="contenu" runat="server" BackColor="#FFFFC0">
                <asp:ContentPlaceHolder ID="ContentPlaceHolder1" runat="server">
                </asp:ContentPlaceHolder>
            </asp:Panel>
        </div>
    </form>
</body>
</html>
  • الأسطر 1-13: الحاوية ذات المعرف "form1"
  • الأسطر 4-6: الحاوية ذات المعرف "entete"، المضمنة في الحاوية ذات المعرف "form1"
  • الأسطر 8-11: الحاوية ذات المعرف "contenu"، المضمنة في الحاوية ذات المعرف "form1"
  • السطور 9-10: الحاوية ذات المعرف "ContentPlaceHolder1"، المضمنة في الحاوية ذات المعرف "contenu"

الصفحة E المُغلفة في الصفحة الرئيسية M، موجودة في الحاوية ذات المعرف "ContentPlaceHolder1". للإشارة إلى مكون ذي المعرف C في هذه الصفحة E، نكتب:


this.FindControl("form1").FindControl("contenu").FindControl("ContentPlaceHolder1").FindControl("C");

أما شجرة مكونات الصفحة [Erreurs.aspx] فهي كما يلي:


<%@ Page Language="C#" MasterPageFile="~/MasterPage.master" AutoEventWireup="true"
  CodeBehind="Erreurs.aspx.cs" Inherits="pam_v7.PageErreurs" Title="Pam : erreurs" %>

<%@ MasterType VirtualPath="~/MasterPage.master" %>
<asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
        <h3>Les erreurs suivantes se sont produites au démarrage de l'application</h3>
        <ul>
            <asp:Repeater id="rptErreurs" runat="server">
                <ItemTemplate>
                    <li>
                        <%# Container.DataItem %>
                    </li>
                </ItemTemplate>
            </asp:Repeater>
        </ul>
</asp:Content>

عندما يتم دمج الصفحة [Erreurs.aspx] مع الصفحة الرئيسية M، يتم دمج محتوى العلامة <asp:Content> أعلاه (الأسطر 5-16) في العلامة <asp:ContentPlaceHolder> ذات المعرف "ContentPlaceholder1" في الصفحة M، ليصبح شجرة مكونات هذه الصفحة كما يلي:

...
<body background="ressources/standard.jpg">
    <form id="form1" runat="server">
        <asp:panel ID="entete" runat="server" BackColor="#FFE0C0" Width="1239px" >
...
        </asp:Panel>
        <div>
            <asp:Panel ID="contenu" runat="server" BackColor="#FFFFC0">
                <asp:ContentPlaceHolder ID="ContentPlaceHolder1" runat="server">
              <h3>Les erreurs suivantes se sont produites au démarrage de l'application</h3>
              <ul>
                  <asp:Repeater id="rptErreurs" runat="server">
                      <ItemTemplate>
                          <li>
                              <%# Container.DataItem %>
                          </li>
                      </ItemTemplate>
                  </asp:Repeater>
              </ul>
                </asp:ContentPlaceHolder>
            </asp:Panel>
        </div>
    </form>
</body>
</html>
  • السطر 12: يمكن استخدام المكون [rptErreurs] لمعرفة ما إذا كانت الصفحة الرئيسية M تحتوي على الصفحة [Erreurs.aspx] أم لا. في الواقع، لا يوجد هذا المكون إلا في هذه الصفحة.

هذه التفسيرات كافية لفهم كود الإجراء [Page_Init] في الصفحة الرئيسية:


protected void Page_Init(object sender, System.EventArgs e)
        {
            // مدير الأحداث 
            LinkButtonTerminerSession.Click += LinkButtonTerminerSession_Click;
            // أخطاء في التهيئة؟ 
            if (Global.Erreur)
            {
                // هل الصفحة المُغلفة هي صفحة الأخطاء؟ 
                bool isPageErreurs = this.FindControl("form1").FindControl("contenu").FindControl("ContentPlaceHolder1").FindControl("rptErreurs") != null;
                // إذا كانت صفحة الأخطاء هي التي تظهر، نترك الأمر كما هو، وإلا فإننا نعيد توجيه العميل إلى صفحة الأخطاء 
                if (!isPageErreurs)
                    Response.Redirect("Erreurs.aspx");
                return;
            }
        }
  • السطر 4: يتم ربط معالج الأحداث بالحدث Click على الرابط LinkButtonTerminerSession. يوجد هذا المعالج في الفئة MasterPage.
  • السطر 6: يتم التحقق مما إذا كانت الفئة [Global] قد قامت بتعيين القيمة المنطقية Erreur
  • السطر 9: إذا كان الجواب نعم، فإن المتغير المنطقي IsPageErreurs يشير إلى ما إذا كانت الصفحة المُغلفة في الصفحة الرئيسية هي الصفحة [Erreurs.aspx]
  • السطر 12: إذا لم تكن الصفحة المُدمجة في الصفحة الرئيسية هي الصفحة [Erreurs.aspx]، يتم إعادة توجيه العميل إلى هذه الصفحة، وإلا لا يتم القيام بأي شيء.

11.5.2.6. حدث النقر على الرابط [LinkButtonTerminerSession]

عندما ينقر المستخدم على الرابط [Terminer la session] في العرض (1) أعلاه، يجب إفراغ محتوى الجلسة وعرض نموذج فارغ (2).

قد يكون كود مدير هذا الحدث كما يلي:


        protected void LinkButtonTerminerSession_Click(object sender, System.EventArgs e)
        {
            // نقوم بإلغاء الجلسة 
            Session.Abandon();
            // نعرض العرض [formulaire] 
            Response.Redirect("Formulaire.aspx");
}
  • السطر 4: يتم التخلي عن الجلسة الحالية
  • السطر 6: يتم إعادة توجيه العميل إلى الصفحة [Formulaire.aspx]

نلاحظ أن هذا الرمز لا يستخدم أيًا من مكونات الصفحات [Formulaire.aspx, Simulations.aspx, Erreurs.aspx]. وبالتالي، يمكن إدارة الحدث بواسطة الصفحة الرئيسية نفسها.

11.5.3. رمز التحكم في الصفحة [Erreurs.aspx]

قد يكون كود التحكم في الصفحة [Erreurs.aspx] كما يلي:


using System.Collections.Generic;

namespace pam_v7
{
  public partial class Erreurs : System.Web.UI.Page
  {
    protected void Page_Load(object sender, System.EventArgs e)
    {
      // أخطاء في التهيئة؟ 
      if (Global.Erreur)
      {
        // يتم إعداد نموذج الصفحة [erreurs] 
        List<string> erreursInitialisation = new List<string>();
        erreursInitialisation.Add(Global.Msg);
        // ربط قائمة الأخطاء بمكونها 
        rptErreurs.DataSource = erreursInitialisation;
        rptErreurs.DataBind();
      }
      // تثبيت القائمة 
      Master.SetMenu(false, false, false, false, false, false);
    }
  }
}

تجدر الإشارة إلى أن الدور الوحيد للصفحة [Erreurs.aspx] هو عرض خطأ في تهيئة التطبيق عند حدوثه:

  • السطر 10: يتم اختبار ما إذا كان التهيئة قد انتهت بخطأ
  • السطران 13-14: إذا كان الجواب نعم، يتم وضع رسالة الخطأ (Global.Msg) في قائمة [ErreursInitialisation]
  • السطران 16-17: يُطلب من المكون [rptErreurs] عرض هذه القائمة
  • السطر 20: في جميع الحالات (سواء حدث خطأ أم لا)، لا يتم عرض خيارات قائمة الصفحة الرئيسية، بحيث لا يمكن للمستخدم بدء أي إجراء جديد من هذه الصفحة.

ماذا يحدث إذا طلب المستخدم الصفحة [Erreurs.aspx] مباشرةً (وهو ما لا يُفترض أن يفعله في الاستخدام العادي للتطبيق)؟ بمتابعة كود [MasterPage.master.cs] و [Erreurs.aspx.cs]، نلاحظ ما يلي:

  • إذا حدث خطأ في التهيئة، فسيتم عرضه
  • إذا لم يكن هناك خطأ في التهيئة، يتلقى المستخدم صفحة تحتوي فقط على رأس [MasterPage.master] دون عرض أي خيارات من القائمة.

11.5.4. رمز التحكم في الصفحة [Formulaire.aspx]

11.5.4.1. هيكل الفئة

قد يكون الهيكل الأساسي لرمز التحكم في الصفحة [Formulaire.aspx] كما يلي:


using Pam.Metier.Entites;
...

partial class PageFormulaire : System.Web.UI.Page
{

    // تحميل الصفحة 
    protected void Page_Load(object sender, System.EventArgs e)
    {
        // مدير الأحداث
        Master.OptionFaireSimulation.Click += OptFaireSimulation_Click;
        Master.OptionEffacerSimulation.Click += OptEffacerSimulation_Click;
        Master.OptionVoirSimulations.Click += OptVoirSimulations_Click;
        Master.OptionEnregistrerSimulation.Click += OptEnregistrerSimulation_Click;
....
    }

    // حساب الرواتب 
    private void OptFaireSimulation_Click(object sender, System.EventArgs e)
    {
....
    }

    // مسح المحاكاة 
    private void OptEffacerSimulation_Click(object sender, System.EventArgs e)
    {
...
    }

    protected void OptVoirSimulations_Click(object sender, System.EventArgs e)
    {
....
    }

    protected void OptEnregistrerSimulation_Click(object sender, System.EventArgs e)
    {
...
    }

}

يدير رمز التحكم في الصفحة [Formulaire.aspx] خمسة أحداث:

  1. الحدث Load للصفحة
  2. الحدث Click على الرابط [LinkButtonFaireSimulation] في الصفحة الرئيسية
  3. الحدث Click على الرابط [LinkButtonEffacerSimulation] في الصفحة الرئيسية
  4. الحدث Click على الرابط [LinkButtonEnregistrerSimulation] في الصفحة الرئيسية
  5. الحدث Click على الرابط [LinkButtonVoirSimulations] في الصفحة الرئيسية

11.5.4.2. حدث تحميل الصفحة

قد يكون الهيكل الأساسي لمدير الحدث Load للصفحة كما يلي:


    protected void Page_Load(object sender, System.EventArgs e)
    {
        // مدير الأحداث
        Master.OptionFaireSimulation.Click += OptFaireSimulation_Click;
        Master.OptionEffacerSimulation.Click += OptEffacerSimulation_Click;
        Master.OptionVoirSimulations.Click += OptVoirSimulations_Click;
        Master.OptionEnregistrerSimulation.Click += OptEnregistrerSimulation_Click;
        // عرض طريقة العرض [saisies] 
        ...
        // تحديد موضع قائمة الصفحة الرئيسية 
        ...
        // معالجة الطلب GET 
        if (!IsPostBack)
        {
            // تحميل أسماء الموظفين في القائمة المنسدلة 
...
            // تعيين عرض [saisies] مع الإدخالات المخزنة في الجلسة إن وجدت 
....
        }
}

قد يكون المثال التالي توضيحًا للتعليق في السطر 17:

  • في [1]، يُطلب عرض قائمة المحاكاة. تم إدخال بيانات في [A, B, C].
  • في [2]، نرى القائمة
  • في [3]، يُطلب العودة إلى النموذج
  • في [4]، نجد النموذج كما تركناه. نظرًا لوجود طلبين، (1،2) و(3،4)، فهذا يعني أن:
    • عند الانتقال من [1] إلى [2]، تم حفظ الإدخالات من [1]
    • عند الانتقال من [3] إلى [4]، تم استرجاعها. الإجراء [Page_Load] من [Formulaire.aspx] هو الذي يقوم بهذا الاسترجاع.

السؤال: أكمل الإجراء Page_Load بالاستعانة بالتعليقات ورمز الإصدار [pam-v4-3tier-nhibernate-multivues-monopage]


11.5.4.3. إدارة أحداث النقر على روابط القائمة

الهيكل الأساسي لمديري الأحداث Click على روابط الصفحة الرئيسية هو كما يلي:


// حساب الراتب 
    private void OptFaireSimulation_Click(object sender, System.EventArgs e)
    {
        // تأثير Ajax
        Thread.Sleep(3000);
        // هل الصفحة صالحة؟ 
        Page.Validate();
        if (!Page.IsValid)
        {
            // عرض طريقة عرض [saisie] 
...
        }
        // الصفحة صالحة - يتم استرداد المدخلات 
...
        // يتم حساب راتب الموظف 
        FeuilleSalaire feuillesalaire;
        try
        {
            feuillesalaire = ...;
        }
        catch (PamException ex)
        {
            // حدثت مشكلة 
...
            return;
        }
        // يتم وضع النتيجة في الجلسة 
        Session["simulation"] = ...;
        // يتم وضع المدخلات في الجلسة 
...
        // العرض 
...
        // عرض المشاهدات 
...
        // عرض القائمة MasterPage 
...
    }

    // مسح المحاكاة 
    private void OptEffacerSimulation_Click(object sender, System.EventArgs e)
    {
        // عرض اللوحة [saisie] 
...
        // اختيار الموظف الأول 
...
    }

    protected void OptVoirSimulations_Click(object sender, System.EventArgs e)
    {
        // يتم إدخال البيانات في الجلسة 
...
        // عرض العرض [simulations] 
        Response.Redirect("simulations.aspx");
    }

    protected void OptEnregistrerSimulation_Click(object sender, System.EventArgs e)
    {
        // يتم تسجيل المحاكاة الحالية في جلسة عمل المستخدم 
...
        // يتم عرض العرض [simulations] 
        Response.Redirect("simulations.aspx");
}

السؤال: أكمل كود الإجراءات أعلاه بالاستعانة بالتعليقات وكود الإصدار [pam-v4-3tier-nhibernate-multivues-monopage]


11.5.5. رمز التحكم في الصفحة [Simulations.aspx]

قد يكون الهيكل الأساسي لرمز التحكم في الصفحة [Simulations.aspx] كما يلي:


using System.Collections.Generic;
using Pam.Web;
using System.Web.UI.WebControls;

partial class PageSimulations : System.Web.UI.Page
{

    // المحاكاة 
    private List<Simulation> simulations;

    // تحميل الصفحة
    protected void Page_Load(object sender, System.EventArgs e)
    {
        // مدير الأحداث 
        Master.OptionFormulaireSimulation.Click += OptFormulaireSimulation_Click;
        GridViewSimulations.RowDeleting += GridViewSimulations_RowDeleting;
        // يتم استرداد المحاكاة في الجلسة
        simulations = ...;
        // هل توجد محاكاة؟ 
        if (simulations.Count != 0)
        {
            // أول عرض مرئي 
            ...
            // يتم ملء عرض الشبكة 
            ...
        }
        else
        {
            // المنظر الثاني 
            ...
        }
        // يتم تثبيت القائمة 
        ...
    }

    protected void GridViewSimulations_RowDeleting(object sender, System.Web.UI.WebControls.GridViewDeleteEventArgs e)
    {
        // استرداد المحاكاة في الجلسة
        List<Simulation> simulations = ...;
        // حذف المحاكاة المحددة (e.RowIndex يمثل رقم السطر المحذوف في عرض الشبكة)
        ..
        // هل لا تزال هناك محاكاة؟ 
        if (simulations.Count != 0)
        {
            // يتم ملء عرض الشبكة 
            ...
        }
        else
        {
            // عرض [SimulationsVides] 
            ...
        }
    }

    protected void OptFormulaireSimulation_Click(object sender, System.EventArgs e)
    {
        // يتم عرض العرض [formulaire] 
        Response.Redirect("formulaire.aspx");
    }

}

السؤال: أكمل كود الإجراءات المذكورة أعلاه بالاستعانة بالتعليقات وكود الإصدار [pam-v4-3tier-nhibernate-multivues-monopage]


11.5.6. رمز التحكم في الصفحة [Default.aspx]

يمكن توقع وجود صفحة [Default.aspx] في التطبيق، وذلك للسماح للمستخدم بطلب URL من التطبيق دون تحديد صفحة، كما هو موضح أدناه:

تلقى الطلب [1] الصفحة [Formulaire.aspx] (2) كاستجابة. من المعروف أن الطلب (1) تتم معالجته افتراضيًا بواسطة الصفحة [Default.aspx] للتطبيق. للحصول على (2)، يكفي أن تقوم [Default.aspx] بإعادة توجيه العميل إلى الصفحة [Formulaire.aspx]. ويمكن تحقيق ذلك باستخدام الكود التالي:


partial class _Default : System.Web.UI.Page
{

    protected void Page_Init(object sender, System.EventArgs e)
    {
        // إعادة التوجيه إلى نموذج الإدخال 
        Response.Redirect("Formulaire.aspx");
    }
}

أما صفحة العرض [Default.aspx] فتحتوي فقط على التوجيه الذي يربطها بـ [Default.aspx.cs]:


<%@ Page Language="C#" AutoEventWireup="true"
  CodeBehind="Default.aspx.cs" Inherits="pam_v7._Default" Title="Untitled Page" %>