Farsi | English

[خانه] [درباره] [کتابها] [مقالات] [برنامه‌ها] [گالری عکس] [تماس] [ورود]

نگاه دقیق تری به کلکسیون Controls و خصوصیت innerHtml در کنترل های ASP.NET

تاریخ: 1388/03/19
دفعات مشاهده: 2491
بازگشت
ضمائم

مقدمه

مدل کنترل‌های سرویس‌دهنده‌ای که مایکروسافت در ASP.NET ارائه کرده یک مدل شیءگرای کامل است. در این مدل بر اساس پردازش محتوای فایل .aspx ، ساختاری سلسله مراتبی از کنترل‌های صفحه بنا شده و برای استفاده در code-behind مهیا می‌شود. به طوری که فارغ از هرگونه آشنایی با HTML و درگیری با تگ‌ها می‌توانید به زبانی سطح بالا با اجزاء آن کار کنید و از اعضای مختلف کنترل‌های صفحه (متدها، خصوصیت‌ها، ...) استفاده کنید. در این مقاله قصد داریم به حاشیه‌ها و نکاتی که در مورد خصوصیت Control.Controls و HtmlContainerControl.innerHtml وجود دارد بپردازیم.

خصوصیت Controls

این خصوصیت در کلاس پایه‌ای Control تعریف شده و مجموعه‌ی تمام کنترل‌های داخل یک کنترل را در خود نگهداری می‌کند. نوع این خصوصیت از جنس ControlCollection است و واسط ICollection و IEnumerable را پیاده‌سازی کرده است، به همین دلیل یک کلکسیون محسوب می‌شود و می‌توانید آن را توسط ساختاری نظیر foreach پردازش کنید.
بعد از پردازش صفحه‌ی .aspx ، ASP.NET تمام کنترل‌های درون یک کنترل را به کلکسیون Controls آن اضافه می‌کند. توسط این کلکسیون و از طریق کُد می‌توانید کنترل جدیدی به محتویات یک کنترل دربرگیرنده اضافه کنید، کنترلی را از آن حذف کنید و یا کنترل‌های داخل آن را دستکاری کنید. اما قبل از آن باید بدانید ASP.NET چطور تگ‌های صفحه‌ی .aspx را پردازش کرده و کلکسیون Controls را پر می‌کند.
به عنوان مثال برای تگ‌های زیر:

<asp:Panel id=”panel1” runat=”server”>
<asp:Label id="Label1" runat="server" text="This is a label."/>
</div>

خصوصیت Controls در شیء Panel حاوی یک کنترل برچسب خواهد بود.
دقت کنید کلکسیون Controls کنترل‌ها را به صورت اشیاء Control بر می‌گرداند. لذا برای دسترسی به اعضای ویژه‌ی هر یک از کنترل‌های داخل آن، باید شیء به دست آمده را به طور صریح به کلاس واقعی آن کنترل تبدیل کنید. به عنوان مثال برای تنظیم خصوصیت Text برچسب موجود در پنل مثال بالا از طریق panel1.Controls باید چنین کُدی بنویسید:
Label lbl = (Label)panel1.Controls[0];
lbl.Text = "Label changed.";

نحوه‌ی پردازش تگ‌ها در ASP.NET

یکی از نکات مهمی که در مورد خصوصیت Controls باید بدانید نحوه‌ی پردازش تگ‌های کنترل‌های در برگیرنده توسط ASP.NET است. بر اساس این آگاهی است که می‌توانید دریابید خصوصیت Controls و کنترل‌های درون آن را چطور باید استفاده کنید. آنچه که در تشکیل کلکسیون Controls به طور مستقیم تاثیر دارد نوع تگ‌هایی است که داخل کنترل مورد نظر قرار می‌گیرد.
به عنوان مثال برای المان زیر:

<div id=”div1” runat=”server”>
<b>This is a text</b>
<table border=”1”>
<tr><td>This is a table cell</td></tr>
</table>
</div>

با وجودی که داخل کنترل div1 تگ‌های متعددی وجود دارد، اما به دلیل این که تمام آنها المان‌های استاتیک HTML هستند (هیچ کدام کنترل سرویس‌دهنده نیستند)، ASP.NET تمام آنها را به یک کنترل LiteralControl تبدیل می‌کند و داخل کنترل div1 قرار می‌دهد. لذا کلکسیون div1.Controls فقط حاوی یک کنترل LiteralControl خواهد بود. اما اگر بین همین تگ‌ها به شکل زیر یک کنترل سرویس‌دهنده قرار بدهید:

<div id=”div1” runat=”server”>
<b>This is a text</b><asp:Label runat="server" id="Lable1" text="I'm a server-side label" />
<table border=”1”>
<tr><td>This is a table cell</td></tr>
</table>
</div>

