Skip to content

7. کامپوننت‌های سرور ASP – ۱

7.1. Introduction

در این فصل، فناوری توصیه‌شده در ASP.NET برای ساخت رابط کاربری را شرح می‌دهیم. می‌دانیم که در پردازش یک صفحه .aspx توسط سرور وب، دو مرحله متمایز وجود دارد:

  1. ابتدا، کنترل‌کننده صفحه اجرا می‌شود. این شامل کدی است که یا در داخل خود صفحه .aspx (راه حل WebMatrix) یا در یک فایل جداگانه (راه حل Visual Studio.NET) قرار دارد.
  2. سپس کد ارائه صفحه .aspx اجرا شده و به کد HTML تبدیل می‌شود که به کلاینت ارسال می‌گردد.

Image

ASP.NET سه کتابخانه تگ برای نوشتن کد ارائه صفحه ارائه می‌دهد:

  1. تگ‌های استاندارد HTML. این همان چیزی است که تا کنون از آن استفاده کرده‌ایم.
  2. برچسب‌های سمت سرور HTML
  3. برچسب‌های Web Forms

صرف‌نظر از اینکه از کدام کتابخانه تگ استفاده شود، نقش کنترل‌کننده صفحه ثابت می‌ماند. این کنترل‌کننده باید مقدار پارامترهای پویایی را که در کد نمایش ظاهر می‌شوند، محاسبه کند. تا به حال، این پارامترهای پویا ساده بودند: آن‌ها اشیایی از نوع [String] بودند. بنابراین اگر در کد نمایش یک تگ <%=name%> داشته باشیم:

  • کنترل‌کننده صفحه یک متغیر [nom] از نوع [String] را اعلام می‌کند و مقدار آن را محاسبه می‌کند
  • هنگامی که کنترل‌کننده صفحه کار خود را به پایان رساند و کد نمایش برای تولید پاسخ HTML اجرا شد، تگ <%=name%> با مقداری که توسط کد کنترل‌کننده محاسبه شده است جایگزین می‌شود

ما می‌دانیم که جداسازی بین کنترل‌کننده و نمایش دلخواهی است و کد کنترل‌کننده و کد نمایش می‌توانند در یک صفحه با هم ترکیب شوند. ما توضیح داده‌ایم که چرا این رویکرد توصیه نمی‌شود و به جداسازی کنترل‌کننده و نمایش پایبند خواهیم ماند.

تگ‌های [HTML serveur] و [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 از لیست c.a.d باشد. «<select name=..>...</select>». انجام این کار چندان دشوار نیست و به نظر یک راه‌حل شیک می‌آید که از قرار دادن کد تولید محتوا مستقیماً در بخش نمایشی صفحه جلوگیری می‌کند. در اینجا، باید یک حلقه همراه با بررسی‌ها درج شود که کد را به طور قابل توجهی شلوغ می‌کند. با این حال، این روش یک عیب دارد. جداسازی کنترل‌کننده و بخش نمایشی در یک صفحه همچنین برای مشخص کردن دو حوزه مسئولیت به کار می‌رود:

  • و آنِ توسعه‌دهنده .NET، که مسئول کنترل‌کننده صفحه است،
  • و طراح گرافیک، که مسئول بخش ارائه صفحه است

در اینجا می‌بینیم که تولید کد HTML برای لیست به کنترلر منتقل شده است. طراح گرافیک ممکن است بخواهد این کد HTML را برای تغییر ظاهر «نمایان» لیست اصلاح کند. او مجبور خواهد بود در بخش [contrôleur] کار کند و در نتیجه از حوزه مسئولیت خود خارج شود، که این امر با خطر مرتبط با وارد کردن سهوی خطاها به کد همراه است.

کتابخانه‌های تگ سرور این مشکل را حل می‌کنند. آن‌ها یک شیء ارائه می‌دهند که نمایانگر یک لیست HTML است. بنابراین، کتابخانه [WebForms] تگ زیر را ارائه می‌دهد:

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

این تگ نمایانگر یک شی از نوع [ListBox] است که می‌تواند توسط کنترل‌کننده صفحه دستکاری شود. این شی دارای خواصی برای نمایش گزینه‌های مختلف در لیست HTML و مشخص کردن گزینه انتخاب‌شده است. بنابراین کنترل‌کننده صفحه مقادیر مناسب را به این ویژگی‌ها اختصاص خواهد داد. هنگامی که لایه ارائه اجرا می‌شود، تگ

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

با کد HTML که نمایانگر اشیاء [uneListe] و c.a.d است، جایگزین خواهد شد. کد «<select ..>...</select>». در حال حاضر، به جز این که این یک رویکرد شیءگرا به کدنویسی است که جالب است، تفاوت بنیادینی با روش قبلی ندارد. بیایید به طراح گرافیک خود بازگردیم که نیاز دارد «ظاهر» لیست را تغییر دهد. تگ‌های سرور دارای ویژگی‌های استایل (BackColor, Bordercolor, BorderWidth, …) هستند که امکان تعیین ظاهر بصری شیء متناظر HTML را فراهم می‌کنند. بنابراین می‌توانیم بنویسیم:


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

مزیت این است که طراح گرافیک می‌تواند این تغییرات را در حالی که در کد ارائه باقی می‌ماند، اعمال کند. این یک مزیت قطعی نسبت به روش قبلی است. بنابراین، کتابخانه‌های تگ سرور ساخت بخش ارائه صفحات را - آنچه در فصل‌های قبلی به آن رابط کاربری گفته‌ایم - ساده می‌کنند. هدف این فصل معرفی آن‌ها است. خواهیم دید که آنها گاهی اوقات اشیاء پیچیده‌ای مانند تقویم‌ها یا جداول متصل به منابع داده را ارائه می‌دهند. آنها قابل توسعه هستند، c.a.d، به این معنی که کاربر می‌تواند کتابخانه تگ خود را ایجاد کند. بنابراین می‌توانند تگی ایجاد کنند که یک بنر در یک صفحه تولید می‌کند. تمام صفحاتی که از این تگ استفاده می‌کنند، در ادامه دارای همان بنر خواهند بود.

خروجی تولید شده برای یک تگ با نوع مرورگر کلاینت سازگار می‌شود. هنگامی که کلاینت درخواستی را به وب سرور ارسال می‌کند، در میان هدرهای خود، یک هدر HTTP را می‌فرستد که در آن [User-Agent: 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 را بازمی‌گرداند که کلاینت قادر به درک آن خواهد بود. این یک مفهوم جالب و مفید است، زیرا به این معناست که توسعه‌دهنده دیگر نیازی ندارد نگران نوع مرورگر کلاینتی باشد که برنامه‌اش روی آن اجرا می‌شود.

در نهایت، ابزارهای پیشرفته IDE مانند Visual Studio.NET، WebMatrix و … امکان طراحی رابط کاربری وب به سبک ویندوز را فراهم می‌کنند. اگرچه این ابزارها ضروری نیستند، اما با این حال کمک‌های حیاتی به توسعه‌دهنده ارائه می‌دهند. توسعه‌دهنده با استفاده از کامپوننت‌های گرافیکی که روی رابط قرار می‌دهد، رابط وب را طراحی می‌کند. او دسترسی مستقیم به ویژگی‌های هر کامپوننت رابط دارد و می‌تواند آن‌ها را به دلخواه پیکربندی کند. این ویژگی‌ها برای رندر کردن رابط، به کد HTML به صورت ویژگی‌های تگ <asp:> کامپوننت ترجمه می‌شوند. مزیت این کار برای توسعه‌دهنده این است که دیگر نیازی به به خاطر سپردن لیست یا نحو ویژگی‌های هر تگ ندارد. این یک مزیت قابل توجه است، به‌ویژه زمانی که کاربر با کتابخانه‌های تگ سرور ارائه‌شده توسط ASP.NET کاملاً آشنا نباشد. پس از تسلط بر این نحو، برخی از توسعه‌دهندگان ممکن است ترجیح دهند که تگ‌ها را مستقیماً در کد ارائه صفحه بدون طی کردن مرحله طراحی گرافیک کدگذاری کنند. در این صورت دیگر یک IDE لازم نیست. یک ویرایشگر متن ساده کافی خواهد بود. بسته به روش کاری فرد، تمرکز سپس بر روی کامپوننت‌ها (با استفاده از یک IDE) یا تگ‌ها (با استفاده از یک ویرایشگر متن) خواهد بود. این دو اصطلاح معادل یکدیگر هستند. کامپوننت ابجئتی است که توسط کد کنترل صفحه دستکاری می‌شود. IDE در طول فاز طراحی، دسترسی به ویژگی‌های آن را برای ما فراهم می‌کند. مقادیر تخصیص‌یافته به این ویژگی‌ها بلافاصله در کد ارائه به ویژگی‌های تگ کامپوننت تبدیل می‌شوند. در فاز اجرای برنامه، کد کنترل صفحه کامپوننت را دستکاری کرده و مقادیری را به برخی از ویژگی‌های آن تخصیص می‌دهد. کد ارائه، کد HTML کامپوننت را با استفاده از یک سو، ویژگی‌های تعیین‌شده در زمان طراحی برای تگ سرور مربوطه و از سوی دیگر، مقادیر ویژگی‌های کامپوننت که توسط کد کنترل محاسبه شده‌اند، تولید می‌کند.

7.2. زمینهٔ اجرای مثال‌ها

ما طراحی رابط‌های وب مبتنی بر کامپوننت‌های سرور را با استفاده از برنامه‌هایی نشان خواهیم داد که زمینهٔ اجرای آن‌ها عمدتاً به شرح زیر خواهد بود:

  1. برنامهٔ وب از یک صفحهٔ واحد P تشکیل شده است که شامل یک فرم F می‌باشد،
  2. کلاینت اولین درخواست خود را مستقیماً به این صفحه P ارسال خواهد کرد. این شامل درخواست URL صفحه P با استفاده از یک مرورگر است. بنابراین، این یک درخواست GET است که به این URL P ارسال می‌شود. سرور صفحه P و در نتیجه فرم F موجود در آن را تحویل خواهد داد،
  3. کاربر فرم را پر کرده و با انجام عملی که مرورگر را مجبور به ارسال فرم F به سرور می‌کند، آن را ارسال (submit) خواهد کرد. c.a.d عملیات مرورگر POST همچنان متوجه صفحه P خواهد بود. سرور بار دیگر صفحه P را به همراه فرم F بازخواهد گرداند، که محتویات آن ممکن است توسط اقدام کاربر تغییر کرده باشد.
  4. سپس مراحل ۲ و ۳ از سر گرفته می‌شوند.

این یک فرآیند اجرایی بسیار خاص است که خارج از آن، برخی مفاهیم ذکر شده در زیر دیگر کار نمی‌کنند. ما دیگر در زمینه معماری MVC نیستیم که در آن یک برنامه چندصفحه‌ای توسط یک صفحه خاص که آن را «کنترل‌کننده برنامه» نامیده‌ایم، کنترل می‌شود. در این نوع معماری، POST فرم‌ها به کنترل‌کننده هدایت می‌شود و نه به خود فرم‌ها. با این حال، خواهیم دید که ساختن یک فرم با کامپوننت‌های سمت سرور به این معناست که این فرم به خود ارسال می‌شود.

7.3. کامپوننت Label

7.3.1. نحوه استفاده

برچسب <asp:label> به شما امکان می‌دهد متن پویا را در کد ارائه صفحه درج کنید. بنابراین، این برچسب کاری فراتر از برچسب <%=variable%> که تا کنون استفاده می‌کردیم، انجام نمی‌دهد. بررسی این تگ اول به ما کمک می‌کند تا نحوه کار تگ‌های سمت سرور را درک کنیم. ما در حال ایجاد صفحه‌ای با یک بخش کنترل ([form1.aspx.vb]) و یک بخش نمایش ([form1.aspx]) هستیم. هدف نمایش زمان است:

Image

این موضوع پیش‌تر در فصل ۲ پوشش داده شده است و خوانندگان دعوت می‌شوند در صورت تمایل به دانستن نحوهٔ رسیدگی به آن، به آن فصل مراجعه کنند. کد ارائه [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
        'پرس‌وجوی جاری را به‌عنوان request.txt در پوشهٔ صفحه ذخیره می‌کند
        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
        'زمان را در 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 یک تگ <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] را بررسی کنند. دو مورد از این ویژگی‌ها در اینجا مورد توجه هستند:

  • متن: این متنی است که برچسب باید نمایش دهد – ما مقدار رشته را روی خالی (c.a.d. هیچ) تنظیم می‌کنیم
  • ID: این شناسه‌ی آن است – ما آن را روی lblHeure تنظیم می‌کنیم

زبانه‌ی [Design] به صورت زیر درمی‌آید:

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
        'زمان را در 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. کامپوننت Literal

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] را در یک فرم درج کنید که با خود رسیدگی به رویدادهایی مشابه آنچه در برنامه‌های ویندوز یافت می‌شود را به همراه دارد. این همان نکته‌ای است که می‌خواهیم در اینجا با جزئیات بیشتری بررسی کنیم. ما صفحه [form3.aspx] زیر را ایجاد می‌کنیم:

Image

این صفحه که با استفاده از WebMatrix ساخته شده است، سه مؤلفه دارد:

خیر.
name
type
ویژگی‌ها
نقش
۱
Button1
دکمه
text=دکمه1
دکمه ارسال
2
Button2
دکمه
text=دکمه2
دکمه ارسال
۳
lblInfo
برچسب
متن=
پیام اطلاعاتی

کد تولید شده توسط 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، c.a.d. با این حال، یک تگ استاندارد، HTML، توسط ابجکتی نمایش داده می‌شود که کنترلر می‌تواند آن را دستکاری کند. کد تگ <form> بر اساس مقداری که کنترلر به این شیء اختصاص می‌دهد، تولید خواهد شد.

بیایید رویه [Page_Init] را به بخش کنترل‌کننده کد اضافه کنیم که رویداد [Init] صفحه را مدیریت می‌کند. کدی را که درخواست مشتری را در فایل [request.txt] ذخیره می‌کند، در آنجا قرار خواهیم داد. برای درک نحوه کارکرد دکمه‌ها به این بخش نیاز داریم.

<script runat="server">
    Private Sub Page_Init(ByVal sender As Object, ByVal e As System.EventArgs) 
        'درخواست فعلی را در request.txt در دایرکتوری صفحه ذخیره می‌کند
        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. آزمایش‌ها

ما برنامه را از طریق [F5] تحت WebMatrix راه‌اندازی می‌کنیم. صفحه زیر به ما نمایش داده می‌شود:

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]، نیز تنظیم شده‌اند. در بیشتر مواقع، این ویژگی‌ها نادیده گرفته می‌شوند. با این حال، اگر صفحه حاوی کد جاوااسکریپت اجرا شده در سمت کلاینت باشد، ویژگی [name] تگ <form> مفید است.

  • برچسب‌های <asp:button> به برچسب‌های HTML برای دکمه‌های [submit] تبدیل شده‌اند. کلیک روی هر یک از این دکمه‌ها باعث ارسال (post) فرم [_ctl10] به آدرس URL [form3.aspx] می‌شود.
  • برچسب <asp:label> به یک برچسب <span> از نوع HTML تبدیل شده است
  • یک فیلد مخفی [__VIEWSTATE] با مقدار عجیبی ایجاد شده است:
<input type="hidden" name="__VIEWSTATE" value="dDwxNTY0NjIwMjUwOzs+2mcnJczeuvF2PEfvmtv7uiUhWUw=" />

این فیلد به صورت رمزگذاری‌شده وضعیت فرم ارسال‌شده به کلاینت را نشان می‌دهد. این وضعیت نمایانگر مقدار تمام اجزای فرم است. از آنجایی که [__VIEWSTATE] بخشی از فرم است، مقدار آن به همراه سایر بخش‌ها به سرور ارسال خواهد شد. این امر به سرور امکان می‌دهد تا تشخیص دهد کدام بخش از فرم مقدار خود را تغییر داده و در صورت لزوم اقدام مناسب را انجام دهد. این اقدامات به شکل رویدادهایی مانند «TextBox مقدار فلان بخش تغییر کرده است» خواهند بود.

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 استاندارد داریم. اکنون روی دکمه [Bouton1] در صفحه مرورگر کلیک کنیم. به نظر می‌رسد هیچ اتفاقی نمی‌افتد. با این حال می‌دانیم که فرم ارسال شده است. این موضوع توسط کد 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
  • مقدار دکمه‌ای که کلیک شده بود

اگر روی [Bouton2] کلیک کنیم، مقادیر ارسال‌شده توسط مرورگر به شرح زیر است:

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

مقدار فیلد مخفی همچنان یکسان است، اما مقداری که ارسال شده، مقدار [Button2] است. بنابراین، سرور می‌تواند تشخیص دهد که کدام دکمه استفاده شده است. سرور از این مقدار برای راه‌اندازی یک رویداد استفاده می‌کند که صفحه پس از بارگذاری می‌تواند آن را مدیریت کند.

7.5.4. پردازش رویداد کلیک یک شیء Button

بیایید مرور کنیم که یک صفحه .aspx چگونه کار می‌کند. این یک شیء مشتق شده از کلاس [Page] است. بیایید این کلاس مشتق شده را [unePage] بنامیم. هنگامی که سرور درخواستی برای چنین صفحه‌ای دریافت می‌کند، یک شی از نوع [unePage] با استفاده از عملیات new unePage(...) ایجاد می‌شود. سپس سرور دو رویداد به نام‌های [Init] و [Load] به ترتیب ایجاد می‌کند. ابژه [unePage] می‌تواند با ارائهٔ رسیدگی‌کننده‌های رویداد [Page_Init] و [Page_Load] به این رویدادها رسیدگی کند. سپس رویدادهای بیشتری تولید خواهند شد. بعداً فرصت خواهیم داشت تا به این موضوع بازگردیم. اگر درخواست مشتری یک POST باشد، سرور رویداد [Click] را برای دکمه‌ای که این POST را تحریک کرده است، تولید می‌کند. اگر کلاس [unePage] برای این رویداد یک دستگیرکننده تعریف کرده باشد، آن فراخوانده خواهد شد. بیایید این مکانیزم را با استفاده از WebMatrix بررسی کنیم. در برگه [Design] از [form3.aspx]، روی دکمه [Bouton1] دوبار کلیک کنید. سپس به‌طور خودکار به برگه [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)
        ' روی دکمه 1 کلیک کنید
        lblInfo.Text="Vous avez cliqué sur [Bouton1]"
    End Sub

این رویه یک پیام اطلاعاتی را در برچسب [lblInfo] قرار می‌دهد. ما به همین ترتیب برای دکمه [Bouton2] عمل می‌کنیم تا صفحه جدید زیرین [form3.aspx] را به دست آوریم:

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

    Private Sub Page_Init(ByVal sender As Object, ByVal e As System.EventArgs)
        'درخواست فعلی را به عنوان request.txt در پوشه صفحه ذخیره می‌کند
        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)
        'کلیک روی دکمه ۱
        lblInfo.Text="Vous avez cliqué sur [Bouton1]"
    End Sub

    Sub Button2_Click(sender As Object, e As EventArgs)
        ' روی دکمهٔ ۲ کلیک کنید
        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>

