Skip to content

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

8.1. مقدمة

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

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

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

8.2.1. مقدمة

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

Image

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

Image

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

المكون
الدور
RequiredFieldValidator
يتحقق من أن الحقل ليس فارغًا
CompareValidator
يقارن بين قيمتين
RangeValidator
يتحقق من أن القيمة تقع بين حدين
مُثبت التعبير العادي
يتحقق من أن الحقل يطابق تعبيرًا عاديًا
CustomValidator
يسمح للمطور بتحديد قواعد التحقق الخاصة به — يمكن لهذا المكون أن يحل محل جميع المكونات الأخرى
ValidationSummary
يسمح لك بجمع رسائل الخطأ التي تم إنشاؤها بواسطة عناصر التحكم السابقة في مكان واحد على الصفحة

سنقدم الآن كل مكون من هذه المكونات.

8.2.2. RequiredFieldValidator

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

رقم
الاسم
النوع
الخصائص
الدور
1
txtName
مربع نص
EnableViewState=true
حقل الإدخال
2
مُثبت صحة الحقل المطلوب 1
RequiredFieldValidator
EnableViewState=false
تمكين البرنامج النصي للعميل=صحيح
ErrorMessage=الحقل [name] مطلوب
مكون التحقق من الصحة
3
btnSubmit
زر
EnableViewState=false
أسباب التحقق من الصحة=صحيح
زر [submit]

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

ControlToValidate
الحقل الذي يجب التحقق من صحة قيمته بواسطة المكون. ماذا تعني قيمة المكون؟ إنها قيمة السمة [value] لعلامة HTML المقابلة. بالنسبة لـ [TextBox]، هذا هو محتوى حقل الإدخال؛ بالنسبة لـ [DropDownList]، هذه هي قيمة العنصر المحدد.
EnableClientScript
Boolean - عندما تكون القيمة true، تشير إلى أنه يجب أيضًا التحقق من صحة محتوى الحقل السابق على جانب العميل. في هذه الحالة، لن يتم إرسال النموذج بواسطة المتصفح إلا إذا كان النموذج خاليًا من الأخطاء. ومع ذلك، يتم إجراء فحوصات التحقق من الصحة أيضًا على الخادم في حالة عدم كون العميل متصفحًا، على سبيل المثال.
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] مرتبط بوظيفة JavaScript عبر سمة [onclick] الخاصة به. يمكننا أيضًا أن نرى أن الصفحة لا تحتوي على أي كود [JavaScript]. سيفشل الاختبار [typeof(Page_ClientValidate) == 'function']، ولن يتم استدعاء وظيفة JavaScript. سيتم بعد ذلك إرسال النموذج إلى الخادم، الذي سيقوم بإجراء عمليات التحقق من الصحة. دعونا نستخدم زر [Submit] دون إدخال اسم. استجابة الخادم هي كما يلي:

Image

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

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

  • أوقف خادم الويب
  • انقر على زر [إرسال] دون ملء حقل [الاسم]

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

Image

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

Image

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

ما الذي يمكننا تعلمه من هذا المثال؟

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

8.2.3. CompareValidator

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

Image

رقم
الاسم
النوع
الخصائص
الدور
1
cmbChoix1
قائمة منسدلة
EnableViewState=true
قائمة منسدلة
2
cmbChoix2
قائمة منسدلة
EnableViewState=true
قائمة منسدلة
3
CompareValidator1
مُقارن الصحة
EnableViewState=false
تمكين البرنامج النصي للعميل=صحيح
رسالة الخطأ=اختيار غير صالح 1
القيمة المراد مقارنتها=؟
المشغل=غير متساوي
النوع=سلسلة
مكون التحقق من الصحة
4
CompareValidator2
مُقارن التحقق من الصحة
تمكين حالة العرض=كاذب
تمكين البرنامج النصي للعميل=صحيح
رسالة الخطأ=اختيار غير صحيح 2
عنصر التحكم المراد مقارنته=؟
المشغل=غير متساوي
النوع=سلسلة
مكون التحقق من الصحة
5
btnSend
زر
EnableViewState=false
أسباب التحقق من الصحة=صحيح
زر [submit]

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

ControlToValidate
الحقل الذي يجب التحقق من صحة محتواه بواسطة المكون
EnableClientScript
Boolean - عندما تكون القيمة true، تشير إلى أنه يجب أيضًا التحقق من صحة محتوى الحقل السابق على جانب العميل
رسالة الخطأ
رسالة الخطأ التي يجب أن يعرضها المكون في حالة اكتشاف خطأ
القيمة_المقارنة
القيمة التي يجب مقارنة قيمة حقل [ControlToValidate] بها
ControlToCompare
المكون الذي يجب مقارنة قيمة حقل [ControlToValidate] بقيمته
المُشغِّل
عامل المقارنة بين القيمتين
النوع
نوع القيم المراد مقارنتها

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

<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، يتم تضمين كود JavaScript فيها مما يؤدي إلى سلوك مختلف قليلاً. يتم الإبلاغ عن أي أخطاء بمجرد قيام المستخدم بتغيير قيمة في إحدى القوائم المنسدلة. وهذا يحسن تجربة المستخدم.

8.2.4. CustomValidator ، RangeValidator

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

Image

