Skip to content

7. مكونات خادم ASP - 1

7.1. مقدمة

في هذا الفصل، نصف التقنية الموصى بها في ASP.NET لإنشاء واجهة المستخدم. نعلم أن هناك مرحلتين متميزتين في معالجة خادم الويب لصفحة .aspx:

  1. أولاً، يتم تنفيذ وحدة التحكم في الصفحة. وتتكون هذه من كود موجود إما داخل صفحة .aspx نفسها (حل WebMatrix) أو في ملف منفصل (حل Visual Studio.NET).
  2. ثم يتم تنفيذ كود العرض لصفحة .aspx وتحويله إلى كود HTML يتم إرساله إلى العميل.

Image

يوفر ASP.NET ثلاث مكتبات علامات لكتابة كود عرض الصفحة:

  1. علامات HTML الكلاسيكية. وهذا ما كنا نستخدمه حتى الآن.
  2. علامات HTML من جانب الخادم
  3. علامات نماذج الويب

بغض النظر عن مكتبة العلامات المستخدمة، يظل دور وحدة التحكم في الصفحة كما هو. يجب أن تحسب قيمة المعلمات الديناميكية التي تظهر في كود العرض. حتى الآن، كانت هذه المعلمات الديناميكية بسيطة: كانت كائنات من النوع [String]. لذا، إذا كان لدينا في كود العرض علامة <%=name%>:

  • يعلن وحدة التحكم في الصفحة متغير [name] من النوع [String] ويحسب قيمته
  • عندما تنتهي وحدة التحكم في الصفحة من عملها ويتم تنفيذ كود العرض لإنشاء استجابة HTML، يتم استبدال علامة <%=name%> بالقيمة التي حسبها كود وحدة التحكم

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

تسمح لك علامات [server HTML] و [WebForms] بتضمين كائنات في كود العرض أكثر تعقيدًا من كائن [String] البسيط. قد يكون هذا مفيدًا جدًا في بعض الأحيان. لنأخذ مثال نموذج يحتوي على قائمة. يجب عرض هذه القائمة للعميل باستخدام كود HTML يبدو كالتالي:

<select name="uneListe" size="3">
    <option value="opt1">option1</option>
    <option value="opt2">option2</option>
    <option value="opt3" selected>option3</option>
</select>

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

<%=uneListeHTML%>

سيتم استبدال هذه العلامة بقيمة [String] للمتغير [uneListeHTML]. ويجب أن تكون هذه القيمة، التي يحسبها وحدة التحكم، هي كود HTML الخاص بالقائمة، أي "<select name=..>...</select>". وهذا الأمر ليس صعبًا بشكل خاص، ويبدو حلاً أنيقًا يتجنب وضع كود التوليد مباشرةً في طبقة العرض الخاصة بالصفحة. هنا، سيكون هناك حلقة مع اختبارات يجب إدراجها فيها، مما سيؤدي إلى "ازدحامها" بشكل كبير. ومع ذلك، فإن هذه الطريقة لها عيب. فالفصل بين التحكم والعرض في الصفحة يساعد أيضًا على تحديد مجالين للمسؤولية:

  • مسؤولية مطور .NET، الذي يتولى التحكم في الصفحة،
  • ومجال مسؤولية مصمم الجرافيك، الذي يتولى جزء العرض في الصفحة

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

تحل مكتبات علامات الخادم هذه المشكلة. فهي توفر كائنًا يمثل قائمة HTML. على سبيل المثال، توفر مكتبة [WebForms] العلامة التالية:

<asp:ListBox id="uneListe" runat="server"></asp:ListBox>

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

<asp:ListBox id="uneListe" runat="server"></asp:ListBox>

بكود HTML الذي يمثل كائن [uneListe]، أي الكود "<select ..>...</select>". في الوقت الحالي، لا يوجد فرق جوهري عن الطريقة السابقة سوى نهج البرمجة الموجهة للكائنات، وهو أمر مثير للاهتمام. لنعد إلى مصمم الجرافيك الذي يحتاج إلى تعديل "مظهر" القائمة. تحتوي علامات الخادم على سمات نمط (BackColor، Bordercolor، BorderWidth، ...) تسمح لك بتعيين المظهر المرئي للكائن HTML المقابل. لذا يمكننا كتابة:


                <asp:ListBox id="ListBox1" runat="server" BackColor="#ffff99"></asp:ListBox></P>

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

يتكيف HTML الذي يتم إنشاؤه لعلامة ما مع نوع متصفح العميل. عندما يرسل المتصفح طلبًا إلى خادم الويب، فإنه يضم رأس [User-Agent: xx] ضمن رؤوس HTTP الخاصة به، حيث يحدد [xx] هوية العميل. وإليك مثال على ذلك:

User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7b) Gecko/20040316

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

أخيرًا، تسمح بيئات التطوير المتقدمة مثل Visual Studio.NET وWebMatrix وغيرها بتصميم واجهة الويب "بأسلوب Windows". ورغم أن هذه الأدوات ليست أساسية، إلا أنها توفر مساعدة كبيرة للمطور. يقوم المطور بتصميم واجهة الويب باستخدام مكونات رسومية يضعها على الواجهة. ولديه وصول مباشر إلى خصائص كل مكون من مكونات الواجهة، والتي يمكنه تكوينها حسب الرغبة. وتُترجم هذه الخصائص إلى كود عرض HTML للواجهة كسمات لعلامة <asp:> الخاصة بالمكون. وتتمثل الفائدة للمطور في أنه لا يتعين عليه حفظ قائمة سمات كل علامة أو صيغتها. وتعد هذه ميزة كبيرة عندما لا يكون المرء على دراية كاملة بمكتبات علامات الخادم التي يوفرها ASP.NET. وبمجرد إتقان هذه الصيغة، قد يفضل بعض المطورين كتابة العلامات مباشرةً في كود عرض الصفحة دون المرور بمرحلة التصميم الرسومي. وبالتالي، لم يعد هناك حاجة إلى بيئة تطوير متكاملة (IDE)؛ يكفي استخدام محرر نصوص بسيط. اعتمادًا على سير عملك، ينصب التركيز بعد ذلك على المكونات (باستخدام بيئة تطوير متكاملة) أو العلامات (باستخدام محرر نصوص). هذان المصطلحان متكافئان. المكون هو الكائن الذي سيتم التعامل معه بواسطة كود التحكم في الصفحة. تتيح لنا بيئة التطوير المتكاملة الوصول إلى خصائصه خلال مرحلة التصميم. يتم ترجمة القيم المخصصة لهذه الخصائص على الفور إلى سمات علامة المكون في كود العرض. خلال مرحلة وقت التشغيل، سيتحكم كود التحكم في الصفحة في المكون ويخصص قيمًا لبعض خصائصه. سيقوم كود العرض بإنشاء كود HTML الخاص بالمكون باستخدام، من ناحية، السمات المحددة أثناء التصميم لعلامة الخادم المقابلة، ومن ناحية أخرى، قيم خصائص المكون المحسوبة بواسطة كود التحكم.

7.2. سياق تنفيذ الأمثلة

سنوضح تصميم واجهات الويب استنادًا إلى مكونات الخادم باستخدام برامج سيكون سياق تنفيذها في الغالب كما يلي:

  1. سيتألف تطبيق الويب من صفحة واحدة P تحتوي على نموذج F،
  2. سيقوم العميل بتقديم طلبه الأول مباشرة إلى هذه الصفحة P. وسيتضمن ذلك طلب عنوان URL للصفحة P باستخدام متصفح. وبالتالي، سيتم إرسال طلب GET إلى عنوان URL P هذا. سيقوم الخادم بتسليم الصفحة P وبالتالي النموذج F الذي تحتوي عليه،
  3. سيقوم المستخدم بملئه وإرساله، أي أنه سيقوم بإجراء يفرض على المتصفح إرسال النموذج F إلى الخادم. ستوجه عملية POST للمتصفح دائمًا إلى الصفحة P. سيقوم الخادم مرة أخرى بتسليم الصفحة P مع النموذج F، الذي قد يكون محتواه قد تم تعديله بفعل إجراء المستخدم.
  4. ثم ستستأنف الخطوتان 2 و 3.

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

7.3. مكون التسمية

7.3.1. الاستخدام

تسمح لك علامة <asp:label> بإدراج نص ديناميكي في كود العرض للصفحة. وبالتالي، فهي لا تفعل أكثر من علامة <%=variable%> المستخدمة حتى الآن. ستساعدنا دراسة هذه العلامة الأولى على فهم كيفية عمل علامات الخادم. سننشئ صفحة تحتوي على قسم تحكم [form1.aspx.vb] وقسم عرض [form1.aspx]. الهدف هو عرض الوقت:

Image

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


<%@ page src="form1.aspx.vb" inherits="form1" AutoEventWireup="false" %>
<HTML>
    <HEAD>
        <title>Webforms</title>
    </HEAD>
    <body>
        <asp:Label Runat="server" ID="lblHeure" />
    </body>
</HTML>

نقدم علامة <asp:label>. في مكتبات العلامات، تكون السمة [runat="server"] مطلوبة. تحدد السمة ID المكون. يجب أن يشير وحدة التحكم إليه باستخدام هذا المعرف. يكون كود وحدة التحكم [form1.aspx.vb] كما يلي:


Imports System.Web.UI.WebControls
 
Public Class form1
    Inherits System.Web.UI.Page
 
    Protected lblHeure As Label
 
    Private Sub Page_Init(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Init
        ' saves the current query in request.txt of the page folder
        Dim requestFileName As String = Me.MapPath(Me.TemplateSourceDirectory) + "\request.txt"
        Me.Request.SaveAs(requestFileName, True)
    End Sub
 
    Private Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
        ' sets time in lblHeure
        lblHeure.Text = "Il est " + Date.Now.ToString("T")
    End Sub
 
End Class

يجب أن يقوم وحدة التحكم بتعيين قيمة لكائن [lblHeure] من النوع [System.Web.UI.WebControls.Label]. جميع الكائنات المعروضة بواسطة علامات <asp:> تنتمي إلى مساحة الاسم [System.Web.UI.WebControls]. لذلك، يمكننا استيراد مساحة الاسم هذه بشكل منهجي:


Imports System.Web.UI.WebControls

يحتوي الكائن [Label] على خصائص متنوعة، بما في ذلك الخاصية [Text]، التي تمثل النص الذي سيتم عرضه بواسطة علامة <asp:label> المقابلة. هنا، نقوم بتعيين هذه الخاصية على الوقت الحالي. نقوم بذلك في إجراء [Form_Load] الخاص بوحدة التحكم، والذي يتم تنفيذه دائمًا. في إجراء [Form_Init]، الذي يتم تنفيذه أيضًا دائمًا ولكن قبل إجراء [Form_Load]، نقوم بتخزين طلب العميل في ملف [request.txt] في مجلد التطبيق. ستتاح لنا الفرصة لفحص هذا الملف لفهم جوانب معينة من كيفية عمل الصفحات التي تستخدم علامات الخادم.

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

7.3.2. الاختبارات

نضع الملفات (form1.aspx، form1.aspx.vb) في مجلد <application-path> ونطلق Cassini بالمعلمات (<application-path>,/form1). ثم نطلب عنوان URL [http://localhost/form1/form1.aspx]. نحصل على النتيجة التالية:

Image

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

<HTML>
    <HEAD>
        <title>Webforms</title>
    </HEAD>
    <body>
        <span id="lblHeure">Il est 19:39:37</span>
    </body>
</HTML>

يمكننا أن نرى أن علامة الخادم


        <asp:Label Runat="server" ID="lblHeure" />

قد تم تحويلها إلى كود HTML التالي:

        <span id="lblHeure">Il est 19:39:37</span>

إنها الخاصية [Text] للكائن [lblHeure] التي تم وضعها بين علامتي <span> و </span>. الطلب الذي أرسله العميل وتم تخزينه في [request.txt] هو كما يلي:

GET /form1/form1.aspx HTTP/1.1
Cache-Control: max-age=0
Connection: keep-alive
Keep-Alive: 300
Accept: application/x-shockwave-flash,text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Accept-Encoding: gzip,deflate
Accept-Language: en-us,en;q=0.5
Host: localhost
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7b) Gecko/20040316

لا شيء خارج عن المألوف.

7.3.3. بناء التطبيق باستخدام WebMatrix

قمنا ببناء كود العرض لصفحة [form1.aspx] يدويًا. هذه الطريقة مفيدة إذا كنت تعرف العلامات. يكفي عندئذٍ محرر نصوص بسيط لإنشاء واجهة المستخدم. للبدء، غالبًا ما تكون أداة تصميم رسومية مقترنة بإنشاء تلقائي للكود ضرورية لأنك لا تعرف صيغة العلامات التي تحتاجها. سنقوم الآن بإنشاء نفس التطبيق باستخدام أداة WebMatrix. بمجرد تشغيل WebMatrix، نختار خيار [File/New File]:

Image

نقوم بإنشاء صفحة ASP.NET باسم [form2.aspx]. بعد تأكيد المعالج السابق، نرى نافذة التصميم لصفحة [form2.aspx]:

Image

Image

لاحظ أن WebMatrix يضع كلاً من كود التحكم في الصفحة وكود العرض في ملف واحد، وهو في هذه الحالة [form2.aspx]. تعرض علامة التبويب [All] محتويات هذا الملف النصي. يمكننا بالفعل أن نرى أنه ليس فارغًا:

Image

عيب هذا النوع من الأدوات هو أنه غالبًا ما يولد كودًا غير ضروري. هذا هو الحال هنا، حيث قام WebMatrix بإنشاء علامة HTML <form> على الرغم من أننا لن نقوم بإنشاء نموذج... بالإضافة إلى ذلك، يمكننا أن نرى أن المستند يفتقر إلى علامة <title>. سنقوم بإصلاح هاتين المشكلتين على الفور للحصول على النسخة المحدثة التالية:

Image

سيتم إدراج ما نسميه كود وحدة التحكم بين علامتي <script> و </script>، مما يضمن على الأقل فصلًا بصريًا بين نوعي الكود: التحكم والعرض. نعود إلى علامة التبويب [Design] لتصميم واجهتنا. تتوفر قائمة بالمكونات في نافذة أدوات على يسار نافذة التصميم:

Image

توفر نافذة الأدوات الوصول إلى نوعين من المكونات:

  • مكونات [WebControls]، التي تُترجم إلى علامات <asp:>
  • مكونات [HTML Elements]، التي تُترجم إلى علامات HTML قياسية. ومع ذلك، يمكنك إضافة السمة [runat="server"] إلى سمات علامة HTML. في هذه الحالة، يمكن للوحدة التحكم الوصول إلى علامة HTML وسماتها عبر كائن تتوافق خصائصه مع خصائص علامة HTML التي يمثلها. سبق أن أشرنا إلى هذه العلامات باسم علامات HTML من جانب الخادم.

انقر نقرًا مزدوجًا فوق مكون [Label] في قائمة [WebControls]. في علامة التبويب [Design]، ستحصل على النتيجة التالية:

Image

في علامة التبويب [All]، أصبح الكود كما يلي:

<%@ Page Language="VB" %>
<html>
<head>
    <title>webforms</title>
</head>
<body>
    <asp:Label id="Label1" runat="server">Label</asp:Label>
</body>
</html>

أولاً، ستلاحظ أن علامة <script> قد اختفت. تم إنشاء علامة <asp:label>. لها اسم [Label1] وقيمة [Label]. لنعد إلى علامة التبويب [Design] لتغيير هاتين القيمتين. انقر مرة واحدة على مكون [Label] لإظهار نافذة خصائصه في الزاوية اليمنى السفلية:

Image

ندعو القارئ إلى مراجعة خصائص كائن [Label]. هناك خاصيتان مهمتان هنا:

  • Text: هذا هو النص الذي يجب أن تعرضه التسمية — قمنا بتعيين السلسلة على فارغة (أي لا شيء)
  • ID: هذا هو معرفه — قمنا بتعيينه على lblHeure

تبدو علامة التبويب [تصميم] الآن كما يلي:

Image

ويصبح كود [All] كما يلي:

<%@ Page Language="VB" %>
<html>
<head>
    <title>webforms</title>
</head>
<body>
    <asp:Label id="lblHeure" runat="server"></asp:Label>
</body>
</html>

تم الانتهاء من تخطيط الصفحة. لا يزال يتعين علينا كتابة كود التحكم المسؤول عن ضبط الوقت في خاصية [Text] الخاصة بـ [lblHeure]. نضيف الكود التالي في علامة التبويب [All]:


<%@ Page Language="VB" %>
 
<script runat="server">
    Private Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
        ' sets time in lblHeure
        lblHeure.Text = "Il est " + Date.Now.ToString("T")
    End Sub
</script>
 
<html>
<head>
    <title>webforms</title>
</head>
<body>
    <asp:Label id="lblHeure" runat="server"></asp:Label>
</body>
</html>

لاحظ أنه في كود وحدة التحكم، لم يتم تعريف الكائن [lblHeure] كما كان سابقًا:


    Protected lblHeure As New System.Web.UI.WebControls.Label

في الواقع، يتم إعلان جميع مكونات الخادم <asp:> في طبقة العرض ضمنيًا في كود وحدة التحكم. يؤدي الإعلان عنها صراحةً إلى حدوث خطأ في التجميع، مما يشير إلى أن الكائن قد تم إعلانه بالفعل. نحن جاهزون لتشغيل الكود. نختار الخيار [View/Start] أو نضغط على الاختصار [F5]. يتم تشغيل Cassini تلقائيًا بالمعلمات التالية:

Image

نقبل هذه القيم. يفتح متصفح النظام الافتراضي تلقائيًا لطلب عنوان URL [http://localhost/form2.aspx]. نحصل على النتيجة التالية:

Image

من الآن فصاعدًا، سنستخدم أداة WebMatrix بشكل أساسي لتسهيل تطوير واختبار البرامج القصيرة التي سنكتبها.

7.4. المكون الحرفي

7.4.1. الاستخدام

تسمح لك علامة <asp:literal> بإدراج نص ديناميكي في كود عرض الصفحة، على غرار علامة <asp:label>. السمة الرئيسية لها هي [Text]، والتي تمثل النص الذي سيتم إدراجه كما هو في تدفق HTML للصفحة. هذه العلامة كافية إذا كنت لا تنوي تنسيق النص الذي تريد إدراجه في تدفق HTML. في الواقع، بينما تسمح فئة [Label] بالتنسيق باستخدام سمات مثل [BorderColor، BorderWidth، Font، ...]، فإن فئة [Literal] لا تحتوي على أي من هذه السمات. يمكن للقارئ إعادة إنتاج المثال السابق بالكامل عن طريق استبدال مكون [Label] بمكون [Literal].

7.5. مكون Button

7.5.1. الاستخدام

تسمح لك علامة <asp:Button> بإدراج زر من نوع [Submit] في نموذج، مما يجلب معه معالجة أحداث مشابهة لتلك الموجودة في تطبيقات Windows. هذه هي النقطة التي نريد استكشافها أكثر هنا. نقوم بإنشاء الصفحة [form3.aspx] التالية:

Image

تحتوي هذه الصفحة، التي تم إنشاؤها باستخدام WebMatrix، على ثلاثة مكونات:

رقم
الاسم
النوع
الخصائص
الدور
1
زر1
زر
text=Button1
زر الإرسال
2
زر 2
زر
text=زر 2
زر الإرسال
3
lblInfo
تسمية
text=
رسالة معلومات

الرمز الذي أنشأه WebMatrix لهذا القسم هو كما يلي:

<%@ Page Language="VB" %>
<script runat="server">
</script>
<html>
<head>
    <title>asp:button</title>
</head>
<body>
    <form runat="server">
        <p>
            <asp:Button id="Button1" runat="server" Text="Bouton1"></asp:Button>
            <asp:Button id="Button2" runat="server" Text="Bouton2"></asp:Button>
        </p>
        <p>
            <asp:Label id="lblInfo" runat="server"></asp:Label>
        </p>
    </form>
</body>
</html>

نرى مكونات [Button] و [Label] المستخدمة في التصميم الرسومي للصفحة داخل علامات <asp:>. لاحظ علامة <form runat="server">، التي تم إنشاؤها تلقائيًا. هذه علامة HTML من جانب الخادم—أي علامة HTML قياسية يمثلها كائن يمكن للوحدة التحكمية التعامل معه. سيتم إنشاء كود HTML لعلامة <form> بناءً على القيمة التي تخصصها الوحدة التحكمية لهذا الكائن.

دعونا نضيف الإجراء [Page_Init] إلى قسم وحدة التحكم في الكود، الذي يتعامل مع حدث [Init] للصفحة. سنضع الكود هناك الذي يحفظ طلب العميل في ملف [request.txt]. سنحتاج إلى هذا لفهم كيفية عمل الأزرار.

<script runat="server">
    Private Sub Page_Init(ByVal sender As Object, ByVal e As System.EventArgs) 
        ' saves the current request in request.txt of the page folder
        Dim requestFileName As String = Me.MapPath(Me.TemplateSourceDirectory) + "\request.txt"
        Me.Request.SaveAs(requestFileName, True)
    End Sub
</script>

لاحظ أننا لم نقم بتضمين جملة [Handles MyBase.Init] بعد إعلان الإجراء [Page_Init]. وذلك لأن الحدث [Init] للكائن [Page] يحتوي على معالج افتراضي باسم [Page_Init]. إذا استخدمنا اسم المعالج هذا، فإن جملة [Handles Page.Init] تصبح غير ضرورية. ومع ذلك، فإن تضمينها لا يتسبب في حدوث خطأ.

7.5.2. الاختبار

نقوم بتشغيل التطبيق في WebMatrix بالضغط على [F5]. نحصل على الصفحة التالية:

Image

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

<html>
<head>
    <title>asp:button</title>
</head>
<body>
    <form name="_ctl0" method="post" action="form3.aspx" id="_ctl0">
<input type="hidden" name="__VIEWSTATE" value="dDwxNTY0NjIwMjUwOzs+2mcnJczeuvF2PEfvmtv7uiUhWUw=" />

        <p>
            <input type="submit" name="Button1" value="Bouton1" id="Button1" />
            <input type="submit" name="Button2" value="Bouton2" id="Button2" />

        </p>
        <p>
            <span id="lblInfo"></span>
        </p>
    </form>
</body>
</html>

لاحظ النقاط التالية:

  • تم تحويل العلامة <form runat="server"> إلى علامة HTML
    <form name="_ctl0" method="post" action="form3.aspx" id="_ctl0">

تم تعيين سمتين: [method="post"] و [action="form3.aspx"]. هل يمكننا تعيين قيم مختلفة لهذه السمات؟ سنحاول توضيح هذه النقطة لاحقًا. لاحظ هنا أن النموذج سيتم إرساله إلى عنوان URL [form3.aspx]. كما تم تعيين سمتين أخريين [name، id]. في معظم الأحيان، يتم تجاهلهما. ومع ذلك، إذا كانت الصفحة تحتوي على كود JavaScript من جانب المتصفح، فإن السمة [name] لعلامة <form> تكون مفيدة.

  • أصبحت علامات <asp:button> علامات زر [submit] في HTML. وبالتالي، سيؤدي النقر فوق أي من هذه الأزرار إلى تشغيل "إرسال" النموذج [_ctl10] إلى عنوان URL [form3.aspx].
  • أصبحت علامة <asp:label> علامة HTML <span>
  • تم إنشاء حقل مخفي [__VIEWSTATE] بقيمة غريبة:
<input type="hidden" name="__VIEWSTATE" value="dDwxNTY0NjIwMjUwOzs+2mcnJczeuvF2PEfvmtv7uiUhWUw=" />

يمثل هذا الحقل، في شكل مشفر، حالة النموذج المرسلة إلى العميل. تمثل هذه الحالة قيمة جميع مكوناته. نظرًا لأن [__VIEWSTATE] جزء من النموذج، فسيتم إرسال قيمته مع الباقي إلى الخادم. سيسمح هذا للخادم بمعرفة مكون النموذج الذي تغيرت قيمته، واتخاذ القرارات اللازمة إذا لزم الأمر. ستتخذ هذه القرارات شكل أحداث مثل "تغيرت قيمة مربع النص الذي يحمل الاسم الفلاني".

7.5.3. طلبات العميل

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

GET /form3.aspx HTTP/1.1
Connection: keep-alive
Keep-Alive: 300
Accept: application/x-shockwave-flash,text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Accept-Encoding: gzip,deflate
Accept-Language: en-us,en;q=0.5
Host: localhost
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7b) Gecko/20040316

هذا طلب GET قياسي. الآن دعونا نضغط على زر [Button1] في الصفحة في المتصفح. يبدو أنه لم يحدث شيء. ومع ذلك، نحن نعلم أن النموذج قد تم إرساله. ويؤكد ذلك كود HTML للصفحة. ويتم تأكيد ذلك من خلال المحتوى الجديد لملف [request.txt]:

POST /form3.aspx HTTP/1.1
Connection: keep-alive
Keep-Alive: 300
Content-Length: 80
Content-Type: application/x-www-form-urlencoded
Accept: application/x-shockwave-flash,text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Accept-Encoding: gzip,deflate
Accept-Language: en-us,en;q=0.5
Host: localhost
Referer: http://localhost/form3.aspx
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7b) Gecko/20040316

