Skip to content

8. مكونات الخادم ASP - 2

8.1. Introduction

نواصل عملنا على واجهة المستخدم من خلال اكتشاف:

  • مكونات التحقق من صحة البيانات
  • طريقة ربط البيانات بمكونات الخادم
  • مكونات الخادم HTML

8.2. مكونات التحقق من صحة البيانات

8.2.1. مقدمة

في تطبيقات النماذج، من الضروري التحقق من صحة البيانات التي يتم إدخالها فيها. في المثال الخاص بحساب الضريبة، كان لدينا النموذج التالي:

Image

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

Image

يوفر ASP.NET مكونات تسمى مكونات التحقق من الصحة تسمح بالتحقق من الحالات التالية:

المكون
الدور
RequiredFieldValidator
يتحقق من أن الحقل غير فارغ
CompareValidator
التحقق من توافق قيمتين مع بعضهما
RangeValidator
التحقق من أن القيمة تقع بين حدين
RegularExpressionValidator
التحقق من أن حقلًا ما يتوافق مع تعبير عادي
CustomValidator
يسمح للمطور بتحديد قواعد التحقق الخاصة به - يمكن أن يحل هذا المكون محل جميع المكونات الأخرى
ValidationSummary
يسمح بتجميع رسائل الأخطاء الصادرة عن عناصر التحكم السابقة في مكان واحد على الصفحة

سنقوم الآن بعرض كل مكون من هذه المكونات.

8.2.2. RequiredFieldValidator

نقوم بإنشاء الصفحة التالية [requiredfieldvalidator1.aspx]:

رقم
الاسم
النوع
الخصائص
الدور
1
txtNom
TextBox
EnableViewState=true
حقل الإدخال
2
RequiredFieldValidator1
RequiredFieldValidator
EnableViewState=false
EnableClientScript=true
ErrorMessage=الحقل [الاسم] إلزامي
مكون التحقق من الصحة
3
btnEnvoyer
زر
EnableViewState=false
CausesValidation=true
زر [submit]

يُستخدم الحقل [RequiredFieldValidator1] لعرض رسالة خطأ إذا كان الحقل [txtNom] فارغًا أو يحتوي على سلسلة من المسافات. وخصائصه هي كما يلي:

ControlToValidate
حقل يجب أن يتحكم المكون في قيمته. ماذا تعني قيمة المكون؟ إنها قيمة السمة [value] للعلامة HTML المقابلة. بالنسبة لـ [TextBox]، ستكون هذه هي محتويات حقل الإدخال، وبالنسبة لـ [DropDownList]، ستكون هذه هي قيمة العنصر المحدد.
EnableClientScript
منطقية - عند تعيينها إلى "صحيح" تشير إلى أنه يجب التحقق من محتوى الحقل السابق أيضًا من جانب العميل. في هذه الحالة، لن يتم إرسال النموذج بواسطة المتصفح إلا إذا كان النموذج خاليًا من الأخطاء. ومع ذلك، يتم إجراء اختبارات التحقق من الصحة أيضًا على الخادم في حالة عدم استخدام المتصفح كعميل على سبيل المثال.
ErrorMessage
رسالة الخطأ التي يجب أن يعرضها المكون في حالة اكتشاف خطأ

لا يتم التحقق من بيانات الصفحة بواسطة عناصر التحقق من الصحة إلا إذا كان الزر أو الرابط الذي تسبب في [POST] للصفحة يحتوي على الخاصية [CausesValidation=true]. [true] هي القيمة الافتراضية لهذه الخاصية.

لنقم بتشغيل هذا التطبيق. نستخدم أولاً متصفح [Mozilla]:

Image

الرمز HTML الذي تم استلامه بواسطة [Mozilla] هو التالي:

<html>
<head>
</head>
<body>
    <form name="_ctl0" method="post" action="requiredfieldvalidator1.aspx" id="_ctl0">
<input type="hidden" name="__VIEWSTATE" value="dDwxNDI1MDc1NTU1Ozs+SGtdZvVxefDCDxnsqbDnqCaROsk=" />

        <p>
            Demande du dossier de candidature au DESS 
        </p>
        <fieldset>
            <legend>[--Identité--]</legend>

            <table>
                <tbody>
                    <tr>
                        <td>
                            Nom*</td>
                        <td>
                            <input name="txtNom" type="text" id="txtNom" />
                            &nbsp;
                        </td>
                    </tr>
                </tbody>
            </table>
        </fieldset>
        <p>
            <input type="submit" name="btnEnvoyer" value="Envoyer" onclick="if (typeof(Page_ClientValidate) == 'function') Page_ClientValidate(); " language="javascript" id="btnEnvoyer" />
        </p>
    </form>

</body>
</html>

نلاحظ أن الزر [btnEnvoyer] مرتبط بوظيفة جافا سكريبت عبر سمة [onclick]. كما يمكن ملاحظة أن الصفحة لا تحتوي على أي كود [Javascript]. سيفشل الاختبار [typeof(Page_ClientValidate) == 'function'] ولن يتم استدعاء وظيفة جافا سكريبت. سيتم بعد ذلك إرسال النموذج إلى الخادم، والذي سيقوم بدوره بإجراء عمليات التحقق من الصحة. لنستخدم الزر [Envoyer] دون إدخال اسم. رد الخادم هو كما يلي:

Image

ماذا حدث؟ تم إرسال النموذج إلى الخادم. وقام الخادم بتنفيذ كود جميع مكونات التحقق من الصحة الموجودة على الصفحة. هنا يوجد مكون واحد فقط. إذا اكتشف مكون التحقق من الصحة واحد على الأقل وجود خطأ، فإن الخادم يعيد النموذج كما تم إدخاله (في الواقع بالنسبة للمكونات التي تحتوي على [EnableViewState=true]، مع إضافة رسالة خطأ لكل مكون من مكونات التحقق من الصحة اكتشف خطأً). هذه الرسالة هي السمة [ErrorMessage] لمكون التحقق. هذه هي الآلية التي نراها تعمل أعلاه. إذا أدخلنا شيئًا في الحقل [nom]، فلن تظهر رسالة الخطأ عند التحقق من صحة النموذج:

Image