رقم
الاسم
النوع
الخصائص
الدور
1
cmbDiplomas
قائمة منسدلة
EnableViewState=true
قائمة منسدلة
2
مقارنة المدقق 2
CompareValidator
EnableViewState=false
تمكين البرنامج النصي للعميل=صحيح
رسالة الخطأ=شهادة غير صالحة
المشغل=غير متساوي
القيمة المراد مقارنتها=؟
النوع=سلسلة
مكون التحقق من صحة عنصر التحكم [1]
3
txtOtherDegree
مربع نص
EnableViewState=false
حقل الإدخال
4
المحقق المخصص 1
المحقق المخصص
EnableViewState=false
تمكين البرنامج النصي للعميل=صحيح
ErrorMessage=مواصفات الدرجة غير صالحة
ClientValidationFunction=chkOtherDegree
مكون التحقق من صحة الحقل [3]
5
txtAnDiploma
مربع نص
EnableViewState=false
حقل الإدخال
6
RangeValidator1
RangeValidator
EnableViewState=false
EnableClientScript=true
ErrorMessage=يجب أن تكون سنة التخرج ضمن النطاق [1990,2004]
القيمة الدنيا=1990
القيمة_القصوى=2004
النوع=عدد صحيح
عنصر التحقق من صحة الحقل=txtAnDiplome
مكون التحقق من صحة الحقل [5]
7
RequiredFieldValidator2
RequiredFieldValidator
EnableViewState=false
تمكين البرنامج النصي للعميل=صحيح
ErrorMessage=سنة التخرج مطلوبة
عنصر التحقق من صحة الحقل=txtAnDiplome
مكون التحقق من صحة الحقل [5]
8
btnSubmit
زر
EnableViewState=false
أسباب التحقق من الصحة=صحيح
زر [submit]

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

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

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

ControlToValidate
الحقل الذي يجب التحقق من صحة قيمته بواسطة المكون
EnableClientScript
Boolean - عندما تكون القيمة "true"، تشير إلى أنه يجب أيضًا التحقق من صحة محتوى الحقل السابق على جانب العميل
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)
        ' field [txtAutreDiplome] must be non-empty if [cmbDiplomes]=[other]
        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] إلى true إذا كانت البيانات التي تم التحقق من صحتها صحيحة، وإلى false في حالة عدم صحتها.

هنا، يتم إجراء الفحوصات التالية:

  • لا يمكن أن تحتوي القائمة المنسدلة [cmbDiplomes] على [?] كقيمة لها. يتم التحقق من ذلك بواسطة مكون [CompareValidator2]
  • يختار المستخدم درجة علمية من القائمة المنسدلة [cmbDiplomes]. إذا لم تكن الدرجة العلمية موجودة في القائمة، فيمكنه اختيار الخيار [Other] من القائمة. يجب عليه بعد ذلك إدخال الدرجة العلمية في حقل الإدخال [txtAutreDiplome]. إذا تم اختيار [Other] في [cmbDiplomes]، فيجب ألا يكون حقل [txtAutreDiplome] فارغًا. إذا لم يتم تحديد [Other] أو [?] في [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){
            // vérifie la validité du champ 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]:

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

8.2.5. RegularExpressionValidator

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

Image

رقم
الاسم
النوع
الخصائص
الدور
1
txtMel
مربع نص
EnableViewState=true
حقل الإدخال
2
مُثبت صحة الحقل المطلوب3
RequiredFieldValidator
ControlToValidate=txtMel
Display=Dynamic
مكون التحقق من صحة عنصر التحكم [1]
3
مُثبت صحة التعبير العادي 1
مُثبت صحة التعبير العادي
عنصر التحكم المراد التحقق من صحته=txtMel
Display=Dynamic
مكون التحقق من صحة عنصر التحكم [1]
4
btnSend
زر
EnableViewState=false
CausesValidation=true
زر [submit]

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

ControlToValidate
الحقل الذي يجب التحقق من صحة قيمته بواسطة المكون
EnableClientScript
boolean - عندما تكون القيمة true، تشير إلى أنه يجب أيضًا التحقق من صحة محتوى الحقل السابق على جانب العميل
ErrorMessage
رسالة الخطأ التي يجب أن يعرضها المكون في حالة اكتشاف خطأ
التعبير العادي
التعبير العادي الذي سيتم مقارنة محتوى [ControlToValidate] به
Display
وضع العرض: ثابت: يظل الحقل موجودًا دائمًا حتى إذا لم يعرض رسالة خطأ؛ ديناميكي: يظهر الحقل فقط في حالة وجود رسالة خطأ؛ لا شيء: لا يتم عرض رسالة الخطأ. هذا الحقل موجود أيضًا للمكونات الأخرى ولكن لم يتم استخدامه حتى الآن.

قد يكون نمط التعبير العادي لعنوان البريد الإلكتروني كما يلي: \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. ملخص التحقق من الصحة

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

Image

تحتوي جميع عناصر التحكم في التحقق من الصحة على الخصائص التالية:

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

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

Image

لا.
الاسم
النوع
الخصائص
الدور
1
ملخص التحقق من الصحة 1
ملخص التحقق
نص العنوان=حدثت الأخطاء التالية، تمكين البرنامج النصي للعميل=كاذب، إظهار الملخص=صحيح
يعرض الأخطاء من جميع عناصر التحقق من الصحة في الصفحة
2
lnkErrorsToForm
زر الارتباط
ValidationCauses=false
رابط للعودة إلى النموذج

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

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

Image

1

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

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

<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>

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

Image

يعطينا زر [Submit] الاستجابة التالية:

Image

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

Image

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

Image

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

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

    ' procedure executed when the page is loaded
    Sub page_Load(sender As Object, e As EventArgs)
        ' on the 1st request, we present the [form] view
        if not ispostback then
            afficheVues(true,false,false)
        end if
    end sub

    sub afficheVues(byval formulaireVisible as boolean, _
        erreursVisible as boolean, infosVisible as boolean)
        ' set of views
        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)
        ' displays the form view
        afficheVues(true,false,false)
        ' redo validity checks
        Page.validate
    End Sub

    Sub btnEnvoyer_Click(sender As Object, e As EventArgs)
        ' is the page valid?
        if not Page.IsValid then
            ' the [errors] view is displayed
            afficheVues(false,true,false)
        else
            ' otherwise the view [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]، الذي يتم تشغيله عند كل طلب من العميل، نقوم بعرض طريقة العرض [form]، بينما يتم إخفاء الطرق الأخرى. ويتم ذلك فقط عند الطلب الأول. يستدعي الإجراء إجراءً مساعدًا [displayViews]، نمرر إليه ثلاث قيم منطقية لتحديد ما إذا كان سيتم عرض طرق العرض الثلاث أم لا.
  • عند استدعاء الإجراء [btnEnvoyer_Click]، يكون التحقق من صحة البيانات قد تم بالفعل. يحتوي الزر [btnEnvoyer] على الخاصية [CausesValidation=true]، التي تطلق عملية التحقق من صحة البيانات هذه. تم تنفيذ جميع عناصر التحكم في التحقق من الصحة، وتم تعيين خاصية [IsValid] الخاصة بها. تشير هذه الخاصية إلى ما إذا كانت البيانات التي تم التحقق من صحتها بواسطة عنصر التحكم صالحة أم لا. علاوة على ذلك، تم أيضًا تعيين الخاصية [IsValid] للصفحة نفسها. تكون [true] فقط إذا كانت الخاصية [IsValid] لجميع عناصر التحكم في التحقق من الصحة على الصفحة لها القيمة [true]. وبالتالي، يبدأ الإجراء [btnEnvoyer_Click] بالتحقق مما إذا كانت الصفحة صالحة أم لا. إذا كانت غير صالحة، يتم عرض طريقة العرض [errors]. تحتوي طريقة العرض هذه على عنصر التحكم [ValidationSummary]، الذي يسرد سمات [ErrorMessage] لجميع عناصر التحكم (انظر لقطة الشاشة أعلاه). إذا كانت الصفحة صالحة، يتم عرض طريقة العرض [info] مع رسالة إعلامية.
  • الإجراء [lnkErrorsToForm_Click] مسؤول عن عرض طريقة العرض [form] في الحالة التي تم التحقق من صحتها فيها. نظرًا لأن جميع حقول الإدخال في طريقة العرض [form] لها الخاصية [EnableViewState=true]، يتم إعادة إنشاء حالتها تلقائيًا. ومن الغريب أن حالة مكونات التحقق من الصحة لا يتم استعادتها. قد يتوقع المرء، عند العودة إلى عرض [errors]، أن يرى عناصر التحكم غير الصالحة تعرض حقل [Text] الخاص بها. لكن هذا ليس هو الحال. لذلك، قمنا بفرض التحقق من صحة البيانات باستخدام طريقة [Page.Validate] الخاصة بالصفحة. يجب القيام بذلك بمجرد إظهار لوحة [formView]. وبالتالي، يكون هناك إجمالي عمليتين للتحقق من الصحة. يجب تجنب ذلك في الممارسة العملية. هنا، سمح لنا المثال بتقديم مفاهيم جديدة تتعلق بالتحقق من صحة الصفحة.

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] بطريقة مختلفة:

