Гайдар Магдануров
Знакомство с ASP.NET 2.0
Введение
Каждый раз, разрабатывая проект и создавая
Web-приложение, пользуются различными средствами для создания
архитектуры приложения, редактирования кода и публикации результатов
своего труда на Web-сервере. При этом для конечного пользователя
Web-приложение любой сложности остается набором HTML-страниц, каждая из
которых имеет свой уникальный URL. По большом счету, пользователю нет
дела до того на какой платформе или с использованием какой технологии
создано приложение. У пользователя есть цель, которую он хочет достичь и
универсальное средство, практически любой используемый им Web-браузер. В
этом заключено огромное преимущество приложений для Internet по
сравнению с приложениями для настольных систем – существование
общепринятого «языка общения», HTML кода. Для того, чтобы развернуть
Web-приложение достаточно установить его на сервер, после чего
приложение может быть доступно любому клиенту с установленным
Web-браузером. К сожалению, здесь тоже существует очевидная проблема в
обработке HTML кода браузерами разных производителей.
Задача разработчика заключается в том, чтобы создать приложение,
генерирующее HTML код, оптимальный для большинства браузеров, которыми
пользуется конечный пользователь. Каждая технология, используемая для
создания Web-приложения, позволяет генерировать HTML код, поэтому, в
конечном итоге, выбор технологии, на основе которой будет создано
Web-приложение, зависит лишь от удобства и скорости разработки и
простоты развертывания и поддержки конечного продукта.
Преимущество технологии ASP.NET перед остальными в высокой степени
абстракции, построенной над стандартным HTML кодом: использование
объектно-ориентированной парадигмы, поддержку нескольких языков
программирования, наличие универсального основания, содержащего тысячи
уже готовых для использования в проектах решений – Microsoft .NET
Framework.
При этом для разработки приложения может быть использована любая среда
разработки или даже текстовый редактор, позволяющий создавать,
редактировать и сохранять текстовые файлы. Если же разработчик желает
воспользоваться всей мощью Microsoft .NET Framework и ASP.NET и, при
этом, затратить как можно меньше усилий, то ему нужно воспользоваться
подходящей для его задач средой разработки. Одним из наиболее
эффективных средств создания Web-приложений ASP.NET является Microsoft
Visual Studio. Поддержка ASP.NET 2.0 включена в версию Visual Studio
2005.
В данной статье кратко рассказывается о
большинстве новых возможностей ASP.NET 2.0, новой версии среды ASP.NET.
Далее в тексте считается, что читатель знаком с технологией ASP.NET 1.0
или ASP.NET 1.1. Если рассматриваемая тема выходит за
рамки статьи, то непосредственно в тексте располагается гиперссылка на
статью, содержащую подробную информацию. Также, в конце статьи
приводится список литературы в порядке изложения информации в данной
статье.
Использование нескольких языков программирования
Microsoft .NET Framework и Visual Studio 2005
поддерживает несколько языков программирования, таких как Visual Basic,
Visual C#, Visual C++, Visual J#. Поскольку эти языки используют единую
среду выполнения CLR (Common
Language Runtime) и соответствуют общим стандартам
CLS (Common
Language Specification), то сборка, созданная с применением
одного из .NET языков, может быть использована в проекте,
разрабатываемом на другом языке программирования, также, как если бы эта
сборка и приложение были написаны на одном и том же языке.
С выходом Microsoft .NET Framework 2.0
стало возможно использовать в одном и том же проекте разные языки
программирования. То, что Web-приложение ASP.NET может быть написано на
нескольких языках программирования, очень удобно, если уже есть
проверенные решения на одном из языков, а проект пишется с
использованием другого языка, либо, если в команде имеются разработчики,
использующие различные языки программирования. О том, как использовать
разные языки в одном проекте можно прочитать в
этой статье.
Технология разделения кода
Основа всего в Web-приложении – это страница.
Пользователь, пользуясь браузером, перемещается между страницами,
периодически возвращаясь к уже просмотренным ранее страницам, вводя
какие-то данные в HTML формы и получая некоторый результат. В ASP.NET
страница чаще всего представляет собой Web-форму, содержащую различные
элементы управления, реагирующую на события, создаваемые пользователем.
ASP.NET 1.x позволяет разделять код логики от кода представления, то
есть помещать код программной логики страницы в файл .cs или .vb,
отдельно от кода собственно страницы, размещаемом в .aspx файле. Эта
технология называется
Code-Behind. Таким образом, дизайн страницы может быть изменен не
затрагивая кода страницы, что позволяет разделить ответственность за
внешний вид и работу страницы между дизайнером и программистом. Для
этого в .aspx файле можно задать параметры директивы Page.
<%@ Page Language="c#" Src="User.aspx.cs" %>
Но для поддержки редактирования с помощью Microsoft
Visual Studio .NET в ASP.NET странице необходимо
указать класс, соответствующей данной странице и файл, в котором
находится код этого класса. Для этого директива Page преобразуется с
использованием ключевых слов Codebenind и Inherits.
<%@ Page Language="c#" Codebehind="TheProject.User" Inherits="User.aspx.cs" %>
В ASP.NET 2.0 используется иной механизм разделения
кода. В директиве Page при этом необходимо использовать другие ключевые
слова: CodeFile и Inherits.
<%@ Page Language="c#" CodeFile="TheProject.User" Inherits="User.aspx.cs" %>
В этом случае код класса программной логики страницы будет размещен в
файле указанном в атрибуте CodeFile. Надо отметить, что Visual Studio
2005 использует разделяемые классы (partial
classes).
public partial class Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
}
Поэтому разработчик может поместить код класса в
нескольких файлах, но подобное рассредоточение кода делает приложение
весьма громоздким и трудным в поддержке и разработке.
Используемая в Visual Studio 2003 модель Code-Behind обладает
несколькими весьма существенными недостатками. Прежде всего, используя
Visual Studio разработчику необходимо компилировать проект перед
публикацией, поскольку ASP.NET компилирует страницы, только если указан
атрибут Src, не используемый Visual Studio. При этом, поскольку среда
ASP.NET обнаруживает изменение даты создания сборки, после каждой замены
старой сборки в каталоге bin происходит перезапуск домена приложения,
что выливается во временную «заторможенность» в работе приложения.
Visual Studio 2005 использует новые ключевые слова, поддерживаемые
средой выполнения ASP.NET 2.0, а среда выполнения, в свою очередь,
использует новую технику компиляции страниц. Это позволяет решить
проблему замены сборки на более новую.
Несмотря на это, Visual Studio 2005 по-прежнему позволяет отказаться от
разделения кода и поместить код программной логики в самом файле
страницы, и использовать теги <script runat="server"></script>. Более
того, по умолчанию Visual Studio создает именно страницы без разделения
кода.
Компиляция страниц по требованию
Сравним действия, которые совершает ASP.NET 2.0 и
ASP.NET 1.0, когда пользователь запрашивает файл с расширением .aspx. В
ASP.NET 1.x среда выполнения анализирует директиву Page страницы,
осуществляет поиск соответствующего класса в сборке приложения, затем на
основании кода страницы создается класс, производный от класса
программной логики страницы. В случае если сборка отсутствует, то
осуществляется поиск файла программной логики, указанного в атрибуте Src
директивы Page. Если файл найден, то происходит компиляция сборки, если
нет, то ASP.NET выбрасывает исключение.
В ASP.NET 2.0 среда выполнения также анализирует директивы Page,
осуществляет поиск сборки соответствующей классу логики страницы, после
чего создается класс страницы. В отличие от ASP.NET 1.x, родительским
классом для класса страницы является System.Web.UI.Page, поскольку
создаваемый динамический класс является собственно классом страницы
(используются разделяемые классы для класса страницы и класса
программной логики), а не потомком класса программной логики. Поэтому,
если в ASP.NET 1.x класс Web-формы мог называться также как и сама
Web-форма.
<form id="frmDefault" runat="server"></form>
…
public class frmDefault : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
}
В ASP.NET 2.0 это недопустимо, поскольку элемент
управления System.Web.UI.Form является элементом класса.
Основным преимуществом ASP.NET является то, что в случае отсутствия
сборки, необходимой для выполнения страницы, происходит компиляция
только файла программной логики страницы, без перекомпиляции всей
сборки. Поскольку существует динамическая компиляция, то необходимо
обеспечить возможность создавать код, общий для всех страниц приложения.
Для этой цели в ASP.NET 2.0 существуют специальные директории,
являющиеся дочерними директориями корневой директории приложения, одна
из которых App_Code, служит для хранения файлов, содержащих общий код
для всех страниц. При выполнении динамической компиляции, код из
директории App_Code компилируется и становится доступным для всех
страниц Web-приложения. При этом Visual Studio
2005 поддерживает код, находящийся в директории
App_Code, поэтому работает подсветка синтаксиса и
IntelliSense.
Исследовать генерируемые средой выполнения
ASP.NET 2.0 файлы, и подробно разобраться в процессе компиляции можно
изучив содержимое директории:
%WINDIT%\Microsoft.NET\Framework\версия\Temporary ASP.NET
Files\имя_приложения, куда ASP.NET 2.0
помещает динамически созданные сборки.
Либо, вызвав ошибку на ASP.NET
странице в режиме отладки выбрать ссылку
Show Complete Compilation Source.
Если же необходимо заренее
скомпилировать проект, перед развертыванием на сервере в
ASP.NET 2.0 существует возможность полной или
частичной прекомпиляции Web-приложения. Подробнее
о прекомпиляции можно прочесть в
этой статье.
Изменения в структуре проекта Visual Studio
В версии Visual Studio 2005 больше нет файлов .csproj
или .vbproj, которые раньше создавались для каждого из типов проектов
ASP.NET. Вместо файлов проекта Visual Studio использует структуру
директорий, таким образом, для того чтобы включить в проект существующий
файл достаточно просто скопировать его в директорию проекта.
Следует отметить, что если файл или директория удаляется из дерева
файлов проекта в окне Solution Explorer, то файл
или директория физически удаляются из файловой системы.
Страница ASP.NET 2.0
Директива @Page
Для внесения новых возможностей
в ASP.NET 2.0 было необходимо внести изменения и
дополнения в класс страницы Page. Поскольку для
установке свойств страницы в design-time
используются атрибуты директивы Page, то здесь
будут рассмотрены новые атрибуты, появившиеся для реализации механизмов
персонализации, шаблонов дизайна, оформления и асинхронной работы
станиц. Подробнее о назначении новых атрибутов можно узнать в части
статьи, посвященной новым свойствам и методам класса Page.
• Async
Указывает на то, какой из интерфейсов
IHttpHandler или
IHttpAsyncHandler реализует класс страницы.
После установки этого атрибута в true, генерируемый динамически класс
страницы будет реализовать от IHttpAsyncHandler,
в противном случае класс будет реализовать
IHttpHandler. Если класс страницы реализует
IHttpAsyncHandler, то код страницы может
выполнятся асинхронно до наступления нового
события в жизненном цикле страницы PreRender,
ко времени наступления которого происходит
синхронизация и подготовка HTML-кода для отправки браузеру клиента.
• AsyncTimeOut
Позволяет установить ограничение по
времени, отведенное для выполнения асинхронных операций. По умолчанию
этот параметр равен 45 секундам.
• Culture
Устанавливает набор региональных параметров (Culture),
используемый для страницы.
• EnableTheming
Позволяет включить или выключить
поддержку тем оформления. По умолчанию включено.
• MasterPageFile
Указывает путь к шаблону, который будет использован для создания кода
этой страницы.
• StyleSheetTheme
Позволяет установить идентификатор
темы оформления, которая будет использоваться для изменения
установленной темы оформления (в атрибуте Theme
или в файле web.confg). Таким образом можно
установить общую тему оформления для всего сайта, а с помощью атрибута
StyleSheetTheme вносить некоторые изменения в
общее оформление страницы и/или некоторых
элементов управления, содержащихся на странице.
• Theme
Указывает название темы оформления, которая будет использована для
оформления кода данной страницы.
• UICulture
Устанавливает набор региональных параметров (Culture),
используемый для пользовательского интерфейса страницы.
Жизненный цикл страницы
Жизненный цикл страницы ASP.NET начинается с
получения и обработки Web-сервером IIS запроса к данной странице и
передачи этого запроса среде выполнения ASP.NET. В момент получения
запроса, среда выполнения загружает класс
вызываемой страницы, устанавливает свойства
класса страницы, выстраивает дерево элементов, заполняет свойства
Request и Response и вызывает метод
IHttpHandler.ProcessRequest. После этого среда
выполнения проверяет каким образом была вызвана эта страницы
и если страница вызвана путем передачи данных с
другой страницы, о чем будет рассказано далее, то среда выполнения
устанавливает свойство PreviousPage.
Стоит отметить также, что помимо рассмотренных ниже этапов
выполнения страницы существуют еще и этапы уровня приложения, не
специфичные для страницы. Подробно об этапах выполнения приложения
написано в
этой
статье.
Этап |
Описание |
Запрос станицы |
Запрос страницы осуществляетс ядо начала
жизненноцо цикла страницы. Когда пользователь осуществляет
запрос, среда выполнения ASP.NET
устанавливает, необходимо ли осуществить компиляцию страницы и
начать жизненный цикл, либо можно выдать в качестве ответа
страницу из кеша, таким образом не выполняя страницы. |
Начало жизненного цикла |
На этом этапе происходит установка свойство
Response и Request
и свойства UICulture. Также, на этом
этапе устанавливается, была ли эта страница запрошена в
результате постбэка (отправления данных на сервер) и
соответствующим образом устанавливается свойсто
IsPostBack. |
Инициализация страницы |
Ко времени инициализации страницы все
дочерние пользовательские элементы управления уже созданы и
имеют установленный свойства UniqueID. В
это же время к странице применяются темы оформления. Если
страница вызвна в результате постбэка, то данные, отправленные
на сервер, еще не загружены в свойства элементов управления, на
этом этапе. |
Загрузка |
Если страница вызвана в результате постбэка,
то на этом этапе устанавливаются свойства элементов управления,
на основании информации о состоянии (ViewState
и ControlState). |
Валидация |
Вызывается метод Validate()
для всех, находящихся на странице валидаторов. |
Обработка постбэка |
Вызываются обработчики событий (при условии,
что постбэк произошел). |
Рендеринг |
Сохраняется информация о состоянии, затем
класс страницы вызывает соответствующие методы дочерних
элементов управления для генерации HTML
представления и передачи его в Response.OutputStream. |
Выгрузка |
Выгрузка происходит после того, как
создано HTML
представление для всей страницы. |
Во время прохождения этапов
жизненного цикла возникают события, подписавшись на которые, разработчик
может выполнять свой собственный код. Стоит упомянуть атрибут
AutoEventWireup, директивы @Page:
если этот атрибут установлен в true (значение по
умолчанию), то методы класса страницы, названные Page_НазваниеСобытия,
автоматически становятся обработчиками соответствующих событий
жизненного цикла станицы.
Для того, чтобы проследить жизненный цикл страницы и
последовательность возникновения событий, можно установить атрибут
Trace директивы @Page в
true, а атрибут TraceMode
в "SortByTime". Тогда в разделе Trace
Information можно найти список произошедших событий
(колонка Message). Например:
Trace Information
|
Category |
Message |
From First(s) |
From Last(s) |
aspx.page |
Begin PreInit |
|
|
aspx.page |
End PreInit |
0.0364973314167865 |
0.036497 |
aspx.page |
Begin Init |
0.0379050459346291 |
0.001408 |
aspx.page |
End Init |
0.047693704143491 |
0.009789 |
aspx.page |
Begin InitComplete |
0.0477864508468221 |
0.000093 |
aspx.page |
End InitComplete |
0.0481875670270608 |
0.000401 |
aspx.page |
Begin PreLoad |
0.0489879732516718 |
0.000800 |
aspx.page |
End PreLoad |
0.0494462283607275 |
0.000458 |
aspx.page |
Begin Load |
0.0494924892194238 |
0.000046 |
aspx.page |
End Load |
0.0553441897381414 |
0.005852 |
aspx.page |
Begin LoadComplete |
0.0554711043059809 |
0.000127 |
aspx.page |
End LoadComplete |
0.055942153615399 |
0.000471 |
aspx.page |
Begin PreRender |
0.0561455634022874 |
0.000203 |
aspx.page |
End PreRender |
0.0618604874695332 |
0.005715 |
aspx.page |
Begin PreRenderComplete |
0.06269871008062 |
0.000838 |
aspx.page |
End PreRenderComplete |
0.0633259746265858 |
0.000627 |
aspx.page |
Begin SaveState |
0.080360541216174 |
0.017035 |
aspx.page |
End SaveState |
0.213795377788888 |
0.133435 |
aspx.page |
Begin SaveStateComplete |
0.213911298043872 |
0.000116 |
aspx.page |
End SaveStateComplete |
0.214385763389788 |
0.000474 |
aspx.page |
Begin Render |
0.214440078745078 |
0.000054 |
aspx.page |
End Render |
0.315044337228923 |
0.100604 |
Из всех событий жизненного цикла страницы, разработчик может
подписаться только на пять, помимо событий дочерних элементов
управления. Эти события: PreInit, Init, Load, PreRender, Unload.
Рассмотрим варианты использования этих событий.
Событие |
Использование |
PreInit |
Во время этого события можно
использовать свойство IsPostBack, для
того, чтобы определить вызвана ли эта страница в первый раз или
в результате постбэка. В плане управления страницей разработчик
может: создавать динамически элементы управления, динамически
устанавливать шаблон дизайна или тему оформления, считывать или
устанавливать свойства объекта Profile.
Стоит особо отметить, что на
данном этапе, если страница была вызвана в результате постбэка,
свойства элементов управления еще не установлены. В случае, если
разработчик самостоятельно установит свойства на этом этапе, на
следующем установленный значения могут быть изменены. |
Init |
На этом этапе разработчик может считывать
или инициализировать свойства элементов управления. |
Load |
На этом этапе разработчик может считывать
или изменять свойства элементов управления. |
PreRender |
Последняя возможность внести изменения во
внешний вид страницы. |
Unload |
Освобождение занятых ресурсов (закрытие
открытых соединений с базой данных, завершение работы с файлами
и т.п.)
Важно, что на этом этапе уже создано
HTML представление страницы и попытка
внести какие-либо изменения (например, вызвав
метод Response.Write()), приведет к
исключению. |
Новые свойства и методы объекта Page
Для управления приведенными в предыдущем разделе
событиями и получении информации о результатах их выполнения, у объекта
Page в ASP.NET 2.0 появились следующие
новые методы и свойства. Подробная информация по
перечисленным ниже объектам и свойствам, а также их
использовании последует далее в следующем
разделе этой статьи.
• ClientScript
Содержит ссылку на экземпляр объекта ClientScriptManager, который
позволяет работать с клиентскими скриптами (регистрировать блоки
клиентского кода, создавать скрытые поля и т.п.)
• EnableTheming
Свойство, позволяющее отключить поддержку тем оформления на странице,
если применение темы нежелательно.
• GetValidators
Метод, возвращающий коллекцию валидаторов данной страницы.
• Header
Ссылка на объект HtmlHead, позволяющий контролировать содержимое раздела
<head> HTML страницы, при
условии, что для элемента head установлен атрибут
runat="server".
• IsAsync
Свойство, указывающее на способ обработки страницы – синхронный или
асинхронный.
• IsCrossPagePostBack
Свойство, позволяющее определить была ли данная страница запрошена в
ответ на отправку данных с другой страницы.
• Master
Ссылка на экземпляр объекта шаблона страницы.
• MasterPageFile
Свойство, содержащее имя файла шаблона страницы.
• MaxPageStateFieldLength
Устанавливает максимальный размер одного поля, хранящего
состояние, в байтах. Если этому свойству присвоено число, меньшее, чем
размер информации о состоянии, которую необходимо сохранить, то будет
создано несколько скрытых полей, объем данных в каждом не будет
превышать значения MaxPageStateFieldLength.
• PageAdapter
Свойство возвращает объект
PageAdapter, управляющий генерацией HTML
представления страницы. Конкретный PageAdapter
устанавливается средой выполнения в зависимости от
параметров Requets. Если выбран
PageAdapter, то события жизненного цикла страницы
замещаются событиями PaeAdapter.
• PreviousPage
Ссылка на экземпляр объекта страницы, с которой было осуществлено
отправление формы.
• SetFocus
Метод, позволяющий установить выделение на какой-либо из видимых на
странице элементов управления.
• StyleSheetTheme, Theme
Смотрите выше описание новых
атрибутов директивы @Page.
• TestDeviceFilter
Метод, проверяющий является ли текущий браузер браузером указанного
в качестве аргумента типа.
• Title
Свойство, позволяющее получить и изменить заголовок страницы.
Использование новых объектов
страницы ASP.NET 2.0
Объект ClientScriptManager
ClientScriptManager предоставляющий методы для работы
с кодом сценариев, которые
будут использоваться в клиентской части
Web-приложения. Теперь все методы, которые в ASP.NET 1.x использовались
для регистрации клиентских скриптов или скрытых полей
принадлежат классу ClientScriptManager. Например,
иногда бывает необходимо регистрировать клиентский сценарий динамически,
в зависимости от каких-либо параметров. Например, при перезагрузке
страницы форума нужно указать пользователю, что ему пришло новое личное
сообщение. Это можно сделать как показано в нижеследующем примере.
protected void Page_Load(object sender, EventArgs e)
{
if (CheckMail())
{
string startupScript = "<script>" +
"if(confirm('Вам пришло новое сообщение. Прочесть?'))" +
"window.navigate('/ReadMail.aspx');" +
"</script>";
ClientScript.RegisterStartupScript(this.GetType(), "MailAlertScript", startupScript);
}
}
Объект HtmlHead
Для каждой страницы ASP.NET, в HTML коде которой
присутствует тег <head runat="server"></head> автоматически создается
экземпляр объекта HtmlHead, позволяющий управлять заголовком страницы,
добавлять теги META и ссылки на каскадные таблицы стилей.
Раньше разработчику страницы ASP.NET 1.0 приходилось добавлять
специальное свойство или переменную в класс страницы или придумывать
какой-либо обходной путь для того, чтобы получить возможность управлять
заголовком и метаданными страницы. Например, так как это показано в
следующем примере.
<head>
<title><%=Page.Title%></title>
<%=Page.MetaData%>
<%=Page.Css%>
</head>
С появлением объекта HtmlHead подобные ухищрения
уходят в прошлое.
Page.Header.Title = "HtmlHead Test Page";
Page.Header.Metadata.Add("Author", "Gaidar Magdanurov");
Page.Header.Metadata.Add("E-Mail", "gaidar@vbstreets.ru");
Помимо этого, как и любой элемент, способный
содержать вложенные элементы, HtmlHead позволяет добавлять произвольные
элементы.
((Control)Page.Header).Controls.Add(new LiteralControl
("<meta http-equiv=\"Content-Type\" content=\"text/html;
charset=windows-1251\">"));
Управление страницей
Установка выделения на элемент управления
Иногда бывает необходимо, чтобы после загрузки
страницы выделение было установлено на какой-то определенный элемент
управления (например в поисковой форме логично установить фокус ввода на
основное поле поиска), для этого можно задать значение атрибута
defaultfocus тега form.
<form id="frmMain" runat="server" defaultfocus="txtFirstName">
<asp:TextBox ID="txtFirstName" runat="server"></asp:TextBox>
<asp:TextBox ID="txtSecondName" runat="server"></asp:TextBox>
<asp:TextBox ID="txtLastName" runat="server"></asp:TextBox>
</form>
Выделением можно управлять программно, вызывая метод
SetFocus, в качестве параметра передавая уникальный идентификатор этого
элемента:
protected void Page_Load(object sender, EventArgs e)
{
if(isPostBack)
{
SetFocus("txtLastName");
}
else
{
SetFocus("txtFirstText");
}
}
Обновление данных без перезагрузки страницы
В ASP.NET 2.0 стало возможным обновлять данные на
странице без отправки страницы на сервер и ее полного обновления. Это
стало возможно благодаря появлению клиентских сценариев с обратным
вызовом (callback scripts или клиентские коллбэки). После того, как
некоторое событие вызывает запрос к серверу, обновленные данные
передаются непосредственно в клиентский сценарий в качестве аргументов
функции.
Подобный способ обновления данных на странице удобно и выгодно
применять, когда для обновления всей информации требуется много ресурсов
и достаточно длительное время, при этом эти часть данных обновляется
часто, а остальные данные статичны. Тогда время на обновления данных,
например, одного пользовательского элемента, учитывая время запроса к
серверу, обработки и получения ответа, будет существенно ниже, чем время
обновления всей страницы.
Допустим, существует страница, на которой находится выпадающий
список. Когда пользователь выбирает некоторое значение из списка, в
некоторый элемент управления загружается значение, логически связанное с
выбранным из списка значением. При этом задача такова, что нельзя
хранить все данные на стороне клиента (возможно данных слишком много и
страница будет долго передаваться по сети, либо данные генерируются на
сервере не только в зависимости от выбранного пользователем элемента
выпадающего списка). В ASP.NET 1.x для решения
этой задачи необходимо привязать к событию изменения значения в
выпадающем списке серверный метод. При этом список должен вызывать
отправку страницы на сервер при каждом изменении значения
(AutoPostBack="True").
protected void ddStatic_SelectedIndexChanged(object sender, EventArgs e)
{
// На основании значения ddStatic.Items[ddStatic.SelectedIndex].Value
// метод устанавливает свойства дригих элементов управления
}
В ASP.NET 2.0, как уже было сказано выше, существует
возможность не обновлять всю страницу целиком. В данном случае разумно
обновить только необходимые данные, поскольку обновлять всю страницу
только для того, чтобы установить одно значение слишком расточительно.
Для реализации механизма обновления данных без перезагрузки страницы
необходимо создать клиентскую функцию обратного вызова, принимающую
переданные с сервера параметры, серверную функцию, принимающую параметры
от клиента и возвращающую клиенту значения на основании полученных
параметров и связать эти две функции. Мало того, механизм обратного
вызова в ASP.NET 2.0 позволяет возвращать результат асинхронно. Для
этого в интерфейсе ICallbackEventHandler определены два метода:
RaiseCallbackEvent, для получения параметров на сервере и
GetCallbackResult для возвращения результата клиенту. Для реализации
функциональности предыдущего примера в ASPX файле помещается следующий
код.
<script>
function UpdateText(result, context)
{
dSpan.innerText = result;
}
</script>
<asp:DropDownList ID="ddDynamic" runat="server">
</asp:DropDownList><br />
<span id="dSpan" style="font-weight: bold;"></span>
Класс страницы, использующей функции с обратным
вызовом должен реализовывать интерфейс ICallbackEventHandler.
public partial class ScriptCallback_aspx :
System.Web.UI.Page, System.Web.UI.ICallbackEventHandler { }
Сигнатуры функций, поддерживающие обратный вызов
выглядят следующим образом:
public virtual void PrepareCallbackEvent(string Аргументы)
public virtual string RenderCallbackResult()
private string EventArgument = "";
public void PrepareCallbackEvent(string eventArgument)
{
EventArgument = eventArgument;
}
public string RenderCallbackResult()
{
return EventArgument; // значение типа string
}
Последним шагом к поставленной цели является
связывание серверной и клиентских функций.
protected void Page_Load(object sender, EventArgs e)
{
// создаем ссылку на функцию обратного вызова
string callbackFunction = Page.ClientScript.GetCallbackEventReference
(
this,
"document.all['ddDynamic'].value",
"UpdateText",
"null"
);
// Привязываем сценарий к событию изменения значения выпадающего
// списка
ddDynamic.Attributes["onchange"] = String.Format("javascript:{0}",
callbackFunction);
}
Метод GetCallbackEventReference объекта
ClientScriptManager принимает в качестве параметров ссылку на объект
страницы, строку, указывающую на значение, которое необходимо передавать
на сервер при обратном вызове, имя метода на стороне клиента и
принимающего ответ сервера. Подробное описание можно получить в
документации MSDN или с помощью инструмента Visual Studio - Object
Browser.
Используя эту технологию можно создавать сложные методы обновления
данных страницы и позволяющие получить значительный выигрыш в
производительности, для этого достаточно разобраться в том, какой код
генерирует среда выполнения ASP.NET, реализуя эту функциональность.
// Список, вызывающий перезагрузку страницы
<select name="ddStatic"
onchange="javascript:setTimeout('__doPostBack(\'ddStatic\',\'\')', 0)"
id="ddStatic">
</select>
// Список вызывающий метод с обратным вызовом
<select name="ddDynamic" id="ddDynamic"
onchange="javascript:WebForm_DoCallback
('__Page',document.all['ddDynamic'].value,UpdateText,null,null,false)">
Стандартная функция doPostBack весьма проста и
служит для сохранения данных о событии в скрытые поля формы.
var theForm = document.forms['frmCallBack'];
function __doPostBack(eventTarget, eventArgument) {
if (theForm.onsubmit == null || theForm.onsubmit()) {
theForm.__EVENTTARGET.value = eventTarget;
theForm.__EVENTARGUMENT.value = eventArgument;
theForm.submit();
}
}
При использовании функций с обратным вызовом механизм
значительно сложнее. Код функции WebForm_DoCallback гораздо больше, чем
doPostBack, поскольку эта функция определяет тип объектной модели
браузера, после чего загружает необходимый для передачи данных с модуль.
Например, для браузера поддерживающего DOM это будет Microsoft.XMLHTTP.
Посмотреть код различных клиентских функций, используемых средой
ASP.NET 2.0 можно сохранив страницу, поддерживающую функции с обратным
вызовом на жесткий диск и открыв в текстовом редакторе файл
WebResource.axd, ссылка на который имеется в HTML странице.
Более подробное описание возможностей и
области применения клиентских функций обратного вызова описано в
статье Дино Эспозито.
Отправка данных формы другой странице ASP.NET
Существенное ограничением серверных форм в ASP.NET
1.0 – отсутствие возможности непосредственно передавать данные,
введенные в форме, другой странице. Чтобы отправить значения элементов
форму другой странице необходимо использовать простою HTML форму и в
атрибуте action указать путь к странице, которая должна получить данные.
При этом теряются преимущества использования серверных форм.
В ASP.NET 2.0 тег элемента управления может иметь дополнительный
атрибут PostBackUrl, позволяющий указать какой странице система должна
передать Web-форму, если отправление данных на сервер инициировано этим
элементом управления.
<form id="frmTest" runat="server">
<asp:textbox id="txtFirstName" runat="server" /><br />
<asp:textbox id="txtLastName" runat="server" /><br />
<asp:button id="btnSend" Text="Post Data" PostBackUrl="crosspost.aspx"
runat="server" />
</form>
После щелчка по кнопке, браузер пользователя будет
переадресован на страницу crosspost.aspx, при этом вся информация об
элементах управления формы, с которой произошло отправление данных,
будет также передано.
Чтобы реализовать эту возможность, среда ASP.NET 2.0 осуществляет
проверку ASPX страниц на предмет наличия элементов управления с заданным
атрибутом PostBackUrl и, при наличии таковых, создает на странице
дополнительное скрытое поле __PREVIOUSPAGE, которое и содержит
информацию о состоянии элементов формы. Эта информация доступна
странице-получателю через свойство PreviousPage.
void Page_Load(object sender, EventArgs e)
{
if (PreviousPage != null)
{
// Получаем объекты отправленной формы
TextBox txtFirstName =
(TextBox)PreviousPage.FindControl("txtFirstName");
TextBox txtLastName =
(TextBox)PreviousPage.FindControl("txtLastName");
// Используем данные
txtInfo.Text = "Добрый день, " + txtFirstName.Text + "!";
}
}
На уровне HTML кода, отправка данных другой форме
выглядит следующим образом.
<form method="post" action="GetValues.aspx" id="frmTest">
<input type="submit" name="btnSend" value="Post Data"
onclick="javascript:WebForm_DoPostBackWithOptions(
new WebForm_PostBackOptions("btnSend", "", false,
"", "CrossPage.aspx", false, false))" id="btnSend" />
</form>
Из чего можно заключить, что данные формы не
направляются из браузера непосредственно странице CrossPage.aspx, а
предварительно направляются все той же странице GetValues.aspx, которая
содержит форму frmTest. Это необходимо для поддержки серверной проверки
(validation) введенных данных с использованием элементов управления
RequiredFieldValidation, RangeValidation и других. Ознакомиться с
методом WebForm_DoPostBackWithOptions можно в файле WebResource.axd,
упомянутом в предыдущем параграфе этой статьи.
Шаблоны дизайна страниц
Что, прежде всего, отличает один Web-сайт от другого?
Для простого пользователя сети Интернет основное отличие заключается в
разнообразном дизайне странице. Прежде чем встретится с функциональными
возможностями Web-приложения, посетитель сайта оценивает, нравится ли
ему внешний вид страниц. Поэтому дизайн страниц является едва ли менее
важным, чем общая функциональность.
Задача разработчика писать код, а задача художника-дизайнера
заниматься внешним оформлением и версткой HTML кода страниц. При этом в
большинстве случаев необходимо обеспечить возможность параллельной
работы над кодом приложения и HTML шаблоном. С этим весьма успешно
справлялась технология разделения кода программной логики и HTML кода
страницы Code-Behind. Но, при этом, при работе над каждой страницей,
разработчику приходится, так или иначе, сталкиваться с разметкой
страницы.
Стоит заметить, что большинство современных Web-сайтов имеют сходный
внешний вид всех страниц и каждая страница имеет общие элементы дизайна.
В ASP.NET 1.x общие элементы дизайна страниц
заключали в пользовательские элементы управления и включались в каждую
страницу, либо, наоборот, страницы преобразовывали в элементы
управления, используя одну страницу в качестве основы в которую
загружали элементы управления в зависимости от строки URL.
Подход в ASP.NET 2.0 близок ко второму из названных подходов, но
заключает в себе существенные преимущества. Разработчику больше нет
нужды преобразовывать страницы в пользовательские элементы управления,
что существенно упрощает разработку и отладку. А также существенно
упрощает работу дизайнера, поскольку его задача сводится лишь во
включении нескольких ASP.NET элементов в код страницы-шаблона. Для более
подробного сравнения технологии шаблонов ASP.NET 2.0
с существующими ранее техниками использования шаблонов
рекомендуется обратится к
статье Фрица Оньена.
Создание шаблона дизайна
Шаблон дизайна страниц (Master pages) представляет
собой обычную ASP.NET страницу, обладающую несколькими дополнительными
атрибутами и свойствами и содержащую один или несколько специальных
элементов управления ContentPlaceHolder. Для того чтобы преобразовать
обычную страницу в шаблон, достаточно заменить директиву Page на
директиву Master.
После этого необходимо вставить в разметку страницы серверные элементы
управления ContentPlaceHolder, в те части страницы, где будет
размещаться информация, не относящаяся к общему дизайну.
<%@ Master Language="C#" CodeFile="MainMaster.master.cs"
Inherits="MainMaster_master" %>
<html>
<head runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Моя домашняя страница</title>
</head>
<body>
<table width="100%">
<tr>
<span id="PageTitle" runat="server"></span>
</tr>
<tr>
<table width="100%">
<tr>
<td>
<asp:contentplaceholder id="PageMenu"
runat="server"></asp:contentplaceholder>
</td>
<td>
<form runat="server">
<asp:contentplaceholder id="PageContent"
runat="server"></asp:contentplaceholder>
</form>
</td>
</tr>
</table>
</tr>
<tr>
<p align="right">Время: <span id="PageTime" runat="server"></span></p>
</tr>
</table>
</body>
</html>
Также как и для обычной страницы, в файле программной
логики можно работать с имеющимися элементами управления, создавать и
добавлять новые, основываясь на параметрах запроса изменять страницу и
ее поведение. Таким образом, можно вынести логику создания внешнего
облика Web-приложения практически полностью в шаблон страницы.
Элемент управления ContentPlaceHolder позволяет определить
содержимое, которое будет использоваться в случае, если страница,
использующая шаблон, не переопределит содержимое элемента управления.
<asp:contentplaceholder id="PageMenu" runat="server">
<ul>
<li><a href="Default.aspx">Главная страница</a></li>
<li><a href="Contents.aspx">Оглавление</a></li>
</ul>
</asp:contentplaceholder>
Работа с элементами управления страницы шаблона ничем
не отличается от работы с элементами управления для обычной страницы.
Таким же образом можно определить методы и свойства, которые будут
доступны всем страницам использующим данный шаблон.
protected void Page_Load(object sender, EventArgs e)
{
PageTime.InnerText = DateTime.Now.ToShortDateString();
}
public string Title
{
get
{
return PageTitle.InnerText;
}
set
{
PageTitle.InnerText = value;
}
}
Создание страницы
Использование шаблонов дизайна налагает свои
требования на страницы. Поскольку шаблон содержит элементы управления
ContentPlaceHolder, то страница должна содержать элементы управления
Content, содержащие код разметки и другие элементы управления, которые
будут отображать на результирующей странице. На странице не должно быть
каких-либо серверных элементов управления или кода разметки вне
элементов управления Content.
<%@ Page Language="C#" CodeFile="Default.aspx.cs" Inherits="Default_aspx"
MasterPageFile="MainMaster.master"%>
<asp:content runat="server" id="MyMenu" contentplaceholderID="PageMenu">
<ul>
<li><a href="Page1.aspx">Страница 1</a></li>
<li><a href="Page2.aspx">Страница 2</a></li>
<li><a href="Page3.aspx">Страница 3</a></li>
</ul>
</asp:content>
<asp:content runat="server" ID="MyContent" contentplaceholderID="PageContent">
<asp:TextBox id="txtName" runat="server"></asp:TextBox>
<asp:Button id="btnShow" runat="server" Text="Показать"
OnClick="btnShow_Click" /><br />
<asp:PlaceHolder ID="PlaceHolder" runat="server"></asp:PlaceHolder>
</asp:content>
Также как и в случае с шаблоном, код логики страницы создается обычным
образом. Единственное отличие в том, что страница не имеет собственных
объектов вроде HeadControl, поэтому нужно использовать ссылку на
страницу шаблона через свойство Master.
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack) Master.Page.Header.Title = "Домашняя страница";
}
protected void btnShow_Click(object sender, EventArgs e)
{
PlaceHolder.Controls.Add(
new LiteralControl("<script> alert('Добрый день, "
+ txtName.Text +
"'); </script>"));
Master.Page.Header.Title = "Добрый день, " + txtName.Text;
}
Для того чтобы привязать страницу к шаблону используется атрибут
MasterPageFile директивы Page. Если же необходимо привязать один и тот
же шаблон ко всем страницам в директории, то нет необходимости указывать
атрибут MasterPageFile для каждой страницы, достаточно задать базовый
шаблон в файле web.config.
<?xml version="1.0"?>
<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
<system.web>
<pages master="MainMaster.master" />
</system.web>
</configuration>
Кроме того, ASP.NET позволяет устанавливать тему
оформления программным образом. Как было сказано выше, загрузка и
связывание с шаблоном оформления происходит во время подготовки страницы
к инициализации. Поэтому, если необходимо сменить шаблон оформления
страницы, необходимо делать это в обработчике события PreInit.
protected void Page_PreInit(object sender, EventArgs e)
{
Page.MasterPageFile = "AnotherMaster.master";
}
Обработка шаблонов средой ASP.NET
Во время первого обращения к какой-либо странице,
ASP.NET осуществляет поиск и компиляцию сборок для всех шаблонов в
директории. Эта операция занимает некоторое время, зависящее от
количества страниц шаблонов в директории, но выполняется только один
раз. Поэтому, если в директории присутствуют не использующиеся шаблоны,
это не приводит к потерям производительности в процессе работы
приложения, единственное неудобство – дополнительное время, а компиляцию
ненужных сборок для неиспользуемых страниц.
При обращении к aspx файлу страницы с заданным шаблоном оформления
процесс компиляции почти ничем не отличается от обычного процесса
компиляции страницы, за исключением того, что создается класс шаблона
MasterPage ссылка на который доступна в свойстве Page.Master.
Затем страница проходит все те шаги, которые описаны выше в этой
статье, в результате чего генерируется HTML код, который отправляется
клиенту. В полученном браузером HTML коде уже нельзя определить, какая
часть кода задана в шаблоне оформления, а какая часть кода определена на
самой странице, поскольку элементы управления ContentPlaceHolder и
Content не имеют каких-либо HTML соответствий и не порождают
дополнительных тегов, кроме своего содержимого.
<html>
<head id="ctl00_Head1">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Домашняя страница</title>
</head>
<body>
<table width="100%">
<tr>
<span id="ctl00_PageTitle"></span>
</tr>
<tr>
<table width="100%">
<tr>
<td>
<ul>
<li><a href="Page1.aspx">Страница 1</a></li>
<li><a href="Page2.aspx">Страница 2</a></li>
<li><a href="Page3.aspx">Страница 3</a></li>
</ul>
</td>
<td>
<form method="post" action="default.aspx" id="__aspnetForm">
<div>
<input type="hidden" name="__VIEWSTATE" value="" />
</div>
<input name="ctl00$PageContent$txtName" type="text" id="ctl00_PageContent_txtName" />
<input type="submit" name="ctl00$PageContent$btnShow" value="Показать"
id="ctl00_PageContent_btnShow" /><br />
</form>
</td>
</tr>
</table>
</tr>
<tr>
<p align="right">Время: <span id="ctl00_PageTime">20.03.2005</span></p>
</tr>
</table>
</body>
</html>
Поскольку шаблон является подмножеством страницы, то
допустимо создавать вложенные шаблоны, указывая для шаблона в директиве
Master путь к другому шаблону с помощью атрибута MasterPageFile. Для
этого необходимо в основном шаблоне определить элементы управления
ContentPlaceHolder, а в «дочерних» шаблонах на ряду с ContentPlaceHolder
элементами определить элементы управления Content для замещения
содержимого элементов ContentPlaceHolder базового шаблона.
Использование разных шаблонов для разных браузеров
Любому Web-разработчику хорошо известно, что разные
браузеры (например, Microsoft Internet Explorer, Netscape Navigator,
Mozilla FireFox и др.) по разному обрабатывают HTML код и, что особенно
важно, обладают несколько разными программируемыми моделями, что
усложняет создание клиентских сценариев.
Для разрешения этой проблемы существует два
основных метода, которые используются и в ASP.NET 1.x и в классическом
ASP. Первый заключается в том, что браузеру клиента отправляется
клиентский код, который, основываясь на типе и версии браузера,
выполняет некоторые действия. Второй состоит в перенаправлении браузера
пользователя на те страницы, которые специально оптимизированы под
конкретный браузер. ASP.NET упрощает создание подобных страниц,
поскольку среда выполнения генерирует HTML код для стандартных серверных
элементов управления основываясь на информации о браузере клиента. При
использовании шаблонов оформления, можно создать несколько шаблонов, для
каждого браузера и в директиве Page страницы указать, какой шаблон
использовать.
<%@ Page ie:MasterPageFile="ieMainTemplate.master"
opera:MasterPageFile="operaMainTemplate.master "
mozilla:MasterPageFile="mozillaMainTemplate.master " %>
Список браузеров и используемые средой ASP.NET
свойства браузеров можно найти в директории
%WINDIT%\Microsoft.NET\Framework\версия\CONFIG\Browsers.
Упрощенная локализация Web-приложений
Определение региональных настроек пользователя
Для локализации приложений в
ASP.NET 1.x разработчику часто приходилось писать код для
определения региональных настроек пользователя. Этот код часто помещался
в global.asax и его задачей стояла установка
языка пользователя в качестве языка текущего процесса, путем создания
объекта CultureInfo и присвоения его свойствам
CurrentCulture и
CurrentUICulture объекта CurrentThread:
void Application_BeginRequest (Object sender, EventArgs e)
{
if (Request.UserLanguages.Length > 0)
{
CultureInfo UserCulture = CultureInfo.CreateSpecificCulture(
Request.UserLanguages[0]);
Thread.CurrentThread.CurrentCulture = UserCulture;
Thread.CurrentThread.CurrentUICulture = UserCulture;
}
}
В ASP.NET 2.0 существует
возможность переложить определение региональных настроек пользователя на
среду выполнения. Достаточно установить атрибуты
Culture="auto" и UICulture="auto"
директивы @Page, чтобы для страницы
региональные настройки пользователя определялись автоматически. В
случае. если необходимо достичь этого эффекта на всех страницах сайта,
достаточно в файл web.config добавить
следующую строку:
<globalization culture="auto" uiCulture="auto" />
Выражения для локализации
ASP.NET 2.0 позволяет
генерировать сборки с локальными ресурсами для каждой страницы и
связывать элементы управления с данными, хранимыми в этих ресурсах с
помощью выражений, размещаемых непосредственно в коде представления в
ASPX файле. Для этого используется атрибут
meta:resourcekey, значение которого
анализируется во время выполнения страницы.
<asp:LinkButton id="lnkSelectLanguage" PostBackUrl="SelectLanguage.aspx" runat="server"
meta:resourcekey="SelectLanguageResource">Select Language</asp:LinkButton>
Либо может быть использовано новое выражение <%$
resources: [applicationkey], resourcekey%>:
<asp:ImageButton id="btnSelectLanguage" Runat="server"
ImageUrl="~/Images/SelectLanguage.jpg"
AlternateText='<%$ Resources: SelectLanguage %>' PostBackUrl="SelectLanguage.aspx" />
Для того, чтобы таким же образом можно было
локализовать статический текст, в ASP.NET 2.0
появился новый элемент управления Localize,
позволяющий использовать указанные выше выражения локализации.
<asp:Localize id="localText" runat="server" meta:resourcekey="TheText">Text</asp:Localize>
или
<asp:Localize id="localText" runat="server" text='<%$ resources: TextResources, TheText %>'>
Text</asp:Localize>
Подробнее и выражениях, генерации ресурсов и
определении региональных настроек пользователя можно прочесть в
этой статье.
Заключение
В этой статье приведены лишь некоторые основные
сведения о новшествах в технологии ASP.NET 2.0 по сравнению с ASP.NET 1.x.
Особенности ASP.NET были очень описаны кратко и данная статья может
служить отправной точной для изучению этой передовой технологии. Для
более подробного описания упомянутых в статье технологий и новых
возможностей, рекомендуется обратиться к списку дополнительной
литературы. В статье не был затронут ряд важных
нововведений - асинхронное выполнение кода, новые элементы управления,
шифрование фрагментов файла web.config,
новые механизмы доступа к данным и многое-многое другое.
Все это является материалом, которому можно посвятить не одну книгу. По
возможности автор постарается осветить эти нововведения в будущих
статьях.
Благодарности
Данная статья была написана еще на
Beta 1 для книги GotDotNet Community Book
- проект по написанию книги активными участниками сообщества
GotDotNet. К сожалению проект оказался
нежизнеспособен и книга не была написана.
Разумеется, что информация по Beta 1 была
неактуальна к моменту публикации. Поэтому я хочу поблагодарить Виктора
Шатохина, указавшего на фрагменты статьи, ставшие неактуальными и, таким
образом, заставившего переписать статью. Спасибо!
Дополнительная литература
-
Common Language Runtime Overview
-
Домашняя страница Common Language Runtime
-
What is the Common Language Specification?
-
ASP.NET Code-Behind Model Overview
-
ASP.NET Web Page Code Model
-
ASP.NET Compilation Overview
-
Прекомпиляция сайта в ASP.NET 2.0
-
Partial Types
-
Новинки языка
C#
2.0
-
IHttpAsyncHandler Interface
-
IHttpHandler Interface
-
Директива @ Page
-
ASP.NET Application Life Cycle Overview
-
ASP.NET Page Life Cycle Overview
-
ClientScriptManager Class
-
HtmlHead Class
-
Script Callbacks in ASP.NET
-
Implications of Script Callbacks in ASP.NET
-
Custom Script Callbacks in ASP.NET
-
Master Your Site Design with Visual Inheritance and Page Templates
-
ASP.NET 2.0 Localization Features: A Fresh Approach to Localizing
Web Applications
|