__VIEWSTATE=dDwxNTY0NjIwMjUwOzs%2B2mcnJczeuvF2PEfvmtv7uiUhWUw%3D&Button1=Bouton1

يشير رأس HTTP الأول بوضوح إلى أن العميل أرسل طلب POST إلى عنوان URL [/form3.aspx]. ويُظهر السطر الأخير القيم التي تم إرسالها:

  • قيمة الحقل المخفي __VIEWSTATE
  • قيمة الزر الذي تم النقر عليه

إذا نقرنا على [Button2]، فإن القيم التي أرسلها المتصفح هي كما يلي:

__VIEWSTATE=dDwxNTY0NjIwMjUwOzs%2B2mcnJczeuvF2PEfvmtv7uiUhWUw%3D&Button2=Bouton2

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

7.5.4. معالجة حدث النقر على كائن زر

دعونا نستعرض كيفية عمل صفحة .aspx. إنها كائن مشتق من فئة [Page]. دعونا نسمي الفئة المشتقة [aPage]. عندما يتلقى الخادم طلبًا لمثل هذه الصفحة، يتم إنشاء مثيل لكائن من النوع [unePage] عبر العملية new unePage(...). ثم يقوم الخادم بإنشاء حدثين يسميان [Init] و [Load] بهذا الترتيب. يمكن لكائن [unePage] معالجة هذين الحدثين من خلال توفير معالجي الأحداث [Page_Init] و [Page_Load]. سيتم إنشاء أحداث أخرى لاحقًا. ستتاح لنا الفرصة لمراجعتها لاحقًا. إذا كان طلب العميل هو POST، فسيقوم الخادم بإنشاء حدث [Click] للزر الذي أطلق هذا الطلب POST. إذا كانت فئة [unePage] قد وفرت معالجًا لهذا الحدث، فسيتم استدعاؤه. دعونا نستكشف هذه الآلية باستخدام WebMatrix. في علامة التبويب [Design] في [form3.aspx]، انقر نقرًا مزدوجًا على الزر [Button1]. سننتقل بعد ذلك تلقائيًا إلى علامة التبويب [Code]، داخل نص إجراء يسمى [Button1_Click]. لفهم الأمر بشكل أفضل، دعونا ننتقل إلى علامة التبويب [All] ونلقي نظرة على الكود بأكمله. تم إجراء التغييرات التالية:

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

...
    Sub Button1_Click(sender As Object, e As EventArgs)

    End Sub

</script>
<html>
...
<body>
    <form runat="server">
...
            <asp:Button id="Button1" onclick="Button1_Click" runat="server" Text="Bouton1"></asp:Button>
    </form>
</body>
</html>

تمت إضافة سمة جديدة [onclick="Button1_Click"] إلى علامة <asp:Button> لـ [Button1]. تحدد هذه السمة الإجراء المسؤول عن معالجة حدث [Click] على كائن [Button1]، وهو في هذه الحالة الإجراء [Button1_Click]. كل ما تبقى هو كتابته:

    Sub Button1_Click(sender As Object, e As EventArgs)
        ' click on button 1
        lblInfo.Text="Vous avez cliqué sur [Bouton1]"
    End Sub

يعرض الإجراء رسالة إعلامية في التسمية [lblInfo]. نواصل بنفس الطريقة بالنسبة للزر [Button2] للحصول على الصفحة الجديدة التالية [form3.aspx]:

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

    Private Sub Page_Init(ByVal sender As Object, ByVal e As System.EventArgs)
        ' sauve la requte courante dans request.txt du dossier de la page
        Dim requestFileName As String = Me.MapPath(Me.TemplateSourceDirectory) + "\request.txt"
        Me.Request.SaveAs(requestFileName, True)
    End Sub

    Sub Button1_Click(sender As Object, e As EventArgs)
        ' clic sur bouton 1
        lblInfo.Text="Vous avez cliqué sur [Bouton1]"
    End Sub

    Sub Button2_Click(sender As Object, e As EventArgs)
        ' clic sur bouton 2
        lblInfo.Text="Vous avez cliqué sur [Bouton2]"
    End Sub

</script>
<html>
<head>
    <title>asp:button</title>
</head>
<body>
    <form runat="server">
        <p>
            <asp:Button id="Button1" onclick="Button1_Click" runat="server" Text="Bouton1"></asp:Button>
            <asp:Button id="Button2" onclick="Button2_Click" runat="server" Text="Bouton2" BorderStyle="None"></asp:Button>
        </p>
        <p>
            <asp:Label id="lblInfo" runat="server"></asp:Label>
        </p>
    </form>
</body>
</html>

نضغط على [F5] لتشغيل الكود ونحصل على الصفحة التالية:

Image

إذا نظرنا إلى كود HTML الذي تم استلامه، فسنرى أنه لم يتغير عن الإصدار السابق للصفحة:

<html>
<head>
    <title>asp:button</title>
</head>
<body>
    <form name="_ctl0" method="post" action="form3.aspx" id="_ctl0">
<input type="hidden" name="__VIEWSTATE" value="dDwxNTY0NjIwMjUwOzs+2mcnJczeuvF2PEfvmtv7uiUhWUw=" />

        <p>
            <input type="submit" name="Button1" value="Bouton1" id="Button1" />
            <input type="submit" name="Button2" value="Bouton2" id="Button2" />
        </p>
        <p>
            <span id="lblInfo"></span>
        </p>
    </form>
</body>
</html>

إذا نقرنا على [Button1]، فسنحصل على الاستجابة التالية:

Image

رمز HTML الذي تم استلامه لهذا الرد هو كما يلي:

<html>
<head>
    <title>asp:button</title>
</head>
<body>
    <form name="_ctl0" method="post" action="form3.aspx" id="_ctl0">
<input type="hidden" name="__VIEWSTATE" value="dDwxNTY0NjIwMjUwO3Q8O2w8aTwxPjs+O2w8dDw7bDxpPDU+Oz47bDx0PHA8cDxsPFRleHQ7PjtsPFZvdXMgYXZleiBjbGlxdcOpIHN1ciBbQm91dG9uMV07Pj47Pjs7Pjs+Pjs+Pjs+4oO98Vd244kj0lPMXReWOwJ1WW0=" />

        <p>
            <input type="submit" name="Button1" value="Bouton1" id="Button1" />
            <input type="submit" name="Button2" value="Bouton2" id="Button2" />
        </p>
        <p>
            <span id="lblInfo">Vous avez cliqué sur [Bouton1]</span>
        </p>
    </form>
</body>
</html>

يمكننا أن نرى أن الحقل المخفي [__VIEWSTATE] قد غير قيمته. وهذا يعكس التغيير في قيمة مكون [lblInfo].

7.5.5. الأحداث في دورة حياة تطبيق ASP.NET

تسرد وثائق ASP.NET الأحداث التي يولدها الخادم خلال دورة حياة التطبيق:

  • عندما يتلقى التطبيق أول طلب له، يتم إنشاء الحدث [Start] على كائن [HttpApplication] الخاص بالتطبيق. يمكن معالجة هذا الحدث بواسطة الإجراء [Application_Start] في ملف [global.asax] الخاص بالتطبيق.

بعد ذلك، ستكون لدينا سلسلة من الأحداث التي ستتكرر لكل طلب يتم استلامه:

  • إذا لم يرسل الطلب رمز جلسة عمل، يتم بدء جلسة عمل جديدة ويتم إنشاء حدث [Start] على كائن [Session] المرتبط بالطلب. يمكن معالجة هذا الحدث بواسطة الإجراء [Session_Start] في ملف [global.asax] الخاص بالتطبيق.
  • يقوم الخادم بإنشاء حدث [BeginRequest] على كائن [HttpApplication]. ويمكن معالجته بواسطة الإجراء [Application_BeginRequest] في ملف [global.asax] الخاص بالتطبيق.
  • يقوم الخادم بتحميل الصفحة المطلوبة في الطلب. وسيقوم بإنشاء مثيل لكائن [Page] ثم إنشاء حدثين على هذا الكائن: [Init] ثم [Load]. يمكن معالجة هذين الحدثين بواسطة إجراءات [Page_Init] و [Page_Load] الخاصة بالصفحة.
  • بناءً على القيم المرسلة المستلمة، سيقوم الخادم بإنشاء أحداث أخرى: [TextChanged] لمكون [TextBox] الذي تغيرت قيمته، و[CheckedChanged] لزر الاختيار الذي تغيرت قيمته، و[SelectedIndexChanged] لقائمة تغير العنصر المحدد فيها، ... ستتاح لنا الفرصة لذكر الأحداث الرئيسية لكل مكون من مكونات الخادم التي سنقدمها. يمكن معالجة كل حدث E على كائن يسمى O بواسطة إجراء يسمى O_E.
  • لا يمكن ضمان الترتيب الذي يتم به معالجة الأحداث السابقة. لذلك، يجب ألا تفترض المعالجات أي شيء بشأن هذا الترتيب. ومع ذلك، يمكننا التأكد من أن حدث [Click] للزر الذي أطلق POST تتم معالجته أخيرًا.
  • بمجرد أن تصبح الصفحة جاهزة، سيرسلها الخادم إلى العميل. قبل القيام بذلك، يقوم بتوليد حدث [PreRender]، والذي يمكن معالجته بواسطة إجراء [Page_PreRender] الخاص بالصفحة.
  • بمجرد إرسال استجابة HTML إلى العميل، سيتم إلغاء تحميل الصفحة من الذاكرة. سيتم إنشاء حدثين في هذا الوقت: [Unload] و [Disposed]. يمكن للصفحة استخدام هذين الحدثين لتحرير الموارد.

يمكن للتطبيق أيضًا تلقي أحداث خارج نطاق طلب العميل:

  • يحدث الحدث [End] على كائن [Session] للتطبيق عند انتهاء الجلسة. يمكن أن يحدث هذا إما بناءً على طلب صريح من كود الصفحة أو لأن مدة صلاحية الجلسة قد انتهت. يتعامل الإجراء [Session_End] في ملف [global.asax] مع هذا الحدث. وعادةً ما يحرر الموارد التي تم الحصول عليها في [Session_Start].
  • يحدث الحدث [End] في كائن [HttpApplication] الخاص بالتطبيق عند إنهاء التطبيق. يحدث هذا، على سبيل المثال، عند إيقاف تشغيل خادم الويب. تتولى الإجراء [Application_End] في ملف [global.asax] معالجة هذا الحدث. وعادةً ما يُستخدم لتحرير الموارد التي تم الحصول عليها في [Application_Start].

يجب ملاحظة النقاط التالية:

  • يعتمد نموذج الحدث السابق على تبادلات HTTP القياسية بين العميل والخادم. ويتضح ذلك بجلاء عند فحص رؤوس HTTP المتبادلة.
  • تتم معالجة الأحداث السابقة دائمًا على جانب الخادم. يمكن بالطبع معالجة النقر فوق زر بواسطة برنامج نصي JavaScript على جانب الخادم. ومع ذلك، لا يعد هذا حدثًا للخادم، ونحن نتعامل مع تقنية مستقلة عن ASP.NET.

متى تتم معالجة الأحداث، سواء على جانب الخادم (الأحداث المتعلقة بمكونات الخادم) أو على جانب المتصفح بواسطة نصوص JavaScript؟