مصفوفة A
[ListControl].DataSource=A
سيحتوي حقل [Text] و[Value] لعناصر [ListControl] على قيم العناصر الموجودة في A
قائمة المصفوفات 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
قائمة مصفوفة
3
جدول البيانات
4
مجموعة البيانات
5
جدول التجزئة

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. الربط بمصدر بيانات صفيف

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

' global data
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

' procedure executed when the page is loaded
Sub page_Load(sender As Object, e As EventArgs)
    if not IsPostBack then
      ' create the data sources to be linked to the components
      createDataSources
      ' link to an array [Array]
      bindToArray
      ' link to a list [ArrayList]
      bindToArrayList
      ' link to a table [DataTable]
      bindToDataTable
      ' link to a data group [DataSet]
      bindToDataSet
      ' link to a dictionary [HashTable]
      bindToHashTable
    end if
End Sub

sub createDataSources
  ' creates data sources to be linked to components
  ' arraylist
  dim i as integer
  for i=0 to textes.length-1
    myDataListe.add(textes(i))
  next
  ' datatable
  ' we define its two columns
  myDataTable.Columns.Add("id",Type.GetType("System.Int32"))
  myDataTable.Columns.Add("texte",Type.GetType("System.String"))
  ' fill the table
  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
  ' dataset - a single table
  myDataSet.Tables.Add(myDataTable)
  ' hashtable
  for i=0 to textes.length-1
    myHashTable.add(valeurs(i),textes(i))
  next
end sub

' panel connection
sub bindToArray
        ' association with components
        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
        ' item selection
        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

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

' global data
dim textes() as string={"un","deux","trois","quatre"}
dim myDataListe as new ArrayList
..

' procedure executed when the page is loaded
Sub page_Load(sender As Object, e As EventArgs)
    if not IsPostBack then
      ' create the data sources to be linked to the components
      createDataSources
...
    end if
End Sub

sub createDataSources
  ' creates data sources to be linked to components
  ' arraylist
  dim i as integer
  for i=0 to textes.length-1
    myDataListe.add(textes(i))
  next
...
end sub

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

' liaison arraylist
sub bindToArrayList
        ' l'association aux composants
        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
        ' la sélection des éléments
        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]:

' global data
dim textes() as string={"un","deux","trois","quatre"}
dim myDataTable as new DataTable("table1")
...

' procedure executed when the page is loaded
Sub page_Load(sender As Object, e As EventArgs)
    if not IsPostBack then
      ' create the data sources to be linked to the components
      createDataSources
...
    end if
End Sub

sub createDataSources
  ' creates data sources to be linked to components
...
  ' datatable
  ' we define its two columns
  myDataTable.Columns.Add("id",Type.GetType("System.Int32"))
  myDataTable.Columns.Add("texte",Type.GetType("System.String"))
  ' fill the table
  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] و [text]. سيملأ العمود [id] حقل [Value] لعناصر [ListControl]، وسيملأ العمود [text] حقول [Text] الخاصة بها. يتم إنشاء [DataTable] ذي العمودين على النحو التالي:

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

وبذلك ننشئ جدولًا مكونًا من عمودين:

  • الأول، المسمى "id"، هو من النوع integer
  • والثاني، المسمى "text"، هو من النوع 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

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