الآن، لنستخدم Internet Explorer ونطلب عنوان URL [http://localhost/requiredfieldvalidator1.aspx]. نحصل على الصفحة التالية:

Image

الرمز HTML الذي تم استلامه بواسطة IE هو التالي:

<html>
<head>
</head>
<body>
    <form name="_ctl0" method="post" action="requiredfieldvalidator1.aspx" language="javascript" onsubmit="ValidatorOnSubmit();" id="_ctl0">
<input type="hidden" name="__VIEWSTATE" value="dDwxNDI1MDc1NTU1Ozs+SGtdZvVxefDCDxnsqbDnqCaROsk=" />

<script language="javascript" src="/aspnet_client/system_web/1_1_4322/WebUIValidation.js"></script>


        <p>
            Demande du dossier de candidature au DESS 
        </p>
        <fieldset>
            <legend>[--Identité--]</legend>
            <table>
                <tbody>
                    <tr>
                        <td>
                            Nom*</td>
                        <td>
                            <input name="txtNom" type="text" id="txtNom" />
                            <span id="RequiredFieldValidator1" controltovalidate="txtNom" errormessage="Le champ [nom] est obligatoire" evaluationfunction="RequiredFieldValidatorEvaluateIsValid" initialvalue="" style="color:Red;visibility:hidden;">Le champ [nom] est obligatoire</span>
                        </td>
                    </tr>
                </tbody>
            </table>
        </fieldset>
        <p>
            <input type="submit" name="btnEnvoyer" value="Envoyer" onclick="if (typeof(Page_ClientValidate) == 'function') Page_ClientValidate(); " language="javascript" id="btnEnvoyer" />
        </p>

<script language="javascript">
<!--
    var Page_Validators =  new Array(document.all["RequiredFieldValidator1"]);
        // -->
</script>


<script language="javascript">
<!--
var Page_ValidationActive = false;
if (typeof(clientInformation) != "undefined" && clientInformation.appName.indexOf("Explorer") != -1) {
    if (typeof(Page_ValidationVer) == "undefined")
        alert("Impossible de trouver la bibliothèque de scripts /aspnet_client/system_web/1_1_4322/WebUIValidation.js. Essayez de placer ce fichier manuellement ou effectuez une réinstallation en exécutant 'aspnet_regiis -c'.");
    else if (Page_ValidationVer != "125")
        alert("Cette page utilise une version incorrecte de WebUIValidation.js. La page requiert la version 125. La bibliothèque de scripts est " + Page_ValidationVer + ".");
    else
        ValidatorOnLoad();
}

function ValidatorOnSubmit() {
    if (Page_ValidationActive) {
        ValidatorCommonOnSubmit();
    }
}
// -->
</script>


        </form>
</body>
</html>

نلاحظ أن هذا الرمز أكبر بكثير من الرمز الذي تم استلامه بواسطة [Mozilla]. لن ندخل في التفاصيل. الفرق نابع من أن الخادم أدرج وظائف جافا سكريبت في المستند HTML الذي تم إرساله. لماذا يوجد رمزان مختلفان HTML في حين أن عنوان URL المطلوب من قبل المتصفحين هو نفسه؟ سبق أن ذكرنا أن تقنية ASP.NET تجعل الخادم يقوم بتكييف المستند HTML المرسل إلى العميل وفقًا لمستوى هذا العميل. توجد في السوق متصفحات مختلفة لا تتمتع جميعها بنفس القدرات. على سبيل المثال، لا تستخدم متصفحات Microsoft و Netscape نفس نموذج الكائنات للمستند الذي تتلقاه. لذلك، يوجد اختلافات بين المتصفحين في كود Javascript المستخدم لمعالجة هذا المستند من جانب المتصفح. وبالمثل، أنشأ المطورون إصدارات متتالية من متصفحاتهم، مما أدى إلى تحسين قدراتها باستمرار. كما أن مستند HTML الذي يفهمه IE5 قد لا يفهمه IE3. لدينا أعلاه مثال على هذا التكيف من جانب الخادم على مستوى عميله. ولسبب لم يتم التعمق فيه هنا، اعتبر خادم الويب أن العميل [Mozilla] لا يملك القدرة على إدارة كود جافا سكريبت الخاص بالتحقق من الصحة. ولذلك لم يتم وضع هذا الكود في المستند HTML الذي تم إرساله إليه، وتم إجراء التحقق من الصحة من جانب الخادم. بالنسبة لـ [IE6]، تم تضمين كود جافا سكريبت هذا في المستند HTML الذي تم إرساله، كما نرى. لرؤية ذلك في الواقع، لنقم بالتجربة التالية:

  • أوقفوا خادم الويب
  • استخدام الزر [Envoyer] دون ملء الحقل [nom]

نحصل على الصفحة الجديدة التالية:

Image

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

Image

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

ما الذي يمكن استخلاصه من هذا المثال؟

  • أهمية مفهوم مكون التحقق الذي يسمح بإعادة إرسال أي نموذج غير صحيح إلى العميل
  • أهمية [VIEWSTATE] الذي يسمح بإعادة إرسال هذا النموذج كما تم إدخاله
  • قدرة الخادم على التكيف مع عميله. يتم تحديد هوية العميل من خلال الرأس HTTP [User-Agent:] الذي يرسله إلى الخادم. وبالتالي، فإن الخادم لا "يخمن" مع من يتعامل. تعد هذه القدرة على التكيف ذات أهمية كبيرة للمطور الذي لا يحتاج إلى القلق بشأن نوع عميل تطبيقه.

8.2.3. CompareValidator

نقوم بإنشاء الصفحة التالية:

Image

رقم
الاسم
النوع
الخصائص
الدور
1
cmbChoix1
DropDownList
EnableViewState=true
قائمة منسدلة
2
cmbChoix2
DropDownList
EnableViewState=true
قائمة منسدلة
3
CompareValidator1
CompareValidator
EnableViewState=false
EnableClientScript=true
ErrorMessage=الخيار 1 غير صالح
القيمة_المقارنة=؟
المشغل=غير متساوي
النوع=سلسلة
مكون التحقق
4
CompareValidator2
CompareValidator
EnableViewState=false
EnableClientScript=true
ErrorMessage=الخيار 2 غير صالح
عنصر التحكم للمقارنة=؟
المشغل=غير متساوي
النوع=سلسلة
مكون التحقق من الصحة
5
btnEnvoyer
زر
EnableViewState=false
CausesValidation=true
زر [submit]

يستخدم المكون [CompareValidator] لمقارنة قيمتين مع بعضهما البعض. العوامل التي يمكن استخدامها هي [Equal, NotEqual, LessThan, LessThanEqual, GreaterThan, GreaterThanEqual]. يتم تحديد القيمة الأولى بواسطة الخاصية [ControlToValidate]، والثانية بواسطة [ValueToCompare] إذا كان يجب مقارنة القيمة السابقة بثابت، أو بواسطة [ControlToCompare] إذا كان يجب مقارنتها بقيمة مكون آخر. الخصائص المهمة للمكون [CompareValidator] هي التالية:

ControlToValidate
حقل يجب التحكم في محتواه بواسطة المكون
EnableClientScript
منطقية - عند تعيينها إلى "صحيح" تشير إلى أنه يجب التحقق من محتوى الحقل السابق أيضًا من جانب العميل
ErrorMessage
رسالة الخطأ التي يجب أن يعرضها المكون في حالة اكتشاف خطأ
ValeurToCompare
القيمة التي يجب مقارنة قيمة الحقل بها [ControlToValidate]
ControlToCompare
المكون الذي يجب مقارنة قيمة الحقل به [ControlToValidate]
Operator
عامل المقارنة بين القيمتين
Type
نوع القيم المراد مقارنتها

رمز عرض هذه الصفحة هو التالي:

<html>
<head>
</head>
<body>
    <form runat="server">
        <p>
            Demande du dossier de candidature au DESS 
        </p>
        <fieldset>
            <legend>[--Options du DESS pour lesquelles vous candidatez--]</legend>
            <table>
                <tbody>
                    <tr>
                        <td>
                            Option1*</td>
                        <td>
                            Option2</td>
                    </tr>
                    <tr>
                        <td>
                            <asp:DropDownList id="cmbChoix1" runat="server">
                                <asp:ListItem Value="?" Selected="True">?</asp:ListItem>
                                <asp:ListItem Value="Automatique">Automatique</asp:ListItem>
                                <asp:ListItem Value="Informatique">Informatique</asp:ListItem>
                            </asp:DropDownList>
                        </td>
                        <td>
                            <asp:DropDownList id="cmbChoix2" runat="server">
                                <asp:ListItem Value="?">?</asp:ListItem>
                                <asp:ListItem Value="Automatique">Automatique</asp:ListItem>
                                <asp:ListItem Value="Informatique">Informatique</asp:ListItem>
                            </asp:DropDownList>
                        </td>
                    </tr>
                    <tr>
                        <td>
                            <asp:CompareValidator id="CompareValidator1" runat="server" ErrorMessage="Choix 1 invalide" ControlToValidate="cmbChoix1" ValueToCompare="?" Operator="NotEqual"></asp:CompareValidator>
                        </td>
                        <td>
                            <asp:CompareValidator id="CompareValidator2" runat="server" ErrorMessage="Choix 2 invalide" ControlToValidate="cmbChoix2" Operator="NotEqual" ControlToCompare="cmbChoix1"></asp:CompareValidator>
                        </td>
                    </tr>
                </tbody>
            </table>
        </fieldset>
        <p>
            <asp:Button id="btnEnvoyer" runat="server" Text="Envoyer"></asp:Button>
        </p>
    </form>
</body>
</html>

شروط التحقق من صحة الصفحة هي كما يلي:

  • يجب أن تكون القيمة المحددة في [cmbChoix1] مختلفة عن السلسلة "?". وهذا يعني الخصائص التالية للمكون [CompareValidator1]: Operator=NotEqual, ValueToCompare=?, Type=string
  • يجب أن تكون القيمة المحددة في [cmbChoix2] مختلفة عن تلك المحددة في [cmbChoix1]. وهذا ينطوي على الخصائص التالية للمكون [CompareValidator2]: Operator=NotEqual، ControlToCompare=cmbChoix1، Type=string

نقوم بتشغيل هذا التطبيق. فيما يلي مثال لصفحة تم استلامها أثناء التبادل بين العميل والخادم:

Image

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

8.2.4. CustomValidator، RangeValidator

نقوم بإنشاء الصفحة التالية [customvalidator1.aspx]:

Image

رقم
الاسم
النوع
الخصائص
الدور
1
cmbDiplomes
DropDownList
EnableViewState=true
قائمة منسدلة
2
CompareValidator2
CompareValidator
EnableViewState=false
EnableClientScript=true
ErrorMessage=شهادة غير صالحة
Operator=NotEqual
القيمة المراد مقارنتها=؟
Type=String
مكون التحقق من صحة عنصر التحكم [1]
3
txtAutreDiplome
TextBox
EnableViewState=false
حقل الإدخال
4
CustomValidator1
CustomValidator
EnableViewState=false
EnableClientScript=true
ErrorMessage=الدقة غير صالحة
ClientValidationFunction=chkAutreDiplome
مكون التحقق من صحة الحقل [3]
5
txtAnDiplome
TextBox
EnableViewState=false
حقل الإدخال
6
RangeValidator1
RangeValidator
EnableViewState=false
EnableClientScript=true
ErrorMessage=يجب أن تكون سنة التخرج ضمن النطاق [1990,2004]
MinValue=1990
MaxValue=2004
النوع=عدد صحيح
عنصر التحكم المطلوب للتحقق=txtAnDiplome
مكون التحقق من صحة الحقل [5]
7
RequiredFieldValidator2
RequiredFieldValidator
EnableViewState=false
EnableClientScript=true
ErrorMessage=السنة المطلوبة للحصول على الشهادة
ControlToValidate=txtAnDiplome
مكون التحقق من صحة الحقل [5]
8
btnEnvoyer
زر
EnableViewState=false
CausesValidation=true
زر [submit]

يستخدم الحقل [RangeValidator] للتحقق من أن قيمة عنصر التحكم تقع بين الحدين [MinValue] و [MaxValue]. خصائصه هي كما يلي:

ControlToValidate
حقل يجب التحقق من قيمته بواسطة المكون
EnableClientScript
منطقية - عند تعيينها إلى "صحيح" تشير إلى أنه يجب التحقق من محتوى الحقل السابق أيضًا من جانب العميل
ErrorMessage
رسالة الخطأ التي يجب أن يعرضها المكون في حالة اكتشاف خطأ
MinValue
القيمة الدنيا لقيمة الحقل المراد التحقق منه
MaxValue
القيمة القصوى للحقل المراد التحقق منه
Type
نوع قيمة الحقل المراد التحقق منه

يسمح الحقل [CustomValidator] بإجراء عمليات التحقق التي لا تستطيع مكونات التحقق المقترحة بواسطة ASP.NET القيام بها. يتم إجراء هذا التحقق بواسطة دالة يكتبها المطور. ويتم تنفيذ هذه الدالة على جانب الخادم. ونظرًا لأن التحقق يمكن إجراؤه أيضًا على جانب العميل، فقد يضطر المطور إلى تطوير دالة جافا سكريبت ليقوم بتضمينها في المستند HTML. وفيما يلي خصائصها:

ControlToValidate
حقل يجب التحقق من قيمته بواسطة المكون
EnableClientScript
قيمة منطقية - عند تعيينها إلى "صحيح" تشير إلى أنه يجب التحقق من محتوى الحقل السابق أيضًا من جانب العميل
ErrorMessage
رسالة الخطأ التي يجب أن يعرضها المكون في حالة اكتشاف خطأ
ClientValidationFunction
الوظيفة المطلوب تنفيذها على جانب العميل

رمز عرض الصفحة هو التالي:

<%@ Page Language="VB" %>
<script runat="server">
...
</script>
<html>
<head>
</head>
<body>
    <form runat="server">
        <p align="left">
            Demande du dossier de candidature au DESS 
        </p>
        <fieldset>
            <legend>[--Votre dernier diplôme--]</legend>
            <table>
                <tbody>
                    <tr>
                        <td>
                            Diplôme*</td>
                        <td>
                            <asp:DropDownList id="cmbDiplomes" runat="server">
                                <asp:ListItem Value="?">?</asp:ListItem>
                                <asp:ListItem Value="[Autre]">[Autre]</asp:ListItem>
                                <asp:ListItem Value="Ma&#238;trise">Ma&#238;trise</asp:ListItem>
                                <asp:ListItem Value="DESS">DESS</asp:ListItem>
                                <asp:ListItem Value="DEA">DEA</asp:ListItem>
                            </asp:DropDownList>
                        </td>
                        <td>
                            Si [Autre], précisez</td>
                        <td>
                            <asp:TextBox id="txtAutreDiplome" runat="server" EnableViewState="False"></asp:TextBox>
                        </td>
                    </tr>
                    <tr>
                        <td>
                        </td>
                        <td>
                            <p>
                                <asp:CompareValidator id="CompareValidator2" runat="server" ErrorMessage="Diplôme invalide" ControlToValidate="cmbDiplomes" ValueToCompare="?" Operator="NotEqual" ></asp:CompareValidator>
                            </p>
                        </td>
                        <td>
                        </td>
                        <td>
                            <asp:CustomValidator id="CustomValidator1" runat="server" ErrorMessage="Précision diplôme invalide" OnServerValidate="CustomValidator1_ServerValidate_1" EnableViewState="False" ClientValidationFunction="chckAutreDiplome"></asp:CustomValidator>
                        </td>
                    </tr>
                    <tr>
                        <td>
                            Année d'obtention*</td>
                        <td colspan="3">
                            <asp:TextBox id="txtAnDiplome" runat="server" Columns="4"></asp:TextBox>
                            <asp:RangeValidator id="RangeValidator1" runat="server" ErrorMessage="Année diplôme doit être dans l'intervalle [1990,2004]" ControlToValidate="txtAnDiplome" MinimumValue="1990" MaximumValue="2004" Type="Integer"></asp:RangeValidator>
                            <asp:RequiredFieldValidator id="RequiredFieldValidator2" runat="server" ErrorMessage="Année diplôme requise" ControlToValidate="txtAnDiplome"></asp:RequiredFieldValidator>
                        </td>
                    </tr>
                </tbody>
            </table>
        </fieldset>
        <p>
            <asp:Button id="btnEnvoyer" runat="server" Text="Envoyer"></asp:Button>
        </p>
    </form>
</body>
</html>

يسمح السمة [OnServerValidate] للمكون [CustomValidator] بتحديد الوظيفة المسؤولة عن التحقق من الصحة على جانب الخادم. أعلاه، يستدعي المكون [CustomValidator1] الإجراء [CustomValidator1_ServerValidate_1] التالي:

<%@ Page Language="VB" %>
<script runat="server">

    Sub CustomValidator1_ServerValidate_1(sender As Object, e As ServerValidateEventArgs)
        ' يجب ألا يكون الحقل [txtAutreDiplome] فارغًا إذا كان [cmbDiplomes]=[autre]
        e.isvalid=not (cmbDiplomes.selecteditem.text="[Autre]" and txtAutreDiplome.text.trim="") _
            and not (cmbDiplomes.selecteditem.text<>"[Autre]" and cmbDiplomes.selecteditem.text<>"?" and txtAutreDiplome.text.trim<>"")
    End Sub

</script>
....

تتلقى وظيفة التحقق المرتبطة بمكون [CustomValidator] معلمتين:

  • sender: الكائن الذي تسبب في الحدث
  • e: الحدث. يجب أن تقوم الإجراء بتعيين السمة [e.IsValid] على "صحيح" إذا كانت البيانات التي تم التحقق منها صحيحة، و"خطأ" في حالة عدم صحتها.

في هذه الحالة، تتم عمليات التحقق التالية:

  • لا يمكن أن تحتوي القائمة المنسدلة [cmbDiplomes] على القيمة [?]. يتم التحقق من ذلك بواسطة المكون [CompareValidator2]
  • يختار المستخدم شهادة من القائمة المنسدلة [cmbDiplomes]. إذا لم تكن شهادته موجودة في القائمة، فيمكنه اختيار الخيار [Autre] من القائمة. وعليه عندئذٍ أن يحدد شهادته في حقل الإدخال [txtAutreDiplome]. إذا تم تحديد [Autre] في [cmbDiplomes]، فيجب ألا يكون حقل [txtAutreDiplome] فارغًا. إذا لم يتم تحديد [Autre] أو [?] في [cmbDiplomes]، فيجب أن يكون الحقل [txtAutreDiplome] فارغًا. يتم التحقق من ذلك بواسطة الوظيفة المرتبطة بالمكون [CustomValidator1].
  • يجب أن تكون سنة الحصول على الشهادة ضمن النطاق [1900-2004]. يتم التحقق من ذلك بواسطة [RangeValidator1]. ومع ذلك، إذا لم يقم المستخدم بإدخال أي شيء في الحقل، فلن يتم استخدام وظيفة التحقق من صحة [RangeValidator1]. لذلك، يتم إضافة المكون [RequiredFieldValidator2] للتحقق من وجود محتوى. هذه قاعدة عامة. لا يتم التحقق من محتوى الحقل إذا كان فارغًا. الحالة الوحيدة التي يتم فيها التحقق هي عندما يكون مرتبطًا بمكون [RequiredFieldValidator].

فيما يلي مثال على التنفيذ في متصفح [Mozilla]:

Image

إذا استخدمنا المتصفح [IE6]، فيمكننا إضافة وظيفة التحقق من صحة البيانات من جانب العميل للمكون [CustomValidator1]. ولهذا الغرض، يحتوي هذا المكون على الخصائص التالية:

  • EnableClientScript=true
  • ClientValidationFunction=chkAutreDiplome

وظيفة [chkAutreDiplome ] هي كما يلي:

<head>
    <meta http-equiv="pragma" content="no-cache" />
    <script language="javascript">
        function chkAutreDiplome(source,args){
             // تحقق من صحة الحقل txtAutreDiplome
            with(document.frmCandidature){
                diplome=cmbDiplomes.options[cmbDiplomes.selectedIndex].text;
                    args.IsValid= !(diplome=="[Autre]" && txtAutreDiplome.value=="")
                        && ! (diplome!="[Autre]" && diplome!="?" && txtAutreDiplome.value!="");
            }
        }
        </script>
</head>

تستقبل الدالة [chkAutreDiplome] نفس المعلمتين اللتين تستقبلهما دالة الخادم [CustomValidator1_ServerValidate_1]:

  • المصدر: الكائن الذي تسبب في الحدث
  • args: الحدث. يحتوي هذا الحدث على سمة [IsValid] التي يجب أن تضبطها الدالة على "صحيح" إذا كانت البيانات التي تم التحقق منها صحيحة. ستؤدي القيمة "خطأ" إلى عرض رسالة الخطأ المرتبطة بها في مكان مكون التحقق، ولن يتم إرسال النموذج إلى الخادم.

8.2.5. RegularExpressionValidator

نقوم بإنشاء الصفحة التالية [regularexpressionvalidator1.aspx]:

Image

رقم
الاسم
النوع
الخصائص
الدور
1
txtMel
TextBox
EnableViewState=true
حقل الإدخال
2
RequiredFieldValidator3
RequiredFieldValidator
ControlToValidate=txtMel
Display=Dynamic
مكون التحقق من صحة عنصر التحكم [1]
3
RegularExpressionValidator1
RegularExpressionValidator
عنصر التحكم المطلوب التحقق من صحته=txtMel
Display=Dynamic
مكون التحقق من صحة عنصر التحكم [1]
4
btnEnvoyer
زر
تمكين حالة العرض=كاذب
CausesValidation=true
زر [submit]

هنا، نريد التحقق من تنسيق عنوان بريد إلكتروني. نقوم بذلك باستخدام مكون [RegularExpressionValidator] الذي يسمح بالتحقق من صحة حقل ما باستخدام تعبير عادي. تذكر هنا أن التعبير العادي هو نموذج لسلسلة أحرف. لذلك، يتم التحقق من محتوى الحقل مقارنةً بنموذج. نظرًا لأن محتوى الحقل لا يتم التحقق منه إذا كان فارغًا، فإننا نحتاج أيضًا إلى مكون [RequiredFieldValidator] للتحقق من أن العنوان الإلكتروني ليس فارغًا. تحتوي الفئة [RegularExpressionValidator] على خصائص مشابهة لخصائص فئات المكونات التي تمت دراستها سابقًا:

ControlToValidate
حقل يجب التحقق من قيمته بواسطة المكون
EnableClientScript
منطقية - عند تعيينها إلى "صحيح" تشير إلى أنه يجب التحقق من محتوى الحقل السابق أيضًا من جانب العميل
ErrorMessage
رسالة الخطأ التي يجب أن يعرضها المكون في حالة اكتشاف خطأ
RegularExpression
التعبير العادي الذي سيتم مقارنة محتوى [ControlToValidate] به
Display
وضع العرض: Static: الحقل موجود دائمًا حتى إذا لم يعرض رسالة خطأ، Dynamic: الحقل موجود فقط في حالة وجود رسالة خطأ، None: لا يتم عرض رسالة الخطأ. هذا الحقل موجود أيضًا للمكونات الأخرى ولكنه لم يتم استخدامه حتى الآن.

قد يكون التعبير العادي النموذجي لعنوان بريد إلكتروني كما يلي: \s*[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+\s*

عنوان البريد الإلكتروني هو على شكل [champ1.champ2....@champA.champB...]. يجب أن يكون هناك حقل واحد على الأقل قبل علامة @ وحقلان على الأقل بعدها. تتكون هذه الحقول من أحرف أبجدية رقمية وعلامة -. يتم تمثيل الحرف الأبجدي الرقمي بالرمز \w. تشير التسلسل [\w-] إلى الحرف \w أو الحرف -. تعني علامة + بعد التسلسل S أن هذا التسلسل يمكن أن يتكرر مرة واحدة أو أكثر، وتعني علامة * أنه يمكن أن يتكرر صفر أو أكثر من مرة، وتعني علامة ? أنه يمكن أن يتكرر صفر أو مرة واحدة.

يتوافق حقل من العنوان الإلكتروني مع النموذج [\w-]+. إن وجود حقل واحد على الأقل قبل علامة @ وحقلين على الأقل بعدها، مفصولين بعلامة .، يتوافق مع النموذج: [\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+. يمكن السماح للمستخدم بإدخال مسافات قبل العنوان وبعده. سيتم حذفها أثناء المعالجة. يتم تمثيل سلسلة من المسافات، التي قد تكون فارغة، بالنموذج \s*. ومن هنا تأتي التعبير العادي للعنوان الإلكتروني: \s*[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+\s*.

يكون كود عرض الصفحة كما يلي:

<html>
<head>
</head>
<body>
    <form runat="server">
        <p align="left">
            Demande du dossier de candidature au DESS
        </p>
        <fieldset>
            <legend>[--Votre adresse électronique où sera envoyé le dossier de candidature--]</legend>
            <table>
                <tbody>
                    <tr>
                        <td>
                            <asp:TextBox id="txtMel" runat="server" Columns="60"></asp:TextBox>
                        </td>
                    </tr>
                    <tr>
                        <td>
                            <p>
                                <asp:RequiredFieldValidator id="RequiredFieldValidator3" runat="server" Display="Dynamic" ControlToValidate="txtMel" ErrorMessage="Votre adresse électronique est requise"></asp:RequiredFieldValidator>
                            </p>
                            <p>
                                <asp:RegularExpressionValidator id="RegularExpressionValidator1" runat="server" Display="Dynamic" ControlToValidate="txtMel" ErrorMessage="Votre adresse électronique n'a pas un format valide" ValidationExpression="\s*[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+\s*"></asp:RegularExpressionValidator>
                            </p>
                        </td>
                    </tr>
                </tbody>
            </table>
        </fieldset>
        <p>
            <asp:Button id="btnEnvoyer" runat="server" Text="Envoyer"></asp:Button>
        </p>
    </form>
</body>
</html>

8.2.6. ValidationSummary

لأسباب جمالية، قد نرغب في تجميع رسائل الأخطاء في مكان واحد كما في المثال التالي [summaryvalidator1.aspx] حيث قمنا بتجميع جميع الأمثلة السابقة في صفحة واحدة:

Image

تتمتع جميع عناصر التحقق من الصحة بالخصائص التالية:

  • [Display=Dynamic] مما يجعل عناصر التحكم لا تشغل مساحة في الصفحة إذا كانت رسالة الخطأ الخاصة بها فارغة
  • [EnableClientScript=false] لمنع أي تحقق من صحة البيانات من جانب العميل
  • [Text=*]. ستكون هذه هي الرسالة التي يتم عرضها في حالة حدوث خطأ، حيث يتم عرض محتوى السمة [ErrorMessage] بواسطة عنصر التحكم [ValidationSummary] الذي نعرضه أدناه.

يتم وضع هذه المجموعة من عناصر التحكم في مكون [Panel] يسمى [vueFormulaire]. في مكون آخر [Panel] يسمى [vueErreurs]، نضع عنصر تحكم [ValidationSummary]:

Image

رقم
الاسم
النوع
الخصائص
الدور
1
ValidationSummary1
ValidationSummary
HeaderText=حدثت الأخطاء التالية، EnableClientScript=false، ShowSummary=true
يعرض أخطاء جميع عناصر التحقق من صحة الصفحة
2
lnkErreursToFormulaire
LinkButton
CausesValidation=false
رابط العودة إلى النموذج

بالنسبة للرابط [lnkErreursToFormulaire]، لا داعي لتفعيل التحقق من الصحة لأن هذا الرابط لا يرسل أي قيمة للتحقق منها.

عندما تكون جميع البيانات صالحة، يتم عرض العرض التالي [infos] للمستخدم:

Image

1

رقم
الاسم
النوع
الخصائص
الدور
1
lblInfo
التسمية
 
رسالة إعلامية موجهة إلى المستخدم

رمز عرض هذه الصفحة هو:

<html>
<head>
</head>
<body>
    <form runat="server">
        <p align="left">
            Demande du dossier de candidature au DESS 
        </p>
        <p>
            <hr />
            <asp:panel id="vueErreurs" runat="server">
                <p align="left">
                    <asp:ValidationSummary id="ValidationSummary1" runat="server" ShowMessageBox="True" BorderColor="#C04000" BorderWidth="1px" BackColor="#FFFFC0" HeaderText="Les erreurs suivantes se sont produites"></asp:ValidationSummary>
                </p>
                <p>
                    <asp:LinkButton id="lnkErreursToFormulaire" onclick="lnkErreursToFormulaire_Click" runat="server" CausesValidation="False">Retour au formulaire</asp:LinkButton>
                </p>
            </asp:panel>
            <asp:panel id="vueFormulaire" runat="server">
....
                    <asp:Button id="btnEnvoyer" onclick="btnEnvoyer_Click" runat="server" Text="Envoyer"></asp:Button>
                </p>
            </asp:panel>
        <asp:panel id="vueInfos" runat="server">
            <asp:Label id="lblInfo" runat="server"></asp:Label>
        </asp:panel>
    </form>
</body>
</html>

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

Image

يعطينا الزر [Envoyer] الإجابة التالية:

Image

تم عرض العرض [erreurs]. إذا استخدمنا رابط العودة إلى النموذج، فسنجده في الحالة التالية:

Image

هذه هي طريقة العرض [formulaire]. سنلاحظ الحرف [*] بالقرب من البيانات الخاطئة. هذا هو الحقل [Text] من عناصر التحقق من الصحة الذي تم عرضه. إذا قمنا بملء الحقول بشكل صحيح، فسنحصل على العرض [infos]:

Image

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

<%@ Page Language="VB" %>
<script runat="server">

     ' إجراء يتم تنفيذه عند تحميل الصفحة
    Sub page_Load(sender As Object, e As EventArgs)
        ' في الطلب الأول، يتم عرض العرض [formulaire]
        if not ispostback then
            afficheVues(true,false,false)
        end if
    end sub

    sub afficheVues(byval formulaireVisible as boolean, _
        erreursVisible as boolean, infosVisible as boolean)
         ' مجموعة العروض
        vueFormulaire.visible=formulaireVisible
        vueErreurs.visible=erreursVisible
        vueInfos.visible=infosVisible
    end sub

    Sub CustomValidator1_ServerValidate(sender As Object, e As ServerValidateEventArgs)
...
    End Sub

    Sub CustomValidator2_ServerValidate(sender As Object, e As ServerValidateEventArgs)
...
    End Sub

    Sub CustomValidator1_ServerValidate_1(sender As Object, e As ServerValidateEventArgs)
...
    End Sub

    Sub lnkErreursToFormulaire_Click(sender As Object, e As EventArgs)
        ' يعرض عرض النموذج
        afficheVues(true,false,false)
         ' يعيد إجراء عمليات التحقق من الصحة
        Page.validate
    End Sub

    Sub btnEnvoyer_Click(sender As Object, e As EventArgs)
        ' هل الصفحة صالحة؟
        if not Page.IsValid then
            ' يتم عرض العرض [erreurs]
            afficheVues(false,true,false)
        else
             ' وإلا عرض [infos]
            lblInfo.Text="Le dossier de candidature au DESS IAIE a été envoyé à l'adresse ["+ _
                        txtMel.Text + "]. Nous vous en souhaitons bonne réception.<br><br>Le secrétariat du DESS."
            afficheVues(false,false,true)
        end if
    end sub

</script>
<html>
...
</html>
  • في الإجراء [Page_Load] الذي يتم تنفيذه عند كل طلب من العميل، نعرض العرض [formulaire]، بينما يتم إخفاء العروض الأخرى. ويتم ذلك فقط عند الطلب الأول. تستدعي الإجراء إجراءً مساعدًا [afficheVues] يتم تمرير ثلاثة قيم منطقية إليه لتحديد ما إذا كان سيتم عرض العروض الثلاثة أم لا.
  • عند استدعاء الإجراء [btnEnvoyer_Click]، تكون عمليات التحقق من البيانات قد تمت. الزر [btnEnvoyer] ذو الخاصية [CausesValidation=true] هو الذي يفرض إجراء هذا التحقق من البيانات. تم تنفيذ جميع عناصر التحقق من الصحة وتعيين خاصيتها [IsValid]. تشير هذه الخاصية إلى ما إذا كانت البيانات التي تم التحقق منها بواسطة عنصر التحقق صالحة أم لا. علاوة على ذلك، تم أيضًا تعيين الخاصية [IsValid] للصفحة نفسها. وهي تكون [vrai] فقط إذا كانت الخاصية [IsValid] لجميع عناصر التحقق من صحة البيانات في الصفحة لها القيمة [vrai]. وبالتالي، تبدأ الإجراء [btnEnvoyer_Click] بالتحقق مما إذا كانت الصفحة صالحة أم لا. إذا كانت غير صالحة، يتم عرض العرض [erreurs]. وتحتوي هذه العرض على عنصر التحكم [ValidationSummary] الذي يستعيد سمات [ErrorMessage] لجميع عناصر التحكم (انظر لقطة الشاشة أعلاه). إذا كانت الصفحة صالحة، يتم عرض العرض [infos] مع رسالة إعلامية.
  • تتولى الإجراء [lnkErreursToFormulaire_Click] عرض طريقة العرض [formulaire] في الحالة التي تم التحقق من صحتها فيها. يتم إعادة إنشاء حالة جميع حقول الإدخال في العرض [formulaire] التي لها الخاصية [EnableViewState=true] تلقائيًا. ومن الغريب أن حالة مكونات التحقق من الصحة لا يتم استعادتها. كان من المتوقع بالفعل، عند العودة إلى العرض [erreurs]، أن تعرض عناصر التحكم الخاطئة حقلها [Text]. لكن هذا لم يحدث. لذلك تم فرض التحقق من صحة البيانات باستخدام طريقة [Page.Validate] الخاصة بالصفحة. يجب القيام بذلك بمجرد أن يصبح اللوحة [vueFormulaire] مرئية. وبالتالي، هناك عمليتا تحقق من الصحة في المجموع. وينبغي تجنب ذلك في الممارسة العملية. هنا، سمح لنا المثال بإدخال مفاهيم جديدة حول التحقق من صحة الصفحة.

8.3. مكونات ListControl وربط البيانات

يتيح عدد من مكونات الخادم التي تمت دراستها عرض قائمة بالقيم (DropDownList، ListBox). وهناك مكونات أخرى لم نستعرضها بعد تتيح عرض عدة قوائم بالقيم في جداول (HTML). بالنسبة لجميع هذه المكونات، يمكن، عن طريق البرنامج، ربط قيم القوائم بالمكون المرتبط بها واحدة تلو الأخرى. من الممكن أيضًا ربط كائنات أكثر تعقيدًا بهذه المكونات مثل كائنات من النوع [Array]، [ArrayList]، [DataSet]، [HashTable]، ... مما يبسط الكود الذي يربط البيانات بالمكون. يُطلق على هذا الربط اسم "ربط البيانات".

يمكن ربط جميع المكونات المشتقة من الفئة [ListControl] بقائمة بيانات. وهي المكونات [DropDownList]، [ListBox]، [CheckButtonList]، [RadioButtonList]. يمكن ربط كل من هذه المكونات بمصدر بيانات. ويمكن أن يكون هذا المصدر متنوعًا: [Array]، [ArrayList]، [DataTable]، [DataSet]، [HashTable]،....، بشكل عام، كائن ينفذ إحدى الواجهات IEnumerable، ICollection، IListSource. لن نعرض سوى عدد قليل منها. الكائن [DataSet] هو كائن صورة لقاعدة بيانات علائقية. وهو بالتالي مجموعة من الجداول المرتبطة بعلاقات. يمثل الكائن [DataTable] جدولاً من هذا النوع. يؤثر مصدر البيانات على الخصائص [Text] و [Value] لكل عضو من أعضاء [Item] في الكائن [ListControl]. إذا كانت T هي قيمة [Text] و V هي قيمة [Value]، فإن العلامة HTML التي تم إنشاؤها لكل عنصر من عناصر [ListControl] هي كما يلي:

DropDownList, ListBox
<option value="V">T</option>
CheckButtonList
<input type="checkbox" value="V">T
RadioButtonList
<input type="radio" value="V">T

يتم ربط مكون [ListControl] بمصدر البيانات من خلال الخصائص التالية:

DataSource
مصدر بيانات [Array]، [ArrayList]، [DataTable]، [DataSet]، [HashTable]، ...
DataMember
في حالة ما إذا كان مصدر البيانات هو [DataSet]، يمثل اسم الجدول الذي سيتم استخدامه كمصدر للبيانات. يكون مصدر البيانات الفعلي عندئذٍ هو جدول.
DataTextField
في حالة ما إذا كان مصدر البيانات عبارة عن جدول ([DataTable]، [DataSet])، يمثل اسم عمود الجدول الذي سيعطي قيمه للحقل [Text] لعناصر [ListControl]
DataValueField
في حالة ما إذا كان مصدر البيانات عبارة عن جدول ([DataTable],[DataSet])، يمثل اسم عمود الجدول الذي سيوفر قيمه للحقل [Value] لعناصر [ListControl]

لا يؤدي ربط مكون [ListControl] بمصدر بيانات إلى تهيئة المكون بقيم مصدر البيانات. بل إن العملية [ListControl].DataBind هي التي تقوم بذلك.

وفقًا لطبيعة مصدر البيانات، سيتم ربطه بمكون [ListControl] بطريقة مختلفة:

Array A
[ListControl].DataSource=A
ستكون قيم الحقول [Text] و [Value] لعناصر [ListControl] هي عناصر A
ArrayList AL
[ListControl].DataSource=AL
ستكون قيم الحقول [Text] و [Value] لعناصر [ListControl] هي عناصر AL
DataTable DT
[ListControl].DataSource=DT، [ListControl].DataTextField="col1"، [ListControl]. DatavalueField ="col2"
حيث col1 و col2 هما عمودان من الجدول DT. ستكون قيم الحقول [Text] و [Value] لعناصر [ListControl] هي قيم الأعمدة col1 و col2 في الجدول DT
DataSet DS
[ListControl].DataSource=DS ، [ListControl].DataSource="table" حيث "table" هو اسم إحدى الجداول في DS.
[ListControl].DataTextField="col1"، [ListControl]. DatavalueField ="col2" حيث col1 و col2 هما عمودان من الجدول "table". ستكون قيم الحقول [Text] و [Value] لعناصر [ListControl] هي قيم الأعمدة col1 و col2 في الجدول "table"
HashTable HT
[ListControl].DataSource=HT ، [ListControl].DataTextField="key"، [ListControl]. DatavalueField ="value" حيث [key] و [value] هما على التوالي المفاتيح والقيم لـ HT.

نطبق هذه المعلومات عمليًا على المثال التالي [databind1.aspx]:

لدينا هنا خمسة روابط بيانات تحتوي كل منها على أربعة عناصر تحكم من النوع [DropDownList] و [ListBox] و [CheckBoxList] و [RadioButtonList]. تختلف الروابط الخمسة التي ندرسها في مصادر البيانات الخاصة بها:

الوصلة
مصدر البيانات
1
مصفوفة
2
ArrayList
3
DataTable
4
DataSet
5
HashTable

8.3.1. رمز عرض المكونات

رمز عرض عناصر التحكم في الارتباط 1 هو كما يلي:

<td>
<asp:DropDownList id="DropDownList1" runat="server"></asp:DropDownList>
</td>
<td>
<asp:ListBox id="ListBox1" runat="server" SelectionMode="Multiple"></asp:ListBox>
</td>
<td>
<asp:CheckBoxList id="CheckBoxList1" runat="server"></asp:CheckBoxList>
</td>
<td>
<asp:RadioButtonList id="RadioButtonList1" runat="server"></asp:RadioButtonList>
</td>

الرقم الخاص بالروابط من 2 إلى 5 مطابق لرقم الرابط تقريبًا.

8.3.2. الربط بمصدر بيانات من نوع Array

يتم ربط عناصر التحكم الأربعة [ListBox] المذكورة أعلاه بالطريقة التالية في الإجراء [Page_Load] من كود التحكم:

' البيانات العامة
dim textes() as string={"un","deux","trois","quatre"}
dim valeurs() as string={"1","2","3","4"}
dim myDataListe as new ArrayList
dim myDataTable as new DataTable("table1")
dim myDataSet as new DataSet
dim myHashTable as new HashTable

' الإجراء الذي يتم تنفيذه عند تحميل الصفحة
Sub page_Load(sender As Object, e As EventArgs)
    if not IsPostBack then
      ' يتم إنشاء مصادر  البيانات التي سيتم ربطها بالمكونات
      createDataSources
       ' الربط بجدول [Array]
      bindToArray
       ' الربط بقائمة [ArrayList]
      bindToArrayList
       ' ربط بجدول [DataTable]
      bindToDataTable
       ' الربط بمجموعة بيانات [DataSet]
      bindToDataSet
       ' الربط بقاموس [HashTable]
      bindToHashTable
    end if
End Sub

sub createDataSources
   ' إنشاء مصادر البيانات التي سيتم ربطها بالمكونات
  ' arraylist
  dim i as integer
  for i=0 to textes.length-1
    myDataListe.add(textes(i))
  next
   ' جدول البيانات
   ' يتم تحديد عموديه
  myDataTable.Columns.Add("id",Type.GetType("System.Int32"))
  myDataTable.Columns.Add("texte",Type.GetType("System.String"))
  ' يتم ملء الجدول
  dim ligne as DataRow
  for i=0 to textes.length-1
    ligne=myDataTable.NewRow
    ligne("id")=i
    ligne("texte")=textes(i)
    myDataTable.Rows.Add(ligne)
  next
   ' مجموعة البيانات - جدول واحد
  myDataSet.Tables.Add(myDataTable)
  ' جدول التجزئة
  for i=0 to textes.length-1
    myHashTable.add(valeurs(i),textes(i))
  next
end sub

' ربط الجدول
sub bindToArray
         ' الربط بالمكونات
        with DropDownList1
            .DataSource=textes
            .DataBind
        end with
        with ListBox1
            .DataSource=textes
            .DataBind
        end with
        with CheckBoxList1
            .DataSource=textes
            .DataBind
        end with
        with RadioButtonList1
            .DataSource=textes
            .DataBind
        end with
        ' اختيار العناصر
        ListBox1.Items(1).Selected=true
        ListBox1.Items(3).Selected=true
        CheckBoxList1.Items(0).Selected=true
        CheckBoxList1.Items(3).Selected=true
        DropDownList1.SelectedIndex=2
        RadioButtonList1.SelectedIndex=1
end sub

sub bindToArrayList
....
end sub

sub bindToDataTable
...
end sub

sub bindToDataSet
...
end sub

لا يتم ربط عناصر التحكم بالبيانات هنا إلا عند الطلب الأول. بعد ذلك، ستحتفظ عناصر التحكم بعناصرها من خلال آلية [VIEWSTATE]. يتم الربط في الإجراء [bindToArray]. ونظرًا لأن مصدر البيانات من النوع [Array]، يتم تهيئة الحقل [DataSource] فقط من المكونات [ListControl]. يتم ملء عنصر التحكم بقيم مصدر البيانات المرتبط باستخدام الطريقة [ListControl].DataBind. ولا تحتوي كائنات [ListControl] على عناصر إلا بعد ذلك. ويمكن عندئذٍ تحديد بعضها.

8.3.3. الربط بمصدر بيانات من النوع ArrayList

يتم تهيئة مصدر البيانات [myDataListe] في الإجراء [createDataSources]:

' البيانات العامة
dim textes() as string={"un","deux","trois","quatre"}
dim myDataListe as new ArrayList
..

' الإجراء الذي يتم تنفيذه عند تحميل الصفحة
Sub page_Load(sender As Object, e As EventArgs)
    if not IsPostBack then
      ' يتم إنشاء مصادر  البيانات التي سيتم ربطها بالمكونات
      createDataSources
...
    end if
End Sub

sub createDataSources
   ' إنشاء مصادر البيانات التي سيتم ربطها بالمكونات
  ' arraylist
  dim i as integer
  for i=0 to textes.length-1
    myDataListe.add(textes(i))
  next
...
end sub

يتم ربط عناصر التحكم الأربعة [ListBox] للربط 2 بالطريقة التالية في الإجراء [bindToArrayList] لرمز التحكم:

' ربط arraylist
sub bindToArrayList
         ' الربط بالمكونات
        with DropDownList2
            .DataSource=myDataListe
            .DataBind
        end with
        with ListBox2
            .DataSource=myDataListe
            .DataBind
        end with
        with CheckBoxList2
            .DataSource=myDataListe
            .DataBind
        end with
        with RadioButtonList2
            .DataSource=myDataListe
            .DataBind
        end with
        ' اختيار العناصر
        ListBox2.Items(1).Selected=true
        ListBox2.Items(3).Selected=true
        CheckBoxList2.Items(0).Selected=true
        CheckBoxList2.Items(3).Selected=true
        DropDownList2.SelectedIndex=2
        RadioButtonList2.SelectedIndex=1
end sub

نظرًا لأن مصدر البيانات من النوع [ArrayList]، يتم تهيئة الحقل [DataSource] فقط من المكونات [ListControl].

8.3.4. مصدر البيانات من النوع DataTable

يتم تهيئة مصدر البيانات [myDataTable] في الإجراء [createDataSources]:

' البيانات العامة
dim textes() as string={"un","deux","trois","quatre"}
dim myDataTable as new DataTable("table1")
...

' الإجراء الذي يتم تنفيذه عند تحميل الصفحة
Sub page_Load(sender As Object, e As EventArgs)
    if not IsPostBack then
      ' يتم إنشاء مصادر  البيانات التي سيتم ربطها بالمكونات
      createDataSources
...
    end if
End Sub

sub createDataSources
   ' إنشاء مصادر البيانات التي سيتم ربطها بالمكونات
...
   ' جدول البيانات
   ' يتم تحديد عموديها
  myDataTable.Columns.Add("id",Type.GetType("System.Int32"))
  myDataTable.Columns.Add("texte",Type.GetType("System.String"))
  ' يتم ملء الجدول
  dim ligne as DataRow
  for i=0 to textes.length-1
    ligne=myDataTable.NewRow
    ligne("id")=i
    ligne("texte")=textes(i)
    myDataTable.Rows.Add(ligne)
  next
...
end sub

نبدأ بإنشاء كائن [DataTable] يتألف من عمودين هما [id] و [texte]. سيغذي العمود [id] الحقل [Value] لعناصر [ListControl]، وسيغذي العمود [texte] حقولها [Text]. يتم إنشاء [DataTable] بعمودين على النحو التالي:

  myDataTable.Columns.Add("id",Type.GetType("System.Int32"))
  myDataTable.Columns.Add("texte",Type.GetType("System.String"))

لذلك يتم إنشاء جدول من عمودين:

  • الأول يسمى "id" وهو من النوع الصحيح
  • والثاني يسمى "نص" وهو من نوع سلسلة أحرف

بعد إنشاء بنية الجدول، يمكن ملؤه بالرمز التالي:

  dim ligne as DataRow
  for i=0 to textes.length-1
    ligne=myDataTable.NewRow
    ligne("id")=i
    ligne("texte")=textes(i)
    myDataTable.Rows.Add(ligne)
  next

سيحتوي العمود [id] على أعداد صحيحة [0,1,..,n] بينما سيحتوي العمود [texte] على قيم الجدول [data]. وبذلك، يتم ملء الجدول [dataListe]. يتم ربط عناصر التحكم الأربعة [ListBox] للربط 3 بالطريقة التالية في الإجراء [bindToDataTable] لرمز التحكم:

sub bindToDataTable
         ' الربط بالمكونات
        with DropDownList3
            .DataSource=myDataTable
            .DataValueField="id"
            .DataTextField="texte"
            .DataBind
        end with
        with ListBox3
            .DataSource=myDataTable
            .DataValueField="id"
            .DataTextField="texte"
            .DataBind
        end with
        with CheckBoxList3
            .DataSource=myDataTable
            .DataValueField="id"
            .DataTextField="texte"
            .DataBind
        end with
        with RadioButtonList3
            .DataSource=myDataTable
            .DataValueField="id"
            .DataTextField="texte"
            .DataBind
        end with
        ' اختيار العناصر
        ListBox3.Items(1).Selected=true
        ListBox3.Items(3).Selected=true
        CheckBoxList3.Items(0).Selected=true
        CheckBoxList3.Items(3).Selected=true
        DropDownList3.SelectedIndex=2
        RadioButtonList3.SelectedIndex=1
end sub

يرتبط كل مكون [ListControl] بالمصدر [myDataTable] من خلال تعيين ما يلي لكل منها:

            .DataSource= myDataTable
            .DataValueField="id"
            .DataTextField="texte"

الجدول [myDataTable] هو مصدر البيانات. سيتم تغذية الحقول [Value] لعناصر المكونات من العمود [id] في هذه الجدولة، بينما سيتم تغذية الحقول [Text] من العمود [texte].

8.3.5. مصدر البيانات من النوع DataSet

يتم تهيئة مصدر البيانات [myDataSet] في الإجراء [createDataSources]:

' البيانات العامة
dim myDataTable as new DataTable("table1")
dim myDataSet as new DataSet
...


' الإجراء الذي يتم تنفيذه عند تحميل الصفحة
Sub page_Load(sender As Object, e As EventArgs)
    if not IsPostBack then
      ' يتم إنشاء مصادر  البيانات التي سيتم ربطها بالمكونات
      createDataSources
...
    end if
End Sub

sub createDataSources
   ' إنشاء مصادر البيانات التي سيتم ربطها بالمكونات
   ' مجموعة البيانات - جدول واحد
  myDataSet.Tables.Add(myDataTable)
...
end sub

يمثل الكائن [DataSet] مجموعة من الجداول من النوع [DataTable]. يتم إضافة الجدول [myDataTable] الذي تم إنشاؤه مسبقًا إلى [DataSet]. يتم ربط عناصر التحكم الأربعة [ListBox] للربط 4 بالطريقة التالية في الإجراء [bindToDataSet] في كود التحكم:

sub bindToDataSet
         ' الربط بالمكونات
        with DropDownList4
            .DataSource=myDataSet
            .DataMember="table1"
            .DataValueField="id"
            .DataTextField="texte"
            .DataBind
        end with
        with ListBox4
            .DataSource=myDataSet
            .DataMember="table1"
            .DataValueField="id"
            .DataTextField="texte"
            .DataBind
        end with
        with CheckBoxList4
            .DataSource=myDataSet
            .DataMember="table1"
            .DataValueField="id"
            .DataTextField="texte"
            .DataBind
        end with
        with RadioButtonList4
            .DataSource=myDataSet
            .DataMember="table1"
            .DataValueField="id"
            .DataTextField="texte"
            .DataBind
        end with
        ' اختيار العناصر
        ListBox4.Items(1).Selected=true
        ListBox4.Items(3).Selected=true
        CheckBoxList4.Items(0).Selected=true
        CheckBoxList4.Items(3).Selected=true
        DropDownList4.SelectedIndex=2
        RadioButtonList4.SelectedIndex=1
end sub

يرتبط كل مكون [ListControl] بمصدر البيانات على النحو التالي:

            .DataSource= myDataSet
            .DataMember="table1"
            .DataValueField="id"
            .DataTextField="texte"

مجموعة البيانات [myDataSet] هي مصدر البيانات. ونظرًا لأن هذا المصدر قد يتضمن عدة جداول، يتم تحديد اسم الجدول الذي سيتم استخدامه في [DataMember]. سيتم تغذية الحقول [Value] لعناصر المكونات من العمود [id] في هذه الجدولة، بينما سيتم تغذية حقولها [Text] من العمود [texte].

8.3.6. مصدر البيانات من النوع HashTable

يتم تهيئة مصدر البيانات [myHashTable] في الإجراء [createDataSources]:

' البيانات العامة
dim textes() as string={"un","deux","trois","quatre"}
dim valeurs() as string={"1","2","3","4"}
dim myHashTable as new HashTable
...

' الإجراء الذي يتم تنفيذه عند تحميل الصفحة
Sub page_Load(sender As Object, e As EventArgs)
    if not IsPostBack then
      ' يتم إنشاء مصادر  البيانات التي سيتم ربطها بالمكونات
      createDataSources
...
    end if
End Sub

sub createDataSources
   ' إنشاء مصادر البيانات التي سيتم ربطها بالمكونات
...
  ' جدول التجزئة
  for i=0 to textes.length-1
    myHashTable.add(valeurs(i),textes(i))
  next
end sub

يمكن النظر إلى القاموس [myHashTable] على أنه جدول مكون من عمودين يسميان "key" و"value". يمثل العمود [key] مفاتيح القاموس، بينما يمثل العمود [value] القيم المرتبطة بها. يتكون العمود [key] هنا من محتوى الجدول [valeurs]، بينما يتكون العمود [value] من محتوى الجدول [textes]. يتم ربط هذا المصدر بالعناصر التحكمية في الإجراء [bindToHashTable]:

sub bindToHashTable
         ' الربط بالمكونات
        with DropDownList5
            .DataSource=myHashTable
            .DataValueField="key"
            .DataTextField="value"
            .DataBind
        end with
        with ListBox5
            .DataSource=myHashTable
            .DataValueField="key"
            .DataTextField="value"
            .DataBind
        end with
        with CheckBoxList5
            .DataSource=myHashTable
            .DataValueField="key"
            .DataTextField="value"
            .DataBind
        end with
        with RadioButtonList5
            .DataSource=myHashTable
            .DataValueField="key"
            .DataTextField="value"
            .DataBind
        end with
        ' اختيار العناصر
        ListBox5.Items(1).Selected=true
        ListBox5.Items(3).Selected=true
        CheckBoxList5.Items(0).Selected=true
        CheckBoxList5.Items(3).Selected=true
        DropDownList5.SelectedIndex=2
        RadioButtonList5.SelectedIndex=1
end sub

بالنسبة لكل مكون، يتم الربط بواسطة التعليمات التالية:

            .DataSource=myHashTable
            .DataValueField="key"
            .DataTextField="value"

مصدر البيانات هو القاموس [myHashTable]. يتم توفير قيم عنصر التحكم من خلال العمود [key] في القاموس، بينما يتم توفير النصوص من خلال العمود [value]. يتم إدراج عناصر القاموس في عناصر التحكم حسب ترتيب المفاتيح الذي يكون عشوائيًا في البداية.

8.3.7. توجيهات استيراد مساحات الأسماء

يتم استيراد عدد معين من مساحات الأسماء تلقائيًا إلى صفحة ASP.NET. ليس هذا هو الحال بالنسبة لـ "System.Data" حيث توجد الفئات [DataTable] و [DataSet]. لذا يجب استيراد هذه الفئة. ويتم ذلك بالطريقة التالية:

<%@ Page Language="VB" %>
<%@ import Namespace="System.Data" %>
<script runat="server">
...
</script>
<html>
...
</html>

8.4. المكون DataGrid وربط البيانات

يتيح المكون [DataGrid] عرض البيانات في شكل جداول، ولكنه يتجاوز مجرد العرض البسيط:

  • فهو يوفر إمكانية ضبط "العرض المرئي" للجدول بدقة
  • ويسمح بتحديث مصدر البيانات

المكون [DataGrid] هو مكون قوي ومعقد في آن واحد. سنقوم بعرضه على مراحل متتالية.

8.4.1. عرض مصدر بيانات Array، ArrayList، DataTable، DataSet

يسمح المكون [DataGrid] بعرض مصادر البيانات من النوع [Array]، [ArrayList]، [DataTable]، [DataSet]. بالنسبة لهذه الأنواع الأربعة من البيانات، يكفي ربط المصدر بالخاصية [DataSource] للمكون [DataGrid]:

DataSource
مصدر بيانات [Array]، [ArrayList]، [DataTable]، [DataSet]، ...
DataMember
في حالة ما إذا كان مصدر البيانات هو [DataSet]، يمثل اسم الجدول الذي سيتم استخدامه كمصدر للبيانات. يكون مصدر البيانات الفعلي عندئذٍ هو جدول. إذا لم يتم ملء هذا الحقل، يتم عرض جميع جداول [DataSet].

نقدم الآن الصفحة [datagrid1.aspx] التي توضح ربط [DataGrid] بأربعة مصادر بيانات مختلفة:

Image

تحتوي الصفحة على أربعة مكونات [DataGrid] تم إنشاؤها باستخدام [WebMatrix] بالطريقة التالية. يتم وضع المكون في مكانه في علامة التبويب [Design]:

Image

ثم يتم رسم جدول HTML عام. يمكن تعريف خصائص [DataGrid] عند التصميم. وهذا ما نقوم به هنا لخصائص التنسيق الخاصة به. وللقيام بذلك، نختار [DataGrid] المراد تكوينه. تظهر خصائصه في نافذة في أسفل اليمين:

Image

سنستخدم الرابطين أعلاه. يتيح الرابط [Générateur de propriétés] الوصول إلى الخصائص الرئيسية لـ [DataGrid]:

Image

نقوم بإلغاء تحديد العبارة [Afficher l'en-tête] للمكونات الأربعة [DataGrid] ونقوم بتأكيد الصفحة. يتيح الرابط الآخر [Mise en forme automatique] الاختيار من بين عدة أنماط للجدول HTML الذي سيتم عرضه:

Image

نختار [couleur i] لـ [DataGrid] رقم i. يتم ترجمة خيارات التصميم هذه إلى كود عرض الصفحة:

<html>
<head>
</head>
<body>
    <form runat="server">
        <p>
            Liaison de données avec un DataGrid 
        </p>
        <hr />
        <p>
            <table>
                <tbody>
                </tbody>
            </table>
            <table border="1">
                <tbody>
                    <tr>
                        <td>
                            Array</td>
                        <td>
                            ArrayList</td>
                        <td>
                            DataTable</td>
                        <td>
                            DataSet</td>
                    </tr>
                    <tr>
                        <td>
                            <asp:DataGrid id="DataGrid1" runat="server" ShowHeader="False" CellPadding="4" BackColor="White" BorderColor="#CC9966" BorderWidth="1px" BorderStyle="None">
                                <FooterStyle forecolor="#330099" backcolor="#FFFFCC"></FooterStyle>
                                <HeaderStyle font-bold="True" forecolor="#FFFFCC" backcolor="#990000"></HeaderStyle>
                                <PagerStyle horizontalalign="Center" forecolor="#330099" backcolor="#FFFFCC"></PagerStyle>
                                <SelectedItemStyle font-bold="True" forecolor="#663399" backcolor="#FFCC66"></SelectedItemStyle>
                                <ItemStyle forecolor="#330099" backcolor="White"></ItemStyle>
                            </asp:DataGrid>
                        </td>
                        <td>
                            <asp:DataGrid id="DataGrid2" runat="server" ShowHeader="False" CellPadding="4" BackColor="White" BorderColor="#3366CC" BorderWidth="1px" BorderStyle="None">
                                <FooterStyle forecolor="#003399" backcolor="#99CCCC"></FooterStyle>
                                <HeaderStyle font-bold="True" forecolor="#CCCCFF" backcolor="#003399"></HeaderStyle>
                                <PagerStyle horizontalalign="Left" forecolor="#003399" backcolor="#99CCCC" mode="NumericPages"></PagerStyle>
                                <SelectedItemStyle font-bold="True" forecolor="#CCFF99" backcolor="#009999"></SelectedItemStyle>
                                <ItemStyle forecolor="#003399" backcolor="White"></ItemStyle>
                            </asp:DataGrid>
                        </td>
                        <td>
                            <asp:DataGrid id="DataGrid3" runat="server" ShowHeader="False" CellPadding="3" BackColor="#DEBA84" BorderColor="#DEBA84" BorderWidth="1px" BorderStyle="None" CellSpacing="2">
                                <FooterStyle forecolor="#8C4510" backcolor="#F7DFB5"></FooterStyle>
                                <HeaderStyle font-bold="True" forecolor="White" backcolor="#A55129"></HeaderStyle>
                                <PagerStyle horizontalalign="Center" forecolor="#8C4510" mode="NumericPages"></PagerStyle>
                                <SelectedItemStyle font-bold="True" forecolor="White" backcolor="#738A9C"></SelectedItemStyle>
                                <ItemStyle forecolor="#8C4510" backcolor="#FFF7E7"></ItemStyle>
                            </asp:DataGrid>
                        </td>
                        <td>
                            <asp:DataGrid id="DataGrid4" runat="server" ShowHeader="False" CellPadding="3" BackColor="White" BorderColor="#E7E7FF" BorderWidth="1px" BorderStyle="None" GridLines="Horizontal">
                                <FooterStyle forecolor="#4A3C8C" backcolor="#B5C7DE"></FooterStyle>
                                <HeaderStyle font-bold="True" forecolor="#F7F7F7" backcolor="#4A3C8C"></HeaderStyle>
                                <PagerStyle horizontalalign="Right" forecolor="#4A3C8C" backcolor="#E7E7FF" mode="NumericPages"></PagerStyle>
                                <SelectedItemStyle font-bold="True" forecolor="#F7F7F7" backcolor="#738A9C"></SelectedItemStyle>
                                <AlternatingItemStyle backcolor="#F7F7F7"></AlternatingItemStyle>
                                <ItemStyle forecolor="#4A3C8C" backcolor="#E7E7FF"></ItemStyle>
                            </asp:DataGrid>
                        </td>
                    </tr>
                </tbody>
            </table>
        </p>
        <asp:Button id="Button1" runat="server" Text="Envoyer"></asp:Button>
    </form>
</body>
</html>

في وضع التصميم، لم نحدد سوى خصائص التنسيق. وفي كود التحكم، نقوم بربط البيانات بالمكونات الأربعة:

<%@ Page Language="VB" %>
<%@ import Namespace="system.data" %>
<script runat="server">

     ' البيانات الإجمالية
        dim textes1() as string={"un","deux","trois","quatre"}
        dim textes2() as string={"one","two","three","for"}
        dim valeurs() as string={"1","2","3","4"}
        dim myDataListe as new ArrayList
        dim myDataTable as new DataTable("table1")
        dim myDataSet as new DataSet

        ' الإجراء الذي يتم تنفيذه عند تحميل الصفحة
        Sub page_Load(sender As Object, e As EventArgs)
            if not IsPostBack then
              ' يتم إنشاء مصادر  البيانات التي سيتم ربطها بالمكونات
              createDataSources
               ' الربط بجدول [Array]
              bindToArray
               ' الربط بقائمة [ArrayList]
              bindToArrayList
               ' ربط بجدول [DataTable]
              bindToDataTable
               ' الربط بمجموعة بيانات [DataSet]
              bindToDataSet
            end if
        End Sub

        sub createDataSources
           ' ينشئ مصادر البيانات التي سيتم ربطها بالمكونات
          ' arraylist
          dim i as integer
          for i=0 to textes1.length-1
            myDataListe.add(textes1(i))
          next
           ' جدول البيانات
           ' يتم تحديد عموديها
          myDataTable.Columns.Add("id",Type.GetType("System.Int32"))
          myDataTable.Columns.Add("texte1",Type.GetType("System.String"))
          myDataTable.Columns.Add("texte2",Type.GetType("System.String"))
          ' يتم ملء الجدول
          dim ligne as DataRow
          for i=0 to textes1.length-1
            ligne=myDataTable.NewRow
            ligne("id")=valeurs(i)
            ligne("texte1")=textes1(i)
            ligne("texte2")=textes2(i)
            myDataTable.Rows.Add(ligne)
          next
           ' مجموعة البيانات - جدول واحد
          myDataSet.Tables.Add(myDataTable)
        end sub

        ' ربط الجدول
        sub bindToArray
          with DataGrid1
            .DataSource=textes1
            .DataBind
          end with
        end sub

        ' ربط قائمة المصفوفات
        sub bindToArrayList
          with DataGrid2
            .DataSource=myDataListe
            .DataBind
          end with
        end sub

        ' ربط جدول البيانات
        sub bindToDataTable
          with DataGrid3
            .DataSource=myDataTable
            .DataBind
          end with
        end sub

        ' ربط مجموعة البيانات
        sub bindToDataSet
          with DataGrid4
            .DataSource=myDataSet
            .DataBind
          end with
        end sub

</script>
<html>
...
</html>

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

          with [DataGrid]
            .DataSource=[source de données]
            .DataBind
          end with

حيث يكون [source de données] من النوع [Array] أو [ArrayList] أو [DataTable] أو [DataSet]. من الواضح إذن أننا أمام أداة قوية لعرض البيانات في جداول:

  • يتم التنسيق باستخدام [WebMatrix] أو أي IDE آخر عند التصميم
  • يتم ربط البيانات في الكود. باستخدام [WebMatrix]، يمكن إجراء ذلك في مرحلة التصميم إذا كانت مصدر البيانات قاعدة بيانات خادم SQL أو قاعدة بيانات ACCESS. في هذه الحالة، يتم وضع مكون من النوع [SqlDataSource] أو [AccessDataSource] على الورقة. يمكن ربط هذا المكون في مرحلة التصميم بمصدر البيانات الفعلي، وهو قاعدة بيانات خادم SQL أو قاعدة بيانات ACCESS حسب الحالة. إذا تم تعيين كائن [SqlDataSource] أو [AccessDataSource] مرتبط بمصدر مادي إلى الخاصية [DataSource] لمكون [DataGrid]، فسيظهر، في وضع التصميم، البيانات الفعلية في المكون [DataGrid].

8.5. ViewState لمكونات قوائم البيانات

نقترح هنا تسليط الضوء على آلية [VIEWSATE] لمكونات قوائم البيانات. قد يتردد المرء بين طريقتين للحفاظ على حالة مكون قائمة البيانات بين طلبين من العميل:

  • تعيين السمة [VIEWSTATE] على "صحيح"
  • تعيين السمة [VIEWSTATE] على "خطأ" وتخزين مصدر البيانات في الجلسة حتى يمكن ربط المكون بهذا المصدر عند الطلب التالي.

يوضح المثال التالي بعض جوانب آلية [VIEWSTATE] لحاويات البيانات. عند الطلب الأول من العميل، يعرض التطبيق العرض التالي:

رقم
الاسم
النوع
الخصائص
الدور
1
DataGrid1
DataGrid
EnableViewState=true
يعرض مصدر بيانات S
2
DataGrid2
DataGrid
EnableViewState=true
يعرض نفس المصدر S مثل [DataGrid1]
3
Button1
Button
EnableViewState=false
زر [submit]

يتم صيانة المكون [DataGrid1] بواسطة آلية [VIEWSTATE]. نحاول معرفة ما إذا كانت هذه الآلية، التي تعيد إنشاء عرض [DataGrid1] عند كل طلب، تعيد أيضًا إنشاء مصدر البيانات الخاص به. ولهذا الغرض، يتم ربط هذا المصدر بالمكون [DataGrid2]. يتم إنشاء هذا المكون عند كل طلب من خلال ارتباط صريح بمصدر بيانات [DataGrid1]. كما أن له سمة [EnableViewState] في [vrai] أيضًا.

رمز العرض [main.aspx] للتطبيق هو كما يلي:


<%@ page src="main.aspx.vb" inherits="main" autoeventwireup="false" %>
<HTML>
    <HEAD>
        <title></title>
    </HEAD>
    <body>
        <form runat="server">
            <table>
                <tr>
                    <td align="center">DataGrid 1</td>
                    <td align="center">
                        DataGrid 2</td>
                </tr>
                <tr>
                    <td>
                      <asp:DataGrid id="DataGrid1" runat="server" ...>
                            <SelectedItemStyle ...></SelectedItemStyle>
....
                        </asp:DataGrid></td>
                    <td>
                      <asp:DataGrid id="Datagrid2" runat="server" ...>
....
                        </asp:DataGrid></td>
                </tr>
            </table>
            <asp:Button id="Button1" runat="server" Text="Envoyer"></asp:Button>
        </form>
    </body>
</HTML>

أما وحدة التحكم [main.aspx.vb] فهي كما يلي:


Imports System.Data

Public Class main
    Inherits System.Web.UI.Page

    Protected WithEvents DataGrid1 As System.Web.UI.WebControls.DataGrid
    Protected WithEvents Datagrid2 As System.Web.UI.WebControls.DataGrid
    Protected WithEvents Button1 As System.Web.UI.WebControls.Button

    Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        ' في الاستعلام الأول، يتم تعريف مصدر البيانات وربطه بشبكة البيانات الأولى
        If Not IsPostBack Then
            'تعريف مصدر البيانات
            With DataGrid1
                .DataSource = createDataSource()
                .DataBind()
            End With
        End If
        ' في كل استعلام، يتم ربط شبكة البيانات 2 بمصدر شبكة البيانات الأولى
        With Datagrid2
            .DataSource = DataGrid1.DataSource
            .DataBind()
        End With
    End Sub

    Private Function createDataSource() As DataTable
        ' يتم تهيئة مصدر البيانات
        Dim thèmes As New DataTable
        ' الأعمدة
        With thèmes.Columns
            .Add("id", GetType(System.Int32))
            .Add("thème", GetType(System.String))
            .Add("description", GetType(System.String))
        End With
        ' سيكون العمود id هو المفتاح الأساسي
        thèmes.Constraints.Add("cléprimaire", thèmes.Columns("id"), True)
        ' الصفوف
        Dim ligne As DataRow
        For i As Integer = 0 To 4
            ligne = thèmes.NewRow
            ligne.Item("id") = i.ToString
            ligne.Item("thème") = "thème" + i.ToString
            ligne.Item("description") = "description du thème " + i.ToString
            thèmes.Rows.Add(ligne)
        Next
        Return thèmes
    End Function

    Private Sub InitializeComponent()

    End Sub
End Class

تقوم الطريقة [createDataSource] بإنشاء مصدر S من النوع [DataTable]. لن نتطرق إلى كودها لأنه ليس موضوع المثال. ما يهمنا هو الطريقة المستخدمة لبناء المكونين [DataGrid]:

  • يتم ربط المكون [DataGrid1] مرة واحدة بالجدول S، عند الطلب الأول. ولا يتم ربطه بعد ذلك.
  • يتم ربط المكون [DataGrid2] بالمصدر [DataGrid1.DataSource] في كل استعلام جديد.

عند الطلب الأول، نحصل على العرض التالي:

Image

وبشكل منطقي، يعرض المكونان مصدر البيانات الذي تم ربطهما به. نستخدم الزر [Envoyer] لإرسال [PostBack] إلى الخادم. تكون النظرة الناتجة عندئذٍ كما يلي:

Image

نلاحظ أن المكون [DataGrid1] احتفظ بقيمته، ولكن المكون [DataGrid2] لم يحتفظ بها. التفسير:

  • قبل أن تبدأ الإجراء [Page_Load]، استعاد الكائنان [DataGrid1] و [DataGrid2] القيمة التي كانا عليها في الاستعلام السابق، بسبب آلية [viewstate]. ففي الواقع، تمت تعيين خاصية [EnableViewState] لكليهما إلى [vrai].
  • يتم تنفيذ الإجراء [Page_Load]. وبما أننا نتعامل مع عملية [PostBack]، فإن المكون [DataGrid1] لا يتم تعديله بواسطة [Page_Load] (انظر الكود). لذلك يحتفظ بالقيمة التي تم استردادها بفضل [viewstate]. وهذا ما يوضحه الشاشة أعلاه.
  • أما المكون [DataGrid2] فهو مرتبط بـ [DataBind] بمصدر البيانات [DataGrid1.DataSource]. وبالتالي يتم إعادة بنائه وتُفقد القيمة التي كان قد استعادها للتو بفضل [viewstate]. لذلك كان من الأفضل هنا أن يكون له خاصية [EnableViewState] في [faux] لتجنب الإدارة غير الضرورية لحالته. تُظهر الشاشة أعلاه أن [DataGrid2] قد تم ربطه بمصدر فارغ. وبما أن هذا المصدر هو [DataGrid1.DataSource]، نستنتج أنه إذا كانت آلية [viewstate] تستعيد عرض المكون [DataGrid1] بشكل صحيح، فإنها لا تستعيد خصائصه مثل [DataSource].

ما الذي يمكن استنتاجه من هذا المثال؟ يجب تجنب تعيين خاصية [EnableViewState] لحاوية بيانات إلى [vrai] إذا كان يجب ربطها (DataBind) بمصدر بيانات في كل استعلام. ومع ذلك، هناك حالات معينة، حتى في هذا السيناريو، يجب الحفاظ على الخاصية [EnableViewState] للحاوية عند [vrai]، وإلا فلن يتم تشغيل الأحداث التي نرغب في إدارتها. سنرى مثالاً على ذلك لاحقًا.

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

رقم
الاسم
النوع
الخصائص
الدور
1
DataGrid1
DataGrid
EnableViewState=true
يعرض مصدر بيانات S
2
DataGrid2
DataGrid
EnableViewState=false
يعرض نفس المصدر S مثل [DataGrid1]
3
Button1
Button
EnableViewState=false
زر [submit]
4
lblInfo1
lblInfo2
lblInfo3
التسمية
EnableViewState=false
نصوص إعلامية

يرتبط المكون [DataGrid1] بالبيانات فقط عند الطلب الأول. وسيحتفظ بقيمته عبر الطلبات بفضل آلية [viewstate]. يرتبط المكون [DataGrid2] بكل استعلام بمصدر بيانات يُضاف إليه عنصر في كل استعلام جديد. لذا، يجب ربط (DataBind) المكون [DataGrid2] بكل استعلام. لذلك قمنا بتعيين سمة [EnableViewState] إلى [faux] كما تمت الإشارة إليه سابقًا. وبالتالي، عند الطلب الثاني (استخدام الزر [Envoyer])، نحصل على الرد التالي:

Image

احتفظ المكون [DataGrid1] بقيمته الأولية. يحتوي المكون [DataGrid2] على عنصر إضافي. تمثل المعلومات الثلاثة [1,2,2] رقم الطلب. نلاحظ أن إحدى المعلومات خاطئة. سنحاول فهم السبب.

رمز العرض [main.aspx] للتطبيق هو كما يلي:


<%@ Page src="main.aspx.vb" inherits="main" autoeventwireup="false" Language="vb" %>
<HTML>
    <HEAD>
        <title></title>
    </HEAD>
    <body>
        <form runat="server">
            <table>
                <tr>
                    <td align="center" bgColor="#ccffcc">DataGrid 1</td>
                    <td align="center" bgColor="#ffff99">DataGrid 2</td>
                </tr>
                <tr>
                    <td vAlign="top">
                        <asp:DataGrid id="DataGrid1" runat="server" ...>
...
                        </asp:DataGrid>
                    </td>
                    <td vAlign="top">
                        <asp:DataGrid id="Datagrid2" runat="server" ... EnableViewState="False">
....
                        </asp:DataGrid>
                    </td>
                </tr>
            </table>
            <P>Numéro de requête&nbsp;:
                <asp:Label id="lblInfo1" runat="server" EnableViewState="False"></asp:Label>,
                <asp:Label id="lblInfo2" runat="server" EnableViewState="False"></asp:Label>,
                <asp:Label id="lblInfo3" runat="server" EnableViewState="False"></asp:Label></P>
            <P>
                <asp:Button id="Button1" runat="server" Text="Envoyer" EnableViewState="False"></asp:Button></P>
        </form>
    </body>
</HTML>

رمز وحدة التحكم [main.aspx.vb] هو التالي:


Imports System.Data
Imports System

Public Class main
    Inherits System.Web.UI.Page

    Protected WithEvents DataGrid1 As System.Web.UI.WebControls.DataGrid
    Protected WithEvents Datagrid2 As System.Web.UI.WebControls.DataGrid
    Protected WithEvents Button1 As System.Web.UI.WebControls.Button
    Protected WithEvents lblInfo1 As System.Web.UI.WebControls.Label
    Protected WithEvents lblInfo2 As System.Web.UI.WebControls.Label
    Protected WithEvents lblInfo3 As System.Web.UI.WebControls.Label

    Dim dtThèmes As DataTable
    Dim numRequête1 As Integer
    Dim numRequête2 As Integer
    Dim numRequête3 As New entier

    Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        ' في الاستعلام الأول، يتم تعريف مصدر البيانات وربطه بشبكة البيانات الأولى
        If Not IsPostBack Then
            'تعريف مصدر البيانات
            dtThèmes = createDataSource()
            With DataGrid1
                .DataSource = dtThèmes
                .DataBind()
            End With
            ' تخزين المعلومات في الجلسة
            Session("source") = dtThèmes
            numRequête1 = 0 : Session("numRequête1") = numRequête1
            numRequête2 = 0 : Session("numRequête2") = numRequête2
            numRequête3.valeur = 0 : Session("numRequête3") = numRequête3
        End If
        ' في كل استعلام، يتم إضافة سمة جديدة
        dtThèmes = CType(Session("source"), DataTable)
        Dim nbThèmes = dtThèmes.Rows.Count
        Dim ligne As DataRow = dtThèmes.NewRow
        With ligne
            .Item("id") = nbThèmes.ToString
            .Item("thème") = "thème" + nbThèmes.ToString
            .Item("description") = "description du thème " + nbThèmes.ToString
        End With
        dtThèmes.Rows.Add(ligne)
        'ربط شبكة البيانات 2 بمصدر البيانات
        With Datagrid2
            .DataSource = dtThèmes
            .DataBind()
        End With
        ' معلومات عن عدد الطلبات
        numRequête1 = CType(Session("numRequête1"), Integer)
        numRequête1 += 1
        lblInfo1.Text = numRequête1.ToString
        numRequête2 = CType(Session("numRequête2"), Integer)
        numRequête2 += 1
        lblInfo2.Text = numRequête2.ToString
        numRequête3 = CType(Session("numRequête3"), entier)
        numRequête3.valeur += 1
        lblInfo3.Text = numRequête3.valeur.ToString
        ' يتم تخزين بعض المعلومات في الجلسة
        Session("numRequête2") = numRequête2
    End Sub

    Private Function createDataSource() As DataTable
....
    End Function
End Class

Public Class entier
    Private _valeur As Integer
    Public Property valeur() As Integer
        Get
            Return _valeur
        End Get
        Set(ByVal Value As Integer)
            _valeur = Value
        End Set
    End Property
End Class

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


    Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        ' في الطلب الأول، يتم تعريف مصدر البيانات وربطه بشبكة البيانات الأولى
        If Not IsPostBack Then
            'تعريف مصدر البيانات
            dtThèmes = createDataSource()
            With DataGrid1
                .DataSource = dtThèmes
                .DataBind()
            End With
            ' تخزين المعلومات في الجلسة
            Session("source") = dtThèmes
...
        End If
        ' في كل استعلام، يتم إضافة سمة جديدة
        dtThèmes = CType(Session("source"), DataTable)
        Dim nbThèmes = dtThèmes.Rows.Count
        Dim ligne As DataRow = dtThèmes.NewRow
        With ligne
            .Item("id") = nbThèmes.ToString
            .Item("thème") = "thème" + nbThèmes.ToString
            .Item("description") = "description du thème " + nbThèmes.ToString
        End With
        dtThèmes.Rows.Add(ligne)
        'ربط شبكة البيانات 2 بمصدر البيانات
        With Datagrid2
            .DataSource = dtThèmes
            .DataBind()
        End With
...
    End Sub

لا يتم ربط المكون [DataGrid1] بمصدر البيانات S إلا عند الطلب الأول (وليس IsPostBack). ثم يتم وضعه في الجلسة. ولن يتم وضعه بعد ذلك. يتم استرداد مصدر البيانات S الموضوع في الجلسة عند كل طلب ويتم إضافة سطر جديد إليه. يتم ربط المكون [DataGrid2] بشكل صريح، في كل استعلام، بمصدر البيانات S. ولهذا السبب يزداد محتواه بسطر واحد في كل استعلام. نلاحظ أنه بعد تعديل محتوى مصدر البيانات S، لا يتم إعادة وضعه صراحةً في الجلسة من خلال عملية:

            Session("source") = S

لماذا؟ عند بدء طلب، تحتوي الجلسة على كائن Session("source") من النوع [DataTable] وهو مصدر البيانات كما كان في آخر طلب. لنسمي الكائن Session("source") بـ S. عندما نكتب:


        dtThèmes = CType(Session("source"), DataTable)

فإن [dtThèmes] و [S] هما مرجعان لنفس الكائن [DataTable]. وبالتالي، عندما يضيف كود [Page_Load] عنصرًا إلى الجدول المشار إليه بواسطة [dtThèmes]، فإنه يضيفه في الوقت نفسه إلى الجدول المشار إليه بواسطة [S]. عند انتهاء تنفيذ الصفحة، سيتم حفظ جميع الكائنات الموجودة في الجلسة، وبالتالي الكائن Session("source")، c.a.d. S، c.a.d. [dtThèmes]. وبالتالي، فإن المحتوى الجديد لمصدر البيانات هو الذي يتم حفظه. لم تكن هناك حاجة لكتابة:

            Session("source") = dtThèmes

لإجراء هذا الحفظ، لأن Session("source") تساوي بالفعل [dtThèmes]. ولا ينطبق هذا عندما لا تكون البيانات الموضوعة في الجلسة كائنات، مثل الهياكل [Integer, Float, ...]. وهذا ما تظهره إدارة عدادات الطلبات:


    Dim numRequête1 As Integer
    Dim numRequête2 As Integer
    Dim numRequête3 As New entier

    Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        ' في الطلب الأول، يتم تعريف مصدر البيانات وربطه بشبكة البيانات الأولى
....
            ' تخزين المعلومات في الجلسة
            Session("source") = dtThèmes
            numRequête1 = 0 : Session("numRequête1") = numRequête1
            numRequête2 = 0 : Session("numRequête2") = numRequête2
            numRequête3.valeur = 0 : Session("numRequête3") = numRequête3
        End If
        ' في كل استعلام، يتم إضافة سمة جديدة
....
        ' معلومات عن عدد الاستعلامات
        numRequête1 = CType(Session("numRequête1"), Integer)
        numRequête1 += 1
        lblInfo1.Text = numRequête1.ToString
        numRequête2 = CType(Session("numRequête2"), Integer)
        numRequête2 += 1
        lblInfo2.Text = numRequête2.ToString
        numRequête3 = CType(Session("numRequête3"), entier)
        numRequête3.valeur += 1
        lblInfo3.Text = numRequête3.valeur.ToString
        ' يتم تخزين بعض المعلومات في الجلسة
        Session("numRequête2") = numRequête2
    End Sub

    Private Function createDataSource() As DataTable
....
    End Function

    Private Sub InitializeComponent()

    End Sub
End Class

Public Class entier
    Private _valeur As Integer
    Public Property valeur() As Integer
        Get
            Return _valeur
        End Get
        Set(ByVal Value As Integer)
            _valeur = Value
        End Set
    End Property

يتم تخزين عدادات الطلبات في ثلاثة عناصر:

  • numRequête1 و numRequête2 من النوع [Integer] - [Integer] ليست فئة بل بنية
  • numRequête3 من النوع [entier] - [entier] هي فئة محددة لهذه المناسبة

عند كتابة:


        numRequête1 = CType(Session("numRequête1"), Integer)
..
        numRequête2 = CType(Session("numRequête2"), Integer)
..
        numRequête3 = CType(Session("numRequête3"), entier)
..
  • يتم نسخ البنية [Session("numRequête1")] إلى [numRequête1]. وبالتالي، عندما يتم تعديل العنصر [numRequête1]، لا يتم تعديل العنصر [Session("numRequête1")]
  • وينطبق الأمر نفسه على [Session("numRequête2")] و [numRequête2]
  • العنصران [Session("numRequête3")] و [numRequête3] هما إشارتان إلى نفس الكائن من النوع [entier]. يمكن تعديل الكائن المشار إليه من خلال أي من الإشارتين.

ومن هذا، نستنتج أنه لا داعي لكتابة:

        Session("numRequête3") = numRequête3

لتخزين القيمة الجديدة لـ [numRequête3] في الجلسة. في المقابل، يجب كتابة:

        Session("numRequête1") = numRequête1
        Session("numRequête2") = numRequête2

لتخزين القيم الجديدة للهياكل [numRequête1] و [numRequête2]. نقوم بذلك فقط بالنسبة لـ [numRequête2]، وهو ما يفسر أن العداد [numRequête1] غير صحيح في لقطة الشاشة التي تم الحصول عليها في نهاية الاستعلام الثاني.

لذلك، يجب أن نتذكر أنه بمجرد إدخال البيانات في الجلسة، لا يلزم إعادة إدخالها بشكل متكرر إذا كانت ممثلة بكائن. في الحالات الأخرى، يجب القيام بذلك إذا تم تعديلها.

8.6. عرض قائمة بيانات باستخدام DataGrid مقسمة إلى صفحات ومصنفة

يسمح المكون [DataGrid] بعرض محتوى ملف [DataSet]. حتى الآن، كنا نبني ملفات [DataSet] "يدويًا". هذه المرة، نستخدم ملف [DataSet] مستمدًا من قاعدة بيانات. نبني التطبيق MVC التالي:

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

8.6.1. فئات الأعمال

تتيح الفئة [produits] الوصول إلى قاعدة البيانات ACCESS التالية:

Image

رمز الفئة [produits] هو التالي:


Imports System
Imports System.Data
Imports System.Data.OleDb
Imports System.Xml

Namespace st.istia.univangers.fr

    Public Class produits
        Private chaineConnexionOLEDB As String

        Public Sub New(ByVal chaineConnexionOLEDB As String)
            ' يتم حفظ سلسلة الاتصال
            Me.chaineConnexionOLEDB = chaineConnexionOLEDB
        End Sub

        Public Function getDataSet(ByVal commande As String) As DataSet
            ' يتم إنشاء كائن DataAdapter لقراءة البيانات من المصدر OLEDB
            Dim adaptateur As New OleDbDataAdapter(commande, chaineConnexionOLEDB)
            ' يتم إنشاء صورة في الذاكرة لنتيجة عملية الاختيار
            Dim contenu As New DataSet
            Try
                adaptateur.Fill(contenu)
            Catch e As Exception
                Throw New Exception("Erreur d'accès à la base de données (" + e.Message + ")")
            End Try
            ' يتم عرض النتيجة
            Return contenu
        End Function
    End Class
End Namespace

سيتم إدارة قاعدة البيانات ACCESS عبر برنامج تشغيل OLEDB. يتم تزويد منشئ الفئة [produits] بسلسلة الاتصال المعرّفة وبرنامج التشغيل OLEDB وقاعدة البيانات المطلوب إدارتها. تحتوي الفئة [produits] على طريقة [getDataSet] التي تُرجع [DataSet] تم الحصول عليه من خلال تنفيذاستعلام SQL [select] الذي يتم تمرير نصه كمعلمة. خلال هذه الطريقة، تتم عدة عمليات: إنشاء اتصال بالقاعدة، وتنفيذ [select]، وإغلاق الاتصال. كل هذا قد يؤدي إلى استثناء يتم معالجته هنا. قد يكون برنامج الاختبار كما يلي:

Option Explicit On 
Option Strict On

' مساحات الأسماء
Imports System
Imports System.Data
Imports Microsoft.VisualBasic

Namespace st.istia.univangers.fr

     ' صفحة اختبار
    Module testproduits
        Sub Main(ByVal arguments() As String)
             ' يعرض محتوى جدول المنتجات
             ' الجدول موجود في قاعدة بيانات ACCESS التي يحصل pg منها على اسم الملف
            Const syntaxe1 As String = "pg bdACCESS"

             ' التحقق من معلمات البرنامج
            If arguments.Length <> 1 Then
                 ' رسالة خطأ
                Console.Error.WriteLine(syntaxe1)
                 ' النهاية
                Environment.Exit(1)
            End If

             ' يتم إعداد سلسلة الاتصال
            Dim chaineConnexion As String = "Provider=Microsoft.Jet.OLEDB.4.0; Ole DB Services=-4; Data Source=" + arguments(0)

             ' إنشاء كائن منتجات
            Dim objProduits As produits = New produits(chaineConnexion)

             ' استرداد جدول المنتجات في مجموعة بيانات
            Dim contenu As DataSet
            Try
                contenu = objProduits.getDataSet("select id,nom,prix from liste")
            Catch ex As Exception
                Console.Error.WriteLine(("L'erreur suivante s'est produite : " + ex.Message))
                Environment.Exit(2)
            End Try

             ' عرض محتوياتها
            Dim lignes As DataRowCollection = contenu.Tables(0).Rows
            For i As Integer = 0 To lignes.Count - 1
                 ' السطر i من الجدول
                Console.Out.WriteLine(lignes(i).Item("id").ToString + "," + lignes(i).Item("nom").ToString + _
                "," + lignes(i).Item("prix").ToString)
            Next
        End Sub
    End Module
End Namespace

يتم تجميع العناصر المختلفة لهذا التطبيق بالطريقة التالية:

dos>vbc /t:library /r:system.dll /r:system.data.dll /r:system.xml.dll produits.vb

dos>vbc /r:produits.dll /r:system.data.dll /r:system.xml.dll /r:system.dll testproduits.vb

dos>dir
06/05/2004  16:52              118 784 produits.mdb
07/05/2004  11:07                  902 produits.vb
07/04/2004  07:01                1 532 testproduits.vb
07/05/2004  14:21                3 584 produits.dll
07/05/2004  14:22                4 608 testproduits.exe

dos>testproduits produits.mdb
1,produit1,10
2,produit2,20
3,produit3,30

8.6.2. الطرق

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

رقم
الاسم
النوع
الخصائص
الدور
1
txtSelect
TextBox
EnableViewState=true
حقل إدخال طلب الاختيار
2
RequiredFieldValidator1
RequiredFieldValidator
EnableViewState=false
تحقق من وجود 1
3
txtPages
TextBox
EnableViewState=true
حقل الإدخال - يشير إلى عدد أسطر البيانات المراد عرضها في كل صفحة من صفحات النتائج
4
RequiredFieldValidator2
RequiredFieldValidator
EnableViewState=false
يتحقق من وجود 3
5
RangeValidator1
RangeValidator
EnableViewState=false
تحقق من أن (3) يقع في النطاق [1,30]
6
btnExécuter
زر
EnableViewState=false
زر [submit]

سنسمي هذه العرض، عرض [formulaire]. وهي تسمح للمستخدم بتنفيذ استعلام SQL select على قاعدة البيانات [produits.mdb]. يؤدي تنفيذ الاستعلام إلى إنشاء عرض جديد:

رقم
الاسم
النوع
الخصائص
الدور
1
lblSelect
التسمية
EnableViewState=false
حقل المعلومات
2
rdCroissant
rdDécroissant
RadioButton
EnableViewState=false
GroupName=rdTri
يسمح باختيار اتجاه الفرز
3
DataGrid1
DataGrid
EnableViewState=true
AllowPaging=true
AllowSorting=true
جدول عرض نتيجة عملية الاختيار
4
lnkRésultats
LinkButton
EnableViewState=false
زر الارتباط [submit]

سنسمي هذه العرض، عرض [résultats]. وهو الذي يحتوي على [DataGrid] الذي سيعرض نتيجة الأمر SQL select. قد يخطئ المستخدم في طلبه. سيتم الإبلاغ عن بعض الأخطاء في العرض [erreurs] بفضل عناصر التحقق من الصحة.

Image

سيتم الإبلاغ عن أخطاء أخرى من خلال عرض [erreurs]:

Image

يتم عرض العرض [erreurs]:

رقم
الاسم
النوع
الخصائص
الدور
1
erreursHTML
متغير
 
الرمز HTML ضروري لعرض الأخطاء
3
lnkForm2
LinkButton
EnableViewState=false
زر الارتباط [submit]

الطرق الثلاثة للتطبيق هي ثلاثة حاويات (لوحات) مختلفة داخل نفس الصفحة [main.aspx]. رمز العرض الخاص بها هو التالي:


<%@ Page src="main.aspx.vb" inherits="main" autoeventwireup="false" Language="vb" %>
<HTML>
    <HEAD>
    </HEAD>
    <body>
        <P>Liaison de données avec un DataGrid</P>
        <HR width="100%" SIZE="1">
        <form runat="server">
            <asp:panel id="vueFormulaire" runat="server">
                <P>Commande [select] à exécuter sur la table LISTE</P>
                <P>&nbsp;Exemple : select id, nom, prix from LISTE
                </P>
                <P>
                    <asp:TextBox id="txtSelect" runat="server" Columns="60"></asp:TextBox></P>
                <P>
                    <asp:RequiredFieldValidator id="RequiredFieldValidator1" runat="server" EnableViewState="False" EnableClientScript="False"
                        ErrorMessage="Indiquez la requête [select] à exécuter" ControlToValidate="txtSelect" Display="Dynamic"></asp:RequiredFieldValidator></P>
                <P>Nombre de lignes par page :
                    <asp:TextBox id="txtPages" runat="server" Columns="3"></asp:TextBox></P>
                <P>
                    <asp:RequiredFieldValidator id="RequiredFieldValidator2" runat="server" EnableViewState="False" EnableClientScript="False"
                        ErrorMessage="Indiquez le nombre de lignes par page désirées" ControlToValidate="txtPages" Display="Dynamic"></asp:RequiredFieldValidator>
                    <asp:RangeValidator id="RangeValidator1" runat="server" EnableViewState="False" EnableClientScript="False"
                        ErrorMessage="Vous devez indiquer un nombre entre 1 et 30" ControlToValidate="txtPages" Display="Dynamic"
                        Type="Integer" MinimumValue="1" MaximumValue="30"></asp:RangeValidator></P>
                <P>
                    <asp:Button id="btnExécuter" runat="server" EnableViewState="False" Text="Exécuter"></asp:Button></P>
            </asp:panel>
            <asp:Panel id="vueRésultats" runat="server">
                <P>Résultats de la requête
                    <asp:Label id="lblSelect" runat="server"></asp:Label></P>
                <P>Tri
                    <asp:RadioButton id="rdCroissant" runat="server" Text="croissant" Checked="True" GroupName="rdTri"></asp:RadioButton>
                    <asp:RadioButton id="rdDécroissant" runat="server" Text="décroissant" GroupName="rdTri"></asp:RadioButton></P>
                <P>
                    <asp:DataGrid id="DataGrid1" runat="server" BorderColor="#CC9966" BorderStyle="None" BorderWidth="1px"
                        BackColor="White" CellPadding="4" AllowPaging="True" PageSize="4" AllowSorting="True">
                        <SelectedItemStyle Font-Bold="True" ForeColor="#663399" BackColor="#FFCC66"></SelectedItemStyle>
                        <ItemStyle ForeColor="#330099" BackColor="White"></ItemStyle>
                        <HeaderStyle Font-Bold="True" ForeColor="#FFFFCC" BackColor="#990000"></HeaderStyle>
                        <FooterStyle ForeColor="#330099" BackColor="#FFFFCC"></FooterStyle>
                        <PagerStyle NextPageText="Suivant" PrevPageText="Pr&#233;c&#233;dent" HorizontalAlign="Center"
                            ForeColor="#330099" BackColor="#FFFFCC"></PagerStyle>
                    </asp:DataGrid></P>
                <P>
                    <asp:LinkButton id="lnkRésultats" runat="server" EnableViewState="False">Retour au formulaire</asp:LinkButton></P>
            </asp:Panel>
            <asp:Panel id="vueErreurs" runat="server">
                <P>Les erreurs suivantes se sont produites :</P>
                <% =erreursHTML %>
                <P>
                    <asp:LinkButton id="lnkErreurs" runat="server" EnableViewState="False">Retour vers le formulaire</asp:LinkButton></P>
            </asp:Panel>
        </form>
    </body>
</HTML>

8.6.3. تكوين DataGrid

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


                    <asp:DataGrid id="DataGrid1" runat="server" PageSize="4" AllowPaging="True" ...>
...
                        <PagerStyle NextPageText="Suivant" PrevPageText="Pr&#233;c&#233;dent" ...></PagerStyle>
                    </asp:DataGrid></P>
AllowPaging="true"
يسمح بترقيم الصفحات
PageSize="4"
أربعة أسطر من البيانات لكل صفحة
NextPageText="Suivant"
نص الرابط للانتقال إلى الصفحة التالية من مصدر البيانات
PrevPageText="Précédent"
نص الرابط للانتقال إلى الصفحة السابقة من مصدر البيانات

يمكن إدخال هذه المعلومات مباشرة في سمات العلامة <asp:datagrid>. يمكن أيضًا الاستعانة بـ [WebMatrix]. في نافذة خصائص [DataGrid]، اتبع الرابط [Générateur de propriétés]:

Image

سيظهر المساعد التالي:

Image

اختر الخيار [Pagination]:

Image

نجد أعلاه قيم سمات ترقيم الصفحات لـ [DataGrid] من كود العرض.

بالإضافة إلى ذلك، نسمح بفرز البيانات في أحد أعمدة [DataGrid]. هناك طرق مختلفة للقيام بذلك. إحدى هذه الطرق هي تعريف الخاصية [AllowSorting=true] في نافذة خصائص [DataGrid]. يمكن أيضًا استخدام منشئ الخصائص. بغض النظر عن الطريقة المستخدمة، فإن ذلك يترجم إلى وجود السمة [AllowSorting=true] في العلامة <asp:DataGrid> للمكون:


                    <asp:DataGrid id="DataGrid1" runat="server" ... AllowPaging="True" PageSize="4" AllowSorting="True">

8.6.4. وحدات التحكم

وحدة التحكم [global.asax, global.asax.vb] هي التالية:

[global.asax]

<%@ Application src="global.asax.vb" inherits="Global" %>

[global.asax.vb]


Imports st.istia.univangers.fr
Imports System.Configuration

Public Class Global
    Inherits System.Web.HttpApplication

    Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
        ' يتم إنشاء كائن منتجات
        Dim objProduits As produits
        Try
            objProduits = New produits(ConfigurationSettings.AppSettings("OLEDBStringConnection"))
            ' يتم وضع الكائن في التطبيق
            Application("objProduits") = objProduits
            ' لا توجد أخطاء
            Application("erreur") = False
        Catch ex As Exception
            'حدث خطأ، يتم تسجيله في التطبيق
            Application("erreur") = True
            Application("message") = ex.Message
        End Try
    End Sub
End Class

عند بدء تشغيل التطبيق (Application_Start)، نقوم بإنشاء كائن [produits] ووضعه في التطبيق بحيث يكون متاحًا لجميع طلبات جميع العملاء. إذا حدثت استثناءات أثناء هذا الإنشاء، يتم تسجيلها في التطبيق. لن يتم تنفيذ الإجراء [Application_Start] إلا مرة واحدة. بعد ذلك، لن يتدخل المراقب [global.asax] مرة أخرى. وسيقوم المراقب [main.aspx.vb] بالقيام بالمهمة:


Imports System.Collections
Imports Microsoft.VisualBasic
Imports System.Data
Imports st.istia.univangers.fr
Imports System
Imports System.Xml

Public Class main
    Inherits System.Web.UI.Page

    ' مكونات الصفحة
    Protected WithEvents txtSelect As System.Web.UI.WebControls.TextBox
    Protected WithEvents RequiredFieldValidator1 As System.Web.UI.WebControls.RequiredFieldValidator
    Protected WithEvents txtPages As System.Web.UI.WebControls.TextBox
    Protected WithEvents RequiredFieldValidator2 As System.Web.UI.WebControls.RequiredFieldValidator
    Protected WithEvents RangeValidator1 As System.Web.UI.WebControls.RangeValidator
    Protected WithEvents btnExécuter As System.Web.UI.WebControls.Button
    Protected WithEvents vueFormulaire As System.Web.UI.WebControls.Panel
    Protected WithEvents lblSelect As System.Web.UI.WebControls.Label
    Protected WithEvents DataGrid1 As System.Web.UI.WebControls.DataGrid
    Protected WithEvents lnkRésultats As System.Web.UI.WebControls.LinkButton
    Protected WithEvents vueRésultats As System.Web.UI.WebControls.Panel
    Protected WithEvents lnkErreurs As System.Web.UI.WebControls.LinkButton
    Protected WithEvents vueErreurs As System.Web.UI.WebControls.Panel
    Protected WithEvents rdCroissant As System.Web.UI.WebControls.RadioButton
    Protected WithEvents rdDécroissant As System.Web.UI.WebControls.RadioButton
    ' بيانات الصفحة
    Protected erreursHTML As String

    Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        ' ننظر إذا كان التطبيق به خطأ
        If CType(Application("erreur"), Boolean) Then
            ' لم يتم تهيئة التطبيق بشكل صحيح
            Dim erreurs As New ArrayList
            erreurs.Add("Application momentanément indisponible (" + CType(Application("message"), String) + ")")
            afficheErreurs(erreurs, False)
            Exit Sub
        End If
        'الطلب الأول
        If Not IsPostBack Then
            ' يتم عرض النموذج فارغًا
            afficheFormulaire()
        End If
    End Sub

    Private Sub afficheErreurs(ByVal erreurs As ArrayList, ByVal afficheLien As Boolean)
        ' عرض عرض الأخطاء
        erreursHTML = ""
        For i As Integer = 0 To erreurs.Count - 1
            erreursHTML += "<li>" + erreurs(i).ToString + "</li>" + ControlChars.CrLf
        Next
        lnkErreurs.Visible = afficheLien
        ' يتم عرض طريقة العرض [erreurs]
        vueErreurs.Visible = True
        vueFormulaire.Visible = False
        vueRésultats.Visible = False
    End Sub

    Private Sub afficheFormulaire()
        ' يتم عرض طريقة العرض [formulaire]
        vueFormulaire.Visible = True
        vueErreurs.Visible = False
        vueRésultats.Visible = False
    End Sub

    Private Sub afficheRésultats(ByVal sqlTexte As String, ByVal données As DataView)
        ' يتم تهيئة عناصر التحكم
        lblSelect.Text = sqlTexte
        With DataGrid1
            .DataSource = données
            .PageSize = CType(txtPages.Text, Integer)
            .CurrentPageIndex = 0
            .DataBind()
        End With
        ' يتم عرض العرض [résultats]
        vueRésultats.Visible = True
        vueFormulaire.Visible = False
        vueErreurs.Visible = False
    End Sub

    Private Sub btnExécuter_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnExécuter.Click
        ' هل الصفحة صالحة؟
        If Not Page.IsValid Then
            afficheFormulaire()
            Exit Sub
        End If

        ' تنفيذ طلب العميل SELECT
        Dim données As DataView
        Try
            données = CType(Application("objProduits"), produits).getDataSet(txtSelect.Text.Trim).Tables(0).DefaultView
        Catch ex As Exception
            Dim erreurs As New ArrayList
            erreurs.Add("erreur d'accès à la base de données (" + ex.Message + ")")
            afficheErreurs(erreurs, True)
            Exit Sub
        End Try
        ' كل شيء على ما يرام - يتم عرض النتائج
        afficheRésultats(txtSelect.Text.Trim, données)
        ' نضع البيانات في الجلسة
        Session("données") = données
    End Sub

    Private Sub retourFormulaire(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lnkErreurs.Click, lnkRésultats.Click
        ' مجموعة العروض
        vueErreurs.Visible = False
        vueFormulaire.Visible = True
        vueRésultats.Visible = False
    End Sub

    Private Sub DataGrid1_PageIndexChanged(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridPageChangedEventArgs) Handles DataGrid1.PageIndexChanged
        ' تغيير الصفحة
        With DataGrid1
            .CurrentPageIndex = e.NewPageIndex
            .DataSource = CType(Session("données"), DataView)
            .DataBind()
        End With
    End Sub

    Private Sub DataGrid1_SortCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridSortCommandEventArgs) Handles DataGrid1.SortCommand
        ' يتم فرز عرض البيانات
        Dim données As DataView = CType(Session("données"), DataView)
        données.Sort = e.SortExpression + " " + CType(IIf(rdCroissant.Checked, "asc", "desc"), String)
        ' يتم عرضها
        With DataGrid1
            .CurrentPageIndex = 0
            .DataSource = données
            .DataBind()
        End With
    End Sub
End Class

عند تحميل الصفحة [Page_Load]، نتحقق أولاً مما إذا كان التطبيق قد تم تهيئته بشكل صحيح. إذا لم يكن الأمر كذلك، يتم عرض العرض [erreurs] بدون رابط للعودة إلى النموذج لأن هذا الرابط يكون عديم الفائدة في هذه الحالة. في الواقع، لا يمكن عرض سوى العرض [erreurs] إذا لم تتمكن التطبيق من التهيئة بشكل صحيح. بخلاف ذلك، يتم عرض العرض [formulaire] إذا كنا نتعامل مع الطلب الأول للعميل. أما بالنسبة للباقي، فنترك للقارئ مهمة فهم الكود. سنركز فقط على ثلاثة إجراءات، وهي الإجراء [btnExécuter_Click] الذي يتم تنفيذه عندما يطلب المستخدم تنفيذ الطلب SQL الذي تم إدخاله في العرض [formulaire]، والإجراء [DataGrid1_PageIndexChanged] الذي يتم تنفيذه عندمايستخدم المستخدم الروابط [Suivant] و [Précédent] في [DataGrid] والإجراء [DataGrid1_SortCommand] الذي يتم تنفيذه عندما ينقر المستخدم على عنوان أحد الأعمدة لفرز البيانات وفقًاترتيبها. يتم تحديد اتجاه الترتيب، تصاعدي أو تنازلي، بواسطة زري الاختيار الخاصين بالفرز.

في الإجراء [btnExécuter_Click]، نبدأ بالتالي بالتحقق مما إذا كانت الصفحة صالحة أم لا. عندما يتم تنفيذ الإجراء [btnExécuter_Click]، تكون عمليات التحقق المتعلقة بعناصر التحقق المختلفة من صحة الصفحة قد تمت. لكل عنصر تحقق من الصحة، تم تعيين سمتين:

IsValid
إلى صحيح إذا كانت البيانات التي تم التحقق منها صالحة، وإلى خطأ إذا لم تكن كذلك
ErrorMessage
رسالة الخطأ إذا تبين أن البيانات التي تم التحقق منها غير صالحة

بالنسبة للصفحة نفسها، تم تعيين السمة [IsValid]. يكون قيمته "صحيح" فقط إذا كانت جميع عناصر التحقق من الصحة تحتوي على السمة [IsValid] بقيمة "صحيح". إذا لم يكن الأمر كذلك، يجب عرض العرض [formulaire]. يحتوي هذا العرض على عناصر التحقق من الصحة التي ستعرض السمة [errorMessage]. إذا كانت الصفحة صالحة، يتم استخدام الكائن من النوع [produits] الذي تم إنشاؤه بواسطة [Application_Start] للحصول على [DataSet] المطابق لتنفيذ استعلام SQL select. يتم تحويل هذا إلى كائن [DataView]:


        Dim données As DataView
        Try
            données = CType(Application("objProduits"), produits).getDataSet(txtSelect.Text.Trim).Tables(0).DefaultView
...

كان بإمكاننا العمل ببساطة مع [DataSet] وكتابة:


        Dim données As DataSet
        Try
            données = CType(Application("objProduits"), produits).getDataSet(txtSelect.Text.Trim)
...

كائن [DataSet] هو في الأساس مجموعة من الجداول المرتبطة ببعضها البعض. في تطبيقنا المحدد، لا يحتوي كائن [DataSet] الذي تم الحصول عليه من الفئة [produits] إلا على جدول واحد، وهو الجدول الناتج عن الأمر [select]. يمكن فرز جدول بينما لا يمكن فرز [DataSet]، لكننا مهتمون بفرز البيانات التي تم الحصول عليها. للعمل مع الجدول الناتج عن [select]، كان بإمكاننا كتابة:


        Dim données As DataTable
        Try
            données = CType(Application("objProduits"), produits).getDataSet(txtSelect.Text.Trim).Tables(0)
...

الكائن [DataTable]، على الرغم من أنه يمثل جدول قاعدة بيانات، لا يحتوي على طريقة فرز. ولهذا الغرض، يجب أن يكون هناك عرض للجدول. العرض هو كائن من النوع [DataView]. يمكن الحصول على عروض مختلفة لنفس الجدول باستخدام المرشحات. يحتوي الجدول على عرض افتراضي وهو العرض الذي لم يتم تعريف أي مرشح له. وبالتالي فهو يمثل الجدول بأكمله. يتم الحصول على هذا العرض الافتراضي بواسطة [DataTable.DefaultView]. يمكن فرز عرض باستخدام خاصيته [sort] التي سنعود إليها لاحقًا.

إذا تم الحصول على [DataSet] من الفئة [produits] بنجاح، يتم عرض العرض [résultats]، وإلا يتم عرض العرض [erreurs]. يتم عرض طريقة العرض [résultats] من خلال الإجراء [afficheRésultats] الذي يتم تمرير معلمتين إليه:

  • النص المراد وضعه في التسمية [lblSelect]
  • [DataView] المراد ربطه بـ [DataGrid1]

يوضح لنا هذا المثال المرونة الكبيرة التي يتمتع بها المكون [DataGrid]. فهو قادر على التعرف على بنية المكون [DataView] الذي يتم ربطه به والتكيف معها. وأخيرًا، تقوم الإجراء [btnExécuter_Click] بتخزين المكون [DataView] الذي حصلت عليه للتو في جلسة عمل المستخدم لتتمكن من استخدامه عندما يطلب المستخدم صفحات أخرى من نفس المكون [DataView].

يتم تنفيذ الإجراء [DataGrid1_PageIndexChanged] عندما يستخدم المستخدم الروابط [Suivant] و [Précédent] من [DataGrid]. ويتلقى معلمتين:


    Private Sub DataGrid1_PageIndexChanged(ByVal source As Object, ByVal e As System.Web.UI.WebControls.) Handles DataGrid1.PageIndexChanged
source
الكائن المصدر للحدث - هنا، أحد الروابط [Suivant] أو [Précédent]
e
معلومات عن الحدث. السمة e.NewPageIndex هي رقم الصفحة التي يجب عرضها لتلبية طلب العميل

الرمز الكامل لمدير الحدث هو التالي:


    Private Sub DataGrid1_PageIndexChanged(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridPageChangedEventArgs) Handles DataGrid1.PageIndexChanged
        ' تغيير الصفحة
        With DataGrid1
            .CurrentPageIndex = e.NewPageIndex
            .DataSource = CType(Session("données"), DataView)
            .DataBind()
        End With
    End Sub

يحتوي المكون [DataGrid] على سمة [CurrentPageIndex] التي تشير إلى رقم الصفحة التي يعرضها أو سيعرضها. يتم تعيين القيمة [NewPageIndex] للمعلمة [e] لهذه السمة. ثم يتم ربط [DataGrid] بـ [DataView] الذي تم تخزينه في الجلسة، من خلال الإجراء [btnExécuter_Click].

قد يتساءل المرء عما إذا كان [DataGrid] يحتاج إلى السمة [EnableViewState=true] نظرًا لأن محتواه يتم حسابه بواسطة الكود عند كل تشغيل جديد للصفحة. قد يعتقد المرء أن الإجابة هي لا. ولكن، إذا كان [DataGrid] يحتوي على السمة [EnableViewState=false]، نلاحظ أن الحدث [DataGrid1.PageIndexChanged] لا يتم تشغيله أبدًا. ولهذا السبب تم ترك [EnableViewState=true]. نعلم أن هذا يؤدي إلى وضع محتوى [DataGrid] في الحقل المخفي [__VIEWSTATE] للصفحة. قد يؤدي ذلك إلى زيادة كبيرة في حجم الصفحة إذا كان حجم [DataGrid] كبيرًا. إذا كان هذا الأمر مزعجًا، فيمكنك إدارة ترقيم الصفحات بنفسك دون استخدام الترقيم التلقائي لـ [DataGrid].

يتم تنفيذ الإجراء [DataGrid1_SortCommand] عندما ينقر المستخدم على عنوان أحد الأعمدة المعروضة بواسطة [DataGrid] لطلب فرز البيانات حسب ترتيب هذا العمود. ويتلقى هذا الإجراء معلمتين:


    Private Sub DataGrid1_SortCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridSortCommandEventArgs) Handles DataGrid1.SortCommand
source
الكائن المصدر للحدث - هنا، أحد الروابط [Suivant] أو [Précédent]
e
معلومات عن الحدث. السمة [e.SortExpression] هي اسم العمود الذي تم النقر عليه للفرز

الرمز الكامل لمدير الأحداث هو كما يلي:


    Private Sub DataGrid1_SortCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridSortCommandEventArgs) Handles DataGrid1.SortCommand
        ' فرز عرض البيانات
        Dim données As DataView = CType(Session("données"), DataView)
        données.Sort = e.SortExpression + " " + CType(IIf(rdCroissant.Checked, "asc", "desc"), String)
        ' عرضها
        With DataGrid1
            .CurrentPageIndex = 0
            .DataSource = données
            .DataBind()
        End With
    End Sub

يتم استرداد [DataView] الذي تم عرضه بواسطة [DataGrid] في الجلسة الحالية. وقد تم وضعه هناك بواسطة الإجراء [btnExécuter_Click]. يحتوي المكون [DataView] على خاصية [Sort] التي يتم تعيين تعبير الفرز لها. وتتبع هذه الخاصية صيغة [select ... order by expr1, expr2, ...] حيث يمكن أن يتبع كل [expri] الكلمة الرئيسية [asc] للفرز التصاعدي أو [desc] للفرز التنازلي. التعبير [order by] المستخدم هنا هو [order by colonne asc/desc]. تعطينا الخاصية [e.SortExpression] اسم عمود [DataGrid] الذي تم النقر عليه للفرز. يتم تحديد السلسلة [asc/desc] بناءً على قيم أزرار الاختيار في المجموعة [rdTri]. بمجرد تحديد تعبير الترتيب لـ [DataView]، يتم ربط [DataGrid] به. يتم وضع [DataGrid] في صفحته الأولى.

8.7. المكون DataList وربط البيانات

ننتقل الآن إلى المكون [DataList]. يوفر هذا المكون إمكانيات تنسيق أكثر من [DataGrid] ولكنه أقل مرونة. وبالتالي، لا يمكنه التكيف تلقائيًا مع مصدر البيانات المرتبط به. يجب إجراء هذا التكيف برمجيًا إذا كان مطلوبًا. إذا كانت بنية مصدر البيانات معروفة مسبقًا، فإن هذا المكون يوفر إمكانيات تنسيق قد تجعله مفضلًا على المكون [DataGrid].

8.7.1. التطبيق

لتوضيح استخدام [DataList]، نقوم بإنشاء تطبيق MVC مشابه للتطبيق السابق:

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

8.7.2. فئات الأعمال

الفئة [produits] هي نفسها كما في السابق.

8.7.3. طرق العرض

عندما يقوم المستخدم بإرسال أول طلب إلى التطبيق، يحصل على العرض التالي [résultats1]:

رقم
الاسم
النوع
الخصائص
الدور
1
RadioButton1
RadioButton2
RadioButton
EnableViewState=false
يتيح اختيار نمط من بين نمطين لـ [DataList]
2
btnChanger
زر
EnableViewState=false
زر [submit]
3
DataList1
DataList
EnableViewState=true
حقل عرض قائمة البيانات

إذا اختار المستخدم النمط رقم 2، فسيحصل على العرض التالي [résultats2]:

رقم
الاسم
النوع
الخصائص
الدور
1
DataList2
DataList
EnableViewState=true
حقل عرض قائمة البيانات

تشير طريقة العرض [erreurs] إلى وجود مشكلة في الوصول إلى مصدر البيانات:

رقم
الاسم
النوع
الخصائص
الدور
1
erreursHTML
متغير
 
رمز HTML ضروري لعرض الأخطاء

الطرق الثلاثة للتطبيق هي ثلاثة حاويات (لوحات) مختلفة داخل نفس الصفحة [main.aspx]. رمز العرض الخاص بها هو التالي:


<%@ Page src="main.aspx.vb" inherits="main" autoeventwireup="false" Language="vb" %>
<HTML>
    <HEAD>
    </HEAD>
    <body>
        <P>Liaison de données avec un DataList</P>
        <HR width="100%" SIZE="1">
        <form runat="server" ID="Form1">
            <asp:Panel Runat="server" ID="bandeau">
                <P>Choisissez votre style :
                    <asp:RadioButton id="RadioButton1" runat="server" EnableViewState="False" Text="1" GroupName="rdstyle"
                        Checked="True"></asp:RadioButton>
                    <asp:RadioButton id="RadioButton2" runat="server" EnableViewState="False" Text="2" GroupName="rdstyle"></asp:RadioButton>
                    <asp:Button id="btnChanger" runat="server" EnableViewState="False" Text="Changer"></asp:Button></P>
                <HR width="100%" SIZE="1">
            </asp:Panel>
            <asp:Panel id="vueRésultats1" runat="server">
                <P>
                    <asp:DataList id="DataList1" runat="server" BorderColor="Tan" BorderWidth="1px" BackColor="LightGoldenrodYellow"
                        CellPadding="2" RepeatDirection="Horizontal" RepeatColumns="4" ForeColor="Black">
                        <SelectedItemStyle ForeColor="GhostWhite" BackColor="DarkSlateBlue"></SelectedItemStyle>
                        <HeaderTemplate>
                            Contenu de la table [liste] de la base [produits]
                        </HeaderTemplate>
                        <AlternatingItemStyle BackColor="PaleGoldenrod"></AlternatingItemStyle>
                        <ItemTemplate>
                            nom :
                            <%# Container.DataItem("الاسم") %>
                            <br>
                            prix :
                            <%# databinder.eval(Container.DataItem,"السعر","{0:C}") %>
                            <br>
                        </ItemTemplate>
                        <FooterStyle BackColor="Tan"></FooterStyle>
                        <HeaderStyle Font-Bold="True" BackColor="Tan"></HeaderStyle>
                    </asp:DataList></P>
            </asp:Panel>
            <asp:Panel id="vueRésultats2" runat="server">
                <P>
                    <asp:DataList id="DataList2" runat="server">
                        <HeaderTemplate>
                            Contenu de la table [liste] de la base [produits]
                            <HR width="100%" SIZE="1">
                        </HeaderTemplate>
                        <AlternatingItemStyle BackColor="Teal"></AlternatingItemStyle>
                        <SeparatorStyle BackColor="LightSkyBlue"></SeparatorStyle>
                        <ItemStyle BackColor="#C0C000"></ItemStyle>
                        <ItemTemplate>
                            nom :
                            <%# Container.DataItem("الاسم") %>
                            , prix :
                            <%# databinder.eval(Container.DataItem,"السعر","{0:C}") %>
                            <BR>
                        </ItemTemplate>
                        <SeparatorTemplate>
                            <HR width="100%" SIZE="1">
                        </SeparatorTemplate>
                        <HeaderStyle BackColor="#C0C0FF"></HeaderStyle>
                    </asp:DataList></P>
            </asp:Panel>
            <asp:Panel id="vueErreurs" runat="server">
                <P>Les erreurs suivantes se sont produites :</P>
                <%= erreursHTML %>
            </asp:Panel>
        </form>
    </body>
</HTML>

8.7.4. تكوين المكونات [DataList]

لنلقِ نظرة على السمات المختلفة لمكون [DataList]. هناك العديد من السمات، ونحن نعرض هنا جزءًا صغيرًا منها فقط. يمكن تعريف ما يصل إلى سبعة نماذج عرض داخل مكون [DataList]:

HeaderTemplate
نموذج رأس الصفحة لـ [DataList]
ItemTemplate
نموذج الأسطر التي تعرض عناصر قائمة البيانات المرتبطة. هذا النموذج هو الوحيد الإلزامي.
AlternatingItemTemplate
لتمييز العناصر المتتالية المعروضة بصريًا، يمكن استخدام نموذجين: ItemTemplate للعنصر n، وAlternatingItemTemplate للعنصر n+1
SelectedItemTemplate
نموذج العنصر المحدد في [DataList]
SeparatorTemplate
نموذج الفاصل بين عنصرين في [DataList]
EditItemTemplate
يتيح [DataList] تعديل القيم التي يعرضها. [EditItemTemplate] هو نموذج لعنصر في [DataList] يكون فيه المستخدم في وضع "التحرير"
FooterTemplate
قالب تذييل الصفحة لـ [DataList]

تم إنشاء المكون [DataList1] باستخدام [WebMatrix]. في نافذة خصائصه، تم تحديد الرابط [Mise en forme automatique]:

1234567

فيما سبق، سيقوم مخطط [Couleur 5] بإنشاء [DataList] مع أنماط للنماذج التالية: HeaderTemplate (1)، ItemTemplate (2، 6)، AlternatingTemplate (3، 5)، SelectedItemTemplate (4)، FooterTemplate (7). الرمز الذي تم إنشاؤه هو التالي:


                    <asp:DataList id="DataList1" runat="server" BorderColor="Tan" BorderWidth="1px" BackColor="LightGoldenrodYellow"
                        CellPadding="2" ForeColor="Black">
                        <SelectedItemStyle ForeColor="GhostWhite" BackColor="DarkSlateBlue"></SelectedItemStyle>
                        <AlternatingItemStyle BackColor="PaleGoldenrod"></AlternatingItemStyle>
                        <FooterStyle BackColor="Tan"></FooterStyle>
                        <HeaderStyle Font-Bold="True" BackColor="Tan"></HeaderStyle>
                    </asp:DataList></P>

لم يتم تعريف أي نموذج. يتعين علينا القيام بذلك. نحدد النماذج التالية:

HeaderTemplate

                        <HeaderTemplate>
                            Contenu de la table [liste] de la base [produits]
                        </HeaderTemplate>
ItemTemplate

                        <ItemTemplate>
                            nom :
                            <%# Container.DataItem("الاسم") %>
                            <br>
                            prix :
                            <%# DataBinder.Eval(Container.DataItem,"السعر","{0:C}") %>
                            <br>
                        </ItemTemplate>

تجدر الإشارة إلى أن النموذج [ItemTemplate] هو نموذج عرض عناصر مصدر البيانات المرتبط بـ [DataList]. مصدر البيانات هذا عبارة عن مجموعة من أسطر البيانات، تحتوي كل منها على قيمة واحدة أو أكثر. يتم تمثيل السطر الحالي لمصدر البيانات بواسطة الكائن [Container.DataItem]. يحتوي هذا السطر على أعمدة. [Container.DataItem("col1")] هي قيمة العمود "col1" في السطر الحالي. لإدراج هذه القيمة في كود العرض، نكتب <%# Container.DataItem("col") %>. في بعض الأحيان، نرغب في عرض عنصر من السطر الحالي بتنسيق خاص. هنا، نريد عرض العمود "prix" من السطر الحالي باليورو. نستخدم إذن الدالة [DataBinder.Eval] التي تقبل ثلاثة معلمات:

  • السطر الحالي [Container.DataItem]
  • اسم العمود المراد تنسيقه
  • سلسلة التنسيق بالشكل {0:format} حيث [format] هو أحد التنسيقات التي تقبلها الطريقة [string.format].

وبالتالي، فإن الكود <%# DataBinder.Eval(Container.DataItem,"prix",{0:C}) %> سيعرض العمود [prix] للسطر الحالي في شكل نقدي (التنسيق C=Currency).

وبالتالي، سيكون لدينا [DataList] الذي سيبدو كما يلي:

Image

فيما سبق، تم وضع البيانات بمعدل أربع بيانات في كل سطر. يتم الحصول على ذلك باستخدام السمات التالية لـ [DataList]:

RepeatDirection
أفقي
RepeatColumns
عدد الأعمدة المطلوب

في النهاية، رمز [DataList1] هو الذي تم عرضه في رمز العرض أعلاه. نترك للقارئ مهمة دراسة رمز العرض الخاص بـ [DataList2]. كما هو الحال مع المكون [DataGrid]، يمكن تعيين معظم خصائص [DataList] باستخدام مساعد [WebMatrix]. يتم استخدام الرابط [Générateur de propriétés] في نافذة خصائص [DataList] لهذا الغرض:

8.7.5. عناصر التحكم

وحدة التحكم [global.asax, global.asax.vb] هي التالية:

[global.asax]

<%@ Application src="global.asax.vb" inherits="Global" %>

[global.asax.vb]


Imports System
Imports System.Web
Imports System.Web.SessionState
Imports st.istia.univangers.fr
Imports System.Configuration
Imports System.Data
Imports System.Collections

Public Class Global
    Inherits System.Web.HttpApplication

    Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
        ' يتم إنشاء كائن منتجات
        Try
            Dim données As DataSet = New produits(ConfigurationSettings.AppSettings("OLEDBStringConnection")).getDataSet("select * from LISTE")
            ' يتم وضع الكائن في التطبيق
            Application("données") = données
            ' لا توجد أخطاء
            Application("erreur") = False
        Catch ex As Exception
            'حدث خطأ، يتم تسجيله في التطبيق
            Application("erreur") = True
            Application("message") = ex.Message
        End Try
    End Sub
End Class

عند بدء تشغيل التطبيق (Application_Start)، نقوم بإنشاء [dataset] من فئة الأعمال [produits] ونضعه في التطبيق بحيث يكون متاحًا لجميع طلبات جميع العملاء. إذا حدثت استثناءات أثناء هذا الإنشاء، يتم تسجيلها في التطبيق. لن يتم تنفيذ الإجراء [Application_Start] إلا مرة واحدة. بعد ذلك، لن يتدخل المراقب [global.asax] مرة أخرى. وسيقوم المراقب [main.aspx.vb] بهذه المهمة:


Imports System.Collections
Imports Microsoft.VisualBasic
Imports System.Data
Imports st.istia.univangers.fr
Imports System
Imports System.Xml

Public Class main
    Inherits System.Web.UI.Page

    ' مكونات الصفحة
    Protected WithEvents vueErreurs As System.Web.UI.WebControls.Panel
    Protected WithEvents DataList1 As System.Web.UI.WebControls.DataList
    Protected WithEvents DataList2 As System.Web.UI.WebControls.DataList
    Protected WithEvents vueRésultats1 As System.Web.UI.WebControls.Panel
    Protected WithEvents vueRésultats2 As System.Web.UI.WebControls.Panel
    Protected WithEvents RadioButton1 As System.Web.UI.WebControls.RadioButton
    Protected WithEvents RadioButton2 As System.Web.UI.WebControls.RadioButton
    Protected WithEvents btnChanger As System.Web.UI.WebControls.Button
    Protected WithEvents bandeau As System.Web.UI.WebControls.Panel
    ' بيانات الصفحة
    Protected erreursHTML As String

    Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        ' ننظر إذا كان التطبيق به خطأ
        If CType(Application("erreur"), Boolean) Then
            ' لم يتم تهيئة التطبيق بشكل صحيح
            Dim erreurs As New ArrayList
            erreurs.Add("Application momentanément indisponible (" + CType(Application("message"), String) + ")")
            afficheErreurs(erreurs, False)
            Exit Sub
        End If
        'الطلب الأول
        If Not IsPostBack Then
            ' يتم تهيئة عناصر التحكم
            With DataList1
                .DataSource = CType(Application("données"), DataSet)
                .DataBind()
            End With
            With DataList2
                .DataSource = CType(Application("données"), DataSet)
                .DataBind()
            End With
            ' يتم عرض النموذج فارغًا
            afficheRésultats(True, False)
        End If
    End Sub

    Private Sub afficheErreurs(ByVal erreurs As ArrayList, ByVal afficheLien As Boolean)
        ' عرض عرض الأخطاء
        erreursHTML = ""
        For i As Integer = 0 To erreurs.Count - 1
            erreursHTML += "<li>" + erreurs(i).ToString + "</li>" + ControlChars.CrLf
        Next
        ' يتم عرض طريقة العرض [erreurs]
        vueErreurs.Visible = True
        vueRésultats1.Visible = False
        vueRésultats2.Visible = False
        bandeau.Visible = False
    End Sub

    Private Sub afficheRésultats(ByVal visible1 As Boolean, ByVal visible2 As Boolean)
        ' يتم عرض العرض [résultats]
        vueRésultats1.Visible = visible1
        vueRésultats2.Visible = visible2
        vueErreurs.Visible = False
    End Sub

    Private Sub btnChanger_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnChanger.Click
        ' يتم تغيير النمط
        afficheRésultats(RadioButton1.Checked, RadioButton2.Checked)
    End Sub
End Class

8.8. مكون Repeater وربط البيانات

يسمح المكون [Repeater] بتكرار الرمز HTML لكل عنصر من عناصر قائمة البيانات. لنفترض أننا نريد عرض قائمة بالأخطاء بالشكل التالي:

Image

لقد واجهنا هذه المشكلة من قبل وحللناها بإدراج متغير في كود العرض بالصيغة <ul><% =erreursHTML %></ul>، حيث يتم حساب قيمة erreursHTML بواسطة وحدة التحكم. تحتوي هذه القيمة على رمز HTML، وهو رمز قائمة. ويكمن العيب في أنه إذا أردنا تعديل عرض هذه القائمة HTML، فسنضطر للذهاب إلى جزء وحدة التحكم، وهو ما يتعارض مع مبدأ الفصل بين وحدة التحكم والعرض. يوفر لنا المكون [Repeater] حلاً. كما هو الحال مع [DataList]، يمكننا تعريف القوالب <HeaderTemplate> للعنوان، و<ItemTemplate> للعنصر الحالي في قائمة البيانات، و<FooterTemplate> لنهاية البيانات. هنا، يمكن أن يكون لدينا التعريف التالي للمكون [Repeater]:

            <asp:Repeater id="Repeater1" runat="server" EnableViewState="False">
                <HeaderTemplate>
                    Les erreurs suivantes se sont produites :
                    <ul>
                </HeaderTemplate>
                <ItemTemplate>
                    <li>
                        <%# Container.DataItem %>
                    </li>
                </ItemTemplate>
                <FooterTemplate>
                    </ul>
                </FooterTemplate>
            </asp:Repeater>

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

Image

رمز عرض الصفحة هو التالي:

<html>
<head>
</head>
<body>
    <form runat="server">
        <p>
            Liaison de données avec un composant [Repeater]
        </p>
        <hr />
        <p>
            <asp:Repeater id="Repeater1" runat="server" EnableViewState="False">
                <ItemTemplate>
                    <li>
                        <%# Container.DataItem %>
                    </li>
                </ItemTemplate>
                <HeaderTemplate>
                    Les erreurs suivantes se sont produites :
                    <ul>
                </HeaderTemplate>
                <FooterTemplate>
                    </ul>
                </FooterTemplate>
            </asp:Repeater>
        </p>
    </form>
</body>
</html>

رمز التحقق هو:

<%@ Page Language="VB" %>
<script runat="server">

     ' إجراء يتم تنفيذه عند تحميل الصفحة
    Sub page_Load(sender As Object, e As EventArgs)
        if not IsPostBack then
          ' يتم إنشاء مصدر بيانات
          with Repeater1
              .DataSource=createDataSource
              .DataBind
          end with
        end if
    End Sub

    function createDataSource as ArrayList
      ' يتم إنشاء قائمة صفيف
      dim erreurs as new ArrayList
      dim i as integer
      for i=0 to 5
        erreurs.add("erreur-"+i.ToString)
      next
      return erreurs
    end function

</script>
<html>
...
</html>

عند الطلب الأول للعميل، نربط كائن [ArrayList] بالمكون [Repeater] الذي يُفترض أنه يمثل قائمة بالأخطاء.

8.9. Application

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

  • استخدام مكونات التحقق للتأكد من صحة البيانات
  • استخدام مكونات الخادم المرتبطة بمصادر البيانات لعرض النتائج

8.9.1. هيكل MVC للتطبيق

هيكل التطبيق MVC هو كما يلي:

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

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

الطريقة [formulaire] هي نموذج إدخال المعلومات الذي يسمح بحساب ضريبة المستخدم:

Image

يقوم المستخدم بملء النموذج:

Image

ويستخدم الزر [Envoyer] لطلب حساب ضريبته. ويحصل على العرض التالي [simulations]:

Image

يعود إلى النموذج عبر الرابط أعلاه. ويجده في الحالة التي أدخله فيها. وقد يرتكب أخطاء في الإدخال:

Image

يتم الإبلاغ عن هذه الأخطاء في العرض [formulaire]:

Image

ثم يقوم المستخدم بتصحيح أخطائه. ويمكنه إجراء محاكاة جديدة:

Image

ويحصل عندئذٍ على العرض [simulations] مع محاكاة إضافية:

Image

أخيرًا، إذا لم يكن مصدر البيانات متاحًا، يتم إخطار المستخدم بذلك في العرض [erreurs]:

Image

8.9.2.1. رمز العرض

تجدر الإشارة إلى أن الصفحة [main.aspx] تجمع جميع العروض. وهي عبارة عن نموذج واحد يحتوي على ثلاثة حاويات:

  • [panelform] لعرض [formulaire]
  • [panelerreurs] للعرض [erreurs]
  • [panelsimulations] للعرض [simulations]

سنقوم الآن بتفصيل مكونات هذه الحاويات الثلاثة. الحاوية [panelform] لها التمثيل البصري التالي:

رقم
الاسم
النوع
الخصائص
الدور
0
panelform
لوحة
 
عرض النموذج
1
rdOui
rdNon
RadioButton
GroupName=rdmarie
أزرار الاختيار
2
cvMarie
CustomValidator
ErrorMessage=لم تحدد حالتك الاجتماعية
EnableClientScript=false
تحقق من أن برنامج العميل قد أرسل الحالة الاجتماعية المتوقعة
3
txtEnfants
TextBox
 
عدد الأطفال
4
rfvEnfants
RequiredFieldValidator
ErrorMessage=يرجى تحديد عدد الأطفال
ControlToValidate=txtEnfants
تحقق من أن الحقل [txtEnfants] ليس فارغًا
5
rvEnfants
RangeValidator
ErrorMessage=أدخل رقمًا بين 1 و 30
ControlToValidate=txtEnfants
تحقق من أن الحقل [txtEnfants] يقع في النطاق [1,30]
6
txtSalaire
TextBox
EnableViewState=true
الراتب السنوي
7
rfvSalaire
RequiredFieldValidator
ControlToValidate=txtSalaire
ErrorMessage=أدخل مبلغ راتبك
تحقق من أن الحقل [txtSalaire] ليس فارغًا
8
revSalaire
RegularExpressionValidator
ControlToValidate=txtSalaire
ErrorMessage=الراتب غير صالح
التعبير العادي=\s*\d+\s*
يتحقق من أن الحقل [txtSalaire] عبارة عن سلسلة من الأرقام
9
btnCalculer
Button
CausesValidation=true
زر [submit] في النموذج - يبدأ حساب الضريبة
10
btnEffacer
زر
CausesValidation=false
زر [submit] في النموذج - يفرغ النموذج

قد يثير الدهشة وجود عنصر التحكم [cvMarié] المكلف بالتحقق من أن المستخدم قد حدد بالفعل أحد زري الاختيار. ولا يمكن أن يكون الأمر خلاف ذلك إذا كان يستخدم النموذج الذي أرسله الخادم. وبما أنه لا يمكن التأكد من ذلك، فإننا مضطرون للتحقق من جميع المعلمات المرسلة. يجب أيضًا ملاحظة السمة [CausesValidation=false] للزر [btnEffacer]. في الواقع، عندما يستخدم المستخدم هذا الزر، لا يجب التحقق من البيانات المرسلة لأنها سيتم تجاهلها.

تتمتع جميع مكونات الحاوية بالخاصية [EnableViewState=false] باستثناء [panelForm, txtEnfants, txtSalaire, rdOui, rdNon]. تتمتع الحاوية [panelerreurs] بالتمثيل المرئي التالي:

رقم
الاسم
النوع
الخصائص
الدور
0
panelerreurs
لوحة
EnableViewState=false
عرض الأخطاء
1
rptErreurs
المكرر
EnableViewState=false
يعرض قائمة بالأخطاء

تم تعريف المكون [rptErreurs] على النحو التالي:


                <asp:Repeater id="rptErreurs" runat="server" EnableViewState="False">
                    <ItemTemplate>
                        <li>
                            <%# Container.Dataitem%>
                        </li>
                    </ItemTemplate>
                    <HeaderTemplate>
                        Les erreurs suivantes se sont produites
                        <ul>
                    </HeaderTemplate>
                    <FooterTemplate>
                        </ul>
                    </FooterTemplate>
                </asp:Repeater>

ستكون قائمة البيانات المرتبطة بالمكون [rptErreurs] كائنًا [ArrayList] يحتوي على قائمة برسائل الأخطاء. وبالتالي، فإن <%# Container.Dataitem%> أعلاه تشير إلى رسالة الخطأ الحالية. يتم تمثيل الحاوية [panelsimulations] بصريًا على النحو التالي:

رقم
الاسم
النوع
الخصائص
الدور
0
panelsimulations
لوحة
EnableViewState=false
عرض المحاكاة
2
lnkForm2
LinkButton
EnableViewState=false
رابط إلى النموذج
1
dgSimulations
DataGrid
EnableViewState=false
المسؤول عن عرض المحاكاة الموضوعة في كائن [DataTable]

يتم تكوين المكون [dgSimulations] بالطريقة التالية تحت [Webmatrix]. في نافذة خصائص [dgSimulations]، نختار الرابط [Mise en forme automatique] ونختار المخطط [Couleur 3]:

ثم، في نافذة خصائص [dlgSimulations] نفسها، نختار الرابط [Générateur de propriétés]. نطلب عرض رأس الأعمدة:

Image

نختار الخيار [Colonnes] لتعريف الأعمدة الأربعة لـ [DataGrid]:

Image

نقوم بإلغاء تحديد العبارة [Créer des colonnes automatiquement ...]. سنقوم بأنفسنا بتحديد الأعمدة الأربعة لـ [DataGrid]. سيتم ربط هذا بـ [DataTable] الذي يحتوي على أربعة أعمدة تسمى على التوالي "متزوج"، "أطفال"، "راتب"، "ضريبة". لإنشاء عمود في [DataGrid]، نختار الخيار [Colonnes connexe] ونستخدم زر الإنشاء كما هو موضح أعلاه. يتم إنشاء عمود ويمكننا تحديد خصائصه. سيحتوي العمود الأول من جدول المحاكاة على العمود "متزوج" من الكائن [DataTable] الذي سيتم ربطه بـ [DataGrid]. يتم تعريف هذا العمود الأول من [DataGrid] على النحو التالي في المساعد:

Image

Texte de l'en-tête
عنوان العمود، هنا "متزوج"
Champ de données
اسم عمود مصدر البيانات الذي سيتم عرضه بواسطة هذا العمود في [DataGrid]. هنا، هو عمود "متزوج" في [DataTable].

يتم تعريف العمود الثاني على النحو التالي:

Texte de l'en-tête
الأطفال
Champ de données
الأطفال

يتم تعريف العمود الثالث على النحو التالي:

Texte de l'en-tête
الراتب السنوي
Champ de données
الراتب
Expression de mise en forme
{0:C} - عرض العملة للحصول على رمز اليورو

يتم تعريف العمود الرابع على النحو التالي:

Texte de l'en-tête
مبلغ الضريبة
Champ de données
الضريبة
Expression de mise en forme
{0:C} - عرض نقدي للحصول على علامة اليورو

نضع رمز العرض ورمز التحكم في ملفين منفصلين. سيكون الأول في [main.aspx] والثاني في [main.aspx.vb]. رمز [main.aspx] هو التالي:


<%@ page src="main.aspx.vb" inherits="main" AutoEventWireUp="false" %>
<HTML>
    <HEAD>
        <title>Calculer votre impôt</title>
    </HEAD>
    <body>
        <P>Calculer votre impôt</P>
        <HR width="100%" SIZE="1">
        <FORM id="Form1" runat="server">

            <asp:panel id="panelform" Runat="server">
                <TABLE id="Table1" cellSpacing="1" cellPadding="1" border="0">
                    <TR>
                        <TD height="19">Etes-vous marié(e)</TD>
                        <TD height="19">
                            <asp:RadioButton id="rdOui" runat="server" GroupName="rdMarie"></asp:RadioButton>Oui
                            <asp:RadioButton id="rdNon" runat="server" GroupName="rdMarie" Checked="True"></asp:RadioButton>Non
                            <asp:CustomValidator id="cvMarie" runat="server" ErrorMessage="Vous n'avez pas indiqué votre état marital"
                                Display="Dynamic" EnableClientScript="False" Visible="False" EnableViewState="False"></asp:CustomValidator></TD>
                    </TR>
                    <TR>
                        <TD>Nombre d'enfants</TD>
                        <TD>
                            <asp:TextBox id="txtEnfants" runat="server" Columns="3" MaxLength="3"></asp:TextBox>
                            <asp:RequiredFieldValidator id="rfvEnfants" runat="server" ErrorMessage="Indiquez le nombre d'enfants" Display="Dynamic"
                                ControlToValidate="txtEnfants" EnableViewState="False"></asp:RequiredFieldValidator>
                            <asp:RangeValidator id="rvEnfants" runat="server" ErrorMessage="Tapez un nombre entre 1 et 30" Display="Dynamic"
                                ControlToValidate="txtEnfants" Type="Integer" MaximumValue="30" MinimumValue="0" EnableViewState="False"></asp:RangeValidator></TD>
                    </TR>
                    <TR>
                        <TD>Salaire annuel (euro)</TD>
                        <TD>
                            <asp:TextBox id="txtSalaire" runat="server" Columns="10" MaxLength="10"></asp:TextBox>
                            <asp:RequiredFieldValidator id="rfvSalaire" runat="server" ErrorMessage="Indiquez le montant de votre salaire"
                                Display="Dynamic" ControlToValidate="txtSalaire" EnableViewState="False"></asp:RequiredFieldValidator>
                            <asp:RegularExpressionValidator id="revSalaire" runat="server" ErrorMessage="Salaire invalide" Display="Dynamic"
                                ControlToValidate="txtSalaire" ValidationExpression="\s*\d+\s*" EnableViewState="False"></asp:RegularExpressionValidator></TD>
                    </TR>
                </TABLE>
                <P>
                    <asp:Button id="btnCalculer" runat="server" Text="Calculer"></asp:Button>
                    <asp:Button id="btnEffacer" runat="server" Text="Effacer" CausesValidation="False"></asp:Button></P>
            </asp:panel>

             <asp:panel id="panelerreurs" runat="server" EnableViewState="False">
                <asp:Repeater id="rptErreurs" runat="server" EnableViewState="False">
                    <ItemTemplate>
                        <li>
                            <%# Container.Dataitem%>
                        </li>
                    </ItemTemplate>
                    <HeaderTemplate>
                        Les erreurs suivantes se sont produites
                        <ul>
                    </HeaderTemplate>
                    <FooterTemplate>
                        </ul>
                    </FooterTemplate>
                </asp:Repeater>
            </asp:panel>
 
          <asp:panel id="panelsimulations" runat="server" EnableViewState="False">
                <P>
                    <asp:DataGrid id="dgSimulations" runat="server" BorderColor="#DEBA84" BorderStyle="None" CellSpacing="2"
                        BorderWidth="1px" BackColor="#DEBA84" CellPadding="3" AutoGenerateColumns="False" EnableViewState="False">
                        <SelectedItemStyle Font-Bold="True" ForeColor="White" BackColor="#738A9C"></SelectedItemStyle>
                        <ItemStyle ForeColor="#8C4510" BackColor="#FFF7E7"></ItemStyle>
                        <HeaderStyle Font-Bold="True" ForeColor="White" BackColor="#A55129"></HeaderStyle>
                        <FooterStyle ForeColor="#8C4510" BackColor="#F7DFB5"></FooterStyle>
                        <Columns>
                            <asp:BoundColumn DataField="mari&#233;" HeaderText="Mari&#233;"></asp:BoundColumn>
                            <asp:BoundColumn DataField="enfants" HeaderText="Enfants"></asp:BoundColumn>
                            <asp:BoundColumn DataField="salaire" HeaderText="Salaire annuel" DataFormatString="{0:C}"></asp:BoundColumn>
                            <asp:BoundColumn DataField="imp&#244;t" HeaderText="Montant de l'imp&#244;t" DataFormatString="{0:C}"></asp:BoundColumn>
                        </Columns>
                        <PagerStyle HorizontalAlign="Center" ForeColor="#8C4510" Mode="NumericPages"></PagerStyle>
                    </asp:DataGrid></P>
                <P>
                    <asp:LinkButton id="lnkForm2" runat="server" EnableViewState="False">Retour au formulaire</asp:LinkButton></P>
            </asp:panel>

      </FORM>
    </body>
</HTML>

8.9.3. رمز التحكم في التطبيق

يوجد رمز التحكم في التطبيق موزعًا في الملفين [global.asax.vb] و [main.aspx.vb]. يتم تعريف الملف [global.asax] على النحو التالي:

<%@ Application src="Global.asax.vb" Inherits="Global" %>

الملف [global.asax.vb] هو التالي:


Imports System
Imports System.Web
Imports System.Web.SessionState
Imports st.istia.univangers.fr
Imports System.Configuration
Imports System.Collections
Imports System.Data

Public Class Global
    Inherits System.Web.HttpApplication

    Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
        ' يتم إنشاء كائن مصفوفة
        Dim objImpot As impot
        Try
            objImpot = New impot(New impotsOLEDB(ConfigurationSettings.AppSettings("chaineConnexion")))
            ' يتم وضع الكائن في التطبيق
            Application("objImpot") = objImpot
            ' لا توجد أخطاء
            Application("erreur") = False
        Catch ex As Exception
            'حدث خطأ، يتم تسجيله في التطبيق
            Application("erreur") = True
            Application("message") = ex.Message
        End Try
    End Sub

    Sub Session_Start(ByVal sender As Object, ByVal e As EventArgs)
        ' بداية الجلسة - يتم إنشاء قائمة محاكاة فارغة
        Dim simulations As New DataTable("simulations")
        simulations.Columns.Add("marié", Type.GetType("System.String"))
        simulations.Columns.Add("enfants", Type.GetType("System.String"))
        simulations.Columns.Add("salaire", Type.GetType("System.Int32"))
        simulations.Columns.Add("impôt", Type.GetType("System.Int32"))
        Session.Item("simulations") = simulations
    End Sub
End Class

عند بدء تشغيل التطبيق (الطلب الأول المقدم إلى التطبيق)، يتم تنفيذ الإجراء [Application_Start]. ويهدف هذا الإجراء إلى إنشاء كائن من النوع [impot] يستمد بياناته من مصدر OLEDB. يُطلب من القارئ قراءة الفصل 5 حيث تم تعريف هذه الفئة، إذا كان قد نسيها. قد يفشل إنشاء الكائن [impot] إذا لم يكن مصدر البيانات متاحًا. في هذه الحالة، يتم تخزين الخطأ في التطبيق حتى تعرف جميع الاستعلامات اللاحقة أن هذا الكائن لم يتم تهيئته بشكل صحيح. إذا سارت عملية الإنشاء بشكل جيد، يتم أيضًا تخزين الكائن [impot] الذي تم إنشاؤه في التطبيق. وسيتم استخدامه من قبل جميع استعلامات حساب الضريبة. عندما يقوم العميل بطلبه الأول، يتم إنشاء جلسة له بواسطة الإجراء [Application_Start]. تهدف هذه الجلسة إلى تخزين مختلف عمليات محاكاة حساب الضريبة التي سيقوم بها. سيتم تخزين هذه العمليات في كائن [DataTable] مرتبط بمفتاح الجلسة "simulations". عند بدء الجلسة، يتم ربط هذا المفتاح بكائن [DataTable] فارغ ولكن تم تحديد هيكله. يتم وضع المعلومات اللازمة للتطبيق في ملف التكوين الخاص به [wenConfig]:


<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <appSettings>
        <add key="chaineConnexion" value="Provider=Microsoft.Jet.OLEDB.4.0; Ole DB Services=-4; Data Source=D:\data\devel\aspnet\poly\webforms2\vs\impots6\impots.mdb" />
    </appSettings>
</configuration>

يشير المفتاح [chaineConnexion] إلى سلسلة الاتصال بالمصدر OLEDB. ويوجد الجزء الآخر من رمز التحكم في [main.aspx.vb]:


Imports System.Collections
Imports Microsoft.VisualBasic
Imports st.istia.univangers.fr
Imports System
Imports System.Web.UI.Control
Imports System.Data

Public Class main
    Inherits System.Web.UI.Page

    Protected WithEvents rdOui As System.Web.UI.WebControls.RadioButton
    Protected WithEvents rdNon As System.Web.UI.WebControls.RadioButton
    Protected WithEvents txtEnfants As System.Web.UI.WebControls.TextBox
    Protected WithEvents txtSalaire As System.Web.UI.WebControls.TextBox
    Protected WithEvents btnCalculer As System.Web.UI.WebControls.Button
    Protected WithEvents btnEffacer As System.Web.UI.WebControls.Button
    Protected WithEvents panelform As System.Web.UI.WebControls.Panel
    Protected WithEvents lnkForm2 As System.Web.UI.WebControls.LinkButton
    Protected WithEvents panelerreurs As System.Web.UI.WebControls.Panel
    Protected WithEvents rfvEnfants As System.Web.UI.WebControls.RequiredFieldValidator
    Protected WithEvents rvEnfants As System.Web.UI.WebControls.RangeValidator
    Protected WithEvents rfvSalaire As System.Web.UI.WebControls.RequiredFieldValidator
    Protected WithEvents rptErreurs As System.Web.UI.WebControls.Repeater
    Protected WithEvents dgSimulations As System.Web.UI.WebControls.DataGrid
    Protected WithEvents revSalaire As System.Web.UI.WebControls.RegularExpressionValidator
    Protected WithEvents cvMarie As System.Web.UI.WebControls.CustomValidator
    Protected WithEvents panelsimulations As System.Web.UI.WebControls.Panel

    ' المتغيرات المحلية

    Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
...
    End Sub

    Private Sub afficheErreurs(ByRef erreurs As ArrayList)
...
    End Sub

    Private Sub afficheFormulaire()
...
    End Sub

    Private Sub afficheSimulations(ByRef simulations As DataTable, ByRef lien As String)
...
    End Sub

    Private Sub btnCalculer_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCalculer.Click
....
    End Sub

    Private Sub lnkForm1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
...
    End Sub

    Private Sub lnkForm2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lnkForm2.Click
...
    End Sub

    Private Sub btnEffacer_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnEffacer.Click
...
    End Sub

    Private Sub razForm()
...
    End Sub

    Private Sub cvMarie_ServerValidate(ByVal source As System.Object, ByVal args As System.Web.UI.WebControls.ServerValidateEventArgs)
...
    End Sub
End Class

الحدث الأول الذي يعالجه الرمز هو [Page_Load]:


    Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        ' أولاً، ننظر إلى حالة التطبيق
        If CType(Application("erreur"), Boolean) Then
            ' لم يتمكن التطبيق من التهيئة
            ' يتم عرض شاشة الأخطاء
            Dim erreurs As New ArrayList
            erreurs.Add("Application momentanément indisponible (" + CType(Application("message"), String) + ")")
            afficheErreurs(erreurs)
            Exit Sub
        End If
        ' لا توجد أخطاء - عند الطلب الأول، نعرض النموذج
        If Not IsPostBack Then afficheFormulaire()
    End Sub

قبل البدء في معالجة الطلب، نتأكد من أن التطبيق قد تم تهيئته بشكل صحيح. إذا لم يكن الأمر كذلك، يتم عرض العرض [erreurs] باستخدام الإجراء [afficheErreurs]. إذا كان هذا هو الطلب الأول (IsPostBack=false)، فإننا نعرض طريقة العرض [formulaire] باستخدام [afficheFormulaire].

الإجراء الذي يعرض طريقة العرض [erreurs] هو التالي:


    Private Sub afficheErreurs(ByRef erreurs As ArrayList)
        ' يتم إنشاء قائمة الأخطاء
        With rptErreurs
            .DataSource = erreurs
            .DataBind()
        End With
        ' عرض الحاويات 
        panelerreurs.Visible = True
        panelform.Visible = False
        panelsimulations.Visible = False
        Exit Sub
    End Sub

تتلقى الإجراء كمعلمة قائمة برسائل الأخطاء في [erreurs] من النوع [ArrayList]. نكتفي بربط مصدر البيانات هذا بالمكون [rptErreurs] المسؤول عن عرضه.

الإجراء الذي يعرض العرض [formulaire] هو التالي:


    Private Sub afficheFormulaire()
        ' عرض النموذج
        panelform.Visible = True
        ' يتم إخفاء الحاويات الأخرى
        panelerreurs.Visible = False
        panelsimulations.Visible = False
    End Sub

يقتصر هذا الإجراء على إظهار الحاوية [panelform]. يتم عرض المكونات بقيمتها المرسلة أو السابقة (VIEWSTATE).

عندما ينقر المستخدم على الزر [Calculer] في العرض [formulaire]، يتم إجراء انتقال من POST إلى [main.aspx]. يتم تنفيذ الإجراء [Page_Load] ثم الإجراء [btnCalculer_Click]:


    Private Sub btnCalculer_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCalculer.Click
        ' يتم التحقق من صحة البيانات المدخلة، وفي حالة وجود أخطاء، يتم الإبلاغ عنها
        If Not Page.IsValid Then
            afficheFormulaire()
            Exit Sub
        End If
        ' لا توجد أخطاء - يتم حساب الضريبة
        Dim impot As Long = CType(Application("objImpot"), impot).calculer( _
        rdOui.Checked, CType(txtEnfants.Text, Integer), CType(txtSalaire.Text, Long))
        ' يتم إضافة النتيجة إلى المحاكاة الحالية
        Dim simulations As DataTable = CType(Session.Item("simulations"), DataTable)
        Dim simulation As DataRow = simulations.NewRow
        simulation("marié") = CType(IIf(rdOui.Checked, "oui", "non"), String)
        simulation("enfants") = txtEnfants.Text.Trim
        simulation("salaire") = CType(txtSalaire.Text.Trim, Long)
        simulation("impôt") = impot
        simulations.Rows.Add(simulation)
        ' يتم وضع المحاكاة في الجلسة
        Session.Item("simulations") = simulations
        ' يتم عرض صفحة المحاكاة
        afficheSimulations(simulations, "Retour au formulaire")
    End Sub

تبدأ العملية بالتحقق من صحة الصفحة. تجدر الإشارة هنا إلى أنه عند تنفيذ العملية [btnCalculer_Click]، تكون عمليات التحقق من الصحة قد أتمت مهمتها ويتم تعيين السمة [IsValid] للصفحة. إذا كانت الصفحة غير صالحة، يتم عرض طريقة العرض [formulaire] مرة أخرى وتنتهي العملية. ستعرض مكونات التحقق من صحة العرض [formulaire] التي تتراوح سمة [IsValid] الخاصة بها بين [false] و [ErrorMessage] السمة [ErrorMessage] الخاصة بها. إذا كانت الصفحة صالحة، يتم حساب مبلغ الضريبة باستخدام الكائن من النوع [impot] الذي تم تخزينه في التطبيق عند بدء تشغيله. تضاف هذه المحاكاة الجديدة إلى قائمة المحاكاة التي تم إجراؤها بالفعل وتخزينها في الجلسة.

وأخيرًا، يتم عرض العرض [simulations] بواسطة الإجراء [afficheSimulations] التالي:


    Private Sub afficheSimulations(ByRef simulations As DataTable, ByRef lien As String)
        ' ربط شبكة البيانات بمصدر المحاكاة
        With dgSimulations
            .DataSource = simulations
            .DataBind()
        End With
        ' الربط
        lnkForm2.Text = lien
        ' طرق العرض
        panelsimulations.Visible = True
        panelerreurs.Visible = False
        panelform.Visible = False
    End Sub

تحتوي الإجراء على معلمتين:

  • قائمة محاكاة في [simulations] من النوع [DataTable]
  • نص رابط في [lien]

مصدر البيانات [simulations] مرتبط بالمكون المسؤول عن عرضه. أما نص الرابط فيتم وضعه في الخاصية [Text] للكائن [LinkButton] في العرض.

عندما ينقر المستخدم على الزر [Effacer] في العرض [formulaire]، يتم تنفيذ الإجراء [btnEffacer_click] (دائمًا بعد [Page_Load]):


    Private Sub btnEffacer_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnEffacer.Click
        ' عرض النموذج الفارغ
        razForm()
        afficheFormulaire()
    End Sub

    Private Sub razForm()
        ' إفراغ النموذج
        rdOui.Checked = False
        rdNon.Checked = True
        txtEnfants.Text = ""
        txtSalaire.Text = ""
    End Sub

الرمز أعلاه بسيط بما يكفي بحيث لا يحتاج إلى تعليق. يبقى لنا التعامل مع النقر على روابط العرض [erreurs] و [simulations]:


    Private Sub lnkForm1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lnkForm1.Click
        ' يعرض النموذج
        afficheFormulaire()
    End Sub

    Private Sub lnkForm2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lnkForm2.Click
        ' يعرض النموذج
        afficheFormulaire()
    End Sub

تقتصر الإجراءان على عرض العرض [formulaire]. ونعلم أن حقول هذه العرض ستحصل على قيمة هي إما القيمة التي تم إرسالها لها أو قيمتها السابقة. وكما هو الحال هنا، لا يرسل POST الخاص بالعميل أي قيمة لحقول النموذج، لذا ستستعيد هذه الحقول قيمتها السابقة. وبالتالي، يتم عرض النموذج بالقيم التي أدخلها المستخدم.

8.9.4. الاختبارات

يتم وضع جميع الملفات اللازمة للتطبيق في مجلد <application-path>:
يحتوي المجلد [bin] على ملف dll الذي يحتوي على الفئات [impot] و [impotsData] و [impotsOLEDB] اللازمة للتطبيق:

يمكن للقارئ، إذا رغب في ذلك، إعادة قراءة الفصل 5 الذي يشرح كيفية إنشاء الملف [impot.dll] أعلاه. بعد ذلك، يتم تشغيل خادم Cassini باستخدام المعلمات (<application-path>,/impots6). يتم طلب عنوان URL [http://impots6/main.aspx] باستخدام متصفح:

Image

إذا قمنا بتغيير اسم الملف ACCESS [impots.mdb] إلى [impots1.mdb]، فسنحصل على الصفحة التالية:

Image

8.9.5. الخلاصة

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