لنأخذ مثال قائمة منسدلة. عندما يغير المستخدم العنصر المحدد فيها، قد تتم معالجة الحدث (تغيير العنصر المحدد) أو لا تتم، وإذا تمت معالجته، فيمكن معالجته في أوقات مختلفة.

  • إذا كنت ترغب في معالجته على الفور، فهناك حلان:
    • يمكن معالجته بواسطة المتصفح باستخدام نصوص JavaScript. لا يتدخل الخادم في هذه الحالة. لكي يكون ذلك ممكنًا، يجب أن تكون الصفحة قابلة لإعادة البناء باستخدام القيم الموجودة على الصفحة.
    • يمكن معالجته بواسطة الخادم. ولهذا، هناك حل واحد فقط: يجب إرسال النموذج إلى الخادم للمعالجة. لذلك لدينا عملية [submit]. سنرى أنه في هذه الحالة، نستخدم مكون خادم يسمى [DropDownList] ونضبط سمة [AutoPostBack] الخاصة به على [true]. وهذا يعني أنه إذا تغير العنصر المحدد في القائمة المنسدلة، يجب إرسال النموذج على الفور إلى الخادم. في هذه الحالة، يقوم الخادم بإنشاء كود HTML لكائن [DropDownList] المرتبط بوظيفة JavaScript مسؤولة عن تنفيذ [submit] بمجرد حدوث حدث "تغيير العنصر المحدد". سيقوم هذا [submit] بإرسال النموذج إلى الخادم، وسيتم تضمين الحقول المخفية في الإرسال للإشارة إلى أن [post] نتج عن تغيير في التحديد في القائمة المنسدلة. ثم سيقوم الخادم بإنشاء حدث [SelectedIndexChanged]، الذي يمكن للصفحة معالجته.
    • إذا كنت ترغب في معالجة الأمر ولكن ليس على الفور، فقم بتعيين السمة [AutoPostBack] لمكون الخادم [DropDownList] على [false]. في هذه الحالة، يقوم الخادم بإنشاء كود HTML القياسي لقائمة <select> الخاصة بكائن [DropDownList]، دون وظيفة JavaScript مرتبطة بها. لا يحدث شيء عندما يغير المستخدم التحديد في القائمة المنسدلة. ومع ذلك، عندما يرسل المستخدم النموذج باستخدام زر [submit]، على سبيل المثال، سيتمكن الخادم من التعرف على حدوث تغيير في التحديد. لقد رأينا بالفعل أن النموذج المرسل إلى الخادم يحتوي على حقل مخفي [__VIEWSTATE] يمثل، في شكل مشفر، حالة جميع العناصر في النموذج المرسل. عندما يتلقى الخادم النموذج الجديد الذي أرسله العميل، يمكنه التحقق مما إذا كان العنصر المحدد في القائمة المنسدلة قد تغير. إذا كان الأمر كذلك، فسيؤدي ذلك إلى تشغيل حدث [SelectedIndexChanged]، والذي يمكن للصفحة معالجته بعد ذلك. ولتمييز هذه الآلية عن الآلية السابقة، يقول بعض المؤلفين إن حدث "تغيير التحديد" يتم "تخزينه مؤقتًا" عندما يحدث في المتصفح. ولن تتم معالجته بواسطة الخادم إلا عندما يرسل المتصفح النموذج إليه، وغالبًا ما يكون ذلك بعد النقر على زر [إرسال].
    • أخيرًا، إذا كنت لا ترغب في معالجة الحدث، فقم بتعيين السمة [AutoPostBack] لمكون الخادم [DropDownList] على [false] ولا تكتب معالجًا لحدث [SelectedIndexChanged] الخاص به.

بمجرد فهم آلية معالجة الأحداث، لن يقوم المطور بتصميم تطبيق ويب مثل تطبيق Windows. في الواقع، في حين أن تغيير التحديد في مربع القائمة المنسدلة داخل تطبيق Windows يمكن استخدامه لتحديث مظهر النموذج الذي يحتوي عليه على الفور، فمن المرجح أن يتردد المرء في معالجة هذا الحدث على الفور في تطبيق ويب إذا كان ينطوي على "إرسال" نموذج إلى الخادم — وبالتالي رحلة ذهاب وإياب بين العميل والخادم. لهذا السبب يتم تعيين الخاصية [AutoPostBack] لمكونات جانب الخادم على [false] بشكل افتراضي. علاوة على ذلك، لا يمكن استخدام آلية [AutoPostBack]، التي تعتمد على نصوص JavaScript التي يتم إنشاؤها تلقائيًا بواسطة خادم الويب في النموذج المرسل إلى العميل، إلا إذا كان المرء متأكدًا من أن متصفح العميل قد قام بتمكين تنفيذ نصوص JavaScript. لذلك غالبًا ما يتم إنشاء النماذج على النحو التالي:

  • يتم تعيين خاصية [AutoPostBack] لمكونات النموذج من جانب الخادم على [false]
  • يحتوي النموذج على زر واحد أو أكثر مسؤول عن تنفيذ عملية [POST]
  • في كود وحدة التحكم بالصفحة، تكتب معالجات للأحداث التي تريد معالجتها فقط، وغالبًا ما يكون ذلك حدث [Click] على أحد الأزرار.

7.6. مكون TextBox

7.6.1. الاستخدام

تسمح لك العلامة <asp:TextBox> بإدراج حقل إدخال في كود عرض الصفحة. نقوم بإنشاء صفحة [form4.aspx] لتحقيق التخطيط التالي:

Image

1234تحتوي هذه الصفحة، التي تم إنشاؤها باستخدام WebMatrix، على المكونات التالية:

رقم
الاسم
النوع
الخصائص
الدور
1
TextBox1
مربع النص
AutoPostback=true
Text=
حقل الإدخال
2
مربع النص 2
مربع نص
إعادة الإرسال التلقائي=false
Text=
حقل الإدخال
3
lblInfo1
تسمية
text=
رسالة معلومات حول محتويات [TextBox1]
3
lblInfo2
تسمية
text=
رسالة معلومات حول محتويات [TextBox2]

الرمز الذي أنشأه WebMatrix لهذا القسم هو كما يلي:

<%@ Page Language="VB" %>
<script runat="server">
</script>
<html>
<head>
    <title>asp:textbox</title>
</head>
<body>
    <form runat="server">
        <p>
            Texte 1 :
            <asp:TextBox id="TextBox1" runat="server" AutoPostBack="True"></asp:TextBox>
        </p>
        <p>
            Texte 2 :
            <asp:TextBox id="TextBox2" runat="server"></asp:TextBox>
        </p>
        <p>
            <asp:Label id="lblInfo1" runat="server"></asp:Label>
        </p>
        <p>
            <asp:Label id="lblInfo2" runat="server"></asp:Label>
        </p>
    </form>
</body>
</html>

في علامة التبويب [تصميم]، انقر نقرًا مزدوجًا فوق المكون [TextBox1]. ثم يتم إنشاء الهيكل الأساسي لمعالج الحدث [TextChanged] لهذا الكائن (في علامة التبويب [الكل]):

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

    Sub TextBox1_TextChanged(sender As Object, e As EventArgs)
    End Sub
</script>
<html>
...
<body>
...
            <asp:TextBox id="TextBox1" runat="server" AutoPostBack="True" OnTextChanged="TextBox1_TextChanged"></asp:TextBox>
        </p>
....
    </form>
</body>
</html>

تمت إضافة السمة [OnTextChanged="TextBox1_TextChanged"] إلى العلامة <asp:TextBox id="TextBox1"> لتحديد معالج الحدث [TextChanged] في [TextBox1]. ونقوم الآن بكتابة الإجراء [TextBox1_Changed].

    Sub TextBox1_TextChanged(sender As Object, e As EventArgs)
      ' text change
      lblInfo1.text=Date.now.Tostring("T") + ": evt [TextChanged] sur [TextBox1]. Texte 1=["+textbox1.Text+"]"
    End Sub

في الإجراء، نكتب رسالة في التسمية [lblInfo1] تشير إلى الحدث وتعرض محتويات [TextBox1]. ونفعل الشيء نفسه مع [TextBox2]. كما نضيف الوقت لتتبع معالجة الأحداث بشكل أفضل. فيما يلي الكود النهائي لـ [form4.aspx]:

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

    Private Sub Page_Init(ByVal sender As Object, ByVal e As System.EventArgs)
        ' sauve la requte courante dans request.txt du dossier de la page
        Dim requestFileName As String = Me.MapPath(Me.TemplateSourceDirectory) + "\request.txt"
        Me.Request.SaveAs(requestFileName, True)
    End Sub

    Sub TextBox1_TextChanged(sender As Object, e As EventArgs)
      ' changement de texte
      lblInfo1.text=Date.now.Tostring("T") + ": evt [TextChanged] sur [TextBox1]. Texte 1=["+textbox1.Text+"]"
    End Sub

    Sub TextBox2_TextChanged(sender As Object, e As EventArgs)
      ' changement de texte
      lblInfo2.text=Date.now.Tostring("T") + ": evt [TextChanged] sur [TextBox2]. Texte 2=["+textbox2.Text+"]"
    End Sub

</script>
<html>
<head>
    <title>asp:textbox</title>
</head>
<body>
    <form runat="server">
        <p>
            Texte 1 :
            <asp:TextBox id="TextBox1" runat="server" AutoPostBack="True" OnTextChanged="TextBox1_TextChanged"></asp:TextBox>
        </p>
        <p>
            Texte 2 :
            <asp:TextBox id="TextBox2" runat="server" OnTextChanged="TextBox2_TextChanged"></asp:TextBox>
        </p>
        <p>
            <asp:Label id="lblInfo1" runat="server"></asp:Label>
        </p>
        <p>
            <asp:Label id="lblInfo2" runat="server"></asp:Label>
        </p>
    </form>
</body>
</html>

لقد أضفنا الإجراء [Page_Init] لتخزين طلب العميل، كما في المثال السابق.

7.6.2. الاختبار

نقوم بتشغيل التطبيق في WebMatrix بالضغط على [F5]. نحصل على الصفحة التالية:

Image

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

<html>
<head>
    <title>asp:textbox</title>
</head>
<body>
    <form name="_ctl0" method="post" action="form4.aspx" id="_ctl0">
<input type="hidden" name="__EVENTTARGET" value="" />
<input type="hidden" name="__EVENTARGUMENT" value="" />
<input type="hidden" name="__VIEWSTATE" value="dDwtMTY4MDc0MTUxOTs7PoqpeSYSCX7lCiWZvw5p7u+/OrTD" />

<script language="javascript">
<!--
    function __doPostBack(eventTarget, eventArgument) {
        var theform = document._ctl0;
        theform.__EVENTTARGET.value = eventTarget;
        theform.__EVENTARGUMENT.value = eventArgument;
        theform.submit();
    }
// -->
</script>

        <p>
            Texte 1 :
            <input name="TextBox1" type="text" id="TextBox1" onchange="__doPostBack('TextBox1','')" language="javascript" />
        </p>
        <p>
            Texte 2 :
            <input name="TextBox2" type="text" id="TextBox2" />
        </p>
        <p>
            <span id="lblInfo1"></span>

        </p>
        <p>
            <span id="lblInfo2"></span>
        </p>
    </form>
</body>
</html>

هناك العديد من العناصر في هذا الكود التي تم إنشاؤها تلقائيًا بواسطة الخادم. لاحظ النقاط التالية:

  • هناك ثلاثة حقول مخفية: [__VIEWSTATE]، التي سبق أن تناولناها، و[__EventTarget]، و[__EventArgument]. ويُستخدم الحقلان الأخيران لمعالجة حدث "التغيير" في المتصفح في حقل الإدخال [TextBox1]
  • قامت علامات الخادم <asp:textbox> بإنشاء علامات HTML <input type="text" ...> التي تتوافق مع حقول الإدخال
  • أنشأت علامة الخادم <asp:textbox id="TextBox1" AutoPostBack="true" ...> علامة <input type="text" ...> مع سمة [onchange="__doPostBack('TextBox1','')"]. تحدد هذه السمة أنه في حالة تغيير محتوى [TextBox1]، يجب تنفيذ دالة JavaScript [_doPostBack(...)]. وهي كما يلي:
<script language="javascript">
<!--
    function __doPostBack(eventTarget, eventArgument) {
        var theform = document._ctl0;
        theform.__EVENTTARGET.value = eventTarget;
        theform.__EVENTARGUMENT.value = eventArgument;
        theform.submit();
    }
// -->
</script>

ماذا تفعل الدالة أعلاه؟ إنها تعين قيمة لكل من الحقلين المخفيين [__EventTarget] و [__EventArgument]، ثم ترسل النموذج. وبالتالي يتم إرسال النموذج إلى الخادم. هذا هو تأثير [AutoPostBack]. يؤدي حدث "التغيير" في المتصفح إلى تشغيل الكود "__doPostBack('TextBox1','')". يمكننا استنتاج أنه في النموذج المرسَل، سيحتوي الحقل المخفي [__EventTarget] على القيمة 'TextBox1' وسيحتوي الحقل المخفي [__EventArgument] على القيمة ''. وهذا يسمح للخادم بتحديد المكون الذي أطلق POST.

  • أنشأت علامة الخادم <asp:textbox id="TextBox2"...> علامة <input type="text" ...> قياسية لأن سمة [AutoPostBack] الخاصة بها لم تكن مضبوطة على [true].
  • تشير علامة <form> إلى أن النموذج سيتم إرساله إلى [form4.aspx]:
    <form name="_ctl0" method="post" action="form4.aspx" id="_ctl0">

دعونا نجري اختبارنا الأول. اكتب بعض النص في حقل الإدخال الأول:

Image

ثم انقل المؤشر إلى حقل الإدخال الثاني. ستظهر صفحة جديدة على الفور:

Image

ماذا حدث؟ عندما غادر المؤشر حقل الإدخال الأول، تحقق المتصفح مما إذا كان محتواه قد تغير. وقد تغير بالفعل. لذلك، من جانب المتصفح، تم تشغيل الحدث [change] في حقل HTML [TextBox1]. ثم رأينا أن دالة JavaScript تم تنفيذها وأرسلت النموذج إلى [form4.aspx]. ثم أعاد الخادم تحميل هذه الصفحة. سمحت القيم التي أرسلها النموذج للخادم بالتعرف على أن محتوى علامة [TextBox1] من جانب الخادم قد تغير. لذلك تم تنفيذ الإجراء [TextBox1_Changed] من جانب الخادم. ووضع رسالة في التسمية [lblInfo1]. بمجرد انتهاء هذا الإجراء، تم إرسال [form4.aspx] مرة أخرى إلى المتصفح. وهذا هو السبب في وجود نص الآن في [lblInfo1]. ومع ذلك، قد يبدو من المفاجئ وجود أي شيء في حقل الإدخال [TextBox1]. في الواقع، لا يوجد إجراء من جانب الخادم يعين قيمة لهذا الحقل. هذه آلية عامة لنماذج الويب ASP.NET: يعيد الخادم النموذج في الحالة التي استلمه بها. للقيام بذلك، يعيد الخادم تعيين القيمة التي أرسلها العميل للمكونات. بالنسبة لبعض المكونات، لا يرسل العميل أي قيمة. هذا هو الحال، على سبيل المثال، مع مكونات <asp:label>، التي يتم عرضها كعلامات HTML <span>. تذكر أن النموذج يحتوي على حقل مخفي [__VIEWSTATE] يمثل حالة النموذج عند إرساله إلى العميل. هذه الحالة هي مجموع حالات جميع مكونات النموذج، بما في ذلك أي مكونات <asp:label>. نظرًا لأن الحقل المخفي [__VIEWSTATE] يتم إرساله بواسطة متصفح العميل، فإن الخادم قادر على استعادة الحالة السابقة لجميع مكونات النموذج. كل ما يتبقى هو تعديل تلك التي تم تغيير قيمها بواسطة طلب POST.

لنلقِ نظرة الآن في [request.txt] على الطلب الذي قدمه المتصفح:

POST /form4.aspx HTTP/1.1
Connection: keep-alive
Keep-Alive: 300
Content-Length: 137
Content-Type: application/x-www-form-urlencoded
Accept: application/x-shockwave-flash,text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Accept-Encoding: gzip,deflate
Accept-Language: en-us,en;q=0.5
Host: localhost
Referer: http://localhost/form4.aspx
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7b) Gecko/20040316

__EVENTTARGET=TextBox1&__EVENTARGUMENT=&__VIEWSTATE=dDwtMTY4MDc0MTUxOTs7PoqpeSYSCX7lCiWZvw5p7u%2B%2FOrTD&TextBox1=premier+texte&TextBox2=

يمكننا أن نرى بوضوح طلب POST والمعلمات التي يتم إرسالها. لنعد إلى متصفحنا وندخل بعض النص في حقل الإدخال الثاني:

Image

لنعد إلى حقل الإدخال رقم 1 لإدخال نص جديد: لم يحدث شيء هذه المرة. لماذا؟ لأن حقل الإدخال [TextBox2] لا تحتوي خاصية [AutoPostBack] الخاصة به على القيمة [true]، لذا فإن العلامة <input type="text"...> التي تم إنشاؤها له لا تتعامل مع الحدث [Change]، كما هو موضح في كود HTML الخاص به:

            <input name="TextBox2" type="text" id="TextBox2" />

لذلك، لا يحدث أي حدث عند مغادرة حقل الإدخال رقم 2. الآن دعونا ندخل نصًا جديدًا في الحقل رقم 1:

Image

دعونا نغادر حقل الإدخال رقم 1. على الفور، يتم الكشف عن حدث [Change] في هذا الحقل، ويتم إرسال النموذج إلى الخادم، الذي يعرض الصفحة التالية:

Image

ماذا حدث؟ أولاً، أرسل المتصفح النموذج. وينعكس ذلك في طلب العميل المخزن في [request.txt]:

POST /form4.aspx HTTP/1.1
....
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7b) Gecko/20040316

__EVENTTARGET=TextBox1&__EVENTARGUMENT=&__VIEWSTATE=dDwtMTY4MDc0MTUxOTt0PDtsPGk8MT47PjtsPHQ8O2w8aTwxPjtpPDU%2BOz47bDx0PHA8cDxsPFRleHQ7PjtsPHByZW1pZXIgdGV4dGU7Pj47Pjs7Pjt0PHA8cDxsPFRleHQ7PjtsPDE4OjQyOjI5OiBldnQgW1RleHRDaGFuZ2VkXSBzdXIgW1RleHRCb3gxXS4gVGV4dGUgMT1bcHJlbWllciB0ZXh0ZV07Pj47Pjs7Pjs%2BPjs%2BPjs%2BxLOermpUUUz5rTAa%2FFsjda6lVmo%3D&TextBox1=troisi%C3%A8me+texte&TextBox2=second+texte

يبدأ الخادم باستعادة المكونات إلى قيمها السابقة باستخدام الحقل المخفي [__VIEWSTATE] الذي أرسله العميل إليه. وباستخدام الحقول المنشورة [TextBox1] و [TextBox2]، يقوم بتعيين القيم التي تم نشرها إلى المكونات [TextBox1] و [TextBox2]. ومن خلال هذه الآلية يسترد العميل النموذج كما تم نشره. ثم، باستخدام الحقول المنشورة [__VIEWSTATE] و [TextBox1] و [TextBox2] مرة أخرى، يكتشف الخادم أن قيم حقول الإدخال [TextBox1] و [TextBox2] قد تغيرت. وبالتالي، يقوم بتشغيل أحداث [TextChanged] لهذين الكائنين. سيتم تنفيذ الإجراءات [TextBox1_TextChanged] و [TextBox2_TextChanged]، وستتلقى التسميتان [labelInfo1] و [labelInfo2] قيمًا جديدة. ثم يتم إرسال الصفحة [form4.aspx] المعدلة مرة أخرى إلى العميل.

الآن دعونا نعدل حقل الإدخال رقم 1 مرة أخرى:

Image

عندما ننقل المؤشر خارج الحقل 1، يحدث الحدث [Change] في المتصفح. ثم تحدث سلسلة الأحداث التي تم شرحها بالفعل (الإرسال من المتصفح إلى الخادم، ...، إرسال استجابة الخادم). نحصل على الاستجابة التالية:

Image

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

Image

ثم ضع المؤشر على الحقل رقم 1 وحركه مرة أخرى إلى الحقل رقم 2. لا تتغير الصفحة. لماذا؟ نظرًا لأننا لا نغير قيمة الحقل رقم 1، لا يحدث حدث [Change] للمتصفح عندما نغادر هذا الحقل. وبالتالي، لا يتم إرسال النموذج إلى الخادم. وبالتالي، لا يتغير شيء في الصفحة. إن حقيقة أن محتوى [lblInfo2] لا يتغير هي التي توضح لنا أنه لا يوجد POST. لو كان هناك POST، لكان الخادم قد اكتشف أن محتوى [TextBox2] قد تغير وكان يجب أن يعكس ذلك في [lblInfo2].

الدرس المستفاد من هذا المثال هو أنه لا فائدة من تعيين خاصية [AutoPostBack] لـ [TextBox] إلى [true]. فهذا يتسبب في رحلة ذهاب وإياب غير ضرورية بين العميل والخادم في معظم الأحيان.

7.6.3. دور حقل __VIEWSTATE

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

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

...
            <asp:TextBox id="TextBox1" runat="server" OnTextChanged="TextBox1_TextChanged" AutoPostBack="True" EnableViewState="False"></asp:TextBox>
...
            <asp:TextBox id="TextBox2" runat="server" OnTextChanged="TextBox2_TextChanged" EnableViewState="False"></asp:TextBox>
...

الآن دعونا نشغل التطبيق ونكتب بعض النص في الحقل رقم 1 قبل الانتقال إلى الحقل رقم 2. ثم يتم إرسال طلب POST إلى الخادم، ونحصل على الاستجابة التالية:

Image

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

Image

في الوقت الحالي، كل شيء كما كان من قبل. الآن دعونا نعدل محتوى الحقل رقم 1 ثم ننتقل إلى الحقل رقم 2. يتم إرسال طلب POST جديد. استجابة الخادم هي كما يلي:

Image

هذه المرة، هناك تغيير. اكتشف الخادم حدث [TextChanged] في الحقل رقم 2 لأن قيمة [lblInfo2] تم تعديلها. ومع ذلك، لم يكن هناك تغيير فعلي. ويرجع ذلك إلى الخاصية [EnableViewState=false] الخاصة بـ [TextBox2]. وهذا يؤدي إلى عدم قيام الخادم بتخزين الحالة السابقة لـ [TextBox2] في حقل [__VIEWSTATE] الخاص بالنموذج. وهذا يعادل تعيين السلسلة الفارغة كحالة سابقة. عندما حدثت عملية POST التي أطلقها التغيير في محتوى [TextBox1]، قارن الخادم القيمة الحالية لـ [TextBox2]، والتي كانت [two]، بقيمتها السابقة (السلسلة الفارغة). وخلص إلى أن [TextBox2] قد غيرت قيمتها وقام بإنشاء حدث [TextChanged] لـ [TextBox2]. يمكننا التحقق من هذا السلوك عن طريق إدخال سلسلة فارغة في [TextBox2]. استنادًا إلى ما تم شرحه للتو، لا ينبغي أن يقوم الخادم بإنشاء حدث [TextChanged] لـ [TextBox2]. دعونا نجرب ذلك:

Image

هذا هو بالضبط ما حدث. يُظهر الوقت ومحتوى [lblInfo2] أن الإجراء [TextBox2_TextChanged] لم يتم تنفيذه. مع أخذ ذلك في الاعتبار، دعونا نفحص الخاصية [EnableViewState] لمكونات النموذج الأربعة:

TextBox1
نريد الحفاظ على حالة هذا المكون حتى يعرف الخادم ما إذا كان قد تغير أم لا
TextBox2
نفس
lblInfo1
لا نريد الحفاظ على حالة هذا المكون. نريد إعادة حساب النص مع كل طلب POST جديد. إذا لم يتم إعادة حسابه، فيجب أن يكون فارغًا. يتم تحقيق كل هذا باستخدام [EnableViewState=false]
lblInfo2 
نفس

تبدو صفحتنا الرئيسية الآن كما يلي:

...
            <asp:TextBox id="TextBox1" runat="server" OnTextChanged="TextBox1_TextChanged" AutoPostBack="True"></asp:TextBox>
...
            <asp:TextBox id="TextBox2" runat="server" OnTextChanged="TextBox2_TextChanged"></asp:TextBox>
....
            <asp:Label id="lblInfo1" runat="server" enableviewstate="False"></asp:Label>
...
            <asp:Label id="lblInfo2" runat="server" enableviewstate="False"></asp:Label>
...

دعونا نجري نفس سلسلة الاختبارات كما فعلنا من قبل. حيث حصلنا على الشاشة

الإصدار 1

Image

، نحصل الآن على:

الإصدار 2

Image

في هذه الخطوة، قمنا بتغيير محتوى الحقل 1 دون تغيير محتوى الحقل 2، مما يضمن عدم تنفيذ الإجراء [TextBox2_TextChanged] على جانب الخادم، مما يعني أن الحقل [lblInfo2] لم يتلقَ قيمة جديدة. ولذلك يتم عرضه بقيمته السابقة. في الإصدار 1 [EnableViewState=true]، كانت هذه القيمة السابقة هي القيمة التي تم إدخالها. في الإصدار 2 [EnableViewState=false]، هذه القيمة السابقة هي سلسلة فارغة.

في بعض الأحيان، لا يكون من الضروري الحفاظ على الحالة السابقة للمكونات. بدلاً من تعيين [EnableViewState=false] لكل منها، يمكنك تحديد أن الصفحة يجب ألا تحافظ على حالتها. يتم ذلك في توجيه [Page] في كود العرض:

<%@ Page Language="VB" EnableViewState="False" %>

في هذه الحالة، بغض النظر عن قيمة خاصية [EnableViewState] الخاصة به، لا يتم تخزين حالة المكون في الحقل المخفي [__VIEWSTATE]. ثم يتصرف كل شيء كما لو كانت حالته السابقة هي السلسلة الفارغة.

لنستخدم الآن عميل [curl] لتسليط الضوء على آليات أخرى. أولاً، نطلب عنوان URL [http://localhost/form4.aspx]:

dos>curl --include --url http://localhost/form4.aspx

HTTP/1.1 200 OK
Server: Microsoft ASP.NET Web Matrix Server/0.6.0.0
Date: Sun, 04 Apr 2004 17:51:14 GMT
Cache-Control: private
Content-Type: text/html; charset=utf-8
Content-Length: 1077
Connection: Close


<html>
<head>
    <title>asp:textbox</title>
</head>
<body>
    <form name="_ctl0" method="post" action="form4.aspx" id="_ctl0">
<input type="hidden" name="__EVENTTARGET" value="" />
<input type="hidden" name="__EVENTARGUMENT" value="" />
<input type="hidden" name="__VIEWSTATE" value="dDwtMTY4MDc0MTUxOTs7PoqpeSYSCX7lCiWZvw5p7u+/OrTD" />

<script language="javascript">
<!--
    function __doPostBack(eventTarget, eventArgument) {
        var theform = document._ctl0;
        theform.__EVENTTARGET.value = eventTarget;
        theform.__EVENTARGUMENT.value = eventArgument;
        theform.submit();
    }
// -->
</script>

        <p>
            Texte 1 :
            <input name="TextBox1" type="text" id="TextBox1" onchange="__doPostBack('TextBox1','')" language="javascript" />
        </p>
        <p>
            Texte 2 :
            <input name="TextBox2" type="text" id="TextBox2" />
        </p>
        <p>
            <span id="lblInfo1"></span>
        </p>
        <p>
            <span id="lblInfo2"></span>
        </p>
    </form>
</body>
</html>

نتلقى كود HTML الخاص بـ [form4.aspx] من الخادم. وهو لا يختلف عن الكود الذي تلقّاه المتصفح. تذكر هنا الطلب الذي أرسله المتصفح عند إرسال النموذج:

POST /form4.aspx HTTP/1.1
Connection: keep-alive
...
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7b) Gecko/20040316

__EVENTTARGET=TextBox1&__EVENTARGUMENT=&__VIEWSTATE=dDwtMTY4MDc0MTUxOTs7PoqpeSYSCX7lCiWZvw5p7u%2B%2FOrTD&TextBox1=premier+texte&TextBox2=

لنقم بتنفيذ نفس عملية POST ولكن دون إرسال حقل [__VIEWSTATE]:

dos>curl --include --url http://localhost/form4.aspx --data __EVENTTARGET=TextBox1 --data __EVENTARGUMENT= --data TextBox1=premier+texte --data TextBox2=

...................
        <p>
            Texte 1 :
            <input name="TextBox1" type="text" value="premier texte" id="TextBox1" onchange="__doPostBack('TextBox1','')" language="javascript" />
        </p>
        <p>
            Texte 2 :
            <input name="TextBox2" type="text" id="TextBox2" />
        </p>
        <p>
            <span id="lblInfo1">19:57:48: evt [TextChanged] sur [TextBox1]. Texte 1=[premier texte]</span>
        </p>
        <p>
            <span id="lblInfo2"></span>
        </p>
..............

لاحظ النقاط التالية:

  • اكتشف الخادم حدث [TextChanged] في [TextBox1] لأنه أنشأ النص [lblInfo1]. ولم يمنع غياب [__VIEWSTATE] حدوث ذلك. في حالة غيابه، يفترض أن القيمة السابقة لحقل الإدخال هي سلسلة فارغة.
  • وقد تمكن من استعادة النص الذي تم نشره لـ [TextBox1] في سمة [value] لعلامة [TextBox1] بحيث يظهر حقل [TextBox1] مرة أخرى بالقيمة التي تم إدخالها. للقيام بذلك، لا يحتاج إلى [__VIEWSTATE] بل يحتاج فقط إلى القيمة التي تم نشرها لـ [TextBox1]

الآن، دعونا نرسل الطلب نفسه مرة أخرى دون تغيير أي شيء. نحصل على الاستجابة الجديدة التالية:

dos>curl --include --url http://localhost/form4.aspx --data __EVENTTARGET=TextBox1 --data __EVENTARGUMENT= --data TextBox1=premier+texte --data TextBox2=


        <p>
            Texte 1 :
            <input name="TextBox1" type="text" value="premier texte" id="TextBox1" onchange="__doPostBack('TextBox1','')" language="javascript" />
        </p>
        <p>
            Texte 2 :
            <input name="TextBox2" type="text" id="TextBox2" />
        </p>
        <p>
            <span id="lblInfo1">20:05:47: evt [TextChanged] sur [TextBox1]. Texte 1=[premier texte]</span>
        </p>
        <p>
            <span id="lblInfo2"></span>
        </p>

في حالة عدم وجود [__VIEWSTATE]، لم يتمكن الخادم من رؤية أن قيمة حقل [TextBox1] لم تتغير. لذلك يتصرف كما لو أن القيمة السابقة كانت سلسلة فارغة. ونتيجة لذلك، قام بإنشاء حدث [TextChanged] على [TextBox1] هنا. دعونا نُشغّل الطلب نفسه مرة أخرى، هذه المرة مع ترك حقل [TextBox1] فارغًا وحقل [TextBox2] غير فارغ:

dos>curl --include --url http://localhost/form4.aspx --data __EVENTTARGET=TextBox1 --data __EVENTARGUMENT= --data TextBox2=second+texte --data TextBox1=
......
        <p>
            Texte 1 :
            <input name="TextBox1" type="text" id="TextBox1" onchange="__doPostBack('TextBox1','')" language="javascript" />
        </p>
        <p>
            Texte 2 :
            <input name="TextBox2" type="text" value="second texte" id="TextBox2" />
        </p>
        <p>
            <span id="lblInfo1"></span>
        </p>
        <p>
            <span id="lblInfo2">20:11:54: evt [TextChanged] sur [TextBox2]. Texte 2=[second texte]</span>
        </p>
......

في حالة عدم وجود [__VIEWSTATE]، تم التعامل مع القيمة السابقة لـ [TextBox1] على أنها سلسلة فارغة. ونظرًا لأن القيمة المرسلة لـ [TextBox1] كانت أيضًا سلسلة فارغة، لم يتم إنشاء حدث [TextChanged] في [TextBox1]. ولم يتم تنفيذ الإجراء [TextBox1_TextChanged]، وبالتالي لم يتلق الحقل [lblInfo1] قيمة جديدة. نعلم أنه في هذه الحالة، يحتفظ المكون بقيمته القديمة. ومع ذلك، هذا ليس هو الحال هنا؛ فقد فقد [lblInfo1] قيمته السابقة. ويرجع ذلك إلى أن هذه القيمة يتم استردادها من [__VIEWSTATE]. ونظرًا لعدم وجود هذا الحقل، تم تعيين السلسلة الفارغة إلى [lblInfo1]. بالنسبة إلى [TextBox2]، قارن الخادم قيمته المنشورة [النص الثاني] بقيمته السابقة. في حالة عدم وجود [__VIEWSTATE]، تكون هذه القيمة السابقة مساوية للسلسلة الفارغة. ونظرًا لأن القيمة المنشورة لـ [TextBox2] تختلف عن السلسلة الفارغة، فقد تم تشغيل حدث [TextChanged] في [TextBox2]. تم تنفيذ الإجراء [TextBox2_TextChanged]، وتلقى الحقل [lblInfo2] قيمة جديدة.

قد يتساءل المرء عما إذا كانت المعلمات [__EVENTTARGET] و [__EVENTARGUMENT] مفيدة بالفعل. من خلال عدم إرسال هذه المعلمات، لن يعرف الخادم الحدث الذي أدى إلى تشغيل [submit]. دعونا نجرب:

dos>curl --include --url http://localhost/form4.aspx --data TextBox2=second+texte --data TextBox1=premier+texte
..............................
        <p>
            Texte 1 :
            <input name="TextBox1" type="text" id="TextBox1" onchange="__doPostBack('TextBox1','')" language="javascript" />
        </p>
        <p>
            Texte 2 :
            <input name="TextBox2" type="text" id="TextBox2" />
        </p>
        <p>
            <span id="lblInfo1"></span>
        </p>
        <p>
            <span id="lblInfo2"></span>
        </p>
    </form>
.....................

يمكننا أن نرى أنه لم يتم معالجة أي حدث [TextChanged]. علاوة على ذلك، لا تسترد الحقول المنشورة [TextBox1] و [TextBox2] قيمها المنشورة. في الواقع، يتصرف كل شيء كما لو تم إجراء طلب GET. يعود كل شيء إلى طبيعته إذا تم تضمين الحقل [__EVENTTARGET] في الحقول المنشورة، حتى لو لم يكن له قيمة:

dos>curl --include --url http://localhost/form4.aspx --data __EVENTTARGET= --data TextBox2=second+texte --data TextBox1=premier+texte
.......
        <p>
            Texte 1 :
            <input name="TextBox1" type="text" value="premier texte" id="TextBox1" onchange="__doPostBack('TextBox1','')" language="javascript" />
        </p>
        <p>
            Texte 2 :
            <input name="TextBox2" type="text" value="second texte" id="TextBox2" />
        </p>
        <p>
            <span id="lblInfo1">20:34:14: evt [TextChanged] sur [TextBox1]. Texte 1=[premier texte]</span>
        </p>
        <p>
            <span id="lblInfo2">20:34:14: evt [TextChanged] sur [TextBox2]. Texte 2=[second texte]</span>
        </p>
........

7.6.4. خصائص أخرى لمكون TextBox

يتيح لك مكون الخادم [TextBox] أيضًا إنشاء علامات HTML <input type="password"..> و<textarea>..</textarea>، أي العلامات التي تتوافق على التوالي مع حقل كلمة المرور وحقل الإدخال متعدد الأسطر. يتم التحكم في هذا الإنشاء بواسطة خاصية [TextMode] لمكون [TextBox]. ولها ثلاث قيم ممكنة:

القيمة
علامة HTML التي تم إنشاؤها
SingleLine
<input type="text" ...>
MultiLine
<textarea>...</textarea>
كلمة المرور
<input type="password ...>

سنقوم بفحص استخدام هذه الخصائص من خلال المثال التالي [form5.aspx]:

Image

رقم
name
النوع
الخصائص
الدور
1
btnAdd
زر
 
زر [submit] - يُستخدم لإضافة محتويات [TextBox1] إلى محتويات [TextBox2]
2
TextBox1
TextBox
TextMode=Password
Text=
حقل إدخال محمي بكلمة مرور
3
مربع النص 2
TextBox
TextMode=Multi-line
Text=
يجمع الإدخالات التي تمت في [TextBox1]

تم تعيين خاصية [EnableViewState] للصفحة على [false]. على جانب الخادم، نتعامل مع حدث النقر على الزر [btnAjouter]:

Sub btnAjouter_Click(sender As Object, e As EventArgs)
  ' the contents of [textBox1] are added to those of [TextBox2]
  textbox2.text=textbox2.text + textbox1.text+controlchars.crlf
End Sub

لفهم هذا الرمز، عليك أن تتذكر كيف تتم معالجة طلب POST للنموذج. يتم تنفيذ الإجراءات [Page_Init] و [Page_Load] أولاً. بعد ذلك تأتي جميع إجراءات الأحداث المخزنة مؤقتًا. وأخيرًا، يتم تنفيذ الإجراء الذي يتعامل مع الحدث الذي أدى إلى تشغيل [POST] — في هذه الحالة، الإجراء [btnAjouter_Click]. عند تشغيل معالجات الأحداث، تكون جميع مكونات الصفحة التي تحتوي على قيمة في طلب POST قد اتخذت تلك القيمة. أما المكونات الأخرى، فقد عادت إلى قيمتها السابقة إذا تم تعيين خاصية [EnableViewState] الخاصة بها على [true]، أو إلى قيمتها في وقت التصميم إذا تم تعيين خاصية [EnableViewState] الخاصة بها على [false]. هنا، ستكون قيم حقول [TextBox1] و [TextBox2] جزءًا من طلب POST المرسل من العميل. لذلك، في الكود السابق، سيحتوي [textbox1.text] على القيمة التي أرسلها العميل، وينطبق الأمر نفسه على [textbox2.text]. تقوم الإجراء [btnAjouter_Click] بتعيين قيمة حقل [TextBox2] إلى القيمة المرسلة لـ [TextBox2] مضافًا إليها القيمة المرسلة لـ [TextBox1]، بالإضافة إلى حرف فاصل الأسطر [ControlChars.CrLf] المحدد في مساحة الاسم [Microsoft.VisualBasic]. ليس من الضروري استيراد مساحة الاسم هذه، حيث يستوردها خادم الويب بشكل افتراضي.

فيما يلي الكود النهائي لـ [form5.aspx]:

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

    Sub btnAjouter_Click(sender As Object, e As EventArgs)
      ' on ajoute le contenu de [textBox1] à celui de [TextBox2]
      textbox2.text+=textbox1.text+controlchars.crlf
    End Sub