sub bindToDataTable
        ' l'association aux composants
        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
        ' la sélection des éléments
        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] هو مصدر البيانات. سيملأ العمود [id] في هذا الجدول حقول [Value] لعناصر المكون، بينما سيملأ العمود [text] حقول [Text] الخاصة بها.

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

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

' global data
dim myDataTable as new DataTable("table1")
dim myDataSet as new DataSet
...


' procedure executed when the page is loaded
Sub page_Load(sender As Object, e As EventArgs)
    if not IsPostBack then
      ' create the data sources to be linked to the components
      createDataSources
...
    end if
End Sub

sub createDataSources
  ' creates data sources to be linked to components
  ' dataset - a single table
  myDataSet.Tables.Add(myDataTable)
...
end sub

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

sub bindToDataSet
        ' l'association aux composants
        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
        ' la sélection des éléments
        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]. وسيقوم عمود [id] في هذا الجدول بتعبئة حقول [Value] لعناصر المكون، بينما سيقوم عمود [text] بتعبئة حقول [Text] الخاصة بها.

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

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

' global data
dim textes() as string={"un","deux","trois","quatre"}
dim valeurs() as string={"1","2","3","4"}
dim myHashTable as new HashTable
...

' procedure executed when the page is loaded
Sub page_Load(sender As Object, e As EventArgs)
    if not IsPostBack then
      ' create the data sources to be linked to the components
      createDataSources
...
    end if
End Sub

sub createDataSources
  ' creates data sources to be linked to components
...
  ' hashtable
  for i=0 to textes.length-1
    myHashTable.add(valeurs(i),textes(i))
  next
end sub

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

sub bindToHashTable
        ' l'association aux composants
        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
        ' la sélection des éléments
        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] في جدول HTML. بالنسبة لهذه الأنواع الأربعة من البيانات، ما عليك سوى ربط المصدر بخاصية [DataSource] لمكون [DataGrid]:

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

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

Image

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

Image

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

Image

سنستخدم الرابطين أعلاه. يوفر رابط [Property Generator] الوصول إلى الخصائص الرئيسية لـ [DataGrid]:

Image

نقوم بإلغاء تحديد خيار [Show Header] للمكونات الأربعة [DataGrid] ونحفظ الصفحة. يتيح لك الرابط الآخر، [Auto Format]، الاختيار من بين عدة أنماط لجدول HTML الذي سيتم عرضه:

Image

نختار [color 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">

    ' global data
        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

        ' procedure executed when the page is loaded
        Sub page_Load(sender As Object, e As EventArgs)
            if not IsPostBack then
              ' create the data sources to be linked to the components
              createDataSources
              ' link to an array [Array]
              bindToArray
              ' link to a list [ArrayList]
              bindToArrayList
              ' link to a table [DataTable]
              bindToDataTable
              ' link to a data group [DataSet]
              bindToDataSet
            end if
        End Sub

        sub createDataSources
          ' creates data sources to be linked to components
          ' arraylist
          dim i as integer
          for i=0 to textes1.length-1
            myDataListe.add(textes1(i))
          next
          ' datatable
          ' we define its two columns
          myDataTable.Columns.Add("id",Type.GetType("System.Int32"))
          myDataTable.Columns.Add("texte1",Type.GetType("System.String"))
          myDataTable.Columns.Add("texte2",Type.GetType("System.String"))
          ' fill the table
          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
          ' dataset - a single table
          myDataSet.Tables.Add(myDataTable)
        end sub

        ' panel connection
        sub bindToArray
          with DataGrid1
            .DataSource=textes1
            .DataBind
          end with
        end sub

        ' arraylist link
        sub bindToArrayList
          with DataGrid2
            .DataSource=myDataListe
            .DataBind
          end with
        end sub

        ' datatable link
        sub bindToDataTable
          with DataGrid3
            .DataSource=myDataTable
            .DataBind
          end with
        end sub

        ' dataset link
        sub bindToDataSet
          with DataGrid4
            .DataSource=myDataSet
            .DataBind
          end with
        end sub

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

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

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

حيث يكون [مصدر البيانات] من النوع [Array] أو [ArrayList] أو [DataTable] أو [DataSet]. وبالتالي، يمكننا أن نرى أن هذه أداة قوية لعرض البيانات في الجداول:

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

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

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

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

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

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

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

فيما يلي كود العرض [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
        ' on the 1st query, the data source is defined and linked to the 1st datagrid
        If Not IsPostBack Then
            'define data source
            With DataGrid1
                .DataSource = createDataSource()
                .DataBind()
            End With
        End If
        ' for each query, datagrid2 is linked to the source of the 1st datagrid
        With Datagrid2
            .DataSource = DataGrid1.DataSource
            .DataBind()
        End With
    End Sub
 
    Private Function createDataSource() As DataTable
        ' initialize the data source
        Dim thèmes As New DataTable
        ' columns
        With thèmes.Columns
            .Add("id", GetType(System.Int32))
            .Add("thème", GetType(System.String))
            .Add("description", GetType(System.String))
        End With
        ' column id will be primary key
        thèmes.Constraints.Add("cléprimaire", thèmes.Columns("id"), True)
        ' lines
        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

من المنطقي تمامًا أن يعرض كلا المكونين مصدر البيانات الذي تم ربطهما به. نستخدم الزر [Submit] لتشغيل [PostBack] إلى الخادم. تكون النتيجة كما يلي:

Image

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

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

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

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

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

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

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
        ' on the 1st query, the data source is defined and linked to the 1st datagrid
        If Not IsPostBack Then
            'define data source
            dtThèmes = createDataSource()
            With DataGrid1
                .DataSource = dtThèmes
                .DataBind()
            End With
            ' store information in the session
            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
        ' a new theme is added to each query
        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)
        'links datagrid2 with the data source
        With Datagrid2
            .DataSource = dtThèmes
            .DataBind()
        End With
        ' info no. of requests
        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
        ' store some information in the session
        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
        ' on the 1st query, the data source is defined and linked to the 1st datagrid
        If Not IsPostBack Then
            'define data source
            dtThèmes = createDataSource()
            With DataGrid1
                .DataSource = dtThèmes
                .DataBind()
            End With
            ' store information in the session
            Session("source") = dtThèmes
...
        End If
        ' a new theme is added to each query
        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)
        'links datagrid2 with the data source
        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")، أي S، أي [dtThemes]. وبالتالي، فإن المحتوى الجديد لمصدر البيانات هو الذي يتم حفظه بالفعل. لم تكن هناك حاجة لكتابة:

            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
        ' on the 1st query, the data source is defined and linked to the 1st datagrid