اگر روی [Bouton1] کلیک کنیم، پاسخ زیر را دریافت می‌کنیم:

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 کاملاً مشهود است.
  • پردازش رویدادهای قبلی همیشه در سمت سرور انجام می‌شود. البته، کلیک روی یک دکمه می‌تواند توسط یک اسکریپت جاوااسکریپت سمت سرور مدیریت شود. با این حال، این دیگر یک رویداد سرور نیست و ما در اینجا با فناوری مستقلی از ASP.NET سروکار داریم.

رویدادها چه زمانی پردازش می‌شوند، چه در سمت سرور (رویدادهای مرتبط با کامپوننت‌های سمت سرور) و چه در سمت مرورگر توسط اسکریپت‌های جاوااسکریپت؟

بیایید مثال یک لیست کشویی را در نظر بگیریم. هنگامی که کاربر آیتم انتخاب‌شده در لیست را تغییر می‌دهد، رویداد (تغییر آیتم انتخاب‌شده) ممکن است مدیریت شود یا نشود، و اگر مدیریت شود، می‌تواند در زمان‌های مختلفی انجام گیرد.

  • اگر بخواهید فوراً آن را مدیریت کنید، دو گزینه وجود دارد:
    • می‌تواند با استفاده از یک اسکریپت جاوااسکریپت توسط مرورگر مدیریت شود. در این حالت سرور دخالتی ندارد. برای اینکه این کار ممکن شود، صفحه باید قابلیت بازسازی با استفاده از مقادیر موجود در خود را داشته باشد.
    • می‌تواند توسط سرور پردازش شود. برای این کار، تنها یک راه‌حل وجود دارد: فرم باید برای پردازش به سرور ارسال شود. بنابراین ما یک عملیات [submit] داریم. در این مورد، می‌بینیم که از یک کامپوننت سرور به نام [DropDownList] استفاده می‌کنیم و ویژگی [AutoPostBack] آن را روی [true] تنظیم می‌کنیم. این بدان معناست که اگر آیتم انتخاب‌شده در لیست کشویی تغییر کند، فرم باید فوراً به سرور ارسال شود. در این مورد، سرور برای شیء [DropDownList]، یک کد HTML تولید می‌کند که با یک تابع جاوااسکریپت مرتبط است و مسئول ایجاد یک [submit] به محض وقوع رویداد «تغییر آیتم انتخاب‌شده» (selected item changed) می‌باشد. این [submit] فرم را برای سرور ارسال خواهد کرد و فیلدهای پنهان در ارسال گنجانده می‌شوند تا نشان دهند که [post] ناشی از تغییر در انتخاب درون لیست کشویی بوده است. سپس سرور رویداد [SelectedIndexChanged] را تولید می‌کند که صفحه می‌تواند آن را مدیریت کند.
    • اگر می‌خواهید آن را پردازش کنید اما نه بلافاصله، ویژگی [AutoPostBack] از کامپوننت سرور [DropDownList] را روی [false] تنظیم کنید. در این حالت، سرور برای شیء [DropDownList]، کد استاندارد HTML را برای یک لیست <select> بدون تابع جاوااسکریپت مرتبط تولید می‌کند. بنابراین، وقتی کاربر گزینش را در لیست کشویی تغییر می‌دهد، هیچ اتفاقی نمی‌افتد. با این حال، هنگامی که کاربر فرم را با استفاده از دکمه‌ای مانند [submit] ارسال می‌کند، سرور می‌تواند تشخیص دهد که تغییری در انتخاب رخ داده است. در واقع، ما مشاهده کرده‌ایم که فرم ارسالی به سرور حاوی یک فیلد پنهان، [__VIEWSTATE]، است که وضعیت تمام عناصر موجود در فرم ارسالی را به صورت رمزگذاری‌شده نشان می‌دهد. هنگامی که سرور فرم جدیدی را که توسط کلاینت ارسال شده دریافت می‌کند، قادر خواهد بود بررسی کند که آیا آیتم انتخاب‌شده در لیست کشویی تغییر کرده است یا خیر. در صورت مثبت بودن، رویداد [SelectedIndexChanged] را ایجاد می‌کند که صفحه می‌تواند آن را مدیریت کند. برای تمایز این مکانیزم از مکانیزم قبلی، برخی از نویسندگان می‌گویند که رویداد «تغییر انتخاب» (selection change) هنگامی که در مرورگر رخ می‌دهد، «پوچ شده» (cached) است. این رویداد تنها زمانی توسط سرور پردازش می‌شود که مرورگر فرم را برای آن ارسال کند، که اغلب پس از کلیک روی دکمه‌ای با شناسه [submit] انجام می‌شود.
    • در نهایت، اگر مایل به رسیدگی به رویداد نیستید، ما ویژگی [AutoPostBack] از کامپوننت سرور [DropDownList] را روی [false] تنظیم می‌کنیم و هندلر رویداد [SelectedIndexChanged] آن را نمی‌نویسیم.