</script>
<html>
<head>
</head>
<body>
    <form runat="server">
        <p>
            <asp:Button id="btnAjouter" onclick="btnAjouter_Click" runat="server" Text="Ajouter" EnableViewState="False"></asp:Button>
            <asp:TextBox id="TextBox1" runat="server" TextMode="Password" Width="353px" EnableViewState="False"></asp:TextBox>
        </p>
        <p>
            <asp:TextBox id="TextBox2" runat="server" TextMode="MultiLine" Width="419px" Height="121px" EnableViewState="False"></asp:TextBox>
        </p>
    </form>
</body>
</html>

قبل قليل، قدمنا لقطة شاشة لإحدى عمليات التشغيل.

7.7. مكون DropDownList

تسمح لك العلامة <asp:DropDownList> بإدراج قائمة منسدلة في كود عرض الصفحة. نقوم بإنشاء صفحة [form6.aspx] لإنتاج التخطيط التالي:

Image

رقم
الاسم
نوع
الخصائص
الدور
1
قائمة منسدلة 1
قائمة منسدلة
إعادة الإرسال التلقائي=صحيح
تمكين حالة العرض=صحيح
قائمة منسدلة
2
lblInfo
تسمية
EnableViewState=false
رسالة معلومات

رمز العرض الذي تم إنشاؤه هو كما يلي:

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

</script>
<html>
<head>
</head>
<body>
    <form runat="server">
        <p>
            <asp:DropDownList id="DropDownList1" runat="server" OnSelectedIndexChanged="DropDownList1_SelectedIndexChanged" AutoPostBack="True"></asp:DropDownList>
        </p>
        <p>
            <asp:Label id="lblInfo" runat="server" enableviewstate="False"></asp:Label>
        </p>
    </form>
</body>
</html>

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

العناصر
مجموعة من النوع [ListItemCollection] تحتوي على العناصر الموجودة في القائمة المنسدلة. أعضاء هذه المجموعة من النوع [ListItem].
Items.Count
عدد العناصر في مجموعة [Items]
Items(i)
العنصر رقم i في القائمة - من النوع [ListItem]
Items.Add
لإضافة عنصر جديد [ListItem] إلى مجموعة [Items]
Items.Clear
لإزالة جميع العناصر من مجموعة [Items]
Items.RemoveAt(i)
لإزالة العنصر رقم i من مجموعة [Items]
SelectedItem
أول [ListItem] في مجموعة [Items] التي تكون خاصية [Selected] الخاصة بها صحيحة
SelectedIndex
فهرس عنصر [SelectedItem] في مجموعة [Items]

العناصر الموجودة في مجموعة [Items] لفئة [DropDownList] هي من النوع [ListItem]. يولد كل عنصر [ListItem] علامة HTML <option>:

<option value="V" [selected="selected"]>T</option>

نصف بعض الخصائص والأساليب لفئة [ListItem]:

ListItem(String text, String value)
منشئ - ينشئ عنصر [ListItem] بخصائص [text] و [value]. سيقوم عنصر ListItem(T,V) بإنشاء علامة HTML <option value="V">T</option>. وبالتالي، تسمح لنا فئة [ListItem] بوصف عناصر قائمة HTML
المحدد
من نوع boolean. إذا كانت القيمة true، فسيكون للخيار المقابل في قائمة HTML السمة [selected="selected"]. تخبر هذه السمة المتصفح بأن العنصر المقابل يجب أن يظهر محددًا في قائمة HTML
نص
النص T للخيار HTML <option value="V" [selected="selected"]>T</option>
القيمة
القيمة V لخاصية [Value] لخيار HTML <option value="V" [selected="selected"]>T</option>

لدينا معلومات كافية لكتابة الكود لملء القائمة المنسدلة [DropDownList1] في إجراء [Page_Load] للصفحة:

    Sub Page_Load(sender As Object, e As EventArgs)
        ' fill in the combo if it's the 1st time you've been called
        if not IsPostBack then
          dim valeurs() as String = {"1","2","3","4"}
            dim textes() as  String = {"un","deux","trois","quatre"}

            dim i as integer
            for i=0 to valeurs.length-1
                DropDownList1.Items.Add(new ListItem(textes(i),valeurs(i)))
            next
        end if
    end sub

بمجرد تهيئة مكون [DropDownList1]، سيكون HTML الخاص به كما يلي:

<select name="DropDownList1" id="DropDownList1" onchange="__doPostBack('DropDownList1','')" language="javascript">

    <option value="1">un</option>
    <option value="2">deux</option>
    <option value="3">trois</option>
    <option value="4">quatre</option>

</select>

نحن نعلم أن الإجراء [Page_Load] يتم تنفيذه في كل مرة يتم فيها تحميل الصفحة [form6.aspx]. يتم استدعاؤه للمرة الأولى عبر طلب GET، ثم عبر طلب POST في كل مرة يختار فيها المستخدم عنصرًا جديدًا من القائمة المنسدلة. هل يجب تنفيذ الكود الخاص بتعبئة هذه القائمة في كل مرة في [Page_Load]؟ تعتمد الإجابة على السمة [EnableViewState] لمكون [DropDownList1]. إذا تم تعيين هذه السمة على true، فإننا نعلم أن حالة مكون [DropDownList1] سيتم الاحتفاظ بها عبر الطلبات في الحقل المخفي [__VIEWSTATE]. تتضمن هذه الحالة أمرين:

  • قائمة بجميع القيم الموجودة في القائمة المنسدلة
  • قيمة العنصر المحدد في هذه القائمة

قد يبدو من المغري تعيين خاصية [EnableViewState] لمكون [DropDownList1] إلى [true] حتى لا تضطر إلى إعادة حساب القيم التي سيتم وضعها في القائمة. لكن المشكلة هي أنه نظرًا لأن الإجراء [Page_Load] يتم تنفيذه في كل مرة يتم فيها طلب الصفحة [form6.aspx]، فسيتم حساب هذه القيم على أي حال. يحتوي الكائن [Page]، الذي يمثل [form6.aspx] مثيلًا له، على سمة [IsPostBack] ذات قيمة منطقية. إذا كانت هذه السمة صحيحة، فهذا يعني أن الصفحة تم طلبها عبر POST. وإذا كانت خاطئة، فهذا يعني أن الصفحة تم طلبها عبر GET. في نظامنا ذي الرحلة ذهابًا وإيابًا بين العميل والخادم، يطلب العميل دائمًا نفس الصفحة [form6.aspx] من الخادم. في المرة الأولى، يطلبها باستخدام GET؛ وفي المرات اللاحقة باستخدام POST. نستنتج أن خاصية [IsPostBack] يمكن استخدامها لاكتشاف طلب GET الأول للعميل. نقوم بإنشاء قيم القائمة المنسدلة فقط خلال هذا الطلب الأول. بالنسبة للطلبات اللاحقة، سيتم إنشاء هذه القيم بواسطة آلية [VIEWSTATE]. في حالات أخرى، قد يختلف محتوى القائمة من طلب لآخر، وبالتالي يجب إعادة حسابه لكل طلب. في هذه الحالة، سنقوم بتعيين السمة [EnableViewState] للقائمة على [false] لتجنب الحساب المزدوج غير الضروري لمحتوى القائمة، ما لم نحتاج إلى معرفة العناصر المحددة مسبقًا في القائمة، حيث يتم تخزين هذه المعلومات في [VIEWSTATE].

تم تعيين السمة [AutoPostBack] لقائمة [DropDownList1] على true. وهذا يعني أن المتصفح سيرسل النموذج بمجرد اكتشافه لحدث "تغيير العنصر المحدد" في القائمة المنسدلة. وبدوره، سيكتشف الخادم — باستخدام [VIEWSTATE] والقيم المرسلة — أن العنصر المحدد في مكون [DropDownList1] قد تغير. ثم سيقوم بتشغيل حدث [SelectedIndexChanged] على هذا المكون. وسنتعامل مع ذلك من خلال الإجراء التالي:

    Sub DropDownList1_SelectedIndexChanged(sender As Object, e As EventArgs)
      ' selection change
      lblInfo.text="Elément sélectionné : texte="+dropdownlist1.selecteditem.text+ _
        " valeur=" + dropdownlist1.selecteditem.value + _
        " index="+ dropdownlist1.selectedindex.tostring
    End Sub

عند تشغيل هذا الإجراء، يكون الكائن [DropDownList1] قد استرد عناصر [ListItem] الخاصة به عبر [VIEWSTATE]. علاوة على ذلك، يتم تعيين السمة [Selected] لأحد عناصر [ListItem] هذه على "true" — وهو العنصر الذي تم إرسال قيمته بواسطة المتصفح. هناك عدة طرق للوصول إلى هذا العنصر:

DropDownList1.SelectedItem
هو أول عنصر [ListItem] في القائمة الذي تم تعيين سمة [Selected] الخاصة به على true
DropDownList1.SelectedItem.Text
يتوافق مع جزء [text] من علامة HTML لعنصر <option value="...">text</option> الذي حدده المستخدم
DropDownList1.SelectedItem.Value
يتوافق مع جزء [value] من علامة HTML لعنصر <option value="...">text</option> الذي حدده المستخدم
DropDownList1.SelectedIndex
الفهرس في مجموعة [DropDownList1.Items] للعنصر [ListItem] الأول الذي تكون سمة [Selected] الخاصة به صحيحة

الرمز النهائي لـ [form6.aspx] هو كما يلي:

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

    Sub Page_Load(sender As Object, e As EventArgs)
        ' on remplit le combo si c'est la 1ère fois qu'on est appelé
        if not IsPostBack then
          dim valeurs() as String = {"1","2","3","4"}
            dim textes() as  String = {"un","deux","trois","quatre"}

            dim i as integer
            for i=0 to valeurs.length-1
                DropDownList1.Items.Add(new ListItem(textes(i),valeurs(i)))
            next
        end if
    end sub

    Sub DropDownList1_SelectedIndexChanged(sender As Object, e As EventArgs)
      ' changement de sélection
      lblInfo.text="Elément sélectionné : texte="+dropdownlist1.selecteditem.text+ _
        " valeur=" + dropdownlist1.selecteditem.value + _
        " index="+ dropdownlist1.selectedindex.tostring
    End Sub

</script>
<html>
<head>
</head>
<body>
    <form runat="server">
        <p>
            <asp:DropDownList id="DropDownList1" runat="server" OnSelectedIndexChanged="DropDownList1_SelectedIndexChanged" AutoPostBack="True"></asp:DropDownList>
        </p>
        <p>
            <asp:Label id="lblInfo" runat="server" enableviewstate="False"></asp:Label>
        </p>
    </form>
</body>
</html>

7.8. مكون ListBox

تسمح لك علامة <asp:ListBox> بإدراج قائمة في كود عرض الصفحة. نقوم بإنشاء [form7.aspx] للحصول على التخطيط التالي:

Image

رقم
الاسم
النوع
الخصائص
الدور
1
txtInput
مربع نص
EnableViewState=false
حقل الإدخال
2
btnAdd
زر
 
زر [submit] الذي ينقل محتويات txtSaisie إلى القائمة 1 إذا لم تكن فارغة.
3
ListBox1
ListBox
EnableViewState=true
SelectionMode=Single
قائمة القيم للاختيار الفردي
4
ListBox2
ListBox
EnableViewState=true
SelectionMode=Multiple
قائمة القيم للاختيار المتعدد
5
btn1to2
زر
 
زر [إرسال] الذي ينقل العنصر المحدد من [القائمة 1] إلى [القائمة 2]
6
btn2to1
زر
 
زر [إرسال] الذي ينقل العناصر المحددة من [القائمة 2] إلى [القائمة 1]

رمز العرض التقديمي الذي تم إنشاؤه هو كما يلي:

<%@ Page Language="VB" %>
<script runat="server">
</script>
<html>
<head>
</head>
<body>
    <form runat="server">
        <p>
            Tapez un texte pour l'inclure dans Liste 1 :
            <asp:TextBox id="txtSaisie" runat="server" EnableViewState="False"></asp:TextBox>
        </p>
        <p>
            <asp:Button id="btnAjouter" onclick="btnAjouter_Click" runat="server" Text="Ajouter"></asp:Button>
        </p>
        <p>
            <table>
                <tbody>
                    <tr>
                        <td>
                            <p align="center">
                                Liste 1
                            </p>
                        </td>
                        <td>
                        </td>
                        <td>
                            <p align="center">
                                Liste 2
                            </p>
                        </td>
                    </tr>
                    <tr>
                        <td>
                            <asp:ListBox id="ListBox1" runat="server"></asp:ListBox>
                        </td>
                        <td>
                            <p>
                                <asp:Button id="btn1vers2" onclick="btn1vers2_Click" runat="server" Text="-->"></asp:Button>
                            </p>
                            <p>
                                <asp:Button id="btn2vers1" onclick="btn2vers1_Click" runat="server" Text="<--"></asp:Button>
                            </p>
                        </td>
                        <td>
                            <p>
                                <asp:ListBox id="ListBox2" runat="server" SelectionMode="Multiple"></asp:ListBox>
                            </p>
                        </td>
                    </tr>
                    <tr>
                        <td>
                            <p align="center">
                                <asp:Button id="btnRaz1" onclick="btnRaz1_Click" runat="server" Text="Effacer"></asp:Button>
                            </p>
                        </td>
                        <td>
                        </td>
                        <td>
                            <p align="center">
                                <asp:Button id="btnRaz2" onclick="btnRaz2_Click" runat="server" Text="Effacer"></asp:Button>
                            </p>
                        </td>
                    </tr>
                </tbody>
            </table>
        </p>
    </form>
</body>
</html>

فئة [ListBox] مشتقة من نفس فئة [ListControl] مثل فئة [DropDownList] التي تمت مناقشتها سابقًا. وهي تتضمن جميع الخصائص والأساليب التي شوهدت في [DropDownList] لأنها في الواقع تنتمي إلى [ListControl]. تظهر خاصية جديدة:

SelectionMode
يحدد وضع التحديد لقائمة HTML <select> التي سيتم إنشاؤها من المكون. إذا كان SelectionMode=Single، فيمكن تحديد عنصر واحد فقط. إذا كان SelectionMode=Multiple، فيمكن تحديد عناصر متعددة. لتحقيق ذلك، سيتم إنشاء السمة [multiple="multiple"] في علامة <select> لقائمة HTML.

دعونا نتعامل مع الأحداث. سيتم التعامل مع النقر على زر [Add] بواسطة الإجراء [btnAdd_Click] التالي:

    Sub btnAjouter_Click(sender As Object, e As EventArgs)
      ' added to list 1
      dim texte as string=txtSaisie.text.trim
      if texte<> "" then ListBox1.Items.Add(New ListItem(texte))
      ' raz txtSaisie
      txtSaisie.text=""
    End Sub

إذا لم يكن النص الذي تم إدخاله في [txtSaisie] سلسلة فارغة أو خالية، يتم إضافة عنصر جديد إلى قائمة [ListBox1]. نحن نعلم أننا بحاجة إلى إضافة عنصر من النوع [ListItem]. في السابق، استخدمنا منشئ [ListItem(T as String, V as String)] لأداء مهمة مماثلة. يولد هذا العنصر علامة HTML [<option value="V">T</option>]. هنا، نستخدم منشئ [ListItem(T as String)]، الذي يولد علامة HTML [<option value="T">T</option>]، أي أن النص [T] للخيار يُستخدم لتشكيل قيمة الخيار. بمجرد إضافة محتوى [txtSaisie] إلى قائمة [ListBox1]، يتم مسح حقل [txtSaisie].

سيتم التعامل مع النقرات على أزرار [Clear] من خلال الإجراءات التالية:

    Sub btnRaz1_Click(sender As Object, e As EventArgs)
      ' raz list 1
      ListBox1.Items.Clear
    End Sub

    Sub btnRaz2_Click(sender As Object, e As EventArgs)
      ' raz list 2
      ListBox2.Items.Clear
    End Sub

يتم التعامل مع النقرات على الأزرار الخاصة بالنقل بين القوائم من خلال الإجراءات التالية:

    Sub btn1vers2_Click(sender As Object, e As EventArgs)
      ' transfer the item selected in list 1 to list 2
      transfert(ListBox1,ListBox2)
    End Sub

    Sub btn2vers1_Click(sender As Object, e As EventArgs)
      ' transfer of item selected in list 2 to list 1
      transfert(ListBox2,ListBox1)
    End Sub

    sub transfert(l1 as listbox, l2 as listbox)
      ' transfer items selected in l1 to l2
      ' anything to do?
      if l1.selectedindex=-1 then return
      dim i as integer
      ' we start at the end
      for i=l1.items.count-1 to 0 step -1
        ' selected?
        if l1.items(i).selected then
            ' more selected
            l1.items(i).selected=false
          ' transfer to l2
          l2.items.add(l1.items(i))
          ' deletion in l1
          l1.items.removeAt(i)
        end if
      next
    end sub

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

  • l1 من النوع [ListBox]، وهي القائمة المصدر
  • l2 من النوع [ListBox]، وهي قائمة الوجهة

أولاً، نتحقق مما إذا كان هناك عنصر واحد على الأقل محدد في القائمة l1؛ إذا لم يكن الأمر كذلك، فلا يوجد ما نفعله. للقيام بذلك، نفحص الخاصية [l1.selectedindex]، التي تمثل مؤشر العنصر الأول المحدد في القائمة. إذا لم يكن هناك أي عنصر، تكون قيمتها -1. إذا كان هناك عنصر محدد واحد على الأقل في l1، نقوم بنقله إلى l2. للقيام بذلك، نكرر عبر قائمة العناصر بأكملها في l1 ونتحقق من كل عنصر ما إذا كانت سمة [selected] الخاصة به صحيحة. إذا كان الأمر كذلك، يتم تعيين سمة [selected] الخاصة به إلى [false]، ثم يتم نسخه إلى قائمة l2، وأخيرًا يتم إزالته من قائمة l1. تؤدي هذه الإزالة إلى إعادة ترقيم العناصر في قائمة l1. لهذا السبب يتم استعراض قائمة العناصر في l1 بترتيب عكسي. إذا قمنا بتصفحها للأمام وأزلنا العنصر رقم 10، يصبح العنصر رقم 11 هو رقم 10 ويصبح رقم 12 هو رقم 11. بعد معالجة العنصر رقم 10، ستقوم حلقتنا الأمامية بمعالجة العنصر رقم 11، والذي، كما أوضحنا للتو، هو رقم 12 السابق. يتم تجاهل العنصر الذي كان رقم 11 وأصبح الآن رقم 10. من خلال تصفح عناصر القائمة l1 في الاتجاه المعاكس، نتجنب هذه المشكلة.

7.9. مكونات CheckBox و RadioButton