ASP.NET محتوای داخل div1 را به سه جزء تقسیم می‌کند:

  • قسمت قبل از کنترل سرویس‌دهنده ( <b>This is a text</b> )
  • خود کنترل سرویس‌دهنده (Label1 )
  • قسمت بعد از کنترل سرویس‌دهنده ( <table border="1"><tr><td>This is a table cell</td></tr></table> )

لذا کلکسیون div1.Controls حاوی سه کنترل خواهد بود:

  • LiteralControl (قسمت قبل از کنترل Label1 )
  • Label (خود کنترل Label1 )
  • LiteralControl (قسمت بعد از کنترل Label1 )

به طور مشابه هر تعداد کنترل سرویس‌دهنده‌ی دیگری داخل محتوای یک کنترل ظرف قرار داشته باشد این ماجرا تکرار می‌شود.

نحوه‌ی پردازش کاراکترهای فضای سفید

كاراكترهاي فضاي خالي مانند Space ، Tab يا كاراكتر سر سطر نیز در ASP.NET به نحو متفاوتي نسبت به HTML تفسير مي‌شوند. يعني اگرچه در HTML از اين كاراكترها صرفنظر مي‌شود اما در ASP.NET هميشه چنين چيزي برقرار نیست. به مثال زير توجه كنيد:

<div id=”div1” runat=”server”>
<b>This is a text</b>
<table border=”1”>
<tr><td>This is a table cell</td></tr>
</table>
<asp:Label runat="server" id="Lable1" text="I'm a server-side label" />
</div>

در اينجا كنترل Label1 مثال قبل را به قسمت بعد از جدول منتقل کرده‌ایم. اما وقتي كلكسيون div1.Controls را بررسي مي‌كنيد، بر خلاف تصور شما به جای دو كنترل (يك LiteralControl مربوط به محتوای قبل از برچسب و يك Label مربوط به خود برچسب)، كلكسيون div1.Controls حاوي سه كنترل خواهد بود. علتش اين است كه ASP.NET كاراكتر سر سطري را كه پس از تگ كنترل Label1 قرار گرفته به عنوان يك LiteralControl تعبير مي‌كند. تنها به شرطی کلکسیون div1.Controls حاوی دو کنترل خواهد بود که پس از کنترل برچسب، هیچ چیز دیگری (حتی فضای سفید و حتی یک کاراکتر Space یا سر سطر) قرار نداشته باشد. یعنی چنین چیزی:

<div id=”div1” runat=”server”>
<b>This is a text</b>
<table border=”1”>
<tr><td>This is a table cell</td></tr>
</table>
<asp:Label runat="server" id="Lable1" text="I'm a server-side label" /></div>

کامنت‌های HTLML

به طور مشابه نحوه‌ي تفسير توضيحات HTML در ASP.NET نيز فرق دارد. به عنوان مثال اگر در مثال قبل، تگ كنترل Label1 را به يك توضيح HTML تبديل كنيد، يعني آن را به صورت زير بين <!-- … --> قرار بدهيد:

<div id=”div1” runat=”server”>
<b>This is a text</b>
<table border=”1”>
<tr><td>This is a table cell</td></tr>
</table>
<!--<asp:Label runat="server" id="Lable1" text="I'm a server-side label" />-->
</div>

اگرچه ممكن است تصور كنيد از شر كنترل Label1 خلاص شده‌ايد، اما وقتي به كلكسيون div1.Controls نگاه مي‌كنيد خواهيد ديد كنترل Label1 هنوز هم در آن حضور دارد! زيرا ASP.NET محتویات داخل توضيحات را نيز پردازش مي‌كند. به همین دلیل اگر به خروجی صفحه نگاه کنید خواهید دید ASP.NET واقعاً این کنترل را پردازش کرده و خروجی آن را هم تولید کرده است. خروجی مثال بالا بدین صورت است:
<div id=”div1” runat=”server”>
<b>This is a text</b>
<table border=”1”>
<tr><td>This is a table cell</td></tr>
</table>
<!--<span id="Lable1">I'm a server-side label </span>-->
</div>

در واقع ASP.NET در پردازش صفحه وقت خود را صرف چیزی می‌کند که مرورگر نهایتاً به طور کامل از آن صرفنظر می‌کند. لذا برای عدم نمایش کنترل‌های سمت سرور صفحات .aspx خود هرگز از کامنت HTML استفاده نکنید. به جای آن خصوصیت Visible آنها را false کنید.

خصوصیت innerHtml

این خصوصیت یک خصوصیت خواندنی/نوشتنی است و فقط به کنترل‌های HTML سرویس‌دهنده که از نوع ظرف هستند تعلق دارد. توسط این خصوصیت می‌توانید محتویات یک کنترل HTML را به صورت رشته به دست بیاورید یا محتویات آن را تغییر بدهید. به عنوان مثال برای المان زیر:

<div id=”div1” runat=”server”>
<b>This is a text</b>
<table border=”1”>
<tr><td>This is a table cell</td></tr>
</table>
</div>

خصوصیت div1.innerHtml رشته‌ای به صورت زیر بر می‌گرداند:
<b>This is a text</b> <table border=”1”><tr><td>This is a table cell</td></tr></table>