پس از درک مکانیزم رسیدگی به رویدادها، توسعه‌دهنده یک برنامه وب را به همان شیوه‌ای که یک برنامه ویندوزی طراحی نمی‌کند. در واقع، در حالی که تغییر در انتخاب داخل یک کادر ترکیبی در یک برنامه ویندوزی می‌تواند برای تغییر فوری ظاهر فرم حاوی آن استفاده شود، در یک برنامه وب، اگر این رویداد شامل یک درخواست «POST» از فرم به سرور باشد—و در نتیجه یک رفت و برگشت بین کلاینت و سرور—احتمالاً کمتر مایل به رسیدگی فوری به این رویداد خواهند بود. به همین دلیل، ویژگی [AutoPostBack] کامپوننت‌های سرور به طور پیش‌فرض روی [false] تنظیم می‌شود. علاوه بر این، مکانیزم [AutoPostBack]—که به اسکریپت‌های جاوااسکریپت اتکا دارد که توسط وب سرور به طور خودکار در داخل فرم ارسال‌شده به کلاینت تولید می‌شوند—تنها در صورتی قابل استفاده است که اطمینان حاصل شود مرورگر کلاینت اجرای اسکریپت‌های جاوااسکریپت را مجاز کرده است. بنابراین فرم‌ها اغلب به شکل زیر ساخته می‌شوند:

  • اجزای سمت سرور فرم، ویژگی [AutoPostBack] خود را روی [false] تنظیم می‌کنند
  • فرم دارای یک یا چند دکمه است که مسئول راه‌اندازی اقدام [POST] برای فرم هستند
  • در کد کنترل‌کننده صفحه، تنها برای رویدادهایی که نیاز به رسیدگی دارند، هانلدرها نوشته می‌شوند، که رایج‌ترین آن‌ها رویداد [Click] روی یکی از دکمه‌ها است.

7.6. کامپوننت TextBox

7.6.1. نحوه استفاده

برچسب <asp:TextBox> به شما امکان می‌دهد یک فیلد ورودی را در کد ارائه صفحه درج کنید. ما یک صفحه [form4.aspx] ایجاد می‌کنیم تا طرح‌بندی زیر را به دست آوریم:

Image

۴۳۲۱ این صفحه که با استفاده از WebMatrix ایجاد شده است، شامل اجزای زیر است:

خیر.
نام
نوع
ویژگی‌ها
نقش
۱
TextBox1
TextBox
AutoPostback=true
متن=
میدان ورودی
2
TextBox2
TextBox
AutoPostback=false
متن=
میدان ورودی
۳
lblInfo1
برچسب
متن=
پیام اطلاعاتی در مورد محتویات [TextBox1]
3
lblInfo2
برچسب
متن=
پیام اطلاعاتی در مورد محتوای [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>

در برگه [Design]، روی مؤلفه [TextBox1] دوبار کلیک کنید. اسکلتِ رویدادپرداز [TextChanged] برای این شیء سپس ایجاد می‌شود (زبانه‌ی [All]):

<%@ 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"> tag to specify the event handler [TextChanged] for [TextBox1]. We are now writing the procedure [TextBox1_Changed].

    Sub TextBox1_TextChanged(sender As Object, e As EventArgs)
      ' تغییر متن
      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)
        'درخواست فعلی را در request.txt در پوشه صفحه ذخیره می‌کند
        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)
      ' تغییر متن
      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)
      ' تغییر متن
      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]. این دو فیلد آخر برای مدیریت رویداد «change» مرورگر روی فیلد ورودی [TextBox1] استفاده می‌شوند.
  • برچسب‌های سرور <asp:textbox> برچسب‌های HTML <input type="text" ...> را تولید کرده‌اند که با فیلدهای ورودی مطابقت دارند
  • برچسب سرور <asp:textbox id="TextBox1" AutoPostBack="true" ...> یک برچسب <input type="text" ...> با یک ویژگی [onchange="__doPostBack('TextBox1','')"] تولید کرده است. این ویژگی مشخص می‌کند که در صورت تغییر محتوای [TextBox1]، تابع جاوااسکریپت [_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] است. رویداد 'change' مرورگر، اجرای کد '__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] تولید کرد. سپس مشاهده کردیم که یک تابع جاوااسکریپت اجرا شد و فرم را به [form4.aspx] ارسال کرد. بنابراین این صفحه توسط سرور دوباره بارگذاری شد. مقادیر ارسال‌شده توسط فرم به نوبه خود به سرور اجازه داد تا تشخیص دهد که محتوای تگ سرور [TextBox1] تغییر کرده است. بنابراین رویه [TextBox1_Changed] در سمت سرور اجرا شد. این رویه پیامی را در برچسب [lblInfo1] قرار داد. پس از اتمام این رویه، [form4.aspx] به مرورگر ارسال شد. به همین دلیل است که اکنون در [lblInfo1] متن وجود دارد. با این حال، ممکن است این موضوع تعجب‌آور به نظر برسد که چیزی در فیلد ورودی [TextBox1] وجود دارد. در واقع، هیچ رویه‌ای در سمت سرور برای این فیلد مقداری تعیین نمی‌کند. این یک مکانیزم عمومی فرم‌های وب است: سرور فرم را در همان حالتی بازمی‌گرداند که آن را دریافت کرده است. برای این کار، آن مقدار را که توسط کلاینت برای اجزا ارسال شده بود، مجدداً به آن‌ها اختصاص می‌دهد. برای برخی از مؤلفه‌ها، کلاینت هیچ ارزشی را ارسال نمی‌کند. این مورد، برای مثال، در مورد مؤلفه‌های <asp:label> صدق می‌کند که به صورت تگ‌های <span> رندر می‌شوند. به یاد داشته باشید که فرم دارای یک فیلد پنهان است که وضعیت فرم را هنگام ارسال به کلاینت نشان می‌دهد. این وضعیت، مجموع وضعیت‌های تمام اجزای فرم، از جمله هر یک از اجزای <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

بیایید به فیلد ورودی شماره ۱ بازگردیم تا متن جدیدی وارد کنیم: این بار هیچ اتفاقی نمی‌افتد. چرا؟ زیرا مقدار ویژگی فیلد ورودی [TextBox2] از [AutoPostBack] به [true] تنظیم نشده است، بنابراین <input type="text"...> تولید شده برای آن، رویداد [Change] را مدیریت نمی‌کند، همانطور که در کد آن HTML نشان داده شده است:

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

بنابراین هیچ رویدادی زمانی که فیلد ورودی شمارهٔ ۲ را ترک می‌کنید رخ نمی‌دهد. اکنون بیایید مقداری متن جدید در فیلد شمارهٔ ۱ وارد کنیم:

Image

بیایید فیلد ورودی شماره ۱ را ترک کنیم. بلافاصله، رویداد [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] سپس به کلاینت بازگردانده می‌شود.

اکنون دوباره فیلد ورودی شمارهٔ ۱ را اصلاح می‌کنیم:

Image

وقتی نشانگر را از فیلد ۱ خارج می‌کنیم، رویداد [Change] در مرورگر رخ می‌دهد. سپس توالی رویدادهایی که قبلاً توضیح داده شد (ارسال از مرورگر به سرور، …، ارسال پاسخ سرور) اتفاق می‌افتد. پاسخ زیر را دریافت می‌کنیم:

Image

از روی زمان‌بندی‌های نمایش‌داده‌شده برای هر پیام، می‌توانیم ببینیم که تنها رویه [TextBox1_Changed] روی سرور اجرا شده است. رویه [TextBox2_TextChanged] دقیقاً به این دلیل اجرا نشد که مقدار [TextBox2] تغییر نکرده بود. در نهایت، بیایید متن جدیدی را در فیلد شماره ۲ وارد کنیم:

Image

سپس بیایید مکان‌نما را به فیلد شماره ۱ و سپس به فیلد شماره ۲ حرکت دهیم. صفحه تغییر نمی‌کند. چرا؟ زیرا ما مقدار فیلد شماره ۱ را تغییر نمی‌دهیم، رویداد مرورگر [Change] هنگام خروج از آن فیلد رخ نمی‌دهد. در نتیجه، فرم برای سرور ارسال نمی‌شود. بنابراین هیچ چیزی در صفحه تغییر نمی‌کند. همین واقعیت که محتوای [lblInfo2] تغییر نمی‌کند، به ما نشان می‌دهد که POST وجود ندارد. اگر چنین بود، سرور تشخیص می‌داد که محتوای [TextBox2] تغییر کرده است و باید این تغییر را در [lblInfo2] منعکس می‌کرد.

درس عبرت این مثال این است که هیچ دلیلی برای تنظیم ویژگی [AutoPostBack] یک [TextBox] به [true] وجود ندارد. این کار در بیشتر مواقع منجر به یک رفت و برگشت غیرضروری کلاینت-سرور می‌شود.

7.6.3. نقش فیلد __VIEWSTATE