تسمح لك علامتا <asp:RadioButton> و <asp:CheckBox> بإدراج زر اختيار ومربع اختيار، على التوالي، في كود عرض الصفحة. نقوم بإنشاء صفحة [form8.aspx] للحصول على التخطيط التالي:

Image

رقم
الاسم
النوع
الخصائص
الدور
1
زر الاختيار 1
زر الاختيار 2
زر الاختيار 3
زر الاختيار
RadioButton1.Checked = true
RadioButton1.Text = 1
RadioButton2.Checked = false
RadioButton2.Text = 2
RadioButton3.Checked = false
RadioButton3.Text = 3
بالنسبة لجميع الأزرار الثلاثة: GroupName = radio
أزرار الاختيار
2
CheckBoxA
مربع الاختيار B
مربع الاختيار C
مربع الاختيار
Checked=false للجميع
نص مربع الاختيار A = A
نص مربع الاختيار B = B نص مربع الاختيار C = C
مربعات الاختيار
3
btnSend
زر
 
زر [إرسال]
4
lstInfo
ListBox
 
قائمة المعلومات

لضمان أن يتعامل المتصفح مع أزرار الاختيار الثلاثة على أنها متنافية، يجب تجميعها معًا في مجموعة أزرار اختيار. ويتم ذلك باستخدام السمة [GroupName] للفئة [RadioButton]. لا يلزم الحفاظ على حالة الصفحة في هذا التطبيق. لذلك، نقوم بتعيين السمة [EnableViewState="false"] على الصفحة. وفيما يلي كود العرض:

<html>
<head>
</head>
<body>
    <form id="frmControls" runat="server">
        <h3>Cases à cocher 
        </h3>
        <p>
            <asp:RadioButton id="RadioButton1" runat="server" Checked="True" EnableViewState="False" GroupName="radio" Text="1"></asp:RadioButton>
            <asp:RadioButton id="RadioButton2" runat="server" EnableViewState="False" GroupName="radio" Text="2"></asp:RadioButton>
            &nbsp;<asp:RadioButton id="RadioButton3" runat="server" EnableViewState="False" GroupName="radio" Text="3"></asp:RadioButton>
        </p>
        <p>
            <asp:CheckBox id="CheckBoxA" runat="server" EnableViewState="False" Text="A"></asp:CheckBox>
            <asp:CheckBox id="CheckBoxB" runat="server" EnableViewState="False" Text="B"></asp:CheckBox>
            <asp:CheckBox id="CheckBoxC" runat="server" EnableViewState="False" Text="C"></asp:CheckBox>
        </p>
        <p>
            <asp:Button id="btnEnvoyer" onclick="btnEnvoyer_Click" runat="server" Text="Envoyer"></asp:Button>
            <asp:Button id="btnTree" onclick="btnTree_Click" runat="server" Text="Contrôles"></asp:Button>
        </p>
        <p>
            <asp:ListBox id="lstInfos" runat="server" EnableViewState="False" Rows="6" Height="131px"></asp:ListBox>
        </p>
    </form>
</body>
</html>

علينا كتابة الإجراء [btnEnvoyer_Click] لمعالجة حدث [Click] الخاص بهذا الزر. تتحدد حالة زر الاختيار أو مربع الاختيار من خلال السمة [Checked]، التي تكون قيمتها "صحيح" (true) إذا كان المربع محددًا، و"خطأ" (false) في الحالات الأخرى. لذلك، نحتاج ببساطة إلى كتابة قيمة السمة [Checked] للأزرار الستة ومربعات الاختيار في قائمة [lstInfos]. وبما أنه لا توجد صعوبة خاصة في القيام بذلك، فلنجرب شيئًا مختلفًا قليلاً:

<script runat="server">

    Sub btnEnvoyer_Click(sender As Object, e As EventArgs)
      ' place information in the listbox
      for each c as control in FindControl("frmControls").controls
          ' is the control derived from CheckBox?
          if TypeOf(c) is CheckBox then
              lstInfos.Items.Add(c.ID + " : " + Ctype(c,CheckBox).Checked.ToString)
          end if
      next
    End Sub

</script>

يمكن عرض الصفحة كهيكل شجري من عناصر التحكم. في مثالنا، تحتوي صفحتنا على عناصر تحكم نصية وعناصر تحكم خادم. يتم التعامل مع النص كعنصر تحكم خاص يسمى [LiteralControl]. أي نص ينشئ عنصر التحكم هذا، حتى لو كان سلسلة من المسافات بين عنصري تحكم. كل عنصر تحكم له سمة ID تحدد هويته. سمة ID هي التي تظهر في العلامات:

            <asp:CheckBox id="CheckBoxA" runat="server" ></asp:CheckBox>

إذا تجاهلنا عناصر التحكم [LiteralControl]، فإن الصفحة المعنية تحتوي على عناصر التحكم التالية:

  • [HtmlForm]، وهو النموذج [ID=frmControls]. وهذا بدوره عبارة عن حاوية لعناصر التحكم. ويحتوي على عناصر التحكم التالية:

-- [ID=RadioButton1] من النوع [RadioButton]

-- [ID=RadioButton2] من النوع [RadioButton]

-- [ID=RadioButton3] من النوع [RadioButton]

-- [ID=CheckBoxA] من النوع [CheckBox]

-- [ID=CheckBoxA] من النوع [CheckBox]

-- [ID=CheckBoxA] من النوع [CheckBox]

-- [ID=btnEnvoyer] من النوع [Button]

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

[Control].Controls
تُرجع مجموعة عناصر التحكم الفرعية لـ [Control]، إن وجدت
[Control].FindControl(ID)
تُرجع عنصر التحكم المحدد بواسطة ID الموجود في جذر شجرة عناصر التحكم الفرعية لـ [Control]. في المثال أعلاه: تشير Page.FindControl("frmControls") إلى الحاوية [HtmlForm]. للوصول إلى زر الاختيار [RadioButton1]، يجب كتابة
Page.FindControl("frmControls").FindControl("RadioButton1")
[Control].ID
معرف [Control]

لنعد إلى كود الإجراء [btnEnvoyer_Click]:

    Sub btnEnvoyer_Click(sender As Object, e As EventArgs)
      ' place information in the listbox
      for each c as control in FindControl("frmControls").controls
          ' is the control derived from CheckBox?
          if TypeOf(c) is CheckBox then
              lstInfos.Items.Add(c.ID + " : " + Ctype(c,CheckBox).Checked.ToString)
          end if
      next
    End Sub

نريد عرض حالة أزرار الاختيار ومربعات الاختيار في النموذج. نقوم بالتكرار عبر جميع عناصر التحكم في النموذج. إذا كان عنصر التحكم الحالي من نوع مشتق من [CheckBox]، فإننا نعرض خاصية [Checked] الخاصة به. نظرًا لأن فئة [RadioButton] مشتقة من فئة [CheckBox]، فإن الاختبار ينطبق على كلا النوعين من عناصر التحكم. تعرض لقطة الشاشة الموضحة أعلاه مثالاً على الناتج.

7.10. مكونات CheckBoxList و RadioButtonList

في بعض الأحيان، نريد السماح للمستخدم بالاختيار بين قيم غير معروفة في وقت تصميم الصفحة. تأتي هذه الخيارات من ملف تكوين أو قاعدة بيانات أو ما شابه، ولا تُعرف إلا في وقت التشغيل. هناك حلول لهذه المشكلة، وقد صادفناها. تكون قائمة الاختيار الفردي مناسبة عندما يمكن للمستخدم إجراء اختيار واحد فقط، وقائمة الاختيار المتعدد عندما يمكنه إجراء عدة اختيارات. من الناحية الجمالية، وإذا لم يكن عدد الخيارات كبيرًا، فقد ترغب في استخدام أزرار الاختيار بدلاً من قائمة الاختيار الفردي أو مربعات الاختيار بدلاً من قائمة الاختيار المتعدد. وهذا ممكن باستخدام [CheckBoxList] و [RadioButtonList].

تُشتق فئتا [CheckBoxList] و [RadioButtonList] من نفس فئة [ListControl] مثل فئتي [DropDownList] و [ListBox] اللتين تمت مناقشتهما سابقًا. لذلك، سنجد بعض الخصائص والأساليب التي شوهدت في تلك الفئات — على وجه التحديد، تلك التي تنتمي فعليًا إلى [ListControl].

العناصر
مجموعة من النوع [ListItemCollection] تحتوي على العناصر الموجودة في القائمة المنسدلة. أعضاء هذه المجموعة من النوع [ListItem].
Items.Count
عدد العناصر في مجموعة [Items]
العناصر(i)
العنصر رقم i في القائمة - من النوع [ListItem]
Items.Add
لإضافة عنصر جديد [ListItem] إلى مجموعة [Items]
Items.Clear
لإزالة جميع العناصر من مجموعة [Items]
Items.RemoveAt(i)
لإزالة العنصر رقم i من مجموعة [Items]
SelectedItem
أول [ListItem] في مجموعة [Items] التي تكون خاصية [Selected] الخاصة بها صحيحة
SelectedIndex
فهرس العنصر السابق في مجموعة [Items]

بعض الخصائص خاصة بفئتي [CheckBoxList] و [RadioButtonList]:

اتجاه التكرار
[horizontal] أو [vertical] للقوائم الأفقية أو الرأسية.

العناصر الموجودة في مجموعة [Items] هي من النوع [ListItem]. سيقوم كل عنصر [ListItem] بإنشاء علامة مختلفة اعتمادًا على ما إذا كان كائن [CheckBoxList] أو [RadioButtonList]:

<input type="checkbox" [selected="selected"] value="V">Texte

أو

<input type="radio" [selected="selected"] value="V">Texte

نصف بعض خصائص وأساليب فئة [ListItem]:

ListItem(String text, String value)
المنشئ - ينشئ عنصر [ListItem] بخصائص النص والقيمة. سيقوم عنصر ListItem(T,V) بإنشاء علامة HTML <input type="checkbox" value="V">T أو <input type="radio" value="V">T، حسب الاقتضاء.
Selected
Boolean. إذا كانت القيمة true، فسيكون للخيار المقابل في قائمة HTML السمة [selected="selected"]. تخبر هذه السمة المتصفح بأن العنصر المقابل يجب أن يظهر محددًا في قائمة HTML
نص
النص T لخيار HTML <input type=".." value="V" [selected="selected"]>T
القيمة
قيمة السمة Value للخيار HTML <input type=".." value="V" [selected="selected"]>T

نقترح إنشاء الصفحة التالية [form8b.aspx]:

Image

رقم
الاسم
النوع
الخصائص
الدور
1
قائمة أزرار الاختيار 1
قائمة أزرار الاختيار
EnableViewState=true
اتجاه التكرار=أفقي
قائمة أزرار الاختيار
2
قائمة مربعات الاختيار 1
CheckBoxList
تمكين حالة العرض=صحيح
اتجاه التكرار=أفقي
قائمة مربعات الاختيار
3
btnSend
زر
 
زر [إرسال] يعرض في [4] قائمة العناصر المحددة من كلا القائمتين
4
lstInfo
مربع القائمة
EnableViewState=false
قائمة القيم

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

<%@ Page Language="VB" autoeventwireup="false" %>
<script runat="server">
...
</script>
<html>
<head>
</head>
<body>
    <form id="frmControls" runat="server">
        <h3>Listes de cases à cocher
        </h3>
        <p>
            <asp:RadioButtonList id="RadioButtonList1" runat="server" RepeatDirection="Horizontal"></asp:RadioButtonList>
        </p>
        <p>
            <asp:CheckBoxList id="CheckBoxList1" runat="server" RepeatDirection="Horizontal"></asp:CheckBoxList>
        </p>
        <p>
            <asp:Button id="btnEnvoyer" onclick="btnEnvoyer_Click" runat="server" Text="Envoyer"></asp:Button>
        </p>
        <p>
            <asp:ListBox id="lstInfos" runat="server" EnableViewState="False" Rows="6"></asp:ListBox>
        </p>
    </form>
</body>
</html>

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

<script runat="server">

    Sub Page_Load(sender As Object, e As EventArgs) handles MyBase.Load
        ' fill in the lists if it's the 1st time you're called
        if not IsPostBack then
            ' texts for the RadioButton list
          dim textesRadio() as String = {"1","2","3","4"}
          ' texts for the CheckBox list
          dim textesCheckBox() as  String = {"un","deux","trois","quatre"}
                ' radio list filling
            dim i as integer
            for i=0 to textesRadio.length-1
                RadioButtonList1.Items.Add(new ListItem(textesRadio(i)))
            next
            ' selection item no. 1
            RadioButtonList1.SelectedIndex=1
            ' checkbox list filling
            for i=0 to textesCheckBox.length-1
                CheckBoxList1.Items.Add(new ListItem(textesCheckBox(i)))
            next
        end if
    end sub


    Sub btnEnvoyer_Click(sender As Object, e As EventArgs)
      ' place information in the lstinfos listbox
      affiche(RadioButtonList1)
      affiche(CheckBoxList1)
    End Sub

    sub affiche(l1 as ListControl)
      ' displays the values of selected elements of l1
      ' anything to do?
      if l1.selectedindex=-1 then return
      dim i as integer
      ' we start at the end
      for i= 0 to l1.items.count-1
        ' selected?
        if l1.items(i).selected then
          lstInfos.Items.Add("["+TypeName(l1)+"] ["+l1.items(i).text+"] sélectionné")
        end if
      next
    end sub

</script>

في الإجراء [Page_Load]، الذي يتم تشغيله في كل مرة يتم فيها تحميل الصفحة، يتم تهيئة القائمتين. ولمنع تهيئتهما في كل مرة، نستخدم الخاصية [IsPostBack] للقيام بذلك في المرة الأولى فقط. وفي عمليات التحميل اللاحقة، سيتم إعادة إنشاء القوائم تلقائيًا بواسطة آلية [VIEWSTATE]. وبمجرد عرض الصفحة، يقوم المستخدم بتحديد مربعات معينة والنقر فوق الزر [Submit]. ثم يتم إرسال قيم النموذج إلى النموذج نفسه. بعد تنفيذ [Page_Load]، يتم تنفيذ الإجراء [btnEnvoyer_Click]. يستدعي هذا الإجراء الإجراء [affiche] لملء القائمة [lstInfos]. يتلقى هذا الإجراء كائن [ListControl] كمعلمة، مما يسمح له بقبول كائن [RadioButtonList] أو كائن [CheckBoxList] — فئات مشتقة من [ListControl]. يمكن تعيين سمة [EnableViewState] لقائمة [lstInfos] على [false] نظرًا لأن حالتها لا تحتاج إلى الحفاظ عليها بين الطلبات.

7.11. مكونات Panel و LinkButton

تسمح لك علامة <asp:panel> بإدراج حاوية من عناصر التحكم في صفحة. وتتمثل ميزة الحاوية في أن بعض خصائصها تنطبق على جميع عناصر التحكم التي تحتوي عليها. وهذا هو الحال مع خاصية [Visible] الخاصة بها. وتوجد هذه الخاصية لكل عنصر تحكم من جانب الخادم. إذا كان للحاوية خاصية [Visible=false]، فسيتم التحكم في كل عنصر من عناصر التحكم الخاصة بها بواسطة خاصية [Visible] الخاصة بها. إذا كانت تحتوي على خاصية [Visible=false]، فلن يتم عرض الحاوية وكل ما تحتويه. قد يكون هذا أبسط من إدارة خاصية [Visible] لكل عنصر من عناصر التحكم في الحاوية.

تسمح لك علامة <asp:LinkButton> بإدراج رابط في كود عرض الصفحة. وهي تخدم غرضًا مشابهًا لعنصر التحكم [Button]. فهي تطلق POST من جانب العميل باستخدام دالة JavaScript مرتبطة. نقوم بإنشاء صفحة [form9.aspx] لإنتاج التخطيط التالي:

Image

لا.
الاسم
النوع
الخصائص
الدور
1
لوحة 1
لوحة
EnableViewState=true
حاوية التحكم
2
ListBox1
ListBox
EnableViewState=true
قائمة من ثلاث قيم
3
lnkHide
زر الارتباط
EnableViewState=false
رابط لإخفاء الحاوية

عندما يتم إخفاء الحاوية، يظهر رابط جديد:

Image

لا
الاسم
النوع
الخصائص
الدور
4
lnkView
زر الارتباط
EnableViewState=false
رابط لعرض الحاوية

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

<html>
<head>
</head>
<body>
    <form runat="server">
        <p>
            <asp:Panel id="Panel1" runat="server" BorderStyle="Ridge" BorderWidth="1px">
                <p>
                    Conteneur 
                </p>
                <p>
                    <asp:ListBox id="ListBox1" runat="server">
                        <asp:ListItem Value="1">un</asp:ListItem>
                        <asp:ListItem Value="2">deux</asp:ListItem>
                        <asp:ListItem Value="3" Selected="True">trois</asp:ListItem>
                    </asp:ListBox>
                </p>
            </asp:Panel>
        </p>
        <p>
            <asp:LinkButton id="lnkVoir" onclick="lnkVoir_Click" runat="server">Voir le conteneur</asp:LinkButton>
        </p>
        <p>
            <asp:LinkButton id="lnkCacher" onclick="lnkCacher_Click" runat="server">Cacher le conteneur</asp:LinkButton>
        </p>
    </form>
</body>
</html>

لاحظ أن هذا الرمز يقوم بتهيئة قائمة [ListBox1] بثلاث قيم. ومعالجات أحداث [Click] للرابطين هي كما يلي:

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

    Sub Page_Load(sender As Object, e As EventArgs)
...
    end sub

    Sub lnkVoir_Click(sender As Object, e As EventArgs)
        ' displays container 1
        panel1.Visible=true
        ' changing links
        lnkVoir.visible=false
        lnkCacher.visible=true
    End Sub

    Sub lnkCacher_Click(sender As Object, e As EventArgs)
        ' hides container 1
        panel1.Visible=false
        ' changing links
        lnkVoir.visible=true
        lnkCacher.visible=false
    End Sub
</script>

سنستخدم الإجراء [Page_Load] لتهيئة النموذج. سنقوم بذلك عند الطلب الأول (IsPostBack=false):

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

    Sub Page_Load(sender As Object, e As EventArgs)
        ' the 1st time
        if not IsPostBack then
            ' the container is shown
            lnkVoir_Click(nothing,nothing)
        end if
    end sub
.....
</script>

7.12. لمتابعة...

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

  • استكشاف خصائص المكون باستخدام بيئة تطوير متكاملة (IDE) مثل WebMatrix. تعرض هذه الأداة الخصائص الرئيسية للمكونات المستخدمة في النموذج
  • الرجوع إلى وثائق .NET لاستكشاف جميع الفئات المطابقة لكل مكون من مكونات الخادم. هذه هي الطريقة المفضلة لاكتساب إتقان كامل للمكون. هناك، ستجد التسلسل الهرمي للفئات المؤدي إلى المكونات، بالإضافة إلى الخصائص والأساليب والمُنشِئات والأحداث لكل منها. بالإضافة إلى ذلك، توفر الوثائق أحيانًا أمثلة.

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