نكته‌اي كه وجود دارد اين است كه خصوصيت innerHtml را زماني مي‌توانيد بخوانید كه محتواي كنترل مورد نظر به طور کامل از تگ‌های استاتیک HTML تشكيل شده باشد. اگر بين محتواي آن يك كنترل غير Literal (یعنی یک کنترل سرویس‌دهنده) وجود داشته باشد ديگر نمي‌توانيد خصوصيت innerHtml را بخوانید و اين كار به تولید استثناء منجر خواهد شد.
با این حال اگرچه خواندن خصوصیت innerHtml همیشه میسر نیست، اما نوشتن در آن همیشه میسر است. به عنوان مثال با مقداردهی خصوصیت innerHtml در یک کنترل ظرف می‌توانید محتوای آن را از طریق کُد تغییر بدهید:

div1.innerHtml = "<b>Hello World</b>";

اما در این حالت (صرفنظر از این که چه چیزی داخل آن بوده) تمام محتواي قبلي کنترل مزبور پاك شده و کل رشته‌ای که به خصوصیت innerHtml نسبت داده‌اید در قالب یک LiteralControl به کلکسیون Controls اضافه می‌شود.
ممكن است بپرسيد آيا مي‌توانيم يك كنترل سرويس‌دهنده را به خصوصيت innerHtml قالب كنيم، مثلاً كُدي به صورت زير بنويسيم:

div1.innerHtml = “Hello <asp:Label runat="server" id="Lable1" text="To All The" /> World”;

اما پاسخ این سوال منفي است. زيرا خصوصیت innerHtml اساساً توانایی پردازش محتوای سمت سرور را ندارد و فقط با محتوای استاتیک HTML کار می‌کند. لذا چیزی رکه رخ می‌دهد این است که تمام رشته‌ي مزبور به همان صورت (و بدون انکُد شدن) به طور مستقیم در خروجي صفحه قرار داده مي‌شود که البته مرورگر نیز از تگ <asp:Label> آن صرفنظر می‌کند، زیرا در زبان HTML چنین تگی وجود ندارد. خروجی دستور بالا بدین صورت است:
<div id="div1">Hello <asp:Label runat="server" id="Lable1" text="To All The" /> World</div>

در صورتی که قصد دارید رشته‌ای حاوی یک کنترل سرویس‌دهنده را به شکل یک کنترل به محتوای یک کنترل ظرف اضافه کنید باید از متدی به اسم ParseControl() که در کلاس Page تعریف شده استفاده کنید. این متد در .NET 3.5 معرفی شد و رشته‌ی دریافت شده را پردازش کرده و یک شیء از جنس Control بر می‌گرداند که با اضافه کردن آن به کلکسیون Controls در یک کنترل ظرف، می‌توانید آن را به کنترل مورد نظر اضافه کنید. مثال:

Control c = ParseControl("<asp:Label runat="server" id="Lable1" text="I'm a Parsed Control" />");
somePlaceHolder.Controls.Add(c);

البته دقت کنید در این حالت تگ کنترل سرویس‌دهنده نمی‌تواند حاوی اداره‌گر رویداد باشد. اداره‌گر رویداد را فقط از طریق کُد می‌توانید به یک شیء کنترل وصل کنید. به مثال زیر توجه کنید:

Control parsedControl = ParseControl("<asp:Button runat="server" id="Button1" text="Click here" />");
Control buttonControl = parsedControl.Controls[0];
Button button = (Button)(buttonControl;
button.Click += (sender, args) => {  Label1.Text = "You clicked me!";  };
somePlaceHolder.Controls.Add(parsedControl);

در اینجا برای اداره‌گر رویداد از یک عبارت لامبدا استفاده کردیم، اما می‌توانید از نماینده‌های ناشاس و متدهای صریح نیز استفاده کنید. نکته‌ی بسیار مهمی که باید توجه کنید این است که ParseControl() یک شیء Control بر می‌گرداند که محتویات رشته‌ای که به او داده‌اید پس از پردازش داخل خصوصیت Controls آن قرار می‌گیرد. علت چنین رفتاری این است که رشته‌ای که به ParseControl() می‌دهید ممکن است حاوی چندین کنترل باشد. برای این که ParseControl() بتواند همه‌ی آنها را بسته‌بندی کند و یک شیء منفرد برگرداند، یک شیء Control می‌سازد و کنترل‌های پارس شده را یکی یکی به آن اضافه می‌کند.
به همین دلیل در مثال بالا مستقیماً نمی‌توانیم شیء برگشتی متد ParseControl() را به Button تبدیل کنیم. باید خصوصیت Controls آن را بررسی کرده و اولین آیتم آن را به Button تبدیل کنیم. لذا اداره‌گر رویداد را نیز برای همین شیء باید تعریف کنیم نه برای شیء برگشتی ParseControl() .


پایان

= 5 + 13