ما دیدیم که سرور به طور سیستماتیک یک فیلد پنهان به نام __VIEWSTATE را در فرم تولید شده خود قرار می‌دهد. ما توضیح دادیم که این فیلد نمایانگر وضعیت فرم است و اگر این فیلد پنهان به سرور بازگردانده شود، سرور قادر به بازسازی مقدار قبلی فرم خواهد بود. وضعیت یک فرم، مجموع وضعیت‌های اجزای آن است. هر مؤلفه دارای خاصیتی به نام [EnableViewState] است که مقدار آن بولی بوده و نشان می‌دهد آیا وضعیت مؤلفه باید در فیلد مخفی [__VIEWSATE] قرار گیرد یا خیر. به طور پیش‌فرض، این خاصیت دارای مقدار [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>
...

حالا بیایید برنامه را اجرا کنیم و قبل از رفتن به فیلد شمارهٔ ۲، متنی را در فیلد شمارهٔ ۱ وارد کنیم. سپس درخواست POST به سرور ارسال می‌شود و پاسخ زیر را دریافت می‌کنیم:

Image

بیایید مقداری متن را در فیلد شمارهٔ ۲ وارد کنیم، سپس متن را در فیلد شمارهٔ ۱ ویرایش کنیم و بعد به فیلد شمارهٔ ۲ بازگردیم (به همین ترتیب). یک درخواست جدید POST به سرور ارسال می‌شود و پاسخ زیر را دریافت می‌کنیم:

Image

تا اینجا همه چیز مانند قبل است. اکنون بیایید محتوای فیلد شماره ۱ را تغییر دهیم و سپس به فیلد شماره ۲ برویم. یک درخواست جدید POST ارسال می‌شود. پاسخ سرور به شرح زیر است:

Image

این بار، تغییری وجود دارد. سرور یک رویداد [TextChanged] را در فیلد شماره ۲ تشخیص داده است، زیرا زمان برای [lblInfo2] تغییر کرده است. با این حال، هیچ تغییر واقعی رخ نداده است. این موضوع با ویژگی [EnableViewState=false] از [TextBox2] توضیح داده می‌شود. این بدان معناست که سرور وضعیت قبلی [TextBox2] را در فیلد [__VIEWSTATE] فرم وارد نکرده است. این معادل تخصیص رشتهٔ خالی به‌عنوان وضعیت قبلی است. هنگامی که رویداد POST به دلیل تغییر در محتوای [TextBox1] رخ داد، سرور مقدار فعلی [TextBox2]—که [deux] بود—را با مقدار قبلی آن (رشته خالی) مقایسه کرد. نتیجه گرفت که مقدار [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>
...

بیایید همان سری آزمایش‌ها را مانند قبل تکرار کنیم. جایی که صفحه را به دست آوردیم

version 1

Image

اکنون دریافت می‌کنیم:

version 2

Image

در این مرحله، ما محتوای فیلد ۱ را بدون تغییر فیلد ۲ تغییر دادیم، به این معنی که رویه [TextBox2_TextChanged] در سمت سرور اجرا نشد، و در نتیجه فیلد [lblInfo2] مقدار جدیدی دریافت نکرد. بنابراین با مقدار قبلی خود نمایش داده می‌شود. در نسخه ۱ ([EnableViewState=true])، این مقدار قبلی، مقدار وارد شده بود. در نسخه ۲ ([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=first+text --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=first+text --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+text --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]، سرور مقدار ارسال‌شده آن، [second texte]، را با مقدار قبلی آن مقایسه کرد. از آنجایی که [__VIEWSTATE] موجود نیست، این مقدار قبلی برابر با یک رشته خالی است. از آنجایی که مقدار ارسال‌شده برای [TextBox2] خالی نبود، رویداد [TextChanged] بر روی [TextBox2] ایجاد شد. روال [TextBox2_TextChanged] اجرا شد و مقدار جدیدی به فیلد [lblInfo2] اختصاص یافت.

ممکن است این سؤال پیش بیاید که آیا پارامترهای [__EVENTTARGET] و [__EVENTARGUMENT] واقعاً مفید هستند یا خیر. اگر این پارامترها ارسال نشوند، سرور نخواهد دانست کدام رویداد [submit] را فعال کرده است. بیایید امتحان کنیم:

dos>curl --include --url http://localhost/form4.aspx --data TextBox2=second+text --data TextBox1=first+text
..............................
        <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+text --data TextBox1=first+text
.......
        <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>..</textareac.a.d را نیز فراهم می‌کند. این تگ‌ها به ترتیب به یک فیلد ورودی امن و یک فیلد ورودی چندخطی مربوط می‌شوند. این تولید توسط خاصیت [TextMode] از کامپوننت [TextBox] کنترل می‌شود. این خاصیت سه مقدار ممکن دارد:

مقدار
برچسب HTML تولید شد
SingleLine
<input type="text" ...>
MultiLine
<textarea>...</textarea>
Password
<input type="password ...>

ما استفاده از این ویژگی‌ها را با مثال زیر بررسی خواهیم کرد، [form5.aspx]:

Image

خیر.
نام
type
ویژگی‌ها
نقش
۱
btnAjouter
دکمه
 
[submit] دکمه – برای افزودن محتوای [TextBox1] به محتوای [TextBox2]
2
TextBox1
TextBox
TextMode=کلمه عبور
متن=
میدان ورودی محافظت‌شده
۳
TextBox2
TextBox
حالت متن=چندخطی
متن=
ورودی‌های ثبت‌شده در [TextBox1] را ترکیب می‌کند

ویژگی [EnableViewState] صفحه روی [false] تنظیم شده است. در سمت سرور، رویداد کلیک روی دکمه [btnAjouter] را مدیریت می‌کنیم:

Sub btnAjouter_Click(sender As Object, e As EventArgs)
  'محتوای [textBox1] به محتوای [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] ارسال شده، به اضافه نشانگر پایان خط QZXW2HTMLP00 تنظیم می‌کند.2826ZQX در فضای نام [Microsoft.VisualBasic] تعریف شده است. وارد کردن این فضای نام ضروری نیست، زیرا وب‌سرور به‌طور پیش‌فرض آن را وارد می‌کند.

کد نهایی برای [form5.aspx] به شرح زیر است:

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

    Sub btnAjouter_Click(sender As Object, e As EventArgs)
      'محتویات [textBox1] به محتویات [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

خیر.
نام
نوع
ویژگی‌ها
نقش
۱
DropDownList1
DropDownList
AutoPostback=true
EnableViewState=true
لیست کشویی
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] آشنا شویم:

Items
یک مجموعه از نوع [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] true است
SelectedIndex
عنصر شماره [SelectedItem] در مجموعه [Items]

عناصر در مجموعه [Items] از کلاس [DropDownList] از نوع [ListItem] هستند. هر عنصر [ListItem] یک تگ HTML <option> تولید می‌کند:

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

ما برخی از ویژگی‌ها و متدهای کلاس [ListItem] را توصیف می‌کنیم:

ListItem(String texte, String value)
سازنده (constructor) – یک عنصر [ListItem] را با ویژگی‌های [texte] و [value] ایجاد می‌کند. یک عنصر ListItem(T,V) تگ HTML <option value="V">T</option> را تولید می‌کند. کلاس [ListITem] بنابراین اجازه می‌دهد عناصر یک لیست HTML توصیف شوند
Selected
بولین. اگر true باشد، گزینهٔ متناظر در لیست HTML دارای ویژگی [selected="selected"] خواهد بود. این ویژگی به مرورگر دستور می‌دهد که آیتم متناظر باید در لیست HTML به‌صورت انتخاب‌شده نمایش داده شود.
Text
متن T گزینه HTML <option value="V" [selected="selected"]>T</option>
Value
مقدار 'V' برای ویژگی [Value] برای گزینه HTML <option value="V" [selected="selected"]>T</option>

ما اطلاعات کافی برای نوشتن کد جهت پر کردن لیست کشویی [DropDownList1] در رویه [Page_Load] در صفحه داریم:

    Sub Page_Load(sender As Object, e As EventArgs)
        ' این ترکیب زمانی پر می‌شود که برای اولین بار فراخوانی شده باشد
        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] است. اگر این ویژگی روی true تنظیم شود، به این معنی است که صفحه توسط یک POST فراخوانی می‌شود. اگر مقدار آن false باشد، یعنی صفحه توسط 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)
      'تغییر انتخاب
      lblInfo.text="Elément sélectionné : texte="+dropdownlist1.selecteditem.text+ _
        " valeur=" + dropdownlist1.selecteditem.value + _
        " index="+ dropdownlist1.selectedindex.tostring
    End Sub

هنگامی که این رویه اجرا می‌شود، شیء [DropDownList1] عناصر خود از نوع [ListItem] را از طریق [VIEWSTATE] بازیابی کرده است. علاوه بر این، یکی از این عناصر که از نوع [ListItem] است، ویژگی [Selected] آن روی مقدار true تنظیم شده است – این همان عنصری است که مقدار آن توسط مرورگر ارسال شده است. چندین راه برای دسترسی به این عنصر وجود دارد:

DropDownList1.SelectedItem
اولین عنصر در لیست است که ویژگی [Selected] آن روی true تنظیم شده است
DropDownList1.SelectedItem.Text
مطابق با بخش [texte] از تگ HTML در داخل عنصر <option value="...">text</option> است که توسط کاربر انتخاب شده است
DropDownList1.SelectedItem.Value
مطابقت دارد با بخش [value] از تگ HTML عنصر <option value="...">text</option> که توسط کاربر انتخاب شده است
DropdDownList1.SelectedIndex
شماره در مجموعه [DropDownList1.Items] از عنصر اول [ListItem] که ویژگی [Selected] آن روی true تنظیم شده است

کد نهایی برای [form6.aspx] به شرح زیر است:

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

    Sub Page_Load(sender As Object, e As EventArgs)
        'اگر این اولین باری است که با شما تماس گرفته شده، منوی کشویی را پر کنید
        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)
      'تغییر انتخاب
      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
txtSaisie
TextBox
EnableViewState=false
میدان ورودی
2
btnAjouter
دکمه
 
دکمه [submit] که محتویات txtSaisie را به لیست 1 منتقل می‌کند، به شرطی که خالی نباشد.
۳
ListBox1
ListBox
EnableViewState=true
SelectionMode=Single
فهرست تک‌انتخابی
۴
ListBox2
ListBox
EnableViewState=true
حالت‌انتخاب=چندگانه
فهرست مقادیر برای انتخاب چندگانه
۵
btn1vers2
دکمه
 
دکمه [submit] که آیتم انتخاب‌شده را از [liste 1] به [liste 2] منتقل می‌کند
۶
btn2vers1
دکمه
 
دکمه [submit]، که عناصر انتخاب‌شده را از [liste 2] به [liste 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] است، زیرا این‌ها در واقع متعلق به [ListControl] بودند. یک ویژگی جدید ظاهر می‌شود:

SelectionMode
حالت انتخاب را برای لیست <select> HTML که از کامپوننت تولید می‌شود، تنظیم می‌کند. اگر SelectionMode=Single باشد، تنها یک آیتم می‌تواند انتخاب شود. اگر SelectionMode=Multiple باشد، چندین آیتم می‌توانند انتخاب شوند. برای این کار، ویژگی [multiple="multiple"] در تگ <select> لیست HTML تولید خواهد شد.

بیایید رویدادها را مدیریت کنیم. کلیک روی دکمه [Ajouter] توسط رویه زیر [btnAjouter_Click] مدیریت خواهد شد:

    Sub btnAjouter_Click(sender As Object, e As EventArgs)
      ' افزودن به لیست ۱
      dim texte as string=txtSaisie.text.trim
      if texte<> "" then ListBox1.Items.Add(New ListItem(texte))
      'پاک کردن txtSaisie
      txtSaisie.text=""
    End Sub

اگر متنی که در [txtSaisie] وارد شده خالی یا Blank نباشد، یک عنصر جدید به لیست [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>] و c.a.d را تولید می‌کند. متن [T] از گزینه برای تشکیل مقدار گزینه استفاده می‌شود. پس از افزودن محتوای [txtSaisie] به لیست [ListBox1]، فیلد [txTSaisie] پاک می‌شود.

کلیک‌های روی دکمه‌های [Effacer] توسط رویه‌های زیر مدیریت می‌شوند:

    Sub btnRaz1_Click(sender As Object, e As EventArgs)
      ' پاک کردن لیست ۱
      ListBox1.Items.Clear
    End Sub

    Sub btnRaz2_Click(sender As Object, e As EventArgs)
      ' پاک کردن لیست ۲
      ListBox2.Items.Clear
    End Sub