7.13. مكونات الخادم ووحدة التحكم في التطبيق

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

Image

يحتوي التطبيق على عرضين: [form.aspx] و [errors.aspx]. يتم عرض [form.aspx] عند طلب عنوان URL [main.aspx] لأول مرة:

Image

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

Image

وينقر على الزر [Calculate] للحصول على الاستجابة التالية:

Image

في تطبيق MVC، يجب أن يمر كل طلب عبر وحدة التحكم، وهي في هذه الحالة [main.aspx]. وهذا يعني أنه بمجرد أن يملأ المستخدم النموذج [form.aspx]، يجب إرساله إلى [main.aspx] وليس إلى [form.aspx]. وهذا ببساطة غير ممكن إذا قمنا ببناء واجهة المستخدم [form.aspx] باستخدام مكونات خادم ASP. للتأكد من ذلك، لنقم بإنشاء نموذج [formtest.aspx] باستخدام مكون <asp:button>:

<%@ Page Language="VB" EnableViewState="false"%>
<html>
<head>
    <title>test</title>
</head>
<body>
    <form action="main.aspx" runat="server">
        <p>
            <asp:Button id="btnTest" runat="server" EnableViewState="false" Text="Test"></asp:Button>
        </p>
    </form>
</body>
</html>

لاحظ السمة [action="main.aspx"] لعلامة <form>. دعونا نشغل هذا التطبيق. تظهر صفحة العرض زرًا واحدًا فقط:

Image

دعونا نلقي نظرة على كود HTML الذي أرسله الخادم:

<html>
<head>
    <title>test</title>
</head>
<body>
    <form name="_ctl0" method="post" action="formtest.aspx" id="_ctl0">
<input type="hidden" name="__VIEWSTATE" value="dDwtNTMwNzcxMzI0Ozs+" />

        <p>
            <input type="submit" name="btnTest" value="Test" id="btnTest" />
        </p>

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

يمكننا أن نرى أن طلب POST الخاص بالنموذج يستهدف النموذج نفسه [action="formtest.aspx"]، في حين أننا كتبنا علامة HTML من جانب الخادم في [formtest.aspx]:

    <form action="main.aspx" runat="server">

يُعد السمة [runat="server"] لعلامة <form> مطلوبة عند استخدام مكونات جانب الخادم. يحدث خطأ في التجميع إذا لم نقم بتضمين هذه السمة. وعندما نقوم بتضمينها، يتم تجاهل السمة [action] لعلامة <form>. يقوم الخادم دائمًا بإنشاء سمة [action] تشير إلى النموذج نفسه. يمكننا أن نستنتج أنه في تطبيق MVC، لا يمكننا استخدام النماذج التي تم إنشاؤها باستخدام علامة <form ... runat="server">. ومع ذلك، فإن هذه العلامة ضرورية لجميع مكونات خادم ASP التي تسترد مدخلات المستخدم. بعبارة أخرى، لا يمكننا استخدام نماذج خادم ASP في تطبيق MVC. وهذا اكتشاف مهم. في الواقع، تتمثل إحدى الميزات الرئيسية لـ ASP.NET في أنه يمكنك إنشاء تطبيق ويب مثل تطبيق Windows. وهذا صحيح إذا كان تطبيقك لا يتبع بنية MVC، ولكنه أقل صحة في الحالات الأخرى. ومع ذلك، تبدو بنية MVC مفهومًا أساسيًا في تطوير الويب الحديث يصعب تجاهله.

من الممكن استخدام بنية MVC بالاقتران مع نماذج مكونات ASP للتطبيقات التي تحتوي على عدد قليل من طرق العرض المختلفة باستخدام الحل البديل التالي:

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

هذا حل أنيق سنقوم الآن بتطبيقه في بعض الأمثلة

7.14. أمثلة على تطبيقات MVC مع مكونات خادم ASP

7.14.1. المثال 1

في هذا المثال الأول، نقوم بتنفيذ مكونات الخادم التي قدمناها. ستبدو صفحة [form10.aspx] كما يلي:

تُظهر لقطة الشاشة الموجودة أعلى اليسار النموذج كما يظهر للمستخدم. يقوم المستخدم بملئه وإرساله بالنقر فوق [Submit]. يعرض الخادم عرضًا يظهر قائمة بالقيم التي تم إدخالها (لقطة الشاشة اليمنى). يتيح رابط للمستخدم العودة إلى النموذج. ويراه تمامًا كما أرسله. فيما يلي كود العرض لـ [form10.aspx]:

<html>
<head>
    <title>Exemple</title> <script language="javascript">
        function effacer(){
            alert("Vous avez cliqué sur [Effacer]")
        }
    </script>
</head>
<body>
    <p>
        Gestion d'un formulaire
    </p>
    <p>
        <hr />
    </p>
    <form runat="server">
        <p>
            <asp:Panel id="panelinfo" runat="server" EnableViewState="False">
                <p>
                    Liste des valeurs obtenues
                </p>
                <p>
                    <asp:ListBox id="lstInfos" runat="server" EnableViewState="False"></asp:ListBox>
                </p>
                <p>
                    <asp:LinkButton id="LinkButton1" onclick="LinkButton1_Click" runat="server">Retour au formulaire</asp:LinkButton>
                </p>
                <p>
                    <hr />
                </p>
            </asp:Panel>
        </p>
        <p>
            <asp:Panel id="panelform" runat="server" >
                <table>
                    <tbody>
                        <tr>
                            <td>
                                Etes-vous marié(e)</td>
                            <td>
                                <asp:RadioButton id="rdOui" runat="server"  GroupName="rdmarie"></asp:RadioButton>
                                Oui<asp:RadioButton id="rdNon" runat="server"  GroupName="rdmarie" Checked="True"></asp:RadioButton>
                                Non</td>
                        </tr>
                        <tr>
                            <td>
                                Cases à cocher</td>
                            <td>
                                <asp:CheckBox id="chk1" runat="server"></asp:CheckBox>
                                1<asp:CheckBox id="chk2" runat="server"></asp:CheckBox>
                                2<asp:CheckBox id="chk3" runat="server"></asp:CheckBox>
                                3</td>
                        </tr>
                        <tr>
                            <td>
                                Champ de saisie</td>
                            <td>
                                <asp:TextBox id="txtSaisie" runat="server"  MaxLength="20" Columns="20"></asp:TextBox>
                            </td>
                        </tr>
                        <tr>
                            <td>
                                Mot de passe</td>
                            <td>
                                <asp:TextBox id="txtmdp" runat="server"  MaxLength="10" Columns="10" TextMode="Password"></asp:TextBox>
                            </td>
                        </tr>
                        <tr>
                            <td>
                                Boîte de saisie</td>
                            <td>
                                <asp:TextBox id="txtArea" runat="server"  Columns="20" TextMode="MultiLine" Rows="3"></asp:TextBox>
                            </td>
                        </tr>
                        <tr>
                            <td>
                                Liste déroulante</td>
                            <td>
                                <asp:DropDownList id="cmbValeurs" runat="server"></asp:DropDownList>
                            </td>
                        </tr>
                        <tr>
                            <td>
                                Liste à choix unique</td>
                            <td>
                                <asp:ListBox id="lstSimple" runat="server"></asp:ListBox>
                                <asp:Button id="btnRazSimple" onclick="btnRazSimple_Click" runat="server" EnableViewState="False" Text="Raz"></asp:Button>
                            </td>
                        </tr>
                        <tr>
                            <td>
                                Liste à choix multiple</td>
                            <td>
                                <asp:ListBox id="lstMultiple" runat="server" SelectionMode="Multiple"></asp:ListBox>
                                <asp:Button id="razMultiple" onclick="razMultiple_Click" runat="server" EnableViewState="False" Text="Raz"></asp:Button>
                            </td>
                        </tr>
                        <tr>
                            <td>
                                Champ caché</td>
                            <td>
                                <asp:Label id="lblSecret" runat="server" visible="False"></asp:Label></td>
                        </tr>
                        <tr>
                            <td>
                                Bouton simple</td>
                            <td>
                                <input id="btnEffacer" onclick="effacer()" type="button" value="Effacer" /></td>
                        </tr>
                        <tr>
                            <td>
                                Bouton [reset]</td>
                            <td>
                                <input id="btnReset" type="reset" value="Rétablir" /></td>
                        </tr>
                        <tr>
                            <td>
                                Bouton [submit]</td>
                            <td>
                                <asp:Button id="btnEnvoyer" onclick="btnEnvoyer_Click" runat="server" EnableViewState="False" Text="Envoyer"></asp:Button>
                            </td>
                        </tr>
                    </tbody>
                </table>
            </asp:Panel>
        </p>
    </form>
</body>
</html>

تحتوي الصفحة على حاويتين، واحدة لكل عرض: [panelform] لعرض النموذج، و[panelinfo] لعرض المعلومات. قائمة المكونات في حاوية [panelForm] هي كما يلي:

name
النوع
الخصائص
الدور
panelform
لوحة
EnableViewState=true
عرض النموذج
rdYes
rdNo
زر الاختيار
EnableViewState=true
GroupName=rdmarie
أزرار الاختيار
chk1
chk2
chk3
مربع الاختيار
EnableViewState=true
مربعات الاختيار
txtInput
مربع النص
EnableViewState=true
حقل الإدخال
txtPassword
مربع نص
EnableViewState=true
حقل إدخال محمي
txtArea
مربع نص
EnableViewState=true
مربع نص متعدد الأسطر
cmbValues
قائمة منسدلة
EnableViewState=true
قائمة منسدلة
lstSimple
ListBox
EnableViewState=true
SelectionMode=Single
قائمة الاختيار الفردي
btnRazSimple
زر
EnableViewState=false
إلغاء تحديد جميع العناصر في lstSimple
lstMultiple
مربع القائمة
EnableViewState=true
SelectionMode=Multiple
قائمة التحديد المتعدد
btnClearMultiple
زر
EnableViewState=false
إلغاء تحديد جميع العناصر في lstMultiple
lblSecret
تسمية
EnableViewState=true
مرئي=كاذب
حقل مخفي
btnClear
HTML قياسي
 
يعرض تنبيهًا
btnSend
زر
EnableViewState=false
زر [submit] في النموذج
btnReset
HTML قياسي
 
زر [reset] في النموذج

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

  1. يطلب العميل صفحة [form10.aspx] لأول مرة. يتلقى عرض النموذج
  2. ، ويملؤه، ويقوم بإرساله باستخدام زر [Submit]. ثم يتم إرسال حقول الإدخال، ويقوم الخادم بتعيين القيمة المرسلة أو [VIEWSTATE] الخاصة بها إلى المكونات من جانب الخادم، إذا كان لديها واحدة. وبالتالي، يتم تعيين القيمة التي أدخلها المستخدم لحقل [txtSaisie]. لذلك، في هذه الخطوة، لا يخدم [VIEWSTATE] الخاص به أي غرض. ونتيجة لهذه العملية، يتم إرسال عرض [informations]—الذي لا يزال في الواقع هو صفحة [form10.aspx] ولكن مع حاوية عرض مختلفة.
  3. يقوم المستخدم بعرض هذه الصفحة الجديدة ويستخدم رابط [العودة إلى النموذج] للعودة إليها. ثم يتم إرسال طلب POST إلى [form10.aspx]. ولا يوجد سوى قيمة واحدة مرسلة على الأكثر: وهي القيمة التي اختارها المستخدم من قائمة المعلومات، والتي لا يتم استخدامها لاحقًا. وعلى أي حال، لا يوجد حقل [txtSaisie] مرسَل.
  4. يتلقى الخادم طلب POST ويقوم بتعيين القيمة المرسلة إلى مكونات الخادم أو [VIEWSTATE] الخاصة بها إذا كان لديها واحدة. هنا، لا تحتوي [txtNom] على أي قيمة مرسلة. إذا تم تعيين سمة [EnableViewState] الخاصة بها على [false]، فسيتم تعيين السلسلة الفارغة لها. نظرًا لأننا نريدها أن تحتوي على القيمة التي أدخلها المستخدم، فيجب أن تحتوي على الخاصية [EnableViewState=true].

يحتوي الحاوية [panelinfo] على عناصر التحكم التالية:

name
type
الخصائص
role
panelinfo
لوحة
EnableViewState=false
عرض المعلومات
lstInfos
مربع القائمة
EnableViewState=false
قائمة بالمعلومات تلخص القيم التي أدخلها المستخدم
LinkButton1
زر الارتباط
EnableViewState=false
رابط للعودة إلى النموذج

أثناء الاختبار، إذا نظرنا إلى كود HTML الذي تم إنشاؤه بواسطة كود العرض أعلاه، فقد نتفاجأ بالكود الذي تم إنشاؤه للحقل المخفي [lblSecret]:

                    <tr>
                        <td>
                            Champ caché</td>
                        <td></td>
                    </tr>

لا يتم عرض المكون [lblSecret] ككود HTML لأنه يحتوي على الخاصية [Visible=false]. ومع ذلك، نظرًا لأنه يحتوي على الخاصية [EnableViewState=true]، فسيظل قيمته مخزنة في الحقل المخفي [__VIEWSATE]. لذلك، سنتمكن من استرداده، كما ستوضح الاختبارات.

ما زلنا بحاجة إلى كتابة معالجات الأحداث. في [Page_Load]، سنقوم بتهيئة النموذج:

Sub page_Load(sender As Object, e As EventArgs)
         ' the 1st time, we initialize the elements
         ' subsequent times, they are reset to their values by VIEWSTATE
         if IsPostBack then return
         ' init form
         ' panelinfo not displayed
         panelinfo.visible=false
         ' paneform displayed
         panelform.visible=true
         ' radio buttons
         rdNon.Checked=true
         ' checkboxes
         chk2.Checked=true
         ' input field
         txtSaisie.Text="qqs mots"
         ' password field
         txtMdp.Text="ceciestsecret"
         ' input box
         txtArea.Text="ligne"+ControlChars.CrLf+"ligne2"+ControlChars.CrLf
         ' combo
         dim i as integer
         for i=1 to 4
             cmbValeurs.Items.Add(new ListItem("choix"+i.ToString,i.ToString))
         next
         cmbValeurs.SelectedIndex=1
         ' simple selection list
         for i=1 to 7
             lstSimple.Items.Add(new ListItem("simple"+i.ToString,i.ToString))
         next
         lstSimple.SelectedIndex=0
         ' multiple selection list
         for i=1 to 10
             lstMultiple.Items.Add(new ListItem("multiple"+i.ToString,i.ToString))
         next
         lstMultiple.Items(0).Selected=true
         lstMultiple.Items(2).Selected=true
         ' hidden field
         lblSecret.Text="secret"
End Sub

النقر على الزرين [lstRazSimple] و [lstMultiple]:

Sub btnRazSimple_Click(sender As Object, e As EventArgs)
    ' raz single list
    lstSimple.SelectedIndex=-1
End Sub

Sub razMultiple_Click(sender As Object, e As EventArgs)
    ' raz multiple list
    lstMultiple.SelectedIndex=-1
End Sub

النقر على زر [إرسال]:

Sub btnEnvoyer_Click(sender As Object, e As EventArgs)
    ' the info panel is made visible and the form panel is hidden
  panelinfo.Visible=true
  panelform.visible=false
  ' we retrieve the posted values and put them in lstInfos
  ' radio buttons
  dim info as string="état marital : "+iif(rdoui.checked,"marié"," non marié")
  affiche(info)
  ' checkboxes
  info=" cases cochées : "+iif(chk1.checked,"1 oui","1 non")+","+ _
      iif(chk2.checked,"2 oui","2 non")+","+iif(chk3.checked,"3 oui","3 non")
  affiche(info)
  ' input field
  affiche("champ de saisie : " + txtSaisie.Text.Trim)
  ' password
  affiche("mot de passe : " + txtMdp.Text.Trim)
  ' input box
  dim lignes() as String
  lignes=new Regex("\r\n").Split(txtArea.Text.Trim)
  dim i as integer
  for i=0 to lignes.length-1
      lignes(i)="["+lignes(i).Trim+"]"
  next
  affiche("Boîte de saisie : " + String.Join(",",lignes))
  ' combo
  affiche("éléments sélectionnés dans combo : "+selection(cmbValeurs))
  ' simple list
  affiche("éléments sélectionnés dans liste simple : "+selection(lstSimple))
  ' multiple list
  affiche("éléments sélectionnés dans liste multiple : "+selection(lstMultiple))
  ' hidden field
  affiche ("Champ caché : " + lblSecret.Text)
End Sub

sub affiche(msg as String)
    ' displays msg in lstInfos
    lstInfos.Items.Add(msg)
end sub

       function selection(liste as ListControl) as string
           ' browse list elements
           ' to find those selected
           dim i as integer
           dim info as string=""
           for i=0 to liste.Items.Count-1
               if liste.Items(i).Selected then info+="[" + liste.Items(i).Text + "]"
           next
           return info
       end function

أخيرًا، النقر على رابط [العودة إلى النموذج]:

Sub LinkButton1_Click(sender As Object, e As EventArgs)
    ' display the form and hide the info panel
    panelform.visible=true
    panelinfo.visible=false
End Sub

7.14.2. المثال 2

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

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

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

Image

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

7.14.2.2. طرق عرض تطبيق الويب

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

Image

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

Image

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

Image

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

Image

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

Image

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

Image

ثم يرى عرض [المحاكاة] مع محاكاة إضافية واحدة:

Image

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

Image

7.14.2.3. كود عرض التطبيق

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

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

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


<%@ page codebehind="main.aspx.vb" inherits="vs.main" AutoEventWireUp="false" %>
<HTML>
    <HEAD>
        <title>Calcul d'impôt </title>
    </HEAD>
    <body>
        <P>Calcul de 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</TD>
                    </TR>
                    <TR>
                        <TD>Nombre d'enfants</TD>
                        <TD>
                            <asp:TextBox id="txtEnfants" runat="server" MaxLength="3" Columns="3"></asp:TextBox></TD>
                    </TR>
                    <TR>
                        <TD>Salaire annuel (euro)</TD>
                        <TD>
                            <asp:TextBox id="txtSalaire" runat="server" MaxLength="10" Columns="10"></asp:TextBox></TD>
                    </TR>
                </TABLE>
                <P>
                    <asp:Button id="btnCalculer" runat="server" Text="Calculer"></asp:Button>
                    <asp:Button id="btnEffacer" runat="server" Text="Effacer"></asp:Button></P>
            </asp:panel>
          <asp:panel id="panelerreurs" runat="server">
                <P>Les erreurs suivantes se sont produites :</P>
                <P>
                    <asp:Literal id="erreursHTML" runat="server"></asp:Literal></P>
                <P></P>
                <asp:LinkButton id="lnkForm1" runat="server">Retour au formulaire</asp:LinkButton>
            </asp:panel>
          <asp:panel id="panelsimulations" runat="server">
                <P>
                    <TABLE>
                        <TR>
                            <TH>
                                Marié</TH>
                            <TH>
                                Enfants</TH>
                            <TH>
                                Salaire annuel</TH>
                            <TH>
                                Impôt à payer (euro)</TH></TR>
                        <asp:Literal id="simulationsHTML" runat="server"></asp:Literal></TABLE>
                    <asp:LinkButton id="lnkForm2" runat="server">Retour au formulaire</asp:LinkButton></P>
            </asp:panel>
      </FORM>
    </body>