....
            ' store information in the session
            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
        ' a new theme is added to each query
....
        ' info no. of requests
        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
        ' store some information in the session
        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

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

  • numRequest1 و numRequest2 من النوع [Integer] - [Integer] ليس فئة بل بنية
  • numRequest3 من النوع [integer] - [integer] هي فئة محددة لهذا الغرض

عندما نكتب:


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

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

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

لتخزين القيمة الجديدة لـ [numRequest3] في الجلسة. بدلاً من ذلك، يجب كتابة:

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

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

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

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

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

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

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

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

Image

فيما يلي كود فئة [products]:


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)
            ' save the connection string
            Me.chaineConnexionOLEDB = chaineConnexionOLEDB
        End Sub
 
        Public Function getDataSet(ByVal commande As String) As DataSet
            ' create a DataAdapter object to read data from source OLEDB
            Dim adaptateur As New OleDbDataAdapter(commande, chaineConnexionOLEDB)
            ' create a memory image of the select result
            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
            ' we return the result
            Return contenu
        End Function
    End Class
End Namespace

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

Option Explicit On 
Option Strict On

' namespaces
Imports System
Imports System.Data
Imports Microsoft.VisualBasic

Namespace st.istia.univangers.fr

    ' test pg
    Module testproduits
        Sub Main(ByVal arguments() As String)
            ' displays the contents of a product table
            ' the table is in a ACCESS database whose pg receives the file name
            Const syntaxe1 As String = "pg bdACCESS"

            ' checking program parameters
            If arguments.Length <> 1 Then
                ' error msg
                Console.Error.WriteLine(syntaxe1)
                ' end
                Environment.Exit(1)
            End If

            ' prepare the connection chain
            Dim chaineConnexion As String = "Provider=Microsoft.Jet.OLEDB.4.0; Ole DB Services=-4; Data Source=" + arguments(0)

            ' creation of a product object
            Dim objProduits As produits = New produits(chaineConnexion)

            ' retrieve the product table from a dataset
            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

            ' display its contents
            Dim lignes As DataRowCollection = contenu.Tables(0).Rows
            For i As Integer = 0 To lignes.Count - 1
                ' table line 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
مربع نص
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
btnExecute
زر
EnableViewState=false
زر [submit]

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

لا
الاسم
النوع
الخصائص
الدور
1
lblSelect
تسمية
EnableViewState=false
حقل المعلومات
2
rdAscending
rdDescending
زر الاختيار
EnableViewState=false
GroupName=rdSort
يتيح لك اختيار ترتيب الفرز
3
DataGrid1
DataGrid
EnableViewState=true
AllowPaging=true
AllowSorting=true
جدول يعرض نتائج عملية الاختيار
4
نتائج الارتباط
زر الارتباط
EnableViewState=false
[submit]

سنسمي هذه العرض [results]. وهي تحتوي على [DataGrid] الذي سيعرض نتائج عبارة SQL SELECT. قد يرتكب المستخدم خطأ في استعلامه. سيتم إبلاغه ببعض الأخطاء في عرض [errors] بفضل عناصر التحكم في التحقق من الصحة.

Image

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

Image

يتم عرض عرض [errors]:

لا
الاسم
النوع
الخصائص
الدور
1
أخطاء HTML
المتغير
 
كود HTML المطلوب لعرض الأخطاء
3
lnkForm2
زر الارتباط
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"
تمكين ترقيم الصفحات
حجم الصفحة="4"
أربعة صفوف من البيانات لكل صفحة
NextPageText="التالي"
نص الرابط للانتقال إلى الصفحة التالية من مصدر البيانات
PrevPageText="السابق"
نص الرابط للانتقال إلى الصفحة السابقة لمصدر البيانات

يمكن إدخال هذه المعلومات مباشرة في سمات العلامة <asp:datagrid>. يمكنك أيضًا استخدام [WebMatrix]. في نافذة خصائص [DataGrid]، انقر فوق الارتباط [Property Generator]:

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)
        ' create a product object
        Dim objProduits As produits
        Try
            objProduits = New produits(ConfigurationSettings.AppSettings("OLEDBStringConnection"))
            ' put the object in the application
            Application("objProduits") = objProduits
            ' no error
            Application("erreur") = False
        Catch ex As Exception
            'there has been an error, we note it in the application
            Application("erreur") = True
            Application("message") = ex.Message
        End Try
    End Sub
End Class