کلیک‌های مربوط به دکمه‌های جابجایی بین لیست‌ها توسط رویه‌های زیر مدیریت می‌شوند:

    Sub btn1vers2_Click(sender As Object, e As EventArgs)
      ' انتقال آیتم انتخاب‌شده از لیست ۱ به لیست ۲
      transfert(ListBox1,ListBox2)
    End Sub

    Sub btn2vers1_Click(sender As Object, e As EventArgs)
      ' انتقال آیتم انتخاب‌شده از فهرست ۲ به فهرست ۱
      transfert(ListBox2,ListBox1)
    End Sub

    sub transfert(l1 as listbox, l2 as listbox)
      'انتقال موارد انتخاب‌شده از فهرست ۱ به فهرست ۲
       'کاری هست؟
      if l1.selectedindex=-1 then return
      dim i as integer
      ' بیایید از انتها شروع کنیم
      for i=l1.items.count-1 to 0 step -1
        ' انتخاب شده؟
        if l1.items(i).selected then
            ' دیگر انتخاب نشده است
            l1.items(i).selected=false
          ' حرکت به l2
          l2.items.add(l1.items(i))
          ' حذف از 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 به ترتیب معکوس پیمایش می‌شود. اگر ما آن را به صورت رو به جلو پیمایش کرده و عنصر شماره ۱۰ را حذف کنیم، عنصر شماره ۱۱ به شماره ۱۰ تبدیل می‌شود و عنصر شماره ۱۲ به عنصر شماره ۱۱ تبدیل می‌شود. پس از پردازش عنصر شماره ۱۰، حلقه پیش‌رونده ما عنصر شماره ۱۱ را پردازش خواهد کرد که همان‌طور که توضیح دادیم، قبلاً شماره ۱۲ بوده است. عنصری که قبلاً شماره ۱۱ بود و اکنون شماره ۱۰ است، نادیده گرفته می‌شود. با پیمایش عناصر لیست l1 در جهت مخالف، از این مشکل جلوگیری می‌کنیم.

7.9. کامپوننت‌های CheckBox، RadioButton

تگ‌های <asp:RadioButton> و <asp:CheckBox> به شما امکان می‌دهند به ترتیب یک دکمه رادیویی و یک کادر تیک را در کد نمایشی یک صفحه درج کنید. ما یک صفحه با نام [form8.aspx] ایجاد می‌کنیم تا طرح‌بندی زیر را تولید کنیم:

Image

خیر.
نام
نوع
ویژگی‌ها
نقش
۱
RadioButton1
RadioButton2
RadioButton3
RadioButton
RadioButton1.Checked=true
RadioButton1.Text=1
RadioButton2.Checked=false
RadioButton2.Text=2
RadioButton3.Checked=false
RadioButton3.Text=3
برای ۳ دکمه: GroupName=radio
دکمه‌های رادیویی
2
CheckBoxA
CheckBoxB
CheckBoxC
CheckBox
برای همه بررسی‌شده=false
CheckBoxA.Text=A
CheckBoxB.Text=B CheckBoxC.Text=C
تیک‌باکس‌ها
۳
btnEnvoyer
دکمه
 
دکمه [submit]
۴
lstInfos
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)
      'اطلاعات در کادر فهرست قرار می‌گیرد
      for each c as control in FindControl("frmControls").controls
          ' آیا کنترلی است که از 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)
      ' اطلاعات در کادر فهرست قرار می‌گیرد
      for each c as control in FindControl("frmControls").controls
          'آیا این کنترل از 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] از همان کلاسی مشتق شده‌اند که کلاس‌های [DropDownList] و [ListBox] که پیش‌تر مورد بحث قرار گرفتند، از آن مشتق شده‌اند. بنابراین، برخی از ویژگی‌ها و متدهای مشاهده‌شده برای این کلاس‌ها – آن‌هایی که در واقع متعلق به [ListControl] بودند – را پیدا خواهیم کرد.

Items
یک مجموعه از نوع [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
شماره عنصر قبلی در مجموعه [Items]

برخی ویژگی‌ها مختص کلاس‌های [CheckBoxList] و [RadioButtonList] هستند:

RepeatDirection
[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 texte, String value)
constructor – یک عنصر [ListItem] را با ویژگی‌های text و value ایجاد می‌کند. یک عنصر ListItem(T,V) برچسب HTML <input type="checkbox" value="V">T یا <input type="radio" value="V">T را، بسته به مورد، تولید می‌کند.
Selected
بولی. اگر مقدار آن true باشد، گزینهٔ متناظر در لیست HTML دارای ویژگی [selected="selected"] خواهد بود. این ویژگی به مرورگر دستور می‌دهد که عنصر متناظر باید در لیست HTML به‌صورت انتخاب‌شده نمایش داده شود.
Text
متن T گزینه HTML <input type=".." value="V" [selected="selected"]>T
Value
مقدار ویژگی Value گزینه HTML <input type=".." value="V" [selected="selected"]>T

ما پیشنهاد می‌کنیم صفحه زیر را بسازیم: [form8b.aspx]:

Image

خیر.
name
نوع
ویژگی‌ها
نقش
1
RadioButtonList1
RadioButtonList
EnableViewState=true
RepeatDirection=horizontal
فهرست دکمه‌های رادیویی
2
CheckBoxList1
CheckBoxList
EnableViewState=true
جهت تکرار=افقی
فهرست چک‌باکس‌ها
۳
btnEnvoyer
دکمه
 
دکمه [submit] که در [4] فهرست آیتم‌های انتخاب‌شده از هر دو لیست را نمایش می‌دهد
۴
lstInfos
ListBox
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
        ' فهرست‌ها پر می‌شوند اگر این اولین بار باشد که تابع فراخوانی می‌شود
        if not IsPostBack then
            'متن برای لیست RadioButton
          dim textesRadio() as String = {"1","2","3","4"}
          'متن برای لیست RadioButton
          dim textesCheckBox() as  String = {"un","deux","trois","quatre"}
                 ' پر کردن لیست دکمه رادیویی
            dim i as integer
            for i=0 to textesRadio.length-1
                RadioButtonList1.Items.Add(new ListItem(textesRadio(i)))
            next
             ' انتخاب مورد شمارهٔ ۱
            RadioButtonList1.SelectedIndex=1
            ' پر کردن لیست چک‌باکس
            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)
      'اطلاعات در جعبه‌ی فهرست lstinfos قرار می‌گیرد
      affiche(RadioButtonList1)
      affiche(CheckBoxList1)
    End Sub

    sub affiche(l1 as ListControl)
      'مقادیر موارد انتخاب‌شده در l1 را نمایش می‌دهد
       'کاری هست که باید انجام بدیم؟
      if l1.selectedindex=-1 then return
      dim i as integer
      ' بیایید از انتها شروع کنیم
      for i= 0 to l1.items.count-1
        'انتخاب شده؟
        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] مجدداً تولید می‌شوند. پس از نمایش صفحه، کاربر برخی از کادرها را تیک می‌زند و دکمه [Envoyer] را کلیک می‌کند. سپس مقادیر فرم به خود فرم ارسال می‌شوند. پس از اجرای [Page_Load]، رویه [btnEnvoyer_Click] اجرا می‌شود. این رویه، رویه [affiche] را برای پر کردن لیست [lstInfos] فراخوانی می‌کند. این رویه یک شیء از نوع [ListControl] را به عنوان پارامتر دریافت می‌کند، که به این معنی است که می‌توان یک شیء [RadioButtonList] یا یک شیء [CheckBoxList] را به آن پاس داد – که هر دو کلاس مشتق شده از [ListControl] هستند. فهرست [lstInfos] ممکن است ویژگی آن روی [EnableViewState] یا [false] تنظیم شود، زیرا نیازی نیست وضعیت آن بین درخواست‌های مختلف حفظ شود.

7.11. کامپوننت پنل، LinkButton

تگ <asp:panel> امکان درج یک کانتینر از کنترل‌ها در یک صفحه را فراهم می‌کند. مزیت این کانتینر این است که برخی از ویژگی‌های آن بر تمام کنترل‌های موجود در آن اعمال می‌شوند. این موضوع در مورد ویژگی [Visible] آن صدق می‌کند. این ویژگی برای هر کنترل سمت سرور وجود دارد. اگر یک کانتینر دارای خاصیت [Visible=false] باشد، هر یک از کنترل‌های آن تحت تأثیر خاصیت [Visible] خود قرار خواهند گرفت. اگر دارای خاصیت [Visible=false] باشد، آنگاه خودِ کانتینر و تمام محتویات آن نمایش داده نمی‌شوند. این کار ممکن است ساده‌تر از مدیریت خاصیت [Visible] برای هر یک از کنترل‌های کانتینر باشد.

تگ <asp:LinkButton> به شما امکان می‌دهد یک لینک را در کد نمایش صفحه درج کنید. این تگ کاربرد مشابهی با دکمه [Button] دارد. این تگ از طریق یک تابع جاوااسکریپت مرتبط، یک رویداد POST سمت کلاینت را فعال می‌کند. ما یک صفحه [form9.aspx] ایجاد می‌کنیم تا به طرح‌بندی زیر دست یابیم:

Image

خیر.
نام
نوع
ویژگی‌ها
نقش
۱
Panel1
پنل
EnableViewState=true
ظرف کنترل
2
ListBox1
ListBox
EnableViewState=true
فهرستی از سه مقدار
۳
lnkCacher
LinkButton
EnableViewState=false
لینک برای پنهان کردن کانتینر

وقتی کانتینر پنهان می‌شود، یک لینک جدید ظاهر می‌شود:

Image

خیر.
نام
نوع
ویژگی‌ها
نقش
۴
lnkVoir
LinkButton
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] را با سه مقدار اولیه می‌کند. رسیدگی‌کننده‌های رویداد برای [Clic] در هر دو لینک به شرح زیر هستند:

<%@ 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)
        ' کانتینر ۱ را نمایش می‌دهد
        panel1.Visible=true
         'تغییر لینک‌ها
        lnkVoir.visible=false
        lnkCacher.visible=true
    End Sub

    Sub lnkCacher_Click(sender As Object, e As EventArgs)
        'مخفی کردن کانتینر 1
        panel1.Visible=false
         ' لینک‌ها را تغییر می‌دهد
        lnkVoir.visible=true
        lnkCacher.visible=false
    End Sub
</script>

ما از رویه [Page_Load] برای inicialize کردن فرم استفاده خواهیم کرد. این کار را در اولین درخواست انجام می‌دهیم (IsPostBack=false):

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

    Sub Page_Load(sender As Object, e As EventArgs)
        ' اولین بار
        if not IsPostBack then
            ' کانتینر نمایش داده می‌شود
            lnkVoir_Click(nothing,nothing)
        end if
    end sub
.....
</script>

7.12. برای ادامه...

پاراگراف‌های پیشین تعدادی از اجزای سرور را معرفی کردند. در هر مورد تنها چند مورد از ویژگی‌های آن‌ها پوشش داده شده است. برای بررسی عمیق‌تر این اجزا، خواننده می‌تواند از راه‌های مختلفی اقدام کند:

  • ویژگی‌های یک کامپوننت را با استفاده از یک IDE مانند WebMatrix بررسی کنید. این سند ویژگی‌های اصلی کامپوننت‌های مورد استفاده در یک فرم را تشریح می‌کند
  • برای کشف مجموعه کامل کلاس‌های متناظر با هر مؤلفه سرور، به مستندات .NET مراجعه کنید. این روش، روش ترجیحی برای درک کامل مؤلفه است. در آنجا، سلسله‌مراتب کلاس‌های منتهی به مؤلفه‌ها و همچنین ویژگی‌ها، متدها، سازندها و رویدادهای هر کلاس را خواهید یافت. علاوه بر این، مستندات گاهی اوقات مثال‌هایی را نیز ارائه می‌دهد.

در این فصل، ما از تکنیک «همه‌کاره» ([WebMatrix]) استفاده کرده‌ایم، که در آن هر دو کد نمایشی و کنترلی یک صفحه را در یک فایل واحد قرار داده‌ایم. به‌طور کلی، ما این روش را توصیه نمی‌کنیم، بلکه رویکرد موسوم به [codebehind] را که قبلاً استفاده شد، ترجیح می‌دهیم، که این دو نوع کد را در دو فایل جداگانه قرار می‌دهد. شایان ذکر است که مزیت این جداسازی در این است که کد کنترلی می‌تواند بدون نیاز به اجرای برنامه وب کامپایل شود. علاوه بر این، نمونه‌های ما – همانطور که در ابتدای این فصل توضیح دادیم – ساختار بسیار مشخصی داشتند: آنها از یک صفحه واحد تشکیل شده بودند که فرم آن در چرخه‌های متوالی درخواست-پاسخ بین کلاینت و سرور مبادله می‌شد، به طوری که اولین درخواست کلاینت یک GET و درخواست‌های بعدی POST بودند.