</HTML>

لقد قمنا بتعريف الحاويات الثلاث. لاحظ أنها جميعًا موجودة داخل العلامة <form runat="server">. وهذا أمر إلزامي، لأنه للاستفادة من مكونات الخادم، يجب وضعها داخل مثل هذه العلامة. النقطة الأساسية التي يجب فهمها هي أن لدينا هنا نموذجًا واحدًا سيتم تبادله بين العميل وخادم الويب. لذلك نستخدم التكوين الموصوف في هذا الفصل حول مكونات الخادم. دعونا نحلل مكونات كل حاوية:

حاوية [panelform]:

name
النوع
الخصائص
الدور
panelform
لوحة
EnableViewState=true
عرض النموذج
rdYes
rdNo
زر الاختيار
EnableViewState=true
GroupName=rdmarie
أزرار الاختيار
txtChildren
مربع النص
EnableViewState=true
عدد الأطفال
txtSalary
مربع نص
EnableViewState=true
الراتب السنوي
btnCalculate
زر
 
زر [submit] في النموذج - يبدأ حساب الضريبة
btnClear
زر
 
زر [submit] في النموذج - يمسح النموذج

حاوية [errorPanel]:

الاسم
النوع
الخصائص
الدور
أخطاء اللوحة
لوحة
EnableViewState=true
عرض الخطأ
lnkForm1
زر الارتباط
EnableViewState=true
رابط النموذج
أخطاء HTML
نص
 
كود HTML لقائمة الأخطاء

[panelsimulations] الحاوية:

الاسم
النوع
الخصائص
الدور
panelsimulations
لوحة
EnableViewState=true
عرض المحاكاة
lnkForm2
زر الارتباط
EnableViewState=true
رابط إلى النموذج
simulationsHTML
نص
 
كود HTML لقائمة المحاكاة في جدول HTML

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

يتم توزيع كود التحكم في التطبيق عبر ملفات [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
 
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
        Session.Item("simulations") = New ArrayList
    End Sub
End Class

عند بدء تشغيل التطبيق (الطلب الأول المقدم إلى التطبيق)، يتم تنفيذ الإجراء [Application_Start]. ويحاول هذا الإجراء إنشاء كائن من النوع [impot] عن طريق استرداد بياناته من مصدر OLEDB. يُنصح القارئ بمراجعة الفصل 5، حيث تم تعريف هذه الفئة، إذا كان قد نسيها. قد يفشل إنشاء كائن [impot] إذا كان مصدر البيانات غير متاح. في هذه الحالة، يتم تخزين الخطأ في التطبيق بحيث تعرف جميع الطلبات اللاحقة أنه لم يتم تهيئته بشكل صحيح. إذا نجح الإنشاء، يتم أيضًا تخزين كائن [impot] الذي تم إنشاؤه في التطبيق. وسيتم استخدامه من قبل جميع طلبات حساب الضرائب. عندما يقوم العميل بتقديم طلبه الأول، يتم إنشاء جلسة له بواسطة الإجراء [Application_Start]. تهدف هذه الجلسة إلى تخزين مختلف محاكاة حساب الضرائب التي سيقوم بها. سيتم تخزين هذه المحاكاة في كائن [ArrayList] مرتبط بمفتاح الجلسة "simulations". عند بدء الجلسة، يرتبط هذا المفتاح بكائن [ArrayList] فارغ. يتم وضع المعلومات المطلوبة من قبل التطبيق في ملف التكوين الخاص به [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\serge\devel\aspnet\poly\webforms\vs\impots5\impots.mdb" />
    </appSettings>
</configuration>

يحدد المفتاح [connectionString] سلسلة الاتصال بمصدر OLEDB. ويوجد باقي كود التحكم في [main.aspx.vb]:


Imports System.Collections
Imports Microsoft.VisualBasic
Imports st.istia.univangers.fr
Imports System
 
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 lnkForm1 As System.Web.UI.WebControls.LinkButton
    Protected WithEvents lnkForm2 As System.Web.UI.WebControls.LinkButton
    Protected WithEvents panelerreurs As System.Web.UI.WebControls.Panel
    Protected WithEvents panelsimulations As System.Web.UI.WebControls.Panel
    Protected WithEvents simulationsHTML As System.Web.UI.WebControls.Literal
    Protected WithEvents erreursHTML As System.Web.UI.WebControls.Literal
 
    ' local variables
 
    Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
...
    End Sub
 
    Private Sub afficheFormulaire()
...
    End Sub
 
    Private Sub afficheSimulations(ByRef simulations As ArrayList, 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 Function checkData() As ArrayList
...
    End Function
 
    Private Sub lnkForm1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lnkForm1.Click
....
    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
End Class
 
Le premier événement traité par le code est [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

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

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


    Private Sub afficheErreurs(ByRef erreurs As ArrayList, ByRef lien As String)
        ' displays the error container
        panelerreurs.Visible = True
        Dim i As Integer
        erreursHTML.Text = ""
        For i = 0 To erreurs.Count - 1
            erreursHTML.Text += "<li>" + erreurs(i).ToString + "</li>" + ControlChars.CrLf
        Next
        lnkForm1.Text = lien
        ' the other containers are hidden
        panelform.Visible = False
        panelsimulations.Visible = False
    End Sub

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

  • قائمة برسائل الخطأ في [errors]
  • نص رابط في [link]

يتم وضع كود HTML المراد إنشاؤه لقائمة الأخطاء في المتغير الثابت [errorsHTML]. ويتم وضع نص الرابط في الخاصية [Text] للكائن [LinkButton] في العرض.

الإجراء الذي يعرض عرض [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
        ' check the validity of the data entered
        Dim erreurs As ArrayList = checkData()
        ' if there are errors, we report them
        If erreurs.Count <> 0 Then
            ' the error page is displayed
            afficheErreurs(erreurs, "Retour au formulaire")
            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 simulation() As String = New String() {CType(IIf(rdOui.Checked, "oui", "non"), String), _
         txtEnfants.Text.Trim, txtSalaire.Text.Trim, impot.ToString}
        ' the result is added to existing simulations
        Dim simulations As ArrayList = CType(Session.Item("simulations"), ArrayList)
        simulations.Add(simulation)
        ' put simulations in session and context
        Session.Item("simulations") = simulations
        ' the result page is displayed
        afficheSimulations(simulations, "Retour au formulaire")
    End Sub

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

تتحقق الدالة [CheckData] من صحة البيانات. وتُرجع قائمة [ArrayList] من رسائل الخطأ، والتي تكون فارغة إذا كانت البيانات صالحة:


    Private Function checkData() As ArrayList
        ' initially no errors
        Dim erreurs As New ArrayList
        ' no. of children
        Try
            Dim nbEnfants As Integer = CType(txtEnfants.Text, Integer)
            If nbEnfants < 0 Then Throw New Exception
        Catch
            erreurs.Add("Le nombre d'enfants est incorrect")
        End Try
        ' salary
        Try
            Dim salaire As Long = CType(txtSalaire.Text, Long)
            If salaire < 0 Then Throw New Exception
        Catch
            erreurs.Add("Le salaire annuel est incorrect")
        End Try
        ' return the list of errors
        Return erreurs
    End Function

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


    Private Sub afficheSimulations(ByRef simulations As ArrayList, ByRef lien As String)
        ' displays the simulations view
        panelsimulations.Visible = True
        ' the other containers are hidden
        panelerreurs.Visible = False
        panelform.Visible = False
        ' contents of simulations view
        ' each simulation is an array of 4 string elements
        Dim simulation() As String
        Dim i, j As Integer
        simulationsHTML.Text = ""
        For i = 0 To simulations.Count - 1
            simulation = CType(simulations(i), String())
            simulationsHTML.Text += "<tr>"
            For j = 0 To simulation.Length - 1
                simulationsHTML.Text += "<td>" + simulation(j) + "</td>"
            Next
            simulationsHTML.Text += "</tr>" + ControlChars.CrLf
        Next
        ' link
        lnkForm2.Text = lien
    End Sub

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

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

يتم تخزين كود HTML المراد إنشاؤه لقائمة عمليات المحاكاة في المتغير الثابت [simulationsHTML]. يتم تخزين نص الرابط في الخاصية [Text] للكائن [LinkButton] في طريقة العرض.

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

7.14.2.5. الاختبارات

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

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

Image

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

Image

7.14.3. المثال 3

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

Image

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

Image

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

إن حقيقة أننا لم نعد نمتلك وحدة تحكم واحدة تختار العروض التي سيتم إرسالها إلى العميل لها عيوبها. خذ معالجة الأخطاء على سبيل المثال. قد تحتاج كل إجراء يعرضه التطبيق إلى عرض عرض خطأ. سيكون لكل وحدة تحكم [applix.aspx] عرض [errors] خاص بها لأن هذا ببساطة عبارة عن حاوية محددة داخل صفحة وحدة التحكم. لا توجد طريقة للحصول على عرض [errors] واحد يستخدمه جميع التطبيقات الفردية. في الواقع، يتضمن هذا العرض عادةً رابطًا للعودة إلى النموذج الذي يحتوي على الأخطاء، ويجب استعادة هذا النموذج إلى الحالة التي تم التحقق من صحتها فيها للسماح للمستخدم بتصحيح أخطائه. تتم هذه الاستعادة عبر آلية [VIEWSTATE]، التي لا تعمل عبر وحدات التحكم المختلفة. إذا تم تطوير التطبيقات بواسطة أشخاص مختلفين، فهناك خطر وجود صفحات أخطاء تبدو مختلفة اعتمادًا على الإجراء الذي يختاره المستخدم، مما يقوض اتساق التطبيق بشكل عام. سنرى لاحقًا أن ASP.NET يقدم حلاً لهذه المشكلة المحددة المتعلقة بمشاركة طرق العرض. يمكن تنفيذ ذلك كمكون خادم جديد نقوم ببنائه بأنفسنا. يضمن مجرد استخدام هذا المكون عبر التطبيقات المختلفة اتساق التطبيق ككل. أما المسألة الأكثر صعوبة في الإدارة فهي ترتيب الإجراءات. عندما تمر جميع الطلبات عبر وحدة تحكم واحدة، يمكنها التحقق من أن الإجراء المطلوب متوافق مع الإجراء السابق. يقع كود التحقق هذا في مكان واحد. هنا، سيتعين توزيعه عبر وحدات التحكم المختلفة، مما يعقد صيانة التطبيق ككل.

لنعد إلى تطبيقنا أعلاه. صفحة الدخول الخاصة به هي صفحة HTML قياسية [home.htm]:

<html>
    <head>
        <TITLE>Composants ASP Serveur</TITLE>
        <meta name="pragma" content="no-cache">
    </head>
    <frameset rows="130,*" frameborder="0">
        <frame name="banner" src="bandeau.htm" scrolling="no">
        <frameset cols="200,*">
            <frame name="contents" src="options.htm">
            <frame name="main" src="main.htm">
        </frameset>
        <noframes>
            <p id="p1">
                Ce jeu de frames HTML affiche plusieurs pages Web. Pour afficher ce jeu de 
                frames, utilisez un navigateur Web qui prend en charge HTML 4.0 et version 
                ultérieure.
            </p>
        </noframes>
    </frameset>
</html>

تتكون هذه الصفحة الرئيسية من ثلاثة إطارات تسمى banner و contents و main:

الصفحة [bandeau.htm] الموضوعة في إطار [banner] هي كما يلي:

Image

وإليك كود HTML الخاص بها:


<html>
    <head>
        <META HTTP-EQUIV="PRAGMA" CONTENT="NO-CACHE" />
        <title>bandeau</title>
    </head>
    <body>
        <P>
            <TABLE>
                <TR>
                    <TD><IMG alt="logo université d'angers" src="univ01.gif"></TD>
                    <TD>Composants serveurs ASP</TD>
                </TR>
            </TABLE>
        </P>
        <HR>
    </body>
</html>

يتم وضع صفحة [options.htm] في شعار [contents]. وهي عبارة عن مجموعة من الروابط:


<html>
    <head>
        <meta http-equiv="pragma" content="no-cache" />
        <title>options</title>
    </head>
    <body bgcolor="Gold">
        
            
                <a href="main.aspx?action=label" 
                 target="main">تسمية</a>
            
                <a href="main.aspx?action=button" 
                 target="main">زر
            
                <a href="main.aspx?action=textbox1" 
                 target="main">مربع النص-1"
            
                <a href="main.aspx?action=textbox2" 
                 target="main">مربع النص-2
            
                <a href="main.aspx?action=dropdownlist" 
                 target="main">قائمة منسدلة
            
                <a href="main.aspx?action=listbox" 
                 target="main">مربع القائمة
            
                <a href="main.aspx?action=checkbox" 
                  target="main">مربع الاختيار" و
                زر الاختيار
            
                <a href="main.aspx?action=listecasesacocher" 
                  target="main">CheckBoxList و
                قائمة أزرار الاختيار
            
                <a href="main.aspx?action=panel" 
                  target="main">لوحة
        
    </body>
</html>

تشير جميع الروابط المختلفة إلى وحدة التحكم الرئيسية [main.aspx] مع معلمة [action] تشير إلى الإجراء المطلوب تنفيذه. نحدد أن هدف الرابط يجب أن يُعرض في الإطار [main] (target="main").

الصفحة الأولى التي يتم عرضها في الإطار [main] هي [main.htm]:


<html>
    <head>
        <title>main</title>
    </head>
    <body>
        <P>اختر خيارًا لاختبار واكتشاف نوع من مكونات الخادم...</P>
    </body>
</html>

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

[main.aspx]

<%@ page src="main.aspx.vb" inherits="main" autoeventwireup="false" %>

[main.aspx.vb]

فئة عامة main

تورث <bdi dir="ltr" class="odt-ltr-term">System.Web.UI.Page</bdi>

<bdi dir="ltr" class="odt-ltr-term">Private Sub Page</bdi>\_Load(<bdi dir="ltr" class="odt-ltr-term">ByVal sender As System.Object, ByVal e As System.EventArgs</bdi>) <bdi dir="ltr" class="odt-ltr-term">Handles MyBase.Load</bdi>

    &#x27; استرداد الإجراء المطلوب تنفيذه

    <bdi dir="ltr" class="odt-ltr-term">Dim action As String</bdi>

    إذا كان <bdi dir="ltr" class="odt-ltr-term">Request.QueryString</bdi>(&quot;<bdi dir="ltr" class="odt-ltr-term">action</bdi>&quot;) يساوي <bdi dir="ltr" class="odt-ltr-term">Nothing</bdi>

        <bdi dir="ltr" class="odt-ltr-term">action</bdi> = &quot;<bdi dir="ltr" class="odt-ltr-term">label</bdi>&quot;

    <bdi dir="ltr" class="odt-ltr-term">Else</bdi>

        <bdi dir="ltr" class="odt-ltr-term">action</bdi> = <bdi dir="ltr" class="odt-ltr-term">Request.QueryString</bdi>(&quot;<bdi dir="ltr" class="odt-ltr-term">action</bdi>&quot;).<bdi dir="ltr" class="odt-ltr-term">ToString.ToLower</bdi>

    <bdi dir="ltr" class="odt-ltr-term">End If</bdi>

    &#x27; تنفيذ الإجراء

    اختر الحالة <bdi dir="ltr" class="odt-ltr-term">action</bdi>

        <bdi dir="ltr" class="odt-ltr-term">Case</bdi> &quot;<bdi dir="ltr" class="odt-ltr-term">label</bdi>&quot;

            <bdi dir="ltr" class="odt-ltr-term">Server.Transfer</bdi>(&quot;<bdi dir="ltr" class="odt-ltr-term">form2.aspx</bdi>&quot;)

        الحالة &quot;زر&quot;

            <bdi dir="ltr" class="odt-ltr-term">Server.Transfer</bdi>(&quot;<bdi dir="ltr" class="odt-ltr-term">form3.aspx</bdi>&quot;)

        الحالة &quot;<bdi dir="ltr" class="odt-ltr-term">textbox1</bdi>&quot;

            <bdi dir="ltr" class="odt-ltr-term">Server.Transfer</bdi>(&quot;<bdi dir="ltr" class="odt-ltr-term">form4.aspx</bdi>&quot;)

        حقل &quot;<bdi dir="ltr" class="odt-ltr-term">textbox2</bdi>&quot;

            <bdi dir="ltr" class="odt-ltr-term">Server.Transfer</bdi>(&quot;<bdi dir="ltr" class="odt-ltr-term">form5.aspx</bdi>&quot;)

        الحالة &quot;<bdi dir="ltr" class="odt-ltr-term">dropdownlist</bdi>&quot;

            <bdi dir="ltr" class="odt-ltr-term">Server.Transfer</bdi>(&quot;<bdi dir="ltr" class="odt-ltr-term">form6.aspx</bdi>&quot;)

        الحالة &quot;<bdi dir="ltr" class="odt-ltr-term">listbox</bdi>&quot;

            <bdi dir="ltr" class="odt-ltr-term">Server.Transfer</bdi>(&quot;<bdi dir="ltr" class="odt-ltr-term">form7.aspx</bdi>&quot;)

        الحالة &quot;<bdi dir="ltr" class="odt-ltr-term">checkbox</bdi>&quot;

            <bdi dir="ltr" class="odt-ltr-term">Server.Transfer</bdi>(&quot;<bdi dir="ltr" class="odt-ltr-term">form8.aspx</bdi>&quot;)

        الحالة &quot;<bdi dir="ltr" class="odt-ltr-term">checkboxlist</bdi>&quot;

            <bdi dir="ltr" class="odt-ltr-term">Server.Transfer</bdi>(&quot;<bdi dir="ltr" class="odt-ltr-term">form8b.aspx</bdi>&quot;)

        الحالة &quot;<bdi dir="ltr" class="odt-ltr-term">panel</bdi>&quot;

            <bdi dir="ltr" class="odt-ltr-term">Server.Transfer</bdi>(&quot;<bdi dir="ltr" class="odt-ltr-term">form9.aspx</bdi>&quot;)

        الحالة الأخرى

            <bdi dir="ltr" class="odt-ltr-term">Server.Transfer</bdi>(&quot;<bdi dir="ltr" class="odt-ltr-term">form2.aspx</bdi>&quot;)

    نهاية الاختيار

نهاية <bdi dir="ltr" class="odt-ltr-term">Sub</bdi>

نهاية الفئة

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