عند بدء تشغيل التطبيق (Application_Start)، نقوم بإنشاء كائن [products] وتخزينه في التطبيق بحيث يكون متاحًا لجميع الطلبات الواردة من جميع العملاء. إذا حدث استثناء أثناء هذا الإنشاء، يتم تسجيله في التطبيق. لن يتم تشغيل الإجراء [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
 
    ' components 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
    ' data page
    Protected erreursHTML As String
 
    Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        ' check for application errors
        If CType(Application("erreur"), Boolean) Then
            ' the application has not initialized correctly
            Dim erreurs As New ArrayList
            erreurs.Add("Application momentanément indisponible (" + CType(Application("message"), String) + ")")
            afficheErreurs(erreurs, False)
            Exit Sub
        End If
        '1st request
        If Not IsPostBack Then
            ' the empty form is displayed
            afficheFormulaire()
        End If
    End Sub
 
    Private Sub afficheErreurs(ByVal erreurs As ArrayList, ByVal afficheLien As Boolean)
        ' displays the error view
        erreursHTML = ""
        For i As Integer = 0 To erreurs.Count - 1
            erreursHTML += "<li>" + erreurs(i).ToString + "</li>" + ControlChars.CrLf
        Next
        lnkErreurs.Visible = afficheLien
        ' the [errors] view is displayed
        vueErreurs.Visible = True
        vueFormulaire.Visible = False
        vueRésultats.Visible = False
    End Sub
 
    Private Sub afficheFormulaire()
        ' the [form] view is displayed
        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)
        ' initialize controls
        lblSelect.Text = sqlTexte
        With DataGrid1
            .DataSource = données
            .PageSize = CType(txtPages.Text, Integer)
            .CurrentPageIndex = 0
            .DataBind()
        End With
        ' the [results] view is displayed
        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
        ' valid page?
        If Not Page.IsValid Then
            afficheFormulaire()
            Exit Sub
        End If
 
        ' execute query SELECT customer
        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
        ' all's well - the results are in
        afficheRésultats(txtSelect.Text.Trim, données)
        ' put the data in the session
        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
        ' set of views
        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
        ' change page
        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
        ' sort the dataview
        Dim données As DataView = CType(Session("données"), DataView)
        données.Sort = e.SortExpression + " " + CType(IIf(rdCroissant.Checked, "asc", "desc"), String)
        ' we display it
        With DataGrid1
            .CurrentPageIndex = 0
            .DataSource = données
            .DataBind()
        End With
    End Sub
End Class

عندما يتم تحميل الصفحة [Page_Load]، نتحقق أولاً مما إذا كان التطبيق قادراً على التهيئة بشكل صحيح. إذا لم يكن الأمر كذلك، فإننا نعرض طريقة العرض [errors] بدون رابط للعودة إلى النموذج، لأن هذا الرابط يصبح غير ضروري في هذه الحالة. في الواقع، لا يمكن عرض سوى طريقة العرض [errors] إذا لم يتمكن التطبيق من التهيئة بشكل صحيح. بخلاف ذلك، نعرض طريقة العرض [form] إذا كان هذا هو الطلب الأول للعميل. بالنسبة للباقي، نترك للقارئ مهمة فهم الكود. سنركز فقط على ثلاثة إجراءات: إجراء [btnExecute_Click]، الذي يتم تشغيله عندما يطلب المستخدم تنفيذ استعلام SQL الذي تم إدخاله في عرض [form]، وإجراء [DataGrid1_PageIndexChanged]، الذي يتم تشغيله عندما يستخدم المستخدم رابطي [Next] و[Previous] في [DataGrid]، وإجراء [DataGrid1_SortCommand]، الذي يتم تشغيله عندما ينقر المستخدم على رأس عمود لفرز البيانات بهذا الترتيب. يتم تحديد ترتيب الفرز — تصاعدي أو تنازلي — بواسطة زري الاختيار الخاصين بالفرز.

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

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

بالنسبة للصفحة نفسها، تم تعيين سمة [IsValid]. تكون هذه السمة صحيحة فقط إذا تم تعيين سمة [IsValid] لجميع عناصر التحكم في التحقق إلى true. إذا لم يكن الأمر كذلك، يجب عرض طريقة العرض [form]. تحتوي طريقة العرض هذه على عناصر التحكم في التحقق من الصحة التي ستعرض السمة [errorMessage] الخاصة بها. إذا كانت الصفحة صالحة، نستخدم الكائن [products] الذي تم إنشاؤه بواسطة [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] المستمد من فئة [products] على جدول واحد فقط، وهو الجدول الناتج عن عبارة [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] من فئة [products]، يتم عرض عرض [results]؛ وإلا، يتم عرض عرض [errors]. يتم عرض عرض [results] عبر الإجراء [displayResults]، الذي يتم تمرير معلمتين إليه:

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

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

يتم تنفيذ الإجراء [DataGrid1_PageIndexChanged] عندما ينقر المستخدم على الروابط [Next] و [Previous] في [DataGrid]. ويتلقى معلمتين:


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

فيما يلي الكود الكامل لمعالج الحدث:


    Private Sub DataGrid1_PageIndexChanged(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridPageChangedEventArgs) Handles DataGrid1.PageIndexChanged
        ' change page
        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
الكائن الذي أطلق الحدث — في هذه الحالة، أحد الروابط [Next] أو [Previous]
e
معلومات حول الحدث. الخاصية [e.SortExpression] هي اسم العمود الذي تم النقر عليه للفرز

فيما يلي الكود الكامل لمعالج الحدث:


    Private Sub DataGrid1_SortCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridSortCommandEventArgs) Handles DataGrid1.SortCommand
        ' sort the dataview
        Dim données As DataView = CType(Session("données"), DataView)
        données.Sort = e.SortExpression + " " + CType(IIf(rdCroissant.Checked, "asc", "desc"), String)
        ' we display it
        With DataGrid1
            .CurrentPageIndex = 0
            .DataSource = données
            .DataBind()
        End With
    End Sub