7.13. کامپوننت‌های سرور و کنترل‌کنندهٔ برنامه

در فصل‌های قبلی، ما چندین برنامه وب ساختیم. همه‌ی آن‌ها با استفاده از معماری MVC (مدل-نما-کنترل‌کننده) ساخته شدند، که برنامه را به بلوک‌های مجزا تقسیم می‌کند و نگهداری آن را آسان‌تر می‌سازد. در آن زمان، ما رابط‌های کاربری خود را با استفاده از تگ‌های استاندارد HTML می‌ساختیم. با توجه به آنچه به تازگی دیده‌ایم، طبیعی است که اکنون بخواهیم از کامپوننت‌های سمت سرور استفاده کنیم. بیایید مشکلی را که قبلاً به تفصیل مطالعه کرده‌ایم، یعنی محاسبه مالیات، مجدداً بررسی کنیم. معماری MVC آن به شرح زیر بود:

Image

این برنامه دو نما دارد: [formulaire.aspx] و [erreurs.aspx]. نمای [formulaire.aspx] زمانی نمایش داده می‌شود که برای اولین بار به URL [main.aspx] درخواست داده شود:

Image

کاربر فرم را پر می‌کند:

Image

و دکمه [Calculer] را کلیک می‌کند تا پاسخ زیر را دریافت کند:

Image

در یک برنامه MVC، تمام درخواست‌ها باید از طریق کنترلر، در این مورد [main.aspx]، ارسال شوند. این بدان معناست که پس از تکمیل فرم [formulaire.aspx] توسط کاربر، باید آن را به [main.aspx] ارسال کند و نه به [formulaire.aspx]. این امر به سادگی ممکن نیست اگر رابط کاربری [formulaire.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>
<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 این است که شما می‌توانید یک برنامه کاربردی وب را درست مانند یک برنامه کاربردی ویندوز بسازید. این موضوع زمانی صادق است که برنامه ما از معماری MVC پیروی نکند، اما زمانی که از آن پیروی کند، حتی بیشتر هم صادق است. با این حال، به نظر می‌رسد معماری MVC یک مفهوم بنیادی در توسعه وب مدرن است که نادیده گرفتن آن دشوار است.

می‌توان از معماری MVC همراه با فرم‌های مبتنی بر کامپوننت ASP برای برنامه‌هایی با تعداد کمی نمای متفاوت، با استفاده از راه‌حل موقت زیر استفاده کرد:

  • برنامه از یک صفحه واحد تشکیل شده است که به عنوان کنترل‌کننده عمل می‌کند
  • نماها در این صفحه توسط کانتینرهای مختلف، یک کانتینر برای هر نما، نمایش داده می‌شوند. برای نمایش یک نما، کانتینر آن نمایان می‌شود در حالی که سایر کانتینرها پنهان می‌گردند.

این یک راه‌حل شیک است که اکنون آن را در چند مثال پیاده‌سازی خواهیم کرد

7.14. نمونه‌هایی از برنامه‌های MVC با کامپوننت‌های سرور ASP

7.14.1. مثال ۱

در این مثال اول، ما در حال پیاده‌سازی کامپوننت‌های سرور که معرفی کرده‌ایم هستیم. صفحه [form10.aspx] به شکل زیر خواهد بود:

اسکرین‌شات سمت چپ بالا فرم را همان‌طور که برای مشتری نمایش داده می‌شود نشان می‌دهد. مشتری آن را تکمیل کرده و با استفاده از [Envoyer] ارسال می‌کند. سرور صفحه‌ای را بازمی‌گرداند که فهرستی از مقادیر واردشده را به کاربر نشان می‌دهد (تصویر سمت راست). یک پیوند به کاربر امکان بازگشت به فرم را می‌دهد که دقیقاً همان‌طور که ارسال کرده بود نمایش داده می‌شود. کد نمایش برای [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] به شرح زیر است:

نام
نوع
ویژگی‌ها
نقش
panelform
پنل
EnableViewState=true
نما فرم
rdOui
rdNon
RadioButton
EnableViewState=true
نام گروه=rdmarie
دکمه‌های رادیویی
chk1
chk2
chk3
CheckBox
EnableViewState=true
تیک‌باکس‌ها
txtSaisie
TextBox
EnableViewState=true
میدان ورودی
txtMdp
TextBox
EnableViewState=true
میدان ورودی محافظت‌شده
txtArea
TextBox
EnableViewState=true
کادر ورودی چندخطی
cmbValeurs
DropDownList
EnableViewState=true
لیست کشویی
lstSimple
ListBox
EnableViewState=true
حالت انتخاب = تکی
فهرست تک‌انتخابی
btnRazSimple
دکمه
EnableViewState=false
انتخاب همه موارد در lstSimple را لغو می‌کند
lstMultiple
ListBox
EnableViewState=true
حالت انتخاب = چندگانه
فهرست چندگزینی
btnRazMultiple
دکمه
EnableViewState=false
انتخاب همه موارد در lstMultiple را لغو می‌کند
lblSecret
برچسب
EnableViewState=true
Visible=false
میدان مخفی
btnEffacer
استاندارد HTML
 
نمایش هشدار
btnEnvoyer
دکمه
EnableViewState=false
دکمه [submit] در فرم
btnReset
HTML استاندارد
 
دکمه [reset] در فرم

نقش [VIEWSTATE] برای کامپوننت‌ها در اینجا مهم است. همهٔ کامپوننت‌ها به جز دکمه‌ها باید خاصیت [EnableViewState=true] را داشته باشند. برای درک دلیل این موضوع، باید به یاد بیاوریم که برنامه چگونه کار می‌کند. فرض کنیم که فیلد [txtSaisie] دارای خاصیت [EnableViewState=false] است:

  1. مشتری برای اولین بار صفحه [form10.aspx] را درخواست می‌کند. نمای فرم به او نمایش داده می‌شود
  2. آن را پر کرده و با استفاده از دکمه [Envoyer] ارسال می‌کند. سپس فیلدهای ورودی ارسال می‌شوند و سرور مقدار ارسال‌شده را به کامپوننت‌های سرور یا وضعیت آن‌ها ([VIEWSTATE]) در صورت وجود، اختصاص می‌دهد. بدین ترتیب، مقدار واردشده توسط کاربر به فیلد [txtSaisie] اختصاص می‌یابد. بنابراین، برای این مرحله، وضعیت آن [VIEWSTATE] هیچ کاربردی ندارد. در نتیجه این عملیات، نما [informations] ارسال می‌شود – در واقع، این هنوز همان صفحه [form10.aspx] است اما با یک کانتینر متفاوت نمایش داده می‌شود.
  3. کاربر این نمای جدید را مشاهده می‌کند و از لینک [Retour au formulaire] برای بازگشت به آن استفاده می‌کند. سپس یک عملیات POST به [form10.aspx] انجام می‌شود. در این نقطه، حداکثر یک مقدار ارسال‌شده وجود دارد: مقداری که کاربر از لیست اطلاعات انتخاب کرده است و بعداً استفاده نمی‌شود. در هر صورت، هیچ فیلد [txtSaisie] ارسال نمی‌شود.
  4. سرور مقدار POST را دریافت می‌کند و مقدار ارسال‌شده را به اجزای سرور یا وضعیت [VIEWSTATE] آن‌ها در صورت وجود، اختصاص می‌دهد. در اینجا، [txtNom] هیچ مقدار ارسال‌شده‌ای ندارد. اگر ویژگی [EnableViewState] آن روی [false] تنظیم شود، رشته خالی به آن اختصاص داده می‌شود. از آنجایی که می‌خواهیم مقدار وارد شده توسط کاربر را داشته باشد، باید خاصیت [EnableViewState=true] را داشته باشد.

کنترل‌های کانتینر [panelinfo] عبارتند از:

name
نوع
ویژگی‌ها
نقش
panelinfo
پنل
EnableViewState=false
نما اطلاعات
lstInfos
ListBox
EnableViewState=false
فهرستی که مقادیر وارد شده توسط کاربر را خلاصه می‌کند
LinkButton1
LinkButton
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)
         ' در اولین بار، عناصر مقداردهی اولیه می‌شوند
          'در دفعات بعدی، این موارد از طریق VIEWSTATE به مقادیر خود بازگردانده می‌شوند
         if IsPostBack then return
         ' راه‌اندازی فرم
          ' اطلاعات پنل نمایش داده نمی‌شود
         panelinfo.visible=false
         ' پنل فرم نمایش داده شد
         panelform.visible=true
         ' دکمه‌های رادیویی
         rdNon.Checked=true
         ' تیک‌باکس‌ها
         chk2.Checked=true
          ' میدان ورودی
         txtSaisie.Text="qqs mots"
          ' میدان رمز عبور
         txtMdp.Text="ceciestsecret"
         ' جعبه‌ی متنی
         txtArea.Text="ligne"+ControlChars.CrLf+"ligne2"+ControlChars.CrLf
         ' لیست کشویی
         dim i as integer
         for i=1 to 4
             cmbValeurs.Items.Add(new ListItem("choix"+i.ToString,i.ToString))
         next
         cmbValeurs.SelectedIndex=1
         ' فهرست انتخاب تک‌گانه
         for i=1 to 7
             lstSimple.Items.Add(new ListItem("simple"+i.ToString,i.ToString))
         next
         lstSimple.SelectedIndex=0
         ' فهرست چندگانه
         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
         ' میدان مخفی
         lblSecret.Text="secret"
End Sub

کلیک کردن روی دکمه‌های [lstRazSimple] و [lstMultiple]:

Sub btnRazSimple_Click(sender As Object, e As EventArgs)
    ' پاک کردن لیست انتخاب تک‌گانه
    lstSimple.SelectedIndex=-1
End Sub

Sub razMultiple_Click(sender As Object, e As EventArgs)
    ' پاک کردن لیست چندگزینه‌ای
    lstMultiple.SelectedIndex=-1
End Sub

کلیک کردن دکمه [Envoyer]:

Sub btnEnvoyer_Click(sender As Object, e As EventArgs)
    ' پنل اطلاعات نمایش داده می‌شود و پنل فرم پنهان می‌شود
  panelinfo.Visible=true
  panelform.visible=false
  'مقادیر ارسال‌شده بازیابی شده و در lstInfos ذخیره می‌شوند
   ' دکمه‌های رادیویی
  dim info as string="état marital : "+iif(rdoui.checked,"marié"," non marié")
  affiche(info)
   ' تیک‌باکس‌ها
  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)
   ' میدان ورودی متنی
  affiche("champ de saisie : " + txtSaisie.Text.Trim)
   ' رمز عبور
  affiche("mot de passe : " + txtMdp.Text.Trim)
   ' کادر متن
  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))
   ' لیست کشویی
  affiche("éléments sélectionnés dans combo : "+selection(cmbValeurs))
   ' فهرست تکی
  affiche("éléments sélectionnés dans liste simple : "+selection(lstSimple))
   ' لیست چندگانه
  affiche("éléments sélectionnés dans liste multiple : "+selection(lstMultiple))
   ' میدان مخفی
  affiche ("Champ caché : " + lblSecret.Text)
End Sub

sub affiche(msg as String)
    ' یک پیام را در lstInfos نمایش می‌دهد
    lstInfos.Items.Add(msg)
end sub

       function selection(liste as ListControl) as string
           ' در میان آیتم‌های لیست تکرار می‌کند
            ' برای یافتن موارد انتخاب‌شده
           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

در نهایت، با کلیک بر روی لینک [Retour vers le formulaire ]:

Sub LinkButton1_Click(sender As Object, e As EventArgs)
    ' فرم را نمایش می‌دهد و پنل اطلاعات را مخفی می‌کند
    panelform.visible=true
    panelinfo.visible=false
End Sub

7.14.2. مثال ۲

در اینجا به سراغ برنامه‌ای می‌رویم که پیش‌تر بررسی شده است و از فرم‌های استاندارد HTML استفاده می‌کند. این برنامه به کاربران امکان شبیه‌سازی محاسبات مالیاتی را می‌دهد. این برنامه بر پایه کلاسی به نام [impot] ساخته شده است که در اینجا به آن نمی‌پردازیم. این کلاس به داده‌هایی نیاز دارد که آن‌ها را از یک منبع داده OLEDB بازیابی می‌کند. برای اهداف این مثال، ما از یک منبع داده ACCESS استفاده خواهیم کرد.

7.14.2.1. ساختار MVC برنامه به شرح زیر است:

ساختار MVC برنامه به شرح زیر است:

Image

سه نما در قالب کانتینرها در کد ارائه کنترل‌کننده [main.aspx] گنجانده خواهند شد. در نتیجه، این برنامه دارای یک صفحه واحد به نام [main.aspx] است.

7.14.2.2. نماهای برنامه وب

ویوی [formulaire] فرم ورود اطلاعاتی است که برای محاسبه مالیات کاربر استفاده می‌شود:

Image

کاربر فرم را تکمیل می‌کند:

Image

آنها با استفاده از دکمه [Envoyer] درخواست محاسبه مالیات می‌کنند. سپس نمای زیر، [simulations]، به آنها نمایش داده می‌شود:

Image

آنها از طریق لینک بالا به فرم بازمی‌گردند. آنها آن را در وضعیتی می‌یابند که در آن وارد شده بود. ممکن است در وارد کردن اطلاعات دچار خطا شوند:

Image

این موارد در نمای [erreurs] برجسته شده‌اند:

Image

آنها از طریق لینک بالا به فرم بازمی‌گردند. آنها آن را در همان وضعیتی می‌یابند که واردش کرده بودند. آنها می‌توانند شبیه‌سازی‌های بیشتری انجام دهند:

Image

سپس نمای [simulations] به آنها نمایش داده می‌شود که یک شبیه‌سازی اضافی را نشان می‌دهد:

Image

در نهایت، اگر منبع داده در دسترس نباشد، کاربر در نمای [erreurs] از این موضوع مطلع می‌شود:

Image

7.14.2.3. کد نمایش برنامه

به یاد داشته باشید که صفحه [main.aspx] تمام نماها را گرد هم می‌آورد. این یک فرم واحد با سه کانتینر است:

  • [panelform] برای نما [formulaire]
  • [panelerreurs] برای نما [erreurs]
  • [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
type
ویژگی‌ها
نقش
panelform
پنل
EnableViewState=true
نما فرم
rdOui
rdNon
RadioButton
EnableViewState=true
نام گروه=rdmarie
دکمه‌های رادیویی
txtEnfants
TextBox
EnableViewState=true
تعداد زیرمجموعه‌ها
txtSalaire
TextBox
EnableViewState=true
حقوق سالانه
btnCalculer
دکمه
 
دکمه فرم [submit] – محاسبه مالیات را آغاز می‌کند
btnEffacer
دکمه
 
[submit] دکمه روی فرم – فرم را پاک می‌کند

ظرف [panelerreurs]:

نام
نوع
ویژگی‌ها
نقش
panelerreurs
پنل
EnableViewState=true
نما خطا
lnkForm1
LinkButton
EnableViewState=true
پیوند به فرم
erreursHTML
حرف به حرف
 
کد لیست خطا HTML

ظرف [panelsimulations]:

نام
نوع
ویژگی‌ها
نقش
panelsimulations
پنل
EnableViewState=true
نمایه شبیه‌سازی‌ها
lnkForm2
LinkButton
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)
        ' یک شیء واردات ایجاد می‌کند
        Dim objImpot As impot
        Try
            objImpot = New impot(New impotsOLEDB(ConfigurationSettings.AppSettings("chaineConnexion")))
            ' شیء به برنامه اضافه می‌شود
            Application("objImpot") = objImpot
            'خطایی رخ نداده است
            Application("erreur") = False
        Catch ex As Exception
            'خطایی رخ داده است؛ این خطا در برنامه ثبت می‌شود
            Application("erreur") = True
            Application("message") = ex.Message
        End Try
    End Sub

    Sub Session_Start(ByVal sender As Object, ByVal e As EventArgs)
        ' آغاز جلسه – یک لیست خالی از شبیه‌سازی‌ها ایجاد می‌شود
        Session.Item("simulations") = New ArrayList
    End Sub
End Class