نسترد [DataView] المعروض بواسطة [DataGrid] في الجلسة الحالية. وقد تم وضعه هناك بواسطة الإجراء [btnExécuter_Click]. يحتوي مكون [DataView] على خاصية [Sort] التي نخصص لها تعبير الفرز. ويتم ذلك وفقًا لصيغة [select ... order by expr1, expr2, ...]، حيث يمكن أن يتبع كل [expr] الكلمة الرئيسية [asc] للفرز التصاعدي أو [desc] للفرز التنازلي. تعبير [order by] المستخدم هنا هو [order by column 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. فئات الأعمال

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

8.7.3. طرق العرض

عندما يقوم المستخدم بإرسال أول طلب إلى التطبيق، يرى طريقة العرض [results1] التالية:

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

إذا اختار المستخدم النمط رقم 2، فسيظهر له عرض [results2] التالي:

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

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

رقم
الاسم
النوع
الخصائص
الدور
1
HTMLErrors
متغير
 
كود 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("nom") %>
                            <br>
                            prix :
                            <%# databinder.eval(Container.DataItem,"prix","{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("nom") %>
                            , prix :
                            <%# databinder.eval(Container.DataItem,"prix","{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]. في نافذة خصائصه، تم تحديد الرابط [AutoFormat]:

1234567

أعلاه، سيقوم مخطط [Color 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>
                            محتوى جدول [list] في قاعدة بيانات [products]
                        </HeaderTemplate>
ItemTemplate

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

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

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

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

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

Image

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

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

في النهاية، يكون كود [DataList1] هو الكود المقدم في مقتطف الكود أعلاه. نترك للقارئ دراسة كود العرض الخاص بـ [DataList2]. كما هو الحال مع مكون [DataGrid]، يمكن تعيين معظم خصائص [DataList] باستخدام معالج [WebMatrix]. للقيام بذلك، استخدم رابط [Property Generator] في نافذة خصائص [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)
        ' create a product object
        Try
            Dim données As DataSet = New produits(ConfigurationSettings.AppSettings("OLEDBStringConnection")).getDataSet("select * from LISTE")
            ' put the object in the application
            Application("données") = données
            ' no error
            Application("erreur") = False
        Catch ex As Exception
            'there has been an error, we note it in the application
            Application("erreur") = True
            Application("message") = ex.Message
        End Try
    End Sub
End Class

عند بدء تشغيل التطبيق (Application_Start)، نقوم بإنشاء [dataset] من فئة الأعمال [products] وإضافتها إلى التطبيق بحيث تكون متاحة لجميع الطلبات من جميع العملاء. إذا حدث استثناء أثناء هذا الإنشاء، يتم تسجيله في التطبيق. سيتم تشغيل الإجراء [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
 
    ' components 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
    ' data page
    Protected erreursHTML As String
 
    Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        ' check for application errors
        If CType(Application("erreur"), Boolean) Then
            ' the application has not initialized correctly
            Dim erreurs As New ArrayList
            erreurs.Add("Application momentanément indisponible (" + CType(Application("message"), String) + ")")
            afficheErreurs(erreurs, False)
            Exit Sub
        End If
        '1st request
        If Not IsPostBack Then
            ' initialize controls
            With DataList1
                .DataSource = CType(Application("données"), DataSet)
                .DataBind()
            End With
            With DataList2
                .DataSource = CType(Application("données"), DataSet)
                .DataBind()
            End With
            ' the empty form is displayed
            afficheRésultats(True, False)
        End If
    End Sub
 
    Private Sub afficheErreurs(ByVal erreurs As ArrayList, ByVal afficheLien As Boolean)
        ' displays the error view
        erreursHTML = ""
        For i As Integer = 0 To erreurs.Count - 1
            erreursHTML += "<li>" + erreurs(i).ToString + "</li>" + ControlChars.CrLf
        Next
        ' the [errors] view is displayed
        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)
        ' the [results] view is displayed
        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
        ' change the style
        afficheRésultats(RadioButton1.Checked, RadioButton2.Checked)
    End Sub
End Class

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

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

Image

لقد واجهنا هذه المشكلة بالفعل وحللناها عن طريق تضمين متغير في كود العرض بالشكل <% =erreursHTML %>، حيث يتم حساب قيمة 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">

    ' procedure executed when the page is loaded
    Sub page_Load(sender As Object, e As EventArgs)
        if not IsPostBack then
          ' create a data source
          with Repeater1
              .DataSource=createDataSource
              .DataBind
          end with
        end if
    End Sub

    function createDataSource as ArrayList
      ' create an 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. التطبيق

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

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

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

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

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

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

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

Image

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

Image

يستخدم المستخدم الزر [Submit] لطلب حساب الضريبة. يظهر للمستخدم عرض [simulations] التالي:

Image

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

Image

يتم تمييز هذه الأخطاء في عرض [النموذج]:

Image

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

Image

يؤدي ذلك إلى ظهور عرض [المحاكاة] مع محاكاة إضافية واحدة:

Image

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

Image

8.9.2.1. كود العرض

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

  • [panelform] لعرض [form]
  • [panelerrors] لعرض [الأخطاء]
  • [panelsimulations] لعرض [simulations]

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

رقم
الاسم
النوع
الخصائص
الدور
0
نموذج لوحة
لوحة
 
عرض النموذج
1
رقم
لا
زر الاختيار
GroupName=rdmarie
أزرار الاختيار
2
cvMarie
المحقق المخصص
ErrorMessage=لم تحدد حالتك الاجتماعية
EnableClientScript=false
يتحقق من أن برنامج العميل قد أرسل الحالة الاجتماعية المتوقعة
3
txtChildren
مربع نص
 
عدد الأطفال
4
rfvChildren
RequiredFieldValidator
رسالة الخطأ=أدخل عدد الأطفال
ControlToValidate=txtChildren
يتحقق من أن حقل [txtChildren] ليس فارغًا
5
rvChildren
RangeValidator
ErrorMessage=أدخل رقمًا بين 1 و 30
ControlToValidate=txtChildren
يتحقق من أن الحقل [txtChildren] يقع ضمن النطاق [1,30]
6
txtSalary
مربع نص
EnableViewState=true
الراتب السنوي
7
rfvSalary
أداة التحقق من صحة الحقول المطلوبة
ControlToValidate=txtSalary
ErrorMessage=أدخل مبلغ راتبك
يتحقق من أن حقل [txtSalary] ليس فارغًا
8
revSalary
مُثبت صحة التعبير العادي
ControlToValidate=txtSalary
رسالة الخطأ=راتب غير صالح
التعبير العادي=\s*\d+\s*
يتحقق من أن حقل [txtSalary] عبارة عن سلسلة من الأرقام
9
btnCalculate
زر
ValidationTriggers=true
زر [submit] في النموذج - يبدأ حساب الضريبة
10
btnClear
زر
التحقق=false
زر [إرسال] في النموذج - يمسح النموذج

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

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

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

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


                <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>

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

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

يتم تكوين مكون [dgSimulations] على النحو التالي في [Webmatrix]. في نافذة خصائص [dgSimulations]، نختار الرابط [AutoFormat] ونختار نظام الألوان [Color 3]:

ثم، في نافذة خصائص [dlgSimulations] نفسها، نختار الرابط [Property Generator]. نطلب عرض رؤوس الأعمدة:

Image

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

Image

نقوم بإلغاء تحديد الخيار [Create columns automatically...]. سنقوم بتحديد الأعمدة الأربعة لـ [DataGrid] بأنفسنا. سيتم ربط هذا بكائن [DataTable] يحتوي على أربعة أعمدة تسمى "married" و"children" و"salary" و"tax" على التوالي. لإنشاء عمود في [DataGrid]، نختار خيار [Related Columns] ونستخدم زر الإنشاء كما هو موضح أعلاه. يتم إنشاء عمود، ويمكننا تحديد خصائصه. سيحتوي العمود الأول من جدول المحاكاة على عمود "married" من كائن [DataTable] الذي سيتم ربطه بـ [DataGrid]. يتم تحديد هذا العمود الأول من [DataGrid] على النحو التالي في المعالج:

Image

نص العنوان
عنوان العمود، هنا "متزوج"
حقل البيانات
اسم العمود في مصدر البيانات الذي سيتم عرضه بواسطة هذا العمود في [DataGrid]. هنا، هو عمود "married" في [DataTable].

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

نص العنوان
الأطفال
حقل البيانات
الأطفال

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

نص العنوان
الراتب السنوي
حقل البيانات
الراتب
تعبير التنسيق
{0:C} - عرض العملة للحصول على علامة اليورو

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

نص العنوان
مبلغ الضريبة
حقل البيانات
الضريبة
تعبير التنسيق
{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)
        ' create an impot object
        Dim objImpot As impot
        Try
            objImpot = New impot(New impotsOLEDB(ConfigurationSettings.AppSettings("chaineConnexion")))
            ' put the object in the application
            Application("objImpot") = objImpot
            ' no error
            Application("erreur") = False
        Catch ex As Exception
            'there has been an error, we note it in the application
            Application("erreur") = True
            Application("message") = ex.Message
        End Try
    End Sub
 
    Sub Session_Start(ByVal sender As Object, ByVal e As EventArgs)
        ' start of session - create a list of empty simulations
        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]. ويحاول إنشاء كائن من النوع [tax] عن طريق استرداد بياناته من مصدر 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>

يحدد المفتاح [connectionString] سلسلة الاتصال بمصدر 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
 
    ' local variables
 
    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
        ' first, we look at the state of the application
        If CType(Application("erreur"), Boolean) Then
            ' the application failed to initialize
            ' the error view is displayed
            Dim erreurs As New ArrayList
            erreurs.Add("Application momentanément indisponible (" + CType(Application("message"), String) + ")")
            afficheErreurs(erreurs)
            Exit Sub
        End If
        ' no errors - on the 1st request, the form is presented
        If Not IsPostBack Then afficheFormulaire()
    End Sub

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

الإجراء الذي يعرض عرض [errors] هو كما يلي:


    Private Sub afficheErreurs(ByRef erreurs As ArrayList)
        ' builds a list of errors
        With rptErreurs
            .DataSource = erreurs
            .DataBind()
        End With
        ' container display 
        panelerreurs.Visible = True
        panelform.Visible = False
        panelsimulations.Visible = False
        Exit Sub
    End Sub

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

الإجراء الذي يعرض طريقة العرض [form] هو كما يلي:


    Private Sub afficheFormulaire()
        ' displays the form
        panelform.Visible = True
        ' the other containers are hidden
        panelerreurs.Visible = False
        panelsimulations.Visible = False
    End Sub

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

عندما ينقر المستخدم على الزر [Calculate] في عرض [form]، يتم إرسال طلب POST إلى [main.aspx]. يتم تنفيذ الإجراء [Page_Load]، يليه الإجراء [btnCalculate_Click]:


    Private Sub btnCalculer_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCalculer.Click
        ' we check the validity of the data entered; if there are any errors, we report them
        If Not Page.IsValid Then
            afficheFormulaire()
            Exit Sub
        End If
        ' no errors - tax is calculated
        Dim impot As Long = CType(Application("objImpot"), impot).calculer( _
        rdOui.Checked, CType(txtEnfants.Text, Integer), CType(txtSalaire.Text, Long))
        ' the result is added to existing simulations
        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)
        ' put the simulations in the session
        Session.Item("simulations") = simulations
        ' the simulations page is displayed
        afficheSimulations(simulations, "Retour au formulaire")
    End Sub

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

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


    Private Sub afficheSimulations(ByRef simulations As DataTable, ByRef lien As String)
        ' we link the datagrid to the simulations source
        With dgSimulations
            .DataSource = simulations
            .DataBind()
        End With
        ' link
        lnkForm2.Text = lien
        ' the views
        panelsimulations.Visible = True
        panelerreurs.Visible = False
        panelform.Visible = False
    End Sub

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

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

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

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


    Private Sub btnEffacer_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnEffacer.Click
        ' displays the empty form
        razForm()
        afficheFormulaire()
    End Sub
 
    Private Sub razForm()
        ' empty the form
        rdOui.Checked = False
        rdNon.Checked = True
        txtEnfants.Text = ""
        txtSalaire.Text = ""
    End Sub

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


    Private Sub lnkForm1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lnkForm1.Click
        ' displays the form
        afficheFormulaire()
    End Sub
 
    Private Sub lnkForm2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lnkForm2.Click
        ' displays the form
        afficheFormulaire()
    End Sub

يعرض كلا الإجراءين ببساطة عرض [النموذج]. ونحن نعلم أن الحقول في هذا العرض ستتلقى قيمة هي إما القيمة التي تم إرسالها لها أو قيمتها السابقة. وبما أن طلب 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. والآن لم يعد هذا هو الحال.