هنگامی که برنامه اجرا می‌شود (اولین درخواست به برنامه ارسال می‌شود)، رویه [Application_Start] اجرا می‌گردد. این رویه تلاش می‌کند یک شی از نوع [impot] ایجاد کند و داده‌های آن را از منبعی به نام OLEDB دریافت نماید. به خوانندگان توصیه می‌شود در صورت فراموشی، به فصل ۵ که این کلاس در آن تعریف شده است مراجعه کنند. ایجاد شیء [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>

کلید [chaineConnexion] به رشته اتصال منبع 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

    ' متغیرهای محلی

    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
        ' ابتدا وضعیت برنامه را بررسی می‌کنیم
        If CType(Application("erreur"), Boolean) Then
            ' برنامه نتوانست راه‌اندازی شود
            'نمایش نمای خطاها
            Dim erreurs As New ArrayList
            erreurs.Add("Application momentanément indisponible (" + CType(Application("message"), String) + ")")
            afficheErreurs(erreurs, "")
            Exit Sub
        End If
        ' بدون خطا – در اولین درخواست، فرم را نمایش می‌دهیم
        If Not IsPostBack Then afficheFormulaire()
    End Sub

توجه داشته باشید که وقتی رویه [Page_Load] روی یک کلاینت POST اجرا می‌شود، تمام اجزای فرم یک مقدار دارند: یا مقداری که توسط کلاینت ارسال شده است (در صورت وجود)، یا مقدار قبلی آن جزء، همانطور که توسط [VIEWSTATE] تعیین شده است. در این فرم، تمام مؤلفه‌ها دارای ویژگی [EnableViewState=true] هستند. قبل از شروع پردازش درخواست، اطمینان حاصل می‌کنیم که برنامه به درستی راه‌اندازی شده است. اگر اینطور نباشد، نمای [erreurs] را با استفاده از رویه [afficheErreurs] نمایش می‌دهیم. اگر این اولین درخواست باشد (IsPostBack=false)، نما [formulaire] را با استفاده از [afficheFormulaire] نمایش می‌دهیم.

روند نمایش نما [erreurs] به شرح زیر است:


    Private Sub afficheErreurs(ByRef erreurs As ArrayList, ByRef lien As String)
        'نمایش کانتینر خطا
        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
        ' سایر کانتینرها مخفی هستند
        panelform.Visible = False
        panelsimulations.Visible = False
    End Sub

این رویه دو پارامتر دارد:

  • فهرستی از پیام‌های خطا در [erreurs]
  • یک متن پیوند در [lien]

کد HTML که برای لیست خطاها تولید می‌شود، در متغیر [erreursHTML] ذخیره شده است. متن لینک در خصوصیت [Text] از شیء نما [LinkButton] ذخیره می‌شود.

روند نمایش ویوی [formulaire] به شرح زیر است:


    Private Sub afficheFormulaire()
        'فرم را نمایش می‌دهد
        panelform.Visible = True
        ' بقیهٔ کانتینرها پنهان می‌شوند
        panelerreurs.Visible = False
        panelsimulations.Visible = False
    End Sub

این رویه به سادگی کانتینر [panelform] را قابل مشاهده می‌کند. اجزا با مقدار ارسال‌شده یا قبلی خود (VIEWSTATE) نمایش داده می‌شوند.

وقتی کاربر دکمه [Calculer] را در نمای [formulaire] کلیک می‌کند، یک انتقال از POST به [main.aspx] انجام می‌شود. رویه [Page_Load] اجرا می‌شود و پس از آن رویه [btnCalculer_Click] اجرا می‌گردد:


    Private Sub btnCalculer_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCalculer.Click
        'اعتبار داده‌های وارد شده بررسی می‌شود
        Dim erreurs As ArrayList = checkData()
        ' اگر خطایی وجود دارد، لطفاً آن را گزارش کنید
        If erreurs.Count <> 0 Then
            'صفحهٔ خطا را نمایش دهید
            afficheErreurs(erreurs, "Retour au formulaire")
            Exit Sub
        End If
        ' بدون خطا – مالیات را محاسبه کنید
        Dim impot As Long = CType(Application("objImpot"), impot).calculer( _
        rdOui.Checked, CType(txtEnfants.Text, Integer), CType(txtSalaire.Text, Long))
        'نتیجه را به شبیه‌سازی‌های موجود اضافه می‌کند
        Dim simulation() As String = New String() {CType(IIf(rdOui.Checked, "oui", "non"), String), _
         txtEnfants.Text.Trim, txtSalaire.Text.Trim, impot.ToString}
        'نتیجه را به شبیه‌سازی‌های موجود اضافه می‌کند
        Dim simulations As ArrayList = CType(Session.Item("simulations"), ArrayList)
        simulations.Add(simulation)
        'شبیه‌سازی‌ها در جلسه و زمینه قرار می‌گیرند
        Session.Item("simulations") = simulations
        'صفحه نتایج را نمایش دهید
        afficheSimulations(simulations, "Retour au formulaire")
    End Sub

این رویه با بررسی اعتبار فیلدهای فرم با استفاده از رویه [checkData] آغاز می‌شود، که فهرستی ([ArrayList]) از پیام‌های خطا را برمی‌گرداند. اگر این فهرست خالی نباشد، آنگاه نما [erreurs] نمایش داده می‌شود و رویه خاتمه می‌یابد. اگر داده‌های وارد شده معتبر باشند، مبلغ مالیات با استفاده از شیئی از نوع [impot] که هنگام راه‌اندازی برنامه ذخیره شده بود، محاسبه می‌شود. این شبیه‌سازی جدید به لیست شبیه‌سازی‌های انجام شده و ذخیره شده در جلسه اضافه می‌شود.

تابع [CheckData] اعتبار داده‌ها را بررسی می‌کند. این تابع یک لیست ([ArrayList]) از پیام‌های خطا را برمی‌گرداند که در صورت معتبر بودن داده‌ها خالی است:


    Private Function checkData() As ArrayList
        ' در ابتدا هیچ خطایی وجود ندارد
        Dim erreurs As New ArrayList
        'تعداد فرزندان
        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
        ' حقوق
        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 erreurs
    End Function

در نهایت، نمای [simulations] توسط رویه زیر [afficheSimulations] نمایش داده می‌شود:


    Private Sub afficheSimulations(ByRef simulations As ArrayList, ByRef lien As String)
        'نمای شبیه‌سازی‌ها را نمایش می‌دهد
        panelsimulations.Visible = True
        ' سایر کانتینرها پنهان هستند
        panelerreurs.Visible = False
        panelform.Visible = False
        ' محتویات نمای شبیه‌سازی‌ها
        ' هر شبیه‌سازی آرایه‌ای از ۴ عنصر رشته‌ای است
        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
        'لینک
        lnkForm2.Text = lien
    End Sub

این رویه دو پارامتر دارد:

  • فهرستی از شبیه‌سازی‌ها در [simulations]
  • یک متن لینک در [lien]

کد HTML که باید برای لیست شبیه‌سازی‌ها تولید شود، در متغیر [simulationsHTML] ذخیره شده است. متن لینک در ویژگی [Text] از شیء [LinkButton] در نما ذخیره شده است.

وقتی کاربر روی دکمه [Effacer] در نما [formulaire] کلیک می‌کند، رویه [btnEffacer_click] اجرا می‌شود (همیشه پس از [Page_Load]):


    Private Sub btnEffacer_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnEffacer.Click
        ' فرم خالی را نمایش می‌دهد
        razForm()
        afficheFormulaire()
    End Sub

    Private Sub razForm()
        ' فرم را پاک می‌کند
        rdOui.Checked = False
        rdNon.Checked = True
        txtEnfants.Text = ""
        txtSalaire.Text = ""
    End Sub

کد بالا به اندازه کافی ساده است که نیازی به هیچ توضیحی ندارد. اکنون باید کلیک‌های روی لینک‌ها در ویو [erreurs] و [simulations] را مدیریت کنیم:


    Private Sub lnkForm1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lnkForm1.Click
        ' فرم را نمایش می‌دهد
        afficheFormulaire()
    End Sub

    Private Sub lnkForm2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lnkForm2.Click
        'فرم را نمایش می‌دهد
        afficheFormulaire()
    End Sub

هر دو رویه به سادگی نما [formulaire] را نمایش می‌دهند. می‌دانیم که فیلدهای این نما بر روی یکی از دو مقدار ارسال شده برای آن‌ها یا مقدار قبلی‌شان تنظیم خواهند شد. همانطور که در این مورد، POST سمت کلاینت هیچ مقداری را برای فیلدهای فرم ارسال نمی‌کند، بنابراین این فیلدها به مقادیر قبلی خود بازخواهند گشت. بنابراین فرم با مقادیر واردشده توسط کاربر نمایش داده می‌شود. یادآوری می‌کنیم که در نسخه‌ای که فاقد اجزای سمت سرور بود، این بازیابی را خودمان انجام دادیم.

7.14.2.5. آزمایش

تمام فایل‌های مورد نیاز برای برنامه در پوشه‌ای به نام <مسیر-برنامه> قرار می‌گیرند:
پوشه [bin] حاوی DLL شامل کلاس‌های [impot]، [impotsData] و [impotsOLEDB] مورد نیاز برنامه است:

خوانندگان در صورت تمایل می‌توانند به فصل ۵ مراجعه کنند که نحوه ایجاد فایل [impot.dll] ذکر شده در بالا را توضیح می‌دهد. پس از انجام این کار، سرور Cassini با پارامترهای (<application-path>,/impots5) راه‌اندازی می‌شود. URL [http://impots5/main.aspx] با استفاده از یک مرورگر وب درخواست می‌شود:

Image

اگر نام فایل ACCESS [impots.mdb] را به [impots1.mdb] تغییر دهید، صفحه زیر را مشاهده خواهید کرد:

Image

7.14.3. مثال ۳

این دو مثال نشان دادند که می‌توان با استفاده از کامپوننت‌های سمت سرور، برنامه‌های وبی ساخت که با معماری MVC مطابقت دارند. مثال آخر نشان می‌دهد که راه‌حل استفاده از کامپوننت‌های سمت سرور ساده‌تر از راه‌حل استفاده از تگ‌های استاندارد HTML است. هر دو مثال ما از یک صفحه واحد تشکیل شده بودند که چندین نما (view) را در همان صفحه در بر می‌گرفت. امکان ایجاد یک معماری MVC با چندین فرم سمت سرور ASP وجود دارد، به شرطی که ارسال مقادیر این فرم‌ها توسط خودشان مشکلی ایجاد نکند. این امر اغلب در مورد برنامه‌هایی که منو دارند، صادق است. بیایید مثال زیر را در نظر بگیریم:

Image

ما لینک‌های برنامه‌هایی را که تاکنون نوشته‌ایم، در یک صفحه گردآوری کرده‌ایم. این نوع برنامه برای معماری MVC بسیار مناسب است. تنها تفاوت این است که دیگر فقط یک کنترلر وجود ندارد، بلکه چندین کنترلر وجود دارد.

Image

کنترل‌کننده [main.aspx] به‌عنوان کنترل‌کننده اصلی عمل می‌کند. این کنترل‌کننده است که توسط لینک‌های صفحه اصلی برنامه فراخوانی می‌شود. این کنترلر عملیات مشترک برای تمام اقدامات ممکن را انجام داده و سپس اقدام خاصی را که با لینک مورد استفاده مرتبط است اجرا می‌کند. سپس کار را به یکی از کنترلرهای ثانویه، یعنی همان کنترلری که مسئول اجرای آن اقدام است، واگذار می‌کند. از این نقطه به بعد، ارتباط بین کلاینت و این کنترلر خاص برقرار می‌شود. کنترلر اصلی، [main.aspx]، دیگر درگیر نیست. بنابراین ما دیگر در چارچوب MVC با یک کنترل‌کننده واحد که همه درخواست‌ها را فیلتر می‌کند، نیستیم. هر یک از کنترل‌کننده‌های فوق می‌توانند با استفاده از مکانیزم کانتینر، چندین نما را در یک صفحه واحد ارائه دهند، همانطور که توضیح دادیم.

اینکه دیگر یک کنترل‌کننده واحد برای انتخاب ویوهایی که باید به کلاینت ارسال شوند نداریم، معایبی دارد. برای مثال، مدیریت خطا را در نظر بگیرید. هر یک از اکشن‌های ارائه شده توسط برنامه ممکن است نیاز به نمایش یک ویو خطا داشته باشد. هر کنترلر [applix.aspx] دارای ویو (view) خود [erreurs] خواهد بود، زیرا این ویو صرفاً یک کانتینر (container) خاص در صفحه کنترلر است. هیچ راهی برای داشتن یک ویو واحد [erreurs] وجود ندارد که توسط همه برنامه‌های کاربردی استفاده شود. این به این دلیل است که چنین ویویی معمولاً شامل لینکی برای بازگشت به فرم حاوی خطاها است، و این فرم باید به حالتی که در آن اعتبارسنجی شده بود بازگردانده شود تا کاربر بتواند خطاهای خود را اصلاح کند. این بازگردانی از طریق مکانیزم [VIEWSTATE] انجام می‌شود که در سراسر کنترل‌کننده‌های مختلف کار نمی‌کند. اگر برنامه‌ها توسط افراد مختلف توسعه داده شوند، این خطر وجود دارد که صفحات خطا بسته به عملی که کاربر انتخاب می‌کند، متفاوت به نظر برسند و بدین ترتیب یکپارچگی کل برنامه به خطر بیفتد. کمی بعد خواهیم دید که ASP.NET راه‌حلی برای این مشکل خاصِ نماهای مشترک ارائه می‌دهد. این می‌تواند مبنای یک کامپوننت سرور جدید باشد که خودمان آن را می‌سازیم. صرفاً با استفاده از این کامپوننت در سراسر برنامه‌های مختلف، یکپارچگی کل برنامه تضمین می‌شود. مسئله دشوارتر، مدیریت ترتیب اقدامات است. وقتی همه درخواست‌ها از یک کنترل‌کننده واحد عبور می‌کنند، آن کنترل‌کننده می‌تواند بررسی کند که آیا اقدام درخواستی با اقدام قبلی سازگار است یا خیر. این کد اعتبارسنجی در یک مکان واحد قرار دارد. اما در اینجا، لازم است در میان کنترل‌کننده‌های مختلف توزیع شود، که این امر نگهداری کل برنامه را پیچیده می‌کند.

بیایید به اپلیکیشن بالا بازگردیم. صفحه ورود آن یک صفحه استاندارد HTML است:

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

این صفحهٔ اصلی از سه قاب به نام‌های «بنر»، «محتوا» و «اصلی» تشکیل شده است:

صفحه [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">
        <ul>
            <li>
                <a href="main.aspx?action=label" 
                 target="main">Label</a>
            <li>
                <a href="main.aspx?action=button" 
                 target="main">Button</a>
            <li>
                <a href="main.aspx?action=textbox1" 
                 target="main">TextBox-1</a></li>
            <li>
                <a href="main.aspx?action=textbox2" 
                 target="main">TextBox-2</a></li>
            <li>
                <a href="main.aspx?action=dropdownlist" 
                 target="main">DropDownList</a></li>
            <li>
                <a href="main.aspx?action=listbox" 
                 target="main">ListBox</a></li>
            <li>
                <a href="main.aspx?action=casesacocher" 
                  target="main">CheckBox</a> et<br>
                RadioButton</li>
            <li>
                <a href="main.aspx?action=listecasesacocher" 
                  target="main">CheckBoxList</a> et<br>
                RadioButtonList</a></li>
            <li>
                <a href="main.aspx?action=panel" 
                  target="main">Panel</a></li>
        </ul>
    </body>
</html>

لینک‌های مختلف همگی به کنترل‌کننده اصلی [main.aspx] اشاره می‌کنند، با پارامتر [action] که اقدام مورد نظر را مشخص می‌کند. این لینک‌ها طوری تنظیم شده‌اند که هدف خود را در داخل فریم [main] (target="main") نمایش دهند.

اولین صفحه‌ای که در فریم [main] نمایش داده می‌شود، [main.htm] است:


<html>
    <head>
        <title>main</title>
    </head>
    <body>
        <P>Choisissez une option pour tester découvrir un type de composant serveur...</P>
    </body>
</html>

کنترل‌کننده اصلی [main.aspx, main.aspx.vb] به شرح زیر است:

[main.aspx]

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

[main.aspx.vb]

کلاس عمومی main

از System.Web.UI.Page ارث می‌برد

Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) مدیریت MyBase.Load

' بازیابی عملی که باید انجام شود

Dim action As String

اگر Request.QueryString("action") هیچ مقداری نداشته باشد،

action = "label"

Else

action = Request.QueryString("action").ToString.ToLower

End If

' اقدام را اجرا کنید

Select Case action

Case "label"

Server.Transfer("form2.aspx")

Case "button"

Server.Transfer("form3.aspx")

Case "textbox1"

Server.Transfer("form4.aspx")

Case "textbox2"

Server.Transfer("form5.aspx")

مورد "dropdownlist"

Server.Transfer("form6.aspx")

میدان "Listbox"

Server.Transfer("form7.aspx")

"checkbox" field

Server.Transfer("form8.aspx")

مورد "لیست چک‌باکس"

Server.Transfer("form8b.aspx")

"پنل"

Server.Transfer("form9.aspx")

در غیر این صورت

Server.Transfer("form2.aspx")

پایان انتخاب

پایان زیرمجموعه

پایان کلاس

کنترل‌کننده ما ساده است. این کنترل‌کننده بسته به مقدار پارامتر [action]، درخواست را به صفحه مناسب هدایت می‌کند. این کنترل‌کننده در مقایسه با صفحه‌ای مانند HTML که دارای لینک‌ها است، هیچ ارزش افزوده‌ای ارائه نمی‌دهد. با این حال، افزودن یک صفحه احراز هویت به سادگی، کاربرد آن را نشان می‌دهد. اگر کاربر برای دسترسی به برنامه‌ها ملزم به احراز هویت (نام کاربری، رمز عبور) باشد، کنترلر [main.aspx] مکان مناسبی برای تأیید وقوع این احراز هویت خواهد بود.