Содержание
Предисловие
Последнее время акценты в разработке приложений все сильнее смещаются
в сторону разработки Web приложений и приложений для мобильных
устройств. Многих специалистов, занятых разработкой Web приложений,
интересует, какие преимущества предоставляет им технология ASP.NET.
Одним из самых больших преимуществ этой технологии является появление
серверных элементов управления. Их использование позволяет намного
сократить время разработки и увеличить процент повторного использования
кода. После длительных поисков литературы на русском языке, в которой
описывались бы все виды серверных элементов управления и область их
применения, нашлось только несколько статей, опысывающих
пользовательские элементы управления и составные элементы управления.
Это натолкнуло на мысль написать обзорную статью о серверных элементах
управления, области их применения без углубления в вопросы реализации.
При подготовке данной статьи использовались материалы, полученные из
открытых источников.
Краткий обзор серверных элементов управления
Серверные элементы управления ASP.NET - это компоненты, которые
выполняются на сервере и инкапсулируют пользовательский интерфейс и
другие связанные функциональные возможности. Серверный элемент
управления - это фундаментальный строительный блок Web приложения
ASP.NET. Хотя для того, чтобы обеспечить классу возможность
взаимодействовать с оболочкой страницы ASP.NET и принимать участие в
сценарии HTTP запрос/ответ, необходима развитая инфраструктура. ASP.NET
поставляет базовый класс (System.Web.UI.Control), реализующий
инфраструктуру и освобождающий от необходимости реализации деталей
нижнего уровня. Чтобы разработать серверный элемент управления,
необходимо определить класс, который прямо или косвенно наследуется от
System.Web.UI.Control. Все элементы управления ASP.NET, поставляемые с
.NET Framework SDK, наследуются от Control или от одного из его дочерних
классов.
Иерархия серверных элементов управления ASP.NET
На Рисунок 1 приведено пространство имен, в которое входят серверные
элементы управления ASP.NET.
Рисунок 1. Иерархия серверных элементов управления.
Серверные элементы управления ASP.NET наследуются прямо или косвенно
от System.Web.UI.Control. Этот базовый класс принадлежит пространству
имен System.Web.UI, которое содержит элементы, общие для всех серверных
элементов управления ASP.NET. Три обычно используемых элемента
управления, принадлежащих пространству имен System.Web.UI, - это Page,
UserControl, и LiteralControl. Класс Page играет важную роль, потому что
каждая страница ASP.NET компилируется оболочкой ASP.NET к Page, однако
разработчики элементов управления обычно не работают с Page или
UserControl. Разработчики элементов управления широко используют
LiteralControl, поскольку с его помощью текст может быть включен, как
элемент управления. Пользовательские элементы управления разрабатываются
с использованием той же модели программирования, что и страницы ASP.NET,
и сохраняются, как текстовые файлы .ascx.
Класс Control обеспечивает основные функциональные возможности,
необходимые для построения страницы. В частности, он предоставляет
возможность помещать серверный элемент управления в дерево элементов
управления, которые отображаются на странице .aspx. Класс Control также
реализует интерфейс System.ComponentModel.IComponent, который делает
компонент конструктивным. Конструктивный компонент может быть добавлен в
инструментальную панель визуального дизайнера, может быть помещен на
разрабатываемую страницу методом drag-and-drop, может отображать свои
свойства в инспекторе свойств и обеспечивать другие виды поддержки
режима разработки.
Серверные элементы управления ASP.NET, которые предоставляют
пользовательский интерфейс, организованы в два пространства имен -
System.Web.UI.HtmlControls и System.Web.UI.WebControls. Серверные
элементы управления HTML непосредственно преобразовываются в элементы
HTML, в то время как серверные элементы управления Web более богаты и
абстрактны.
Иерархия серверных элементов управления Web
На Рисунок 2 представлена иерархия элементов управления в
пространстве имен System.Web.UI.WebControls. Элементы управления в этом
пространстве имен называют серверными элементами управления Web.
Рисунок 2. Иерархия серверных элементов управления Web.
Большинство серверных элементов управления Web прямо или косвенно
наследуются от базового класса System.Web.UI.WebControls.WebControl.
Однако четыре элемента управления в правом верхнем углу (Literal,
PlaceHolder, Repeater, и Xml) наследуются от System.Web.UI.Control.
Элементы управления, расположенные слева, преобразовываются в элементы
HTML. Элементы управления в центре предназначены для проверки
правильности ввода в другие элементы управления, например, TextBox.
Также в центре расположены элементы управления, предоставляющие богатые
функциональные возможности, такие как Calendar и AdRotator. Элементы
управления, обеспечивающие поддержку привязки данных, - справа.
Класс WebControl добавляет функциональные возможности классу Control
по формированию содержания HTML. WebControl обеспечивает поддержку
стилей через такие свойства, как Font, Height, Width, BackColor, и
ForeColor.
Иерархия серверных элементов управления HTML
Иерархия серверных элементов управления ASP.NET в пространстве имен
System.Web.UI.HtmlControls приведена на Рисунок 3. Эти элементы
управления называют серверными элементами управления HTML.
Рисунок 3. Иерархия серверных элементов управления HTML.
Серверные элементы управления HTML прямо или косвенно наследуются от
базового класса System.Web.UI.HtmlControls.HtmlControl и непосредственно
преобразовываются в элементы HTML. Серверные элементы управления HTML
особенно полезны для преобразования приложения ASP в приложения ASP.NET.
В разделе Выбор базового класса для элемента управления
приведены основные принципы выбора базового класса элемента управления,
от которого необходимо наследоваться.
Основы разработки серверного элемента управления ASP.NET
Существует множество различных способов разработки собственного
элемента управления. Приведем основные из них:
- Существует страница ASP.NET, предоставляющая пользовательский
интерфейс, который нужно многократно использовать в других
приложениях. Необходимо создать серверный элемент управления,
который инкапсулирует пользовательский интерфейс (UI), без написания
дополнительного кода. ASP.NET позволяет сохранять ASP.NET страницу,
как пользовательский элемент управления.
- Необходимо разработать откомпилированный элемент управления,
который объединяет функциональные возможности двух или более
существующих элементов управления. Например, нужен элемент
управления, который инкапсулирует кнопку и текстовое поле. Используя
технологию компоновки элементов управления, можно создать составной
элемент управления.
- Существующий серверный элемент управления ASP.NET отвечает
практически всем вашим требованиям, но есть недостаток в некоторых
функциональных возможностях. Существующий элемент управления может
быть модифицирован посредством механизма наследования от него и
переопределения его свойств, методов или событий.
- Ни один из существующих серверных элементов управления ASP.NET
(или их комбинации) не отвечает поставленным требованиям. В этом
случае, можно создать специальный элемент управления, наследуясь от
одного из основных классов элемента управления. Эти классы
предоставляют всю базовую функциональность, необходимую серверным
элементам управления ASP.NET, что позволяет сконцентрироваться на
реализации необходимых свойств.
Разработка многих специальных элементов управления требует сочетания
приведенных выше сценариев, когда существующие серверные элементы
управления ASP.NET комбинируются с созданными вами элементами
управления.
Выбор базового класса для элемента управления
Это руководство поможет определить базовый класс, от которого
необходимо наследовать ваш элемент управления:
- Наследуйтесь от System.Web.UI.Control, если ваш элемент
управления генерирует невизуальные элементы или не формирует HTML.
Тэги <meta> и <xml> являются примерами невизуального формирования
элементов.
- Наследуйтесь от System.Web.UI.WebControls.WebControl, если
хотите формировать HTML, который генерирует визуальный интерфейс
клиента.
- Наследуйтесь от существующего элемента управления - например,
Label, Button, и TextBox - когда хотите расширить или модифицировать
функциональные возможности этого элемента управления. Можно
наследоваться от любого элемента управления из пространства имен
System.Web.UI.WebControls или от специального элемента управления.
Однако не наследуйтесь от элементов управления в пространстве имен
System.Web.UI.HtmlControls. Использование элементов управления,
которые прямо или косвенно наследуются от HtmlControl, не
позволяется в дизайнере Visual Studio .NET, потому что они разрушают
модель элементов управления HTML. Элементы управления HTML должны
быть без префикса тэга в декларативном синтаксисе страницы (<Button
runat="server" />). Однако все специальные элементы управления,
включая те, которые наследуются от HtmlControl или его наследников,
при декларативном использовании на странице требуют префикс тэга.
Выбор модели разработки элемента управления
Наиболее важным фактором при выборе модели разработки элементов
управления является его распространение.
Модель специального элемента управления создана для разработки
элементов управления, не имеющих ограничений на свободное
распространение, в форме сборки, которая может использоваться большим
количеством приложений. Сборка, содержащая элементы управления, может
использоваться одним приложением, если помещена в директорию приложения
\bin, или может совместно использоваться множеством приложений, если
размещается в глобальном кэше сборок, обычно называемом GAC. Сборка
может развертываться и использоваться в форме скомпилированного
двоичного кода без ассоциированного исходного кода.
Модель пользовательского элемента управления разработана для
применения его в пределах одного приложения. Пользовательский элемент
управления динамически компилируется во время выполнения, когда впервые
запрашивается страница, использующая его. Поэтому он должен быть
развернут в форме исходного кода, и .ascx файл (и ассоциированные с ним
code-behind файлы, если они существуют) должен быть скопирован в каждое
приложение, использующее пользовательский элемент управления. Эти
требования имеют побочный эффект - увеличение затрат на обслуживание,
потому что изменения, сделанные в одной копии, должны быть вручную
сделаны во всех приложениях, в которых развернут этот пользовательский
элемент управления. Обратите внимание, что это недостаток текущей версии
ASP.NET; будущие версии ASP.NET должны предоставить возможность
статически компилировать декларативные .ascx файлы в эквивалент
специальных элементов управления.
Рекомендации к разработке серверных элементов управления
Таблица 1
Сценарий |
Рекомендация |
Определяющие требования |
Коммерческий или не имеющий ограничение на
распространение пакет элементов управления, например,
стандартные элементы управления ASP.NET |
Специальные элементы управления |
Должны иметь возможность использовать
предварительно скомпилированные бинарные файлы. |
Элементы управления, связанные с данными,
например, DataGrid |
Специальные элементы управления |
Необходима возможность динамически
генерировать содержимое и компоновку на основе источника данных.
Способность использовать функциональные возможности статического
содержимого .ascx не помогает. К тому же, обычно связанные с
данными элементы управления нуждаются в обеспечении
функциональности времени разработки, которая недоступна
пользовательским элементам управления. |
Шаблонные элементы управления, например,
DataList |
Специальные элементы управления |
Необходима возможность определять содержимое
для элемента управления, как шаблон в странице, содержащей
элемент управления. Способность использовать функциональные
возможности статического содержимого .ascx в этом случае не
помогает. Чтобы обеспечить возможность редактирования шаблона,
требуется наличие ассоциированного дизайнера, который недоступен
пользовательским элементам управления. |
Расширенные элементы управления - например,
гипотетический элемент управления HoverImage, наследуемый от
стандартного элемента управления Image |
Специальные элементы управления |
Необходимо добавить новые функциональные
возможности существующему элементу управления или модифицировать
его поведение во время подготовки preserving объектной модели
наследуемого класса. |
Многократно используемые части HTML,
например, пользовательские элементы управления SiteHeader и
SiteFooter |
Пользовательские элементы управления |
Необходимо повторно использовать части HTML.
Эти части обычно специализированы и преимущественно содержат
статическое содержимое или разметку. |
Составные элементы управления, например,
пользовательский элемент управления RequiredTextField |
Пользовательские элементы управления |
Необходимо создать новый элемент управления
методом комбинирования определенных ранее элементов управления в
существующей компоновке. Составные элементы управления очень
просто разрабатываются с использованием поддержки разработки
RAD, доступной для файлов .ascx. |
Фрагментарное кэширование |
Пользовательские элементы управления |
Необходимо кэшировать фрагменты страницы.
Пользовательский элемент управления предлагает удобный
декларативный механизм кэширования, используя директиву
OutputCache. |
Разработка специальных элементов управления: ключевые концепции
Серверный элемент управления ASP.NET - это класс, который наследуется
прямо или косвенно от System.Web.UI.Control. Следующие два класса
являются базовыми классами для серверных элементов управления ASP.NET:
- System.Web.UI.Control
Класс Control определяет свойства, методы, и события, общие для всех
серверных элементов управления ASP.NET. Они включают методы и
события, которые управляют жизненным циклом элемента управления и
свойствами, такими как ID, UniqueID, Parent, ViewState, и Controls
(коллекция дочерних элементов управления). Control не имеет никаких
специфичных возможностей пользовательского интерфейса (UI). При
создании элемента управления, который не предоставляет
пользовательский интерфейс, или элемента управления, объединяющего
другие элементы управления, которые имеют собственный
пользовательский интерфейс, наследуйтесь от Control.
- System.Web.UI.WebControls.WebControl
Класс WebControl наследуется от Control и обеспечивает
дополнительные свойства и методы для расширения функциональных
возможностей пользовательского интерфейса. Эти свойства включают
ForeColor, BackColor, Font, BorderStyle, Height, и Width. WebControl
- базовый класс для семейства серверных элементов управления Web в
ASP.NET. Если ваш элемент управления формирует пользовательский
интерфейс, наследуйтесь от WebControl.
В зависимости от функциональных возможностей создаваемого элемента
управления, возможно, придется реализовать один или более из следующих
интерфейсов:
- System.Web.UI.INamingContainer
INamingContainer является маркер-интерфейсом, который не имеет ни
одного метода. Когда этот интерфейс реализован в элементе
управления, оболочка страницы ASP.NET создает новую область имен под
этим элементом управления. Это гарантирует, что дочерние элементы
управления имеют уникальные ID в иерархическом дереве элементов
управления. Если элемент управления является составным элементом
управления (содержит дочерние элементы управления), который
предоставляет связывание данных, или шаблонным элементом управления,
или если он должен направлять события к дочерним элементам
управления, то такой элемент управления должен реализовать интерфейс
INamingContainer. Подробнее см. в разделе Разработка
составного элемента управления.
- System.Web.UI.IPostBackDataHandler
Если разрабатываемый элемент управления должен обрабатывать
возвращаемые данные и обновлять их состояние или генерировать на
сервере события, основанные на изменениях данных, он должен
реализовать интерфейс IPostBackDataHandler. Подробнее см. в разделе
Обработка данных, отправленных обратно.
- System.Web.UI.IPostBackEventHandler
Если разрабатываемый элемент управления фиксирует postback события
со стороны клиента и отвечает на них, обрабатывая их или генерируя
события на сервере, он должен реализовать интерфейс
IPostBackEventHandler. Пример: элемент управления Button, который
фиксирует представление формы и вызывает событие Click на сервере.
Подробнее см. в разделе Фиксация событий при отправке
данных обратно.
Элемент управления может быть создан на любом языке программирования,
совместимом с общеязыковой спецификацией. Можно переопределить свойства,
методы и события, унаследованные от базового класса, а также добавить
новые свойства, методы и события в разрабатываемый специальный элемент
управления.
ASP.NET позволяет элементам управления обращаться и предоставлять
стили, как описано в разделе Стили в серверных элементах
управления. Можно создать серверный элемент управления ASP.NET,
который позволяет разработчикам страницы настраивать ее пользовательский
интерфейс через встроенные шаблоны.
Пользовательские элементы управления Web Forms
Кроме серверных элементов управления HTML и Web, можно создать
собственный, многократно используемый элемент управления, пользуясь теми
же технологиями, что и при разработке страниц Web Forms. Эти элементы
управления называются пользовательскими элементами управления.
Пользовательские элементы управления предлагают простой способ
распределения и многократного использования функциональных возможностей
пользовательского интерфейса в ваших ASP.NET Web приложениях. Как и
страницы Web Forms, эти элементы управления могут быть разработаны в
любом текстовом редакторе или с использованием code-behind классов. Как
и страницы Web Forms, пользовательские элементы управления компилируются
при первом запросе и сохраняются в памяти сервера с целью уменьшения
времени запроса при последующих вызовах. Однако, в отличие от страниц
Web Forms, пользовательские элементы управления не могут запрашиваться
независимо; чтобы работать, они должны быть включены в страницу Web
Forms.
Пользовательские элементы управления предлагают большую гибкость, чем
вставки на стороне сервера (SSI), т.к. имеют доступ к предоставляемой
ASP.NET объектной модели. Чем просто включать функциональность,
поставляемую другим файлом, можно программировать любое свойство,
объявленное в элементе управления, так же как в любом другом элементе
управления ASP.NET.
Несмотря на то, что при разработке пользовательского элемента
управления необходимо выбрать один язык программирования, на одну
страницу Web Forms вы можете поместить множество пользовательских
элементов управления, разработанных на различных языках. Например, можно
разработать на Visual Basic пользовательский элемент управления, который
импортирует данные из XML файла и другого пользовательского элемента
управления, созданного на C#, и поместить оба элемента управления на
одну страницу Web Forms.
Кроме того, можно кэшировать сформированный текст от
пользовательского элемента управления независимо ото всей содержащей его
страницы Web Forms. Названная фрагментарным кэшированием, при правильном
использовании эта технология может увеличить производительность сайта.
Например, если пользовательский элемент управления содержит серверный
элемент управления ASP.NET, который делает запрос к базе данных, но на
странице, кроме этого, расположены только текст и простой код,
выполняемый на сервере, вы можете фрагментарно кэшировать
пользовательский элемент управления, чтобы повысить производительность
приложения. Подробнее смотрите в MSDN Caching
Portions of an ASP.NET Page
Разработка пользовательского элемента управления
Принцип создания пользовательских элементов управления очень
отличается от создания других элементов управления. Пользовательские
элементы управления создаются по той же модели программирования, что и
страницы ASP.NET. Этот принцип идеален для быстрой разработки
пользовательских элементов управления. При создании других типов
элементов управления используется объектно-ориентированное
программирование. Пользовательский элемент управления представляет собой
страницу .ascx, которая является текстовым файлом, в то время как для
других типов серверных элементов управления создаются откомпилированные
сборки.
Пользовательский элемент управления может быть создан декларативно в
текстовом или HTML редакторе. Декларативный синтаксис пользовательского
элемента управления очень похож на синтаксис, используемый при создании
страниц Web Forms; основным отличием является то, что пользовательский
элемент управления не включает элементы <html>, <body> и <form>.
Чтобы создать пользовательский элемент управления:
- Откройте текстовый или HTML редактор и создайте блок кода,
включающий свойства, обработчики событий и все, что вы хотите
включить в функциональные возможности пользовательского элемента
управления.
Есть два варианта создания свойств пользовательских элементов
управления. Во-первых, можно определить новые свойства
пользовательского элемента управления и манипулировать ими.
Во-вторых, можно манипулировать свойствами серверных элементов
управления, которые составляют пользовательский элемент управления.
Например, если вы определили в пользовательском элементе управления
серверный элемент управления Web TextBox и присвоили ему ID
MyTextbox, вы можете манипулировать его свойством Text используя
синтаксис MyTextbox.Text.
В примере кода, приведенном ниже, объявляются свойства UserId и
Password. Этими свойствами можно манипулировать, декларативно или
программно, в любой странице Web Forms, содержащей этот
пользовательский элемент управления.
<script language="C#" runat="server">
public String UserId {
get {
return User.Text;
}
set {
User.Text = value;
}
}
public String Password {
get {
return Pass.Text;
}
set {
Pass.Text = value;
}
}
</script>
- Создайте элементы пользовательского интерфейса, которые должны
отображать пользовательский элемент управления. Например, в
приведенном далее коде создается форма регистрации пользователя,
которая взаимодействует с кодом, приведенном в Шаге 1.
<table style=font: 10pt verdana;border-width:1;
border-style:solid;border-color:black;" cellspacing=15>
<tr>
<td><b>Login: </b></td>
<td><ASP:TextBox id="User" runat="server"/></td>
</tr>
<tr>
<td><b>Password: </b></td>
<td><ASP:TextBox id="Pass" TextMode="Password" runat="server"/></td>
</tr>
<tr>
<td></td>
<td><ASP:Button Text="Submit" runat="server"/></td>
</tr>
</table>
- Присвойте имя пользовательскому элементу управления и сохраните
его с расширением .ascx. Например, данный пример может быть назван
LogonForm.ascx.
Примечание: При включении этого пользовательского элемента
управления в страницу Web Forms необходимо будет включить и имя
этого файла, и маршрут к нему в атрибуте Src директивы @ Register.
Программная разработка экземпляров пользовательского элемента
управления
Так же, как можно программно создавать экземпляры любого серверного
элемента управления ASP.NET на странице Web Forms, можно создать
экземпляры пользовательских элементов управления, используя метод
LoadControl содержащей их страницы. Сначала, однако, нужно назначить
строгий тип пользовательскому элементу управления, используя атрибут
className в директиве @ Control. Это необходимо потому, что метод
LoadControl возвращает тип класса Control, и пользовательский элемент
управления необходимо привести к строгому типу, чтобы иметь возможность
устанавливать индивидуальные свойства элемента управления.
В следующем примере для приведения к строгому типу пользовательского
элемента управления, сохраненного в файле MyUserControl.ascx,
используется атрибут className:
<%@ Control className="MyUserControl" %>
Чтобы программно создать экземпляр пользовательского эемента
управления:
- Зарегистрируйте пользовательский элемент управления в содержащей
его страницы директивой @ Reference. При программном создании
строгий тип для пользовательского элемента управления доступен
странице только тогда, когда создана ссылка на него. Например, в
следующем коде ссылка на пользовательский элемент управления
создается в файле MyUserControl.ascx.
<%@ Reference Control="MyUserControl.ascx" %>
- Создайте экземпляр пользовательского элемента управления или в
файле code-behind класса, или в блоке декларации кода для файла
.aspx. Присвойте значение свойства, если необходимо, и используя
метод Add добавьте элемент управления в объект ControlCollection
содержащей его страницы. Это делает доступным наследуемое страницей
свойство Control.Controls. В следующем примере создается экземпляр
MyUserControl.ascx со свойством BackColor, которому присвоено
значение Red:
Control c1 = LoadControl("MyUserControl.ascx");
((MyUserControl)c1).BackColor = "Red";
Page.Controls.Add(c1);
Примечание: При добавлении элементов управления в объект
ControlCollection методом Add, они размещаются в коллекции в той
последовательности, в которой они обрабатываются. Если вы хотите
поместить элемент управления в определенное место в коллекции,
используйте метод AddAt и определите индекс позиции, в которой
хотите сохранить элемент управления.
Разработка пользовательских элементов управления в Code-Behind файле
Так же, как можно создавать страницы Web Forms, используя code-behind
файлы, т.е. отделяя синтаксис (в файле .aspx) от логики страницы
(code-behind файл), можно создавать пользовательские элементы
управления. Технология практически та же, но с некоторыми
незначительными отличиями.
Примечание: Если для создания приложения ASP.NET вы
используете инструментарий RAD, такой как Visual Studio .NET, для
построения пользовательских элементов управления и страниц Web Forms по
умолчанию используется code-behind технология. Если пользовательский
элемент управления разрабатывается в code-behind классе, необходимо
расширять класс UserControl, а не класс Page.
Чтобы разработать пользовательские элементы управления в
code-behind файле:
- Создайте code-behind файл, включающий пространства имен, которые
понадобятся Вашему элементу управления. Как минимум надо включить
пространства имен System и System.Web.UI.
using System;
using System.Web.UI;
using System.Web.UI.WebControls;
- Объявите класс, наследующийся от класса UserControl и напишите
код, чтобы реализовать необходимые функциональные возможности.
Включите любые методы обработки событий, написанные для
пользовательского элемента управления. Также объявите public
экземпляры серверных элементов управления ASP.NET, которые
планируете использовать в пользовательском элементе управления.
public class MyCodeBehind : UserControl {
public TextBox Name;
public Label Result;
public void SubmitBtn_Click(Object sender, EventArgs e) {
Result.Text = "Hi, " + Name.Text + ", welcome to ASP.NET!";
}
}
- Назовите code-behind файл, убедившись, что использовали
расширение, отражающее язык программирования, на котором разработан
файл. Если этого не сделать, возникнет ошибка компилятора.
- Создайте файл .ascx, используя для обозначения имени класса, от
которого наследуется пользовательский элемент управления, и пути к
исходному файлу, который вы создали в шаге 1, директиву @ Control.
Включите серверные элементы управления и любой текст, которые хотите
отображать. При объявлении атрибута ID на каждом серверном элементе
управления убедитесь, что он соответствует имени экземпляра,
созданного в code-behind файле.
<%@ control inherits = "MyCodeBehind" src = "UserControl.cs" %>
<p>Name: <asp:textbox id="Name" runat="server"/> <br>
<asp:button text="Click Me" OnClick="SubmitBtn_Click" runat="server"/><br>
<asp:label id="Result" runat = "server" />
- Добавьте пользовательский элемент управления на страницу Web
Forms, на которой хотите использовать его функциональные
возможности. О том, как это сделать, описано в разделе
Включение пользовательского элемента управления в
страницу Web Forms.
Преобразование страницы Web Forms в пользовательский элемент
управления
Если вы разработали страницу Web Forms и решили через приложение
доступаться к ее функциональности, можно сделать некоторые
незначительные изменения в файле и преобразовать ее в пользовательский
элемент управления. Т.к. элементы <html>, <body> и <form> не включаются
в пользовательский элемент управления, необходимо включить их в
содержащую его страницу Web Forms.
Чтобы преобразовать страницу Web Forms в пользовательский элемент
управления:
- Удалите все элементы <html>, <body> и <form> из страницы.
Исходная страница Web Forms:
<html>
<script language="C#" runat=server>
void EnterBtn_Click(Object Sender, EventArgs E){
Label1.Text = "Hi " + Name.Text + " welcome to ASP.NET!";
}
</script>
<body>
<h3> <u>Web Forms Page</u> </h3>
<form>
Enter Name: <asp:textbox id="Name" runat=server/>
<asp:button Text="Enter" OnClick="EnterBtn_Click"
runat=server/>
<br><br>
<asp:label id="Label1" runat=server/>
</form>
</body>
</html>
Пользовательский элемент управления, получившийся после
изменений:
<h3> <u>User Control</u> </h3>
<script language="C#" runat=server>
void EnterBtn_Click(Object Sender, EventArgs E){
Label1.Text = "Hi " + Name.Text + " welcome to ASP.NET!";
}
</script>
Enter Name: <asp:textbox id="Name" runat=server/>
<asp:button Text="Enter" OnClick="EnterBtn_Click"
runat=server/>
<br><br>
<asp:label id="Label1" runat=server/>
- Если в странице Web Forms есть директива @ Page, замените ее
директивой @ Control. Чтобы избежать ошибки синтаксического разбора
при преобразовании страницы в элемент управления, удалите все
атрибуты, поддерживаемые директивой @ Page, которые не
поддерживаются директивой @ Control.
- Включите атрибут className в директиву @ Control. Это даст
возможность присвоить строгий тип пользовательскому элементу
управления при программном добавлении на страницу или к другому
серверному элементу управления.
- Присвойте элементу управления имя файла, которое отражает планы
по его использованию, и измените расширение имени файла с .aspx на
.ascx.
Включение пользовательского элемента управления в страницу Web Forms
Пользовательские элементы управления работают только тогда, когда
включены в страницу Web Forms. Когда приходит запрос страницы, и она
содержит пользовательский элемент управления, он проходит через все
этапы обработки, что и серверный элемент управления.
Чтобы включить пользовательский элемент управления в страницу Web
Forms:
- В странице Web Forms объявите директиву @ Register, которая
включает:
- атрибут tagprefix, ассоциирующий префикс с пользовательским
элементом управления. Этот префикс будет включен в открывающий
тэг элемента пользовательского элемента управления;
- атрибут tagname, ассоциирующий имя с пользовательским
элементом управления. Это имя будет включено в открывающий тэг
элемента пользовательского элемента управления.
- атрибут Src, определяющий путь к файлу пользовательского
элемента управления, включенного в страницу Web Forms.
В следующем примере кода регистрируется пользовательский элемент
управления, определенный в файле Login1.ascx. Ему были присвоены
префикс тэга Acme, имя тэга Login. Файл помещен в директорию
Controls:
<%@ Register TagPrefix="Acme" TagName="Login" Src=".\controls\login1.ascx" %>
- Используя синтаксис пользовательского серверного элемента
управления объявите пользовательский элемент управления между
открывающим и закрывающим тэгами серверного элемента управления
HtmlForm (<form runat=server></form>). Независимо от того, сколько
серверных элементов управления ASP.NET (пользовательских или любых
других элементов управления) используется на странице Web Forms, на
ней можно поместить только один серверный элемент управления
HtmlForm. Включите все серверные элементы управления между
открывающим и закрывающим тэгами этого элемента управления. Чтобы
объявить элемент управления, созданный в предыдущем шаге,
используйте следующий синтаксис:
<html>
<body>
<form runat="server">
<Acme:Login id="MyLogin" runat="server"/>
</form>
</body>
</html>
Манипулирование свойствами пользовательского элемента управления
Как только пользовательский элемент управления создан и определены
его свойства, можно программно или декларативно изменять значения этих
свойств из страницы, содержащей этот элемент управления. Эту технологию
также можно использовать и при добавлении пользовательских элементов
управления в другие элементы управления; это называется вложенностью.
Чтобы декларативно изменять значения свойств пользовательского
элемента управления:
Объявите имя свойства и его значение, как пару атрибут/значение, в
тэге пользовательского элемента управления. В следующем примере Color и
Text - свойства, обрабатываемые в тэге Acme:Login пользовательского
серверного элемента управления.
<Acme:Login Color="blue" Text="This is a sample." Runat="server" />
Чтобы программно изменять значения свойств пользовательского
элемента управления:
- Создайте пользовательский элемент управления со свойствами и
серверный элемент управления ASP.NET или элемент HTML для
отображения значений свойств. В данном примере для отображения
значений свойств Color и Text используется тэг <span>.
<script language="C#" runat="server">
public String Color = "blue";
public String Text = "This is a simple message user control!";
</script>
<span id="Message" style="color:<%=Color%>"><%=Text%></span>
- Зарегистрируйте пользовательский элемент управления на
содержащей его странице или элементе управления директивой @
Register, определяя его tagname и tagprefix, а также путь к его
файлу.
- В блоке декларации создайте код, манипулирующий свойствами
пользовательского элемента управления.
- В тело страницы или пользовательского элемента управления
включите любой серверный элемент управления ASP.NET, который будет
участвовать в манипулировании свойствами пользовательского элемента
управления. Например, чтобы манипулировать свойством Color, вы
можете включить серверный элемент управления DropDownList или
серверный элемент управления HtmlSelect.
Пример разработки страницы, манипулирующей пользовательским элементом
управления, представленным в Шаге 1, приведен в
MSDN Manipulating User Control Properties.
Хотя в манипулировании свойствами пользовательского элемента
управления из декларативной страницы, пользовательского элемента
управления, code-behind страницы или класса пользовательского элемента
управления очень много сходного, есть существенные различия. В
частности, если страница-контейнер или пользовательский элемент
управления создаются с code-behind классом, так же как это делается во
всех дизайнерах RAD, необходимо создать пользовательский элемент
управления, который вы планируете использовать с code-behind классом.
Это обеспечивает четкое разделение файлов с кодом и файлов, используемых
для генерирования HTML (.aspx и .ascx файлы).
Чтобы программно манипулировать значениями свойств
пользовательского элемента управления из code-behind файла
- В code-behind файле создайте код пользовательского элемента
управления, включающий свойства, которыми вы хотите манипулировать.
Следующий пример является адаптацией простого пользовательского
элемента управления из предыдущего раздела.
using System;
using System.Web.UI;
using System.Web.UI.HtmlControls;
namespace UserControlTest {
public class MyUserControl : UserControl {
public string Color = "blue";
public string Text = "This is a simple message user control!";
}
}
- В отдельном code-behind файле создайте страницу-контейнер или
пользовательский элемент управления, которые будут манипулировать
значениями свойств пользовательского элемента управления.
using System;
using System.Web.UI;
using System.Web.UI.WebControls;
using UserControlTest;
namespace PageTest {
public class MyPage : Page {
public Button mybutton;
public MyUserControl MyMessage;
public PlaceHolder Placeholder;
void Page_Load(Object sender, EventArgs e) {
Control MyMessage = LoadControl("uc.ascx");
Placeholder.Controls.Add(MyMessage);
Button mybutton = new Button();
mybutton.Text = "Change property values";
mybutton.OnClick = "SubmitBtn_Click";
Placeholder.Controls.Add(mybutton);
}
protected void SubmitBtn_Click(Object sender, EventArgs e) {
MyMessage.Text = "Message text changed!";
MyMessage.Color = "red";
}
}
}
- Откомпилируйте code-behind файл пользовательского элемента
управления и code-behind файл пользовательского элемента управления
страницы-контейнера в сборки. Вы должны быть уверены, что
страница-контейнер или пользовательский элемент управления имеют
доступ к коду пользовательского элемента управления, с которым вы
работаете. Можно откомпилировать их в ту же .dll. В следующем
примере uc.dll представляет .dll файл, скомпилированный из файла
исходного кода пользовательского элемента управления.
Примечание: Следующие инструкции командной строки
компилятора появляются в случае, если компиляция происходит из
директории, в которой находится файл с исходным кодом. Также
предполагается, что вы компилируете в директорию \bin. В таком
случае директория \bin является субдиректорией той директории, в
которой находится файл с исходным кодом.
csc /r:.\bin\uc.dll /target:library /out:.\bin\p.dll MyPage.cs
- Удостоверьтесь, что все созданные вами файлы .dll сохранены в
директории \bin вашего приложения. Это необходимо для того, чтобы
можно было автоматически связывать классы с ассоциированными с ними
.aspx или .ascx файлами.
- Создайте соответствующие .aspx и .ascx файлы. Убедитесь, что
включили серверный элемент управления PlaceHolder в .aspx файл. Этот
элемент управления будет содержать динамически генерируемый из
элементов управления, объявленных в code-behind .dll страницы,
пользовательский интерфейс (UI). Включите также любые необходимые
для кода страницы и пользовательского элемента управления элементы
HTML.
В этом случае не используйте атрибут src в директивах @ Control
или @ Page. Так как вы унаследовали оба класса из сборки, просто
используйте атрибут Inherits. Если страница и пользовательский
элемент управления имеют собственные пространства имен, необходимо
также включить имя пространства имен в значение атрибута Inherits.
В следующем примере представлены файл .ascx для пользовательского
элемента управления и файл .aspx для страницы из предыдущих
примеров.
Файл .ascx:
<%@ Control Inherits="UserControlTest.MyUserControl" %>
<span id="Message" style="color:<%=Color%>"><%=Text%></span>
Файл .aspx:
<%@ Import Namespace="PageTest" %>
<%@ Page language="C#" Inherits="PageTest.MyPage" %>
<%@ Reference control="uc.ascx" %>
<html>
<body style="font: 10pt verdana">
<h3>A Simple User Control w/ Properties</h3>
<form runat="server">
<asp:placeholder id="Placeholder" runat="server" />
</form>
</body>
</html>
Обработка событий пользовательского элемента управления
Между написанием методов, обрабатывающих события, для
пользовательского элемента управления и для страницы Web Forms
существует небольшое различие. Помните, что пользовательские элементы
управления инкапсулируют их собственные события и посылают информацию о
событии для обработки через содержащую их страницу. Не включайте
обработчики событий пользовательского элемента управления в
страницу-контейнер; пишите их в блоке декларации кода пользовательского
элемента управления или в code-behind файле, генерирующем
пользовательский элемент управления.
Чтобы инкапсулировать события пользовательского элемента
управления в элемент управления:
- Включите блок декларации кода в пользовательский элемент
управления, содержащий код-обработчик событий для вашей формы.
Примечание: все серверные элементы управления, вовлеченные
в события пользовательского элемента управления, должны быть
включены в сам пользовательский элемент управления, или используйте
метод FindControl, чтобы локализовать и доступаться к особым
функциональным возможностям элемента управления.
- Объявите пользовательский элемент управления на страницах Web
Forms, на которых хотите его видеть.
Создание шаблонного пользовательского элемента управления
Можно создавать пользовательские элементы управления, которые
реализовывают шаблоны (свойство ASP.NET, позволяющее разделение данных
элемента управления от его представления). Шаблонный элемент управления
не поставляет пользовательский интерфейс (UI). Пользовательский
интерфейс для элемента управления поставляется разработчиком страницы
через встраиваемые шаблоны, которые позволяют разработчику страницы
переделать пользовательский интерфейс для элемента управления. Создание
шаблонных пользовательских элементов управления облегчает добавление
пользовательских элементов управления на страницы в ваших приложениях
без компилирования элемента управления в .dll файл.
Подробнее о создании пользовательских шаблонных элементов управления
см. в разделе Разработка шаблонного элемента управления.
Чтобы создать шаблонный пользовательский элемент управления:
- В файле .ascx декларативно создайте серверный элемент управления
Web ASP.NET Placeholder, в котором должен появиться шаблон.
- В блоке декларации кода пользовательского элемента управления
или code-behind классе реализуйте свойство типа ITemplate.
- В том же блоке кода определите серверный элемент управления,
реализующий INamingContainer, как контейнер для размещения шаблона.
Он называется именованный контейнер для размещения шаблона.
Примечание: Этот элемент управления по существу становится
вложенным классом пользовательского элемента управления, хотя этого
и не требовалось.
- Примените TemplateContainerAttribute к свойству ITemplate и
передайте тип именованного контейнера конструктору или атрибуту, как
аргумент.
- В методе Page_Init повторите следующие шаги один или более раз:
- Создайте экземпляр класса именованного контейнера.
- Создайте экземпляр шаблона в именованного контейнера.
- Добавьте экземпляр именованного контейнера в свойство
Controls серверного элемента управления PlaceHolder.
С точки зрения страницы, использующей пользовательский элемент
управления, синтаксис шаблонного пользовательского элемента управления
идентичен синтаксису обычного шаблонного элемента управления.
Разработка составного элемента управления
Новые элементы управления могут быть созданы путем объединения
существующих элементов управления. Составной элемент управления
формирует пользовательский интерфейс, который многократно использует
функциональные возможности существующих элементов управления. Составной
элемент управления может синтезировать свойства из свойств его дочерних
элементов управления и обрабатывать события, вызванные его дочерними
элементами управления. В нем также можно реализовывать пользовательские
свойства и события.
Чтобы создать составной элемент управления:
- Переопределите protected метод CreateChildControls,
унаследованный от Control, чтобы создать экземпляры дочерних
элементов управления и добавить их в его коллекцию Controls.
- Реализуйте интерфейс System.Web.UI.INamingContainer.
Дочерние элементы управления должны быть созданы в методе
CreateChildControls, а не на определенном этапе жизненного цикла
элемента управления, например, создание или инициализация, для того
чтобы дочерние элементы управления могли создаваться по запросу именно
тогда, когда они необходимы элементу управления. Это особенно важно,
когда создается составной элемент управления, чьи дочерние элементы
управления будут обрабатывают данные, отправленные обратно.
Чтобы обеспечить создание дочерних элементов управления до того, как
код обратится к ним, класс Control определяет protected метод
EnsureChildControls. Этот метод проверяет, были ли созданы дочерние
элементы управления и вызывает метод CreateChildControls для их
создания, только если они не были созданы. Любой код в реализации вашего
элемента управления, который обращается к дочерним элементам управления,
должен сначала вызывать метод EnsureChildControls. Например,
реализованный по умолчанию метод FindControl, который используется
страницей для локализации дочернего элемента управления, сначала
вызывает метод EnsureChildControls. Обратите внимание, что если дочерние
элементы управления не были созданы в жизненном цикле вашего элемента
управления до этапа предварительного формирования, они создаются по
запросу в этот момент. Это происходит потому, что реализованный по
умолчанию метод PreRender вызывает метод EnsureChildControls всех
элементов управления, значение свойства Visible которых - true.
INamingContainer - это маркер-интерфейс, который не имеет методов, но
заставляет страницу создавать новое пространство имен под вашим
элементом управления. Когда вы реализуете этот интерфейс, любой дочерний
элемент управления, содержащийся в вашем элементе управления,
гарантированно имеет действительно уникальный на странице идентификатор
(представленный свойством UniqueID). Например, если разработчик помещает
два экземпляра составного элемента управления на страницу, дочерние
элементы управления в первом экземпляре и во втором экземпляре будут
иметь различные уникальные идентификаторы, даже если оба набора дочерних
элементов управления имеют одинаковые значения свойства ID. Это особенно
важно, если странице нужно найти элемент управления, чтобы направлять к
нему данные и события, отправленные обратно.
Составные элементы управления эквивалентны пользовательским элементам
управления, которые созданы декларативно. Однако есть существенные
различия между поведением в режиме разработки.
Сделать составной элемент управления относительно просто, но при этом
возникают потери производительности из-за необходимости создавать
дочерние элементы управления. Для обеспечения оптимальной
производительности своего элемента управления, можно самостоятельно
реализовать логику исполнения, переопределяя метод Render. Вы также
должны реализовать обработку любых данных, отправленных обратно, и
обработку события при отправке данных обратно.
Сравнение составного и пользовательского элементов управления
В Таблица 2. Сравнение составного и пользовательского элементов
управления представлены основные различия между составными и
пользовательскими элементами управления.
Таблица 2. Сравнение составного и пользовательского элементов
управления
Составной элемент
управления |
Пользовательский элемент
управления
|
Минимальная поддержка в режиме разработки |
Полная поддержка в режиме разработки.
Создание пользовательского элемента управления в визуальном
дизайнере не отличается от создания страницы ASP.NET. |
Создается программно с использованием
объектно-ориентированного языка программирования. |
Создается декларативно, используя синтаксис
страницы ASP.NET и скрипты. |
Компилируется и сохраняется, как сборка
(.dll). |
Сохраняется как текстовый файл с расширением
.ascx. |
Хорошо подходит для создания универсальных
свободно распространяемых элементов управления. |
Приспособлен к специфическим функциональным
возможностям приложения. |
Полная поддержка режима разработки при
использовании в дизайнере. Составной элемент управления может
быть добавлен на панель инструментов визуального дизайнера. |
Минимальная поддержка при использовании в
дизайнере. |
Если необходимо расширить функциональные возможности существующего
элемента управления, например Label, не стоит создавать составной или
пользовательский элемент управления. В этом случае необходимо
наследоваться от класса Label (чтобы получить преимущества наследования
и полиморфизма) и добавить или переопределить свойства, события, методы.
При создании пользовательского или составного элемента управления,
который содержит только один элемент управления - Label, новый элемент
управления не будет иметь объектную модель Label. Пользовательский или
составной элементы управления создается только тогда, когда необходимо
объединить несколько существующих элементов управления.
Разработка специального серверного элемента управления ASP.NET
В этом разделе представлен пример создания простого специального
серверного элемента управления, который имеет одно свойство, не вызывает
и не обрабатывает события.
Чтобы создать специальный серверный элемент управления ASP.NET:
- Определите класс, который прямо или косвенно наследуется от
System.Web.UI.Control:
using System;
using System.Web.UI;
public class FirstControl : Control{...}
- Включите свой элемент управления в пространство имен. Можно
определить новое пространство имен или использовать существующее.
Имя пространства имен - значение псевдоатрибута пространства имен в
директиве Register страницы. (Например, <%@ Register
TagPrefix="Custom" Namespace="CustomControls" Assembly =
"CustomControls" %>).
namespace CustomControls
{
public class FirstControl : Control {...}
...
}
- Определите свойства, необходимые вашему элементу управления.
Следующий фрагмент кода определяет свойство Message.
private String message = "Hello";
//The Message property.
public virtual String Message{
get{
return message;
}
set{
message = value;
}
}
- Переопределите метод Render, который элемент управления
наследует от Control. Этот метод предоставляет логику для того,
чтобы послать HTML браузеру клиента. HTML, который ваш элемент
управления посылает клиенту, передается как строковый параметр к
методу Write экземпляра System.Web.UI.HtmlTextWriter, как показано в
следующем примере:
protected override void Render( HtmlTextWriter writer)
{
writer.Write("<font> "+ this.Message + "<br>" +
"The date and time on the server: " +
System.DateTime.Now.ToLongTimeString()
+ "</font>");
}
Класс System.DateTime, который вызывается в этом фрагменте кода,
- это утилитный класс, который предоставляет информацию о дате и
времени. Обратите внимание, что этот класс активизируется на сервере
и, следовательно, возвращает время на сервере.
Во фрагменте кода необработанный HTML просто передан как
строковый параметр к методу Write HtmlTextWriter. Для получения
дополнительной информации об использовании методов HtmlTextWriter,
чтобы упростить выполнение HTML и чтобы генерировать элемент
управления, который наследуется от WebControl, см. раздел
Формирование серверного элемента управления ASP.NET.
Для упрощения в этом примере FirstControl наследуется от Control.
Если создается элемент управления, который должен икапсулировать
некоторые свойства визуализации, например цвет и шрифт текста,
наследуйтесь от System.Web.UI.WebControls.WebControl, чтобы ваш
элемент управления наследовал специфические свойства
пользовательского интерфейса.
- Добавьте атрибуты времени выполнения и режима разработки, чтобы
обеспечить специальные метаданные для элемента управления. Атрибуты
времени выполнения необходимы некоторым элементам управления.
Примерами таких элементов управления, могут быть элементы
управления, которые предоставляют шаблоны, выполняют связывание
данных или нуждаются в специализированной логике синтаксического
анализа. Примеры атрибутов времени выполнения см. в разделе
Разработка шаблонного элемента управления.
Атрибуты режима разработки необходимы, если ваш элемент управления
будет использоваться в визуальном дизайнере, таком как
VisualStudio.NET. Атрибуты режима разработки в общеязыковой среде
выполнения не требуются, но они поставляют метаданные, необходимые
для отображения элемента управления во время разработки. Фрагмент
кода ниже применяет атрибут режима разработки к свойству Message,
определенному в шаге 2.
[Description("A message string to display to the user")]
public virtual String Message{...}
- Сохраните, откомпилируйте и установите элемент управления по
следующему плану:
- создайте подкаталог /bin в корневом каталоге вашего
приложения;
- откомпилируйте исходный файл в сборку (.dll), и сохраните
сборку в подкаталоге /bin вашего приложения.
Например, если исходный код написан на C# и сохраняется в файл
FirstControl.cs, из каталога, содержащего исходный файл, можно
выполнить следующую команду:
csc /t:library /out:.\bin\CustomControls.dll /r:System.Web.dll
/r:System.dll FirstControl.cs
Опция /r сообщает компилятору, на какие сборки ссылается ваш
элемент управления.
Элемент управления теперь откомпилирован и готов к использованию
из любой страницы ASP.NET в корневом каталоге вашего приложения (или
любом из его подкаталогов).
Далее приведен полный код для FirstControl. Элемент управления
включен в пространство имен CustomControls.
using System;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace CustomControls
{
public class FirstControl : Control
{
private String message = "Hello";
public virtual String Message
{
get
{
return message;
}
set
{
message = value;
}
}
protected override void Render( HtmlTextWriter writer)
{
writer.Write("<font> "
+ this.Message + "<br>"
+ "The time on the server is "
+ System.DateTime.Now.ToLongTimeString()
+ "</font>");
}
}
}
Использование FirstControl на странице ASP.NET
Следующая ASP.NET страница использует специальный элемент управления,
созданный в предыдущем разделе. Директива страницы Register позволяет
разработчику страницы создавать псевдоним для пространства имен и также
предоставляет имя сборки, которая содержит элемент управления. В примере
создается псевдоним Custom для пространства имен CustomControls.
<%@ Register TagPrefix="Custom" Namespace="CustomControls" Assembly = "CustomControls" %>
<html>
<body>
<form runat=server>
Here is a custom ASP.NET server control.<br><br>
<Custom:FirstControl Message= "This control tells time. " runat=server/>
<br>
</form>
</body>
</html>
Разработка шаблонного элемента управления
ASP.NET предоставляет универсальную возможность, названную шаблонами,
которые позволяют разделение данных элемента управления и его
представления. Шаблонный элемент управления самостоятельно не поставляет
пользовательский интерфейс (UI). Пользовательский интерфейс для элемента
управления поставляется разработчиком страницы через встроенные шаблоны,
которые позволяют разработчику страницы настроить пользовательский
интерфейс для элемента управления.
Чтобы разработать шаблонный элемент управления:
- Реализуйте интерфейс System.Web.UI.INamingContainer. Это -
маркер-интерфейс, который не имеет методов. Он создает новую область
имен под вашим элементом управления для того, чтобы дочерние
элементы управления имели уникальные идентификаторы в дереве имен.
public class TemplatedFirstControl : Control,INamingContainer
{...}
- Примените ParseChildrenAttribute к вашему элементу управления, и
передайте ему значение true, как параметр. Это указывает
синтаксическому анализатору страницы, как анализировать тэги
свойства шаблона, когда элемент управления используется на странице
ASP.NET декларативно. В шаге 3 показано, как определить свойство
шаблона.
Примечание: если элемент управления наследуется от
WebControl, ParseChildrenAttribute применять не нужно, потому что
WebControl уже содержит этот атрибут.
[ ParseChildren(ChildrenAsProperties = true)]
public class TemplatedFirstControl : Control, INamingContainer {...}
Подробнее о ParseChildrenAttribute см. в разделе
Использование ParseChildrenAttribute.
- Определите одно или более свойств типа System.Web.UI.ITemplate.
ITemplate имеет метод InstantiateIn, который создает элементы
управления, используя шаблон, встроенный на странице. Вам не надо
реализовывать метод InstantiateIn; оболочка страницы ASP.NET
поставляет реализацию. Свойство ITemplate должно иметь атрибут
метаданных типа System.Web.UI.TemplateContainerAttribute, который
указывает, какой элемент управления INamingContainer будет содержать
обрабатываемый шаблон. Это описано в шаге 4. В следующем фрагменте
кода определяется свойство шаблона:
[TemplateContainer(typeof(FirstTemplateContainer))]
public ITemplate FirstTemplate {...}
- Параметр TemplateContainerAttribute - это тип контейнерного
элемента управления. Контейнерный элемент управления независим от
создаваемого шаблонного элемента управления. Логический контейнер
используется потому, что шаблонный элемент управления часто имеет
шаблон, который должен неоднократно создаваться с различными
данными. Наличие контейнерного элемента управления, который
отличается от корневого шаблонного элемента управления, позволяет
иметь такие множественные экземпляры. Логический контейнер - это
INamingContainer дочерних элементов управления в пределах шаблона.
Эти отношения более подробно рассмотрены в разделе
Разработка шаблонного элемента управления, связанного с данными.
Примечание: сам контейнерный элемент управления должен
реализовать INamingContainer, так как он имеет дочерние элементы
управления, которым нужно присвоить уникальные имена на странице.
public class FirstTemplateContainer : Control, INamingContainer {}
- Переопределите метод CreateChildControls, чтобы создать дочерние
элементы управления в шаблоне. Это делается в три этапа:
- создайте экземпляр контейнера шаблона;
- вызовите метод InstantiateIn свойства шаблона, и передайте
контейнер ему, как параметр. Метод InstantiateIn (объявленный в
интерфейсе ITemplate) создает элементы шаблона, как дочерние
элементы управления контейнера шаблона. Не надо реализовывать
метод InstantiateIn; оболочка страницы ASP.NET поставляет
реализацию;
- добавьте экземпляр контейнера шаблона к коллекции Controls
вашего шаблонного элемента управления.
В следующем фрагменте кода продемонстрирована реализация
CreateChildControls.
private Control myTemplateContainer;
protected override void CreateChildControls ()
{
if (FirstTemplate != null)
{
myTemplateContainer = new FirstTemplateContainer(this);
FirstTemplate.InstantiateIn(myTemplateContainer);
Controls.Add(myTemplateContainer);
}
else
{
Controls.Add(new LiteralControl(Text + " " + DateTime));
}
}
- Переопределите метод OnDataBinding, унаследованный от Control,
чтобы вызвать метод EnsureChildControls. Это гарантирует, что
дочерние элементы управления в шаблоне создаются прежде, чем
оболочка страницы пробует вычислить любые выражения привязки данных
в пределах шаблона. Вы должны также вызвать метод OnDataBinding
базового класса, чтобы гарантировать, что вызваны зарегистрированные
обработчики событий.
protected override void OnDataBinding(EventArgs e) {
EnsureChildControls();
base.OnDataBinding(e);
}
- В шаге 5, повторите логику в пределах метода
CreateChildControls, для того чтобы создать экземпляр шаблона для
каждого из свойств шаблона вашего элемента управления.
Примеры шаблонного элемента управления, связанного контейнерного
элемента управления для шаблона и страницы, которая использует элемент
управления, смотрите в MSDN
Templated Control Sample.
Разработка шаблонного элемента управления, связанного с данными
Используя синтаксис привязки данных ASP.NET легко связать свойство
элемента управления с отдельным элементом данных (или выражением). В
этом разделе рассматривается более сложный сценарий разработки элемента
управления, который имеет шаблонные свойства, связанные с источником
данных, являющимся типом коллекции (System.Collections.ICollection или
System.Collections.IEnumerable). Шаблоны позволяют разработчику страницы
настроить представление данных, связанных с элементом управления.
Элементы управления Repeater и DataList - примеры шаблонных элементов
управления, связанных с данными.
Шаблонный элемент управления, связанный с данными, имеет свойство
источник данных типа ICollection или IEnumerable и одно или более
свойств типа ITemplate. Контейнер для одного из свойств шаблона
определяет свойство (обычно называемое DataItem), чтобы связать данные.
Элемент управления реализует логику привязки данных в методе DataBind,
наследуемом от Control. Он переопределяет метод CreateChildControls,
чтобы обновить иерархию дочерних элементов управления при отправке
данных обратно. Эти шаги более подробно описаны далее.
Чтобы разработать шаблонный элемент управления, связанный с
данными:
- Определите элемент управления, реализующий интерфейс
System.Web.UI.INamingContainer:
public class TemplatedList : WebControl, INamingContainer {}
- Определите свойство типа System.Web.UI.ITemplate:
[TemplateContainer(typeof(TemplatedListItem))]
public virtual ITemplate ItemTemplate {
get {
return itemTemplate;
}
set {
itemTemplate = value;
}
}
Логический контейнер для шаблона (определенный в атрибуте
TemplateContainerAttribute) должен иметь некоторое свойство, чтобы
связывать данные. По общему согласию это свойство называют DataItem.
В следующем примере определяется контейнер для свойства шаблона.
public class TemplatedListItem : TableRow, INamingContainer {
private object dataItem;
public virtual object DataItem {
get {
return dataItem;
}
set {
dataItem = value;
}
}
- Переопределите метод DataBind (унаследованный от Control), чтобы
обеспечить логику привязки данных. Это делается в следующей
последовательности:
- вызовите метод OnDataBinding базового класса, чтобы вызвать
обработчики (прикрепленные страницей), которые определяют
выражения привязки данных в вашем элементе управления;
- очистите коллекцию Controls;
- очистите ViewState дочерних элементов управления;
- создайте дочерние элементы управления, используя источник
данных;
- сигнализируйте оболочке страницы ASP.NET, чтобы проследить
ViewState для элемента управления.
Эти шаги выполняются в коде, приведенном ниже.
CreateChildControlsHierarchy - вспомогательный метод,
предназначенный для того, чтобы выполнить фактическую работу
создания дочерних элементов управления. Подробности см. в шаге 5.
public override void DataBind() {
// Controls with a data-source property perform their
// custom data binding by overriding DataBind to
// evaluate any data-binding expressions on the control
// itself.
base.OnDataBinding(EventArgs.Empty);
// Reset the control's state.
Controls.Clear();
ClearChildViewState();
// Create the control hierarchy using the data source.
CreateControlHierarchy(true);
ChildControlsCreated = true;
TrackViewState();
}
- Переопределите CreateChildControls, чтобы обновить дочерние
элементы управления в сценарии отправки данных обратно. Эта операция
вызывает очистку коллекции Controls и создание иерархии элемента
управления, используя View State вместо источника данных.
Фактическая работа по созданию дочерних элементов управления скрыта
в методе CreateControlHierarchy, описанном в шаге 5.
protected override void CreateChildControls() {
Controls.Clear();
if (ViewState["ItemCount"] != null) {
// Create the control hierarchy using the view state,
// not the data source.
CreateControlHierarchy(false);
}
}
- Определите источник данных, который имеет пустые элементы, и при
создании иерархии элемента управления используйте этот источник
вместо реального источника данных при отправке данных обратно. В
шагах 3 и 4 создается иерархия элементов управления, используя
источник данных и сохраненное View State, соответственно. Фиктивный
источник данных позволяет элементу управления реализовать один
кодовый путь для общих элементов этих двух шагов.
Пример связанного с данными элемента управления, который реализует
шаги, обсужденные в этой теме, смотрите в MSDN
Templated Control Sample.
Свойства в серверных элементах управления ASP.NET
Свойства подобны полям, но имеют методы доступа. Вы должны определить
свойства ваших элементов управления вместо того, чтобы использовать
public поля, потому что свойства позволяют сокрытие данных, могут иметь
версию и поддерживаются визуальными дизайнерами, например,
VisualStudio.NET. Функции доступа могут осуществлять дополнительную
логику программы, кроме установки и получения значения свойства.
Свойства, унаследованные от Control
Класс System.Web.UI.Control определяет основные свойства серверных
элементов управления. В Ошибка! Источник ссылки не найден. приведен
список самых важных public свойств, наследуемых от Control каждым
серверным элементом управления.
Таблица 3. Public свойства класса Control
Public свойство |
Тип |
Описание |
ClientID |
String |
ClientID - уникальный идентификатор,
назначенный элементу управления оболочкой страницы ASP.NET и
представленный клиенту, как HTML ID атрибут. ClientID отличается
от UniqueID тем, что UniqueID может содержать символ двоеточия
(:), который недопустим в HTML ID атрибуте (и не допускается в
именах переменных в скрипте клиентской части). |
Controls (элементы управления) |
ControlCollection |
Коллекция дочерних элементов управления в
дереве элементов управления. |
ViewState |
StateBag |
Структура данных, посылаемая клиенту и
обратно, обычно используется для того, чтобы сохранить данные
формы через циклы обработки. ViewState имеет тип StateBag,
который является словарем, в котором размещаются данные в виде
пары имя/значение. |
EnableViewState |
Boolean |
Определяет сохраняется ли состояние элемента
управления во View. Если родительский элемент управления не
управляет своим View State, View State его дочерних элементов
управления автоматически не поддерживается |
ID |
String |
Идентификатор элемента управления,
присваиваемый пользователем и используемый для доступа к
элементу управления. Оболочка страницы использует значение ID и
расположение элемента управления в дереве элементов управления,
чтобы генерировать свойства ClientID и UniqueID. |
NamingContainer |
Control |
Ближайший вверх по иерархии элемент
управления, реализующий интерфейс
System.Web.UI.INamingContainer. |
Page (страница) |
Page |
Страница, содержащая элемент управления. |
Parent (родитель) |
Control |
Элемент управления, коллекции Controls
которого принадлежит элемент управления. (Элемент управления A
является родителем элемента управления B, если B принадлежит
A.Controls.) |
UniqueID |
String |
Иерархически присваиваемый уникальный
идентификатор, присваиваемый элементу управления оболочкой
страницы ASP.NET. |
Visible |
Boolean |
Определяет, видим ли элемент управления на
странице. |
Таблица 4. Protected свойства класса Control описывает важные
protected свойства, наследуемые элементом управления от класса Control.
Таблица 4. Protected свойства класса Control
Protected свойство |
Тип |
Описание |
Context |
HttpContext |
Обеспечивает текущему Web запросу доступ к
объекту System.Web.HttpContext. Свойство Context позволяет
элементу управления доступаться к другим внутренним объектам
HTTP, таким как Application, Session, Request и Response. |
ViewState |
StateBag |
Словарь информации состояния. |
Свойства, унаследованные от WebControl
Вы должны наследоваться от базового класса
System.Web.UI.WebControls.WebControl, если хотите генерировать видимые
элементы HTML. WebControl наследуется от Control и добавляет свойства,
позволяющие разработчику страницы обеспечить соответствие требованиям
внешнего вида и поведения элемента HTML, который генерируется вашим
элементом управления. Эти свойства включают свойства стиля, такие как
Font, ForeColor, BackColor, и Width.
В Таблица 5. Public свойства, определяемые классом WebControl
представлен список основных public свойств WebControl.
Таблица 5. Public свойства, определяемые классом WebControl
Public свойство |
Тип |
Описание |
AccessKey |
String |
"Горячая" клавиша, используемая для установки
фокуса на сформированном элементе HTML. |
Attributes |
AttributeCollection |
Коллекция специальных пар имя/значение,
формируемых как атрибуты на элементе HTML, который сгенерирован
элементом управления. Свойство Attributes содержит объединение
декларативно установленных атрибутов, которые не соответствуют
свойствам (или событиям) элемента управления и тому, что
установлено программно. |
BackColor |
Color |
Цвет фона элемента HTML, формируемого
элементом управления. |
BorderColor |
Color |
Цвет рамки вокруг элемента HTML, формируемого
элементом управления. |
BorderStyle |
BorderStyle |
Стиль рамки элемента HTML формируемого
элементом управления, такой как сплошной, двойной, или
пунктирный. |
BorderWidth |
Unit |
Толщина рамки вокруг элемента HTML,
формируемого элементом управления. |
ControlStyle |
Style |
Стиль, который инкапсулирует все связанные с
отображением функциональные возможности элемента управления. Тип
ControlStyle - это класс, наследуемый от
System.Web.UI.WebControls.Style, который предоставляет такие
свойства, как Font, Width, BackColor и ForeColor. |
CssClass |
String |
Класс CSS, сгенерированный элементом
управления на стороне клиента. Разработчики страницы могут
использовать это свойство, чтобы связывать сгенерированный
элемент с атрибутами стиля, объявленными в таблице стилей. |
Enabled |
Boolean |
Определяет активен ли элемент HTML,
сформированный серверным элементом управления Web. Обычно не
активный элемент HTML отображается в Web браузере серым и не
может получать фокус ввода. |
Font |
FontInfo |
Шрифт, используемый для отображения текста в
элементе HTML, сформированном элементом управления. |
ForeColor |
Color |
Основной цвет текста в элементе HTML,
сформированном элементом управления. |
Height |
Unit |
Высота элемента HTML, сформированного
элементом управления. |
ToolTip |
String |
Текст, отображаемый в ToolTip, который
возникает, когда курсор располагается над элементом HTML,
сформированном элементом управления. |
Width |
Unit |
Ширина элемента HTML, сформированного
элементом управления. |
В Таблица 6. Protected свойства класса WebControl представлен список
protected свойств, которые наследует ваш элемент управления при
происхождении от WebControl.
Таблица 6. Protected свойства класса WebControl
Protected свойства |
Тип |
Описание |
TagKey |
HtmlTextWriterTag |
Переопределите это свойство, чтобы создать
стандартный тэг HTML, который содержится в списке
System.Web.UI.HtmlTextWriterTag, вместо тэга < span >,
создаваемого WebControl по умолчанию. |
TagName |
String |
Переопределите это свойство, чтобы создать
нестандартный тэг HTML (который не включен в список
System.Web.UI.HtmlTextWriterTag) вместо тэга < span >,
создаваемого по умолчанию. |
Настройка унаследованных свойств
Элемент управления может переопределить унаследованное свойство. Для
переопределения свойства необходимо переопределить его методы доступа. В
следующем примере представлен элемент управления, унаследованный от
Button, в котором переопределено свойство BackColor таким образом, что
цвет подложки всегда остается зеленым.
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Drawing;
namespace CustomControls
{
public class DerivedButton : Button
{
public DerivedButton() : base()
{
base.BackColor = Color.Green;
}
public override Color BackColor
{
get
{
return base.BackColor;
}
set
{
// Do nothing.
}
}
}
Можно также изменить метаданные для свойства, переопределяя (или
предоставляя) атрибут. В следующем фрагменте кода показано, как
присваивая значение false BrowsableAttribute скрыть свойство BackColor
от инспектора свойств дизайнера, например, Visual Studio .NET:
[Browsable(false)]
public override Color BackColor{...}
Подсвойства
В предыдущих разделах показано, как реализовать простые свойства с
такими типами, как примитивные типы, строки и последовательности. В этом
случае не надо что-то делать, чтобы дать возможность разработчику
страницы определять значения свойств в тэгах элемента управления
(декларативная персистентность). Однако вам придется выполнить
дополнительную работу, чтобы осуществлять декларативную персистентность
в случае наличия комплексных свойств.
Комплексные свойства обычно имеют подсвойства. Это свойства, которые
раскрываются типом комплексного свойства. Например, класс WebControl
раскрывает свойство Font, тип которого - класс
System.Web.UI.WebControls.FontInfo, который, в свою очередь, раскрывает
такие свойства, как Bold, Name и Size. Свойства FontInfo являются
подсвойствами свойства Font.
Две взаимосвязанные категории взаимодействуют, чтобы обеспечить
декларативную персистентность комплексных свойств:
- Атрибуты метаданных, относящиеся к сериализации.
- Классы конвертеров типов, осуществляющие преобразование в и из
данного типа в тип String и другие типы.
Подсвойства, сохраняемые в тэгах элемента управления
Чтобы сохранить подсвойство в тэге элемента управления, разработчик
страницы определяет подсвойство, используя дефис между именами свойства
и подсвойства. В следующем примере определены подсвойства Name и Size
свойства Font в тэге элемента управления TextBox:
<asp:TextBox id="textBox1" Font-Name="Verdana" Font-Size="12pt"
runat="server" />
Синтаксический анализатор страницы автоматически обрабатывает такой
синтаксис подсвойства, используя конвертер типа, ассоциированный с типом
подсвойства. Однако, чтобы обеспечить возможность дизайнеру генерировать
разделенный дефисом синтаксис подсвойства, необходимо к свойству и его
подсвойствам применить определенные атрибуты метаданных времени
разработки. В следующем фрагменте кода показаны атрибуты метаданных
времени разработки, которые необходимо применить к комплексному
свойству:
public class WebControl : Control {
[
DesignerSerializationVisibility(
DesignerSerializationVisibility.Content),
NotifyParentProperty(true)
]
public FontInfo Font { ... }
}
Атрибут DesignerSerializationVisibility
(DesignerSerializationVisibility.Content) указывает сериализатору
времени разработки зайти в подсвойства и сериализовать их значения в
последовательную форму. Дизайнер сохраняет свойства в тэге элемента
управления и генерирует разделенный дефисом синтаксис для каждого
подсвойства. Атрибут NotifyParentProperty (true) вызывает сообщения об
изменениях от подсвойств в инспекторе свойств, чтобы передать объектную
модель и сгенерировать сообщения об изменениях в элементе управления,
подсвойства которого изменились, чтобы отметить его в дизайнере. Эта
процедура обязательна для правильного сохранения в HTML тех свойств,
которые разработчик страницы модифицирует в дизайнере.
Атрибут NotifyParentProperty (true) также необходимо применить к
комплексным свойствам. В следующем примере показано применение этого
атрибута к свойствам Name и Size класса FontInfo. Эти свойства являются
подсвойствами свойства Font класса WebControl.
[
TypeConverter(typeof(ExpandableObjectConverter))
]
public sealed class FontInfo {
[
NotifyParentProperty(true),
]
public string Name { ... }
[
NotifyParentProperty(true)
]
public FontUnit Size { ... }
}
Для создания полной картины пример приводит конвертер типа
System.ComponentModel.ExpandableObjectConverter, ассоциированный с типом
FontInfo через TypeConverterAttribute, описанный в следующем разделе.
ExpandableObjectConverter указывает инспектору свойств обеспечить
разворачивание/сворачивание дерева подсвойств, который позволяет
разработчику страницы редактировать подсвойства. По умолчанию
подсвойства свойства Font свернуты в инспекторе свойств. Когда
разработчик страницы кликает свойство Font, ExpandableObjectConverter
заставляет инспектор отображать иерархический список, в котором показаны
подсвойства.
Сохранение внутреннего свойства
По умолчанию подсвойства сохраняются в тэге элемента управления,
используя дефисный синтаксис. Однако может быть обеспечен другой формат
сохранения для комплексных свойств, который представляет собой
вкладывание их в тэги элемента управления на странице. Этот способ
известен, как внутреннее сохранение свойства. Следующий пример
демонстрирует внутреннее сохранение для свойства HeaderStyle элемента
управления DataGrid, где ForeColor - подсвойство свойства HeaderStyle:
<asp:DataGrid runat="server">
<HeaderStyle ForeColor="Red"/>
</asp:DataGrid>
Чтобы обеспечить сохранение внутреннего свойства, необходимо отметить
ваш элемент управления атрибутом ParseChildren (true), который указывает
синтаксическому анализатору страницы анализировать содержимое тэгов
элемента управления, как свойства. Кроме того, должны быть применен
атрибут времени разработки PersistChildren (false), указывающий
дизайнеру сохранить внутреннее содержание, как свойство, а не как
дочерний элемент управления. В классе WebControl эти атрибуты уже
определены, как показано в следующем примере кода. То есть если ваш
элемент управления наследуется от WebControl, вам не надо переопределять
эти атрибуты.
[
ParseChildren(true),
PersistChildren(false)
]
public class WebControl : Control, ... { ... }
Более того, необходимо отметить свойство атрибутами метаданных
времени разработки, показанными в следующем примере:
[
DesignerSerializationVisibility(
DesignerSerializationVisibility.Content),
NotifyParentProperty(true),
PersistenceMode(PersistenceMode.InnerProperty)
]
public virtual TableItemStyle HeaderStyle { ... }
Первые два атрибута были описаны в этом разделе ранее. Последний
атрибут, PersistenceMode (PersistenceMode.InnerProperty), указывает
дизайнеру сохранить свойство, как внутреннее свойство. По умолчанию
дизайнер сохраняет подсвойства в тэге элемента управления, используя
дефисный синтаксис. Режим сохранения, применяемый по умолчанию, не
требует применения PersistenceModeAttribute к свойству, как вы видели
ранее в свойстве Font.
Оболочка страницы поддерживает дополнительную форму сохранения
внутреннего свойства, которая обычно используется для сохранения
коллекций элементов управления: сохранение внутреннего свойства по
умолчанию. В следующем примере показан синтаксис сохранения внутреннего
свойства по умолчанию для свойства Items элемента управления ListBox.
Каждый элемент в пределах тэгов элемента управления является элементом
коллекции свойств Items:
<asp:ListBox id=ListBox1 Width="100px" runat="server">
<asp:ListItem>Item 1</asp:ListItem>
<asp:ListItem>Item 2</asp:ListItem>
<asp:ListItem>Item 3</asp:ListItem>
</asp:ListBox>
Когда элемент управления имеет внутреннее свойство по умолчанию,
содержание в пределах тэгов элемента управления может соответствовать
только этому свойству. Синтаксический анализатор страницы не допускает
размещения других свойств в пределах тэгов элемента управления. Это
объясняет, почему свойство называется внутренним свойством по умолчанию.
Обратите внимание, что имя внутреннего свойства по умолчанию (в
предыдущем примере - Items) не определяется в пределах тэгов элемента
управления.
Чтобы обеспечить сохранение внутреннего свойства по умолчанию, вы
должны отметить ваш элемент управления следующим вариантом
ParseChildrenAttribute, в котором второй аргумент атрибута является
именем внутреннего свойства по умолчанию:
[
ParseChildren(true, "<DefaultPropertyName>")
]
public class MyControl : WebControl { ... }
Кроме того, чтобы правильно сохранить в дизайнере внутренне свойство
по умолчанию, необходимо отметить свойство, как PersistenceMode
(PersistenceMode.InnerDefaultProperty). Например, свойство Items
элемента управления ASP.NET ListControl отмечено таким образом:
[
PersistenceMode(PersistenceMode.InnerDefaultProperty)
]
public virtual ListItemCollection Items { ... }
Методы в серверных элементах управления ASP.NET
В создаваемом элементе управления методы, унаследованные от базового
класса, могут быть переопределены, а также могут быть определены новые
методы.
Переопределенные методы, унаследованные от элемента управления
В Таблица 7 приведены некоторые из обычно переопределяемых методов
Control.
Таблица 7
Метод |
Описание |
CreateChildControls |
Сигнализирует составному элементу управления,
что необходимо создать дочерние элементы управления при
подготовке к отправке данных обратно или формированию. |
Dispose |
Позволяет элементу управления выполнить
заключительную ликвидацию. |
LoadViewState |
Позволяет элементу управления настраивать
способ восстановления информации о состоянии. |
SaveViewState |
Позволяет элементу управления настроить
способ сохранения информации о состоянии для использования в
другом Web-запросе. |
Render |
Позволяет элементу управления генерировать
содержимое в браузере клиента. |
События в серверных элементах управления ASP.NET
В отличие от событий в настольных приложениях, события элемента
управления сервера ASP.NET вызываются и обрабатываются на сервере. Когда
Web-запрос передает клиентское действие на сервер, в ответ на него
элемент управления может генерировать события на сервере. Событие
обрабатывается страницей или ее дочерними элементами управления, и
ASP.NET посылает ответ назад клиенту. Однако разработчики элементов
управления должны понимать, что только один тип клиентских событий
отправляется на сервер - событие отправки данных обратно. Общие события
пользовательского интерфейса, такие как щелчки мыши или ключевые
нажатия, не сообщаются на сервер и, таким образом, не могут быть там
обработаны.
Базовый класс System.Web.UI.Control поставляет события, которые
управляют жизненным циклом выполнения элемента управления, такие как
инициализация, загрузка и выгрузка. Последовательность выполнения этих
событий представлена в разделе Жизненный цикл выполнения
элемента управления. Вы можете обработать эти события, а также
генерировать дополнительные события из вашего элемента управления.
Передача события
Оболочка страницы ASP.NET предоставляет методику, названную передачей
события, которая позволяет дочернему элементу управления распространять
события вверх по включающей его иерархии. Передача события позволяет
вызывать события из более удобного положения в иерархии элементов
управления и позволяет присоединять обработчики событий к оригинальному
элементу управления, а также к элементу управления, который
предоставляет переданное событие.
Передача события используется связанными с данными элементами
управления (Repeater, DataList и DataGrid), чтобы представить события
команды (группы элементов), вызванные дочерними элементами управления (в
пределах шаблонов элемента), как события верхнего уровня. В то время как
серверные элементы управления ASP.NET в .NET Framework используют
передачу управляющих событий (события, чей класс данных события
наследуется от CommandEventArgs), передано может быть любое событие,
определенное в элементе управления сервера.
Элемент управления может участвовать в передаче события посредством
двух методов, которые он наследует от базового класса
System.Web.UI.Control. Это методы OnBubbleEvent и RaiseBubbleEvent. В
следующем участке кода приведены сигнатуры этих методов:
protected virtual bool OnBubbleEvent(
object source,
EventArgs args
);
protected void RaiseBubbleEvent(
object source,
EventArgs args
);
Реализация RaiseBubbleEvent предоставлена Control и не может быть
переопределена. RaiseBubbleEvent посылает данные события вверх по
иерархии родителю элемента управления. Чтобы обрабатывать или вызывать
переданное событие, элемент управления должен переопределить метод
OnBubbleEvent.
Элемент управления, которому передали событие, осуществляет одну из
следующих операций:
- не делает ничего, в таком случае событие автоматически
передается его родителю;
- делает некоторую обработку события и продолжает передавать его
далее. Для этого элемент управления должен переопределить
OnBubbleEvent и вызвать RaiseBubbleEvent из OnBubbleEvent. Пример
передачи события после проверки типа параметров события приведен в
разделе MSDN
Templated Data-Bound Control Sample;
- прекращает передавать событие и вызывает и/или обрабатывает
событие. Генерирование события приводит к вызову метода, который
посылает событие приемникам. Для вызова переданного события элемент
управления должен переопределить OnBubbleEvent, чтобы вызвать метод
OnEventName, который вызывает переданное событие. Элемент
управления, вызывающий переданное событие, обычно представляет
переданное событие, как событие верхнего уровня. Пример вызова
переданного события приведен в разделе MSDN
Templated Data-Bound Control Sample.
Примеры передачи события смотрите в разделах MSDN
Templated Data-Bound Control Sample и
Event Bubbling Control Sample.
Примечание: пока метод OnBubbleEvent, который разрешает
передачу события, следует стандартному шаблону .NET Framework
присваивания имен методам, которые генерируют события, события по имени
BubbleEvent не существует. Переданное событие в элементе управления,
который останавливает передачу события, представляется, как событие
верхнего уровня. Например, элемент управления DataList предоставляет
Command события из элементов управления в его шаблоне, как события
ItemCommand. Обратите внимание также, что стандартная сигнатура методов
OnEventName в .NET Framework имеет один параметр (protected void
OnEventName (EventArgs e)). Однако OnBubbleEvent имеет два параметра,
потому что событие возникает вне элемента управления; второй параметр
определяет источник возникновения события.
Обсуждение до сих пор касалось того, как элемент управления может
ответить на событие, которое передано к нему. В следующем разделе
показано, как создать элемент управления, который определяет
передаваемое событие.
Определение передаваемого события
Чтобы элемент управления имел возможность передачи события,
определяемого им, он должен вызвать RaiseBubbleEvent из метода
OnEventName, который генерирует событие. Внутри элемента управления
дополнительно ничего делать не надо. В следующем фрагменте кода
представлен элемент управления, в котором определено событие Command,
делающее возможной передачу.
protected virtual void OnCommand(CommandEventArgs e) {
CommandEventHandler handler = (CommandEventHandler)Events[EventCommand];
if (handler != null)
handler(this,e);
// The Command event is bubbled up the control hierarchy.
RaiseBubbleEvent(this, e);
}
Механизм передачи событий не ограничивается управляющими событиями.
Его можно использовать для передачи любого события.
Содержимое и компоновка
Специальные и пользовательские элементы управления предлагают
различные возможности для содержимого и компоновки из-за различия
механизмов их разработки. Содержимое элемента управления включает, как
любой статический текст, так и любые дочерние элементы управления,
которые он содержит или формирует. Компоновка элемента управления
обеспечивает структуру, в пределах которой формируется его содержимое.
Специальные элементы управления очень хорошо приспособлены к
динамическому представлению содержимого в программно генерируемой
компоновке. Например, такие сценарии, как связанный с данными табличный
элемент управления с динамическими столбцами, элемент управления Tree с
динамическими узлами или элемент управления Tab с динамическими Tabs,
лучше всего реализовываются как специальные элементы управления. В этих
сценариях содержимое и компоновка обычно зависят от значений свойств и
логических условий, определенных этими значениями. Специальные элементы
управления обычно разрабатываются для многократного использования
приложениями и практически никогда не имеют статического текста. Вместо
этого любой текст в их представлении настраивается через строковые
свойства.
Т.к. компоновка пользовательского элемента управления объявляется во
время разработки в рамках .ascx файла, использование пользовательских
элементов управления предпочтительнее, если нужно реализовать
относительно статическое содержание и неизменную компоновку. Прекрасным
примером пользовательских элементов управления с относительно
статическим содержанием являются элементы управления, используемые как
верхний и нижний колонтитулы страниц на сайтах или в приложениях
(например, пользовательские элементы управления SiteHeader и SiteFooter)
для реализации постоянного и легко модифицируемого облика. Посредством
таких свойств, как Text или URL, можно осуществлять незначительные
изменения в пользовательском элементе управления. Пользовательский
элемент управления, реализующий динамическую компоновку, не может
использовать все возможности декларативной модели и компоновки,
доступные во время разработки, даже несмотря на то, что они являются
сущностью модели пользовательского элемента управления.
Специализированные пользовательские элементы управления хорошо подходят
для размещения статического текстового содержания - общий случай, когда
необходимо вынести многократно используемую часть специализированного
HTML содержания.
Модель выполнения страницы
На Рисунок 4. Как страница обрабатывает запросы Ошибка! Источник
ссылки не найден.показано, как оболочка страницы обрабатывает два
входящих запроса. Модель выполнения страницы начинается с HTTP фабрики
обработчиков страницы, которая регистрируется с рабочим циклом HTTP,
чтобы обрабатывать запросы для всех файлов .aspx. Фабрика обработчиков
страницы отвечает за создание экземпляра объекта Page, который является
обработчиком HTTP, который в конечном счете будет обрабатывать запрос,
чтобы сформировать ответ.
Рисунок 4. Как страница обрабатывает запросы
Первый запрос |
Второй запрос |
1. Web клиент создает HTTP запрос, который
обрабатывается рабочим циклом HTTP и направляется к HTTP фабрике
обработчиков страницы. |
7. Web клиент (тот же, что и раньше, или
любой другой) создает HTTP запрос к той же странице. |
2. HTTP фабрика обработчиков страницы делает
синтаксический разбор запрашиваемого .aspx файла и динамически
генерирует код, представляющий дерево разбора. |
8. HTTP фабрика обработчиков страницы успешно
находит тип, сохраненный в кэше ASP.NET во время предыдущего
запроса. |
3. Сгенерированный код компилируется, и
полученный класс (унаследованный от Page) сохраняется в кэше
ASP.NET. |
9. Новый экземпляр ранее кэшированного класса
Page создается и используется, как HTTP обработчик этого
входящего запроса, как в шагах 4 и 5. |
4. Экземпляр динамически сгенерированного
класса создается и используется для обработки входящего запроса,
как HTTP обработчик. |
10. Результирующий ответ отправляется Web
клиенту, создавшему этот второй запрос. |
5. Выполняется обработанная страница и
обрабатывается сгенерированное дерево элементов управления.
Отдельные элементы управления создают соответствующий HTML
выход. |
|
6. Результирующий ответ отправляется Web
клиенту, создавшему этот запрос. |
|
Фабрика обработчиков страницы сначала пытается найти в кэше ASP.NET,
ассоциированном с запрашиваемым файлом .aspx, ранее откомпилированный
класс страницы. Когда это не получается, как это происходит при первом
запросе, фабрика обработчиков прочитывает файл и производит его
синтаксический разбор для создания дерева разбора. Дерево разбора
аналогично дереву элементов управления, но вместо элементов управления,
в него входят экземпляры объектов, известные как построители элементов
управления. Построители элементов управления содержат информацию об
элементах управления, собранную во время синтаксического разбора. Затем
дерево разбора с помощью атрибута Language директивы Page конвертируется
в код на языке программирования, ассоциированном со страницей. После
этого фабрика обработчиков страницы активизирует соответствующий
компилятор - например, C# компилятор, csc.exe - чтобы динамически
откомпилировать класс, наследуемый от Page. Фабрика обработчиков также
помещает вновь созданный класс страницы в кэш ASP.NET и ассоциирует
содержимое кэша с контроллером изменений. Контрорллер отслеживает
изменения, сделанные в .aspx файле, и обеспечивает, чтобы любое
изменение автоматически сделало недействительным содержимое кэша, что
обусловливает синтаксический разбор модифицированного файла при
следующем запросе.
Фабрика обработчиков страницы обрабатывает динамически
откомпилированный класс страницы и позволяет вновь созданному экземпляру
обрабатывать входящий запрос. Важным моментом в модели программирования
страницы является то, что страницы выполняются, как полностью
откомпилированный код. Это обеспечивает значительно лучшую
производительность, чем интерпретируемый код (такой, как содержится в
ASP). Страница выполняет код, генерируемый из дерева разбора, которое
создает и обрабатывает дерево элементов управления, содержащихся в
исходном .aspx файле. Дерево элементов управления имеет собственный
жизненный цикл, включающий инициализацию, загрузку, формирование и
ликвидацию. На заключительном этапе обработки формируется страница,
чтобы создать содержимое ответа. В конце своего цикла обработки страница
полностью уничтожена. Таким образом, оболочка страницы не сохраняет
состояние или экземпляры страницы между запросами. Это делает возможной
разработку не имеющих состояния (stateless) и масштабируемых Web
приложений.
Во время любого последовательного запроса та же фабрика обработчиков
HTTP может использовать ранее откомпилированный и кэшированный класс
страницы и продолжать пользоваться его нормальной логикой обработки,
повторно обрабатывая новый экземпляр страницы. Это позволяет фабрике
обработчиков опустить всю работу по открытию, чтению и разбору файла,
генерированию кода и активизированию компилятора. Этот подход одного
разбора и компилирования (single-parse-and-compile) модели выполнения
страницы значительно повышает производительность и оперативность Web
приложения.
Жизненный цикл выполнения элемента управления
Сервер загружает страницу ASP.NET при каждом запросе и затем
выгружает ее после завершения запроса. Страница и серверные элементы
управления, которые она содержит, отвечают за выполнение запроса и
передачу HTML назад клиенту. Хотя взаимодействие между клиентом и
сервером не имеет состояния и несвязно, клиенту должно казаться, что это
непрерывно выполняющийся процесс.
Эта иллюзия непрерывности создается оболочкой страницы ASP.NET,
страницей и ее элементами управления. При отправке данных обратно
элемент управления должен вести себя так, как будто он начинает работу
там же, где он остановился в конце предыдущего Web-запроса. Оболочка
страницы ASP.NET относительно упрощает управление состоянием, но
разработчики элементов управления должны знать о последовательности
выполнения элемента управления, чтобы достигнуть эффекта непрерывности.
Разработчики элементов управления должны понимать, какие данные доступны
элементу управления на каждом этапе его жизненного цикла, какие данные
сохраняются и каково состояние элемента управления в момент, когда он
сформирован.
Как разработчик элемента управления, вы должны понимать жизненный
цикл элемента управления, чтобы знать, какую логику применять в
различных фазах. Однако, если вы новичок, вам не надо понимать всю
сложность жизненного цикла элемента управления перед началом реализации
пользовательских элементов управления. Чтобы начать, просто надо
ознакомиться с кратким обзором различных фаз жизненного цикла элемента
управления, которые показаны на Рисунке 5.Рисунок 5. Жизненный цикл
элемента управления
Рисунок 5. Жизненный цикл элемента управления
Методы, начинающиеся с On (такие как OnInit), являются частью
инфраструктуры событий вашего элемента управления и вызывают
соответствующие события (такие как Init). Некоторые из фаз реализованы
как события таким образом, чтобы разработчик страницы мог отвечать на
них, применяя обработчики событий.
- Создание
Элемент управления создается страницей или другим элементом
управления вызовом его конструктора. Фазы, перечисленные после этой
стадии, возникают, только если элемент управления добавляется к
дереву элементов управления.
- Инициализация
В этой фазе страница и все элементы управления в ее дереве элементов
управления вызывают по умолчанию метод OnInit (который вызывает
событие Init). Перед выполнением фаз своего жизненного цикла
страница строит исходное дерево элементов управления, основывающееся
на декларативном синтаксисе страницы .aspx, поэтому свойства
элемента управления, определенные в декларативном синтаксисе
страницы, присваиваются до фазы инициализации. Разработчик страницы
может обеспечить дополнительную логику инициализации страницы,
реализуя метод Page_Init, который передается оболочкой страницы
событию Init страницы. Можно обеспечить логику инициализации,
переопределяя метод OnInit элемента управления. На этом этапе своего
жизненного цикла элемент управления может безопасно доступаться к
дочерним элементам управления, представленным в его коллекции
Controls, но он не может доступаться к своему родителю или любому
другому элементу управления (такому как страница), расположенному
выше него в иерархии.
- Отслеживание View State
Эта фаза совершается в конце фазы инициализации. На этом этапе
страница автоматически вызывает метод TrackViewState элемента
управления. Метод TrackViewState обеспечивает, что по окончании этой
фазы изменения, сделанные в свойствах, которые используются словарем
ViewState, сохраняются во view state элемента управления. Оболочка
страницы вызывает TrackViewState после инициализации, чтобы повысить
производительность. Исходные значения всегда перезагружаются
оболочкой страницы, когда она восстанавливает дерево элементов
управления при отправке данных, и сохранять эти значения во view
state элемента управления неэффективно. В большинстве случаев
реализация TrackViewState, предоставленная базовым классом Control,
удовлетворяет требованиям. Переопределять TrackViewState надо только
в том случае, если элемент управления определяет комплексные
свойства.
- Загрузка View State (только при отправке данных обратно)
Эта фаза совершается при отправке данных обратно, а не во время
начального запроса. В этой фазе элемент управления должен
восстановить свое состояние таким, каким оно было в конце обработки
предыдущего запроса. Оболочка страницы на этой стадии автоматически
восстанавливает словарь ViewState. Если элемент управления не
сохраняет состояние или использует словарь ViewState для сохранения
всей информации о состоянии, не надо реализовывать логику этой фазы.
Однако, если элементу управления необходимо специальное управление
состоянием, надо переопределить метод LoadViewState, чтобы
реализовать восстановление состояния.
- Загрузка данных, отправленных обратно (только при отправке
данных обратно, необязательно)
Эта фаза имеет место только при отправке данных обратно, если
элемент управления принимает участие в обработке данных,
отправленных обратно, реализуя интерфейс IPostBackDataHandler.
Примером в данном случае является элемент управления TextBox. На
этом этапе элемент управления должен обновить свое состояние из
отправленных данных формы, реализуя метод LoadPostData интерфейса
IPostBackDataHandler.
- Загрузка
К началу этого этапа все элементы управления в дереве элементов
управления инициализированы и восстановлены к тому состоянию,
которое они имели в конце предыдущего цикла. Кроме того, элементы
управления, принимающие участие в отправке данных, заполнены
возвращенными данными формы. В этой точке жизненного цикла (но не
ранее) элемент управления может безопасно доступаться к другим
элементам управления страницы. На этом этапе надо реализовать общую
для каждого запроса логику, переопределяя метод OnLoad. Если надо
реализовать логику, которая выполняется только во время начального
запроса к странице, надо проверить свойство страницы IsPostBack (if
(Page.IsPostBack == false) {...}). Страница и все элементы
управления ее дерева элементов управления по умолчанию на этом этапе
вызывают событие Load.
- Вызов Changed событий (только при отправке данных обратно,
необязательно)
Эта фаза имеет место только при отправке данных, если элемент
управления участвует в обработке данных, отправленных обратно,
реализуя интерфейс IPostBackDataHandler. На этом этапе элемент
управления вызывает события (такие как событие TextChanged элемента
управления TextBox), чтобы показать, что в результате отправки
данных обратно его состояние изменилось. Чтобы участвовать в этой
фазе, элемент управления должен реализовать метод
RaisePostDataChangedEvent интерфейса IPostBackDataHandler.
- Вызов Postback событий (только при отправке данных обратно,
необязательно)
Эта фаза имеет место только при отправке данных, если элемент
управления участвует в обработке данных, отправленных обратно,
реализуя интерфейс IPostBackDataHandler. На этом этапе, реализуя
метод RaisePostBackEvent интерфейса IPostBackEventHandler, можено
реализовать логику приведения события клиента к событию со стороны
сервера. Примером этого является элемент управления Button, который
вызывает событие Click на сервере, чтобы предоставить возможность
разработчику страницы обрабатывать событие клиента.
- Перед формированием
На этом этапе, переопределяя метод OnPreRender, надо реализовать
любую работу, которую элемент управления должен сделать до того, как
он сформирован. На этом этапе страница и все элементы управления ее
дерева элементов управления вызывают событие PreRender.
- Сохранение View State
Если элемент управления не управляет состоянием или использует
словарь ViewState для сохранения всей информации о своем состоянии,
на этом этапе не надо реализовывать дополнительную логику. На этом
этапе оболочка страницы автоматически сохраняет словарь ViewState.
Если элемент управления нуждается в специальном управления
состоянием, необходимо переопределить метод SaveViewState, чтобы
реализовать специальное восстановление состояния. Этот метод
вызывается только для элементов управления, чье свойство
EnableViewState (и рекурсивно, свойства его родителя) имеет значение
true. Любые изменения, сделанные в элементе управления после этой
фазы, не будут сохранены в его view state.
- Формирование
В этом методе элемент управления, переопределяя метод Render класса
Control или один из формирующих методов класса WebControl,
записывает размеченный текст в выходной поток.
- Выгрузка
Страница проводит очистку, реализуя метод Page_Unload. Вместо этого
для проведения очистки вы должны переопределить метод Dispose.
Страница и все элементы управления в ее дереве элементов управления
на этом этапе по умолчанию вызывают событие Unload.
- Ликвидация
На этом этапе надо переопределить метод Dispose, чтобы освободить
все ресурсы, занятые элементом управления.
Страница является обработчиком HTTP и отвечает за обработку HTTP
запроса к файлу .aspx. Когда страница начинает обрабатывать запрос, она
создает дерево элементов управления. Затем рекурсивно вызывая методы,
соответствующие каждой фазе, страница последовательно выполняет этапы,
показанные на Рисунке 5. Обратите внимание, что страница - это элемент
управления (наследуемый от Control), и, как и любой другой элемент
управления, она наследует методы и события, показанные на Рисунке 5.
Т.к. некоторые методы являются protected, вам должно быть интересно, как
может страница вызывать их на своих дочерних элементах управления. Это
возможно потому, что каждый элемент управления наследует - от класса
Control - некоторые внутренние (доступные сборке) методы оболочки,
которые рекурсивно вызывают protected методы.
Примечание: Описанная здесь последовательность выполнения
применима к элементам управления, созданным на странице декларативно. А
что, если элемент управления создается в обработчике события и
динамически добавляется к дереву элементов управления? В этом случае
элемент управления "нагоняет" пройденные фазы. Как только он добавлен в
дерево элементов управления, он начинает выполнять все этапы жизненного
цикла до тех пор, пока не дойдет до того этапа, на котором находится
страница в данный момент.
Клиентские функциональные возможности в серверном элементе
управления
В программировании Web, клиентские функциональные возможности -
традиционно реализовываются разработчиком Web-страницы и не
инкапсулируются в компонентах сервера. ASP.NET отступает от этого
принципа и дает возможность элементам управления сервера выпускать
клиентский скрипт, который позволяет им объединять обработку стороны
клиента с серверной обработкой.
Самым простым примером клиентских функциональных возможностей может
быть то, когда серверный элемент управления Web генерирует обработчик
событий для клиентского события через свойство Attributes элемента
управления. Разработчик страницы может легко предоставить клиентские
обработчики событий, используя коллекцию Attributes серверного элемента
управления Web.
ASP.NET позволяет элементу управления принимать участие в различных
клиентских сценариях:
- предоставление библиотеки клиентского скрипта;
- обеспечение того, что блок скрипта появляется на странице только
однажды (даже если на странице множество экземпляров элемента
управления);
- предоставление элементу управления возможности связывать
обработчик событий с событиями на стороне клиента(если страница
включает форму);
- добавление клиентского элемента, приведенного элементом
управления к переменной типа массив, объявленной у клиента.
Эти сценарии разрешаются через методы, предоставленные классом
System.Web.UI.Page, и доступны для серверного элемента управления
ASP.NET через свойство Page. В Таблица 8. Методы класса Page перечислены
методы класса Page, которые предоставляют клиентские функциональные
возможности.
Таблица 8. Методы класса Page
Метод |
Описание |
Применение |
RegisterClientScriptBlock |
Позволяет элементу управления выпускать блок
скрипта (который содержит встроенный скрипт или определяет
местоположение файла скрипта). Блок скрипта генерируется наверху
страницы и регистрируется (с помощью ключа) со страницей так,
чтобы блок скрипта выпускался только однажды, даже если на
странице множество экземпляров элемента управления.
Примечание: блок скрипта, выпускаемый наверху страницы,
называют клиентским блоком скрипта на странице ASP.NET. |
Используйте, когда хотите включить библиотеку
скрипта или выполнить блок скрипта, который содержит общие
функции (вызваемые на странице позже). В частности, функции,
которые вызываются на стадии выполнения, должны быть наверху
страницы. |
RegisterStartupScript
|
Позволяет элементу управления выпустить блок
скрипта (который содержит встроенный скрипт или определяет
местоположение файла скрипта). Блок скрипта выполнен внизу
страницы и зарегистрирован (с помощью ключа) со страницей так,
чтобы блок скрипта выпускался только однажды, даже если на
странице множество экземпляров элемента управления.
Примечание: блок скрипта, выпускаемый внизу страницы,
называют блоком скрипта запуска на странице ASP.NET. |
Используйте, когда хотите выпустить скрипт,
который вызывает элементы на странице или скрипт, который должен
выполняться при запуске. Поскольку этот скрипт выспускается
внизу страницы, гарантировано, что элементы, на которые он
ссылается, будут существовать прежде, чем выполняется скрипт.
|
RegisterArrayDeclaration |
Позволяет элементу управления добавить себя к
клиентской переменной типа массив с указанным именем. Имя
массива зарегистрировано с содержащей его страницей (с помощью
ключа) так, чтобы на странице в клиентском скрипте формировался
только один массив с таким именем. |
Используйте, когда хотите, чтобы элемент,
сформированный элементом управления, принадлежал переменной типа
массив в клиентском скрипте. Массив позволяет группировать
элементы одного типа для легкого доступа клиентским скриптом.
Например, элементы управления проверки правильности
самостоятельно добавляются к массиву
Page_Validators. |
RegisterOnSubmitStatement |
Связывает обработчик событий с submit
событием клиентской стороны и регистрирует его (с помощью ключа)
со страницей, содержащей его. Обработчик генерируется, как
атрибут onSubmit элемента формы. Регистрация обработчика
гарантирует, что множество экземпляров элемента управления не
выпустят множество обработчиков. |
Используйте, когда хотите, чтобы клиентский
обработчик был вызван при представлении формы.
Примечание: встроенный скрипт можно передать
непосредственно, как параметр к этому методу или передать вызов
обработчику событий. Если Вы передаете вызов обработчику
событий, обработчик должен быть определен независимо в блоке
скрипта или в библиотеке скрипта. |
IsClientScriptBlockRegistered |
Определяет, зарегистрирован ли клиентский
блок скрипта с указанным ключом со страницей, содержащей его. |
Используйте возвращаемое значение этого
метода, чтобы определить, должен ли быть вызван
Page.RegisterClientScriptBlock. |
IsStartupScriptRegistered |
Определяет, зарегистрирован ли блок скрипта
запуска с указанным ключом со страницей, содержащей его. |
Используйте возвращаемое значение этого
метода, чтобы определить, должен ли быть вызван
Page.RegisterStartupScript. |
RegisterHiddenField |
Позволяет элементу управления сервера
регистрировать скрытое поле на содержащей его странице, которое
выпускается, когда страница выполняется. К этому полю может
обращаться клиентский скрипт; когда форма отправляется на
сервер, скрытое поле доступно серверу как данные, отправленные
обратно. |
Используйте, когда элемент управления сервера
должен послать скрытую переменную, к которой можно обратиться в
клиентском скрипте. (Это полезно, когда переменная не может быть
выполнена, как атрибут элемента управления, или когда при
обработке стороны клиента требуется переменная, независимая от
элемента управления). |
Элементы управления проверки правильности - единственные серверные
элементы управления ASP.NET, которые полностью используют клиентские
возможности, предоставленные ASP.NET. Поскольку объектная модель
документа, поддерживаемая различными браузерам, не последовательна,
большинство элементов управления сервера, которые поставляются с .NET
Framework SDK, минимально используют клиентский скрипт и в основном для
автоотправки данных обратно. Однако, если приложение предназначено для
специфичного браузера или целевых браузеров, которые поддерживают
специфичную объектную модель документа, можно использовать клиентский
скрипт в ваших элементах управления, чтобы предоставить более богатую
клиентскую часть и по возможности ограничить циклы обработки сервером.
Доступ к элементу управления в клиентском скрипте
Основной класс Control предоставляет свойство ClientID, которое он
генерирует (в HTML) как атрибут ID генерируемого элемента. ASP.NET
генерирует ClientID для элемента управления динамически, и уникальность
ClientID каждого элемента управления на странице гарантируется. Элемент
управления (то есть элемент, сгенерированный элементом управления)
может, таким образом, вызваться у клиента, использующего его ID в
объектной модели документа. Элемент управления может также использовать
ClientID, чтобы генерировать уникальные имена для любых дополнительных
элементов, которые он мог бы генерировать (такие как скрытые поля).
Помещение значения ClientID во встроенный скрипт (или в код в
библиотеке скрипта) может быть обманным, потому что ClientID должен быть
вставлен в строковую переменную в нужном месте. Чтобы вставить ClientID
в строку, которая составляет встроенный скрипт, в следующем примере
используются escape-символы.
string element = "document.getElementById(\"" + ClientID + "\")";
Page.RegisterArrayDeclaration("Page_Validators", element);
Можно также использовать преопределенные методы Format класса String,
чтобы составить клиентский скрипт, который использует ClientID. Для
получения дополнительной информации о форматировании, см. Форматирование
типов.
Развертывание файла скрипта
Блок скрипта, выделяемый поддерживаемым скриптом (script-enabled)
элементом управления, может содержать встроенный скрипт или может
предоставить местоположение файла скрипта. Если блок предоставляет
местоположение файла скрипта, надо развернуть файл скрипта таким
образом, чтобы он мог использоваться из других приложений и не создавать
конфликты версий. В примере файл скрипта помещен в подкаталог script в
виртуальном внедренном каталоге Web-приложения.
// Provides the location of the script file.
location = Page.Request.ApplicationPath + "/script/";
Данное местоположение приведено для примера. Рекомендованное
местоположение файла скрипта, который предназначен для использования
другими приложениями, следующее:
/aspnet_client/<your assembly name>/<your assembly version>/
Каталог aspnet_client - виртуально внедренный каталог Web-приложения,
который создается на вашем компьютере при установке .NET Framework SDK
или Visual Studio .NET. Например, файлы скриптов, которые поставляются с
ASP.NET, находятся по адресу:
/aspnet_client/system_web/<version of SDK installed>
Если у вас установлено множество версий SDK, Вы будете видеть
множество подкаталогов под aspnet_client/system_web. Поскольку
библиотека элемента управления привязана к определенной версии файла
скрипта, рекомендованный образец развертывания позволяет выполнять
различные версии библиотеки элемента управления одновременно.
Обратите внимание также, что один каталог aspnet_client создан для
каждого Web-сайта, размещенного на компьютере. Обычно сервер размещает
только один Web-сайт. Однако допустимо существование более, чем одного
сайта, и наличие множества сайтов приводит к множеству копий каталога
aspnet_client.
Проверка необходимости выполнять скрипт
Поддерживаемый скриптом (script-enabled) элемент управления должен
предоставить своим потребителям выбор переключить исполнение клиентского
скрипта. Серверные элементы управления Web для проверки правильности в
ASP.NET предоставляют свойство EnableClientScript, котороей определяет,
генерирует ли элемент управления скрипт клиенту. Свойство, которое
предоставляет эти функциональные возможности, можно определить следующим
образом:
public bool EnableClientScript {
get {
object o = ViewState["EnableClientScript"];
return((o == null) ? true : (bool)o);
}
set {
ViewState["EnableClientScript"] = value;
}
}
Перед исполнением, поддерживаемый скриптом элемент управления должен
проверить клиентские возможности и также проверить, выключил ли
пользователь (разработчик страницы) создание скрипта. Эти проверки
обычно осуществляются на стадиях предвыполнения и формирования.
Обработка данных, отправленных обратно
Элемент управления, чтобы иметь возможность проверять данные формы,
которые посланы назад клиентом, должен реализовать интерфейс
System.Web.UI.IPostBackDataHandler. Контракт этого интерфейса позволяет
элементу управления определять, должно ли его состояние быть изменено в
результате отправки данных обратно, и генерировать соответствующие
события. IPostBackDataHandler интерфейс содержит два метода:
public interface IPostBackDataHandler{
public bool LoadPostData(string postDataKey,
NameValueCollection postCollection);
public void RaisePostDataChangedEvent();
}
При отправке данных обратно, оболочка страницы проверяет содержимое
отправленного на наличие значений, которые соответствуют уникальным ID
(UniqueIDs) элементов управления сервера, реализующих
IPostBackDataHandler. Затем LoadPostData последовательно вызывается на
каждый элемент управления, который реализует этот интерфейс.
LoadPostData имеет два параметра: ключ, который идентифицирует элемент
управления, и коллекцию, NameValueCollection, которая содержит
отправленные данные. LoadPostData обычно реализуется, чтобы обновить
состояние элемента управления в результате отправки данных обратно.
Следующий пример показывает реализацию LoadPostData для специального
текстового поля.
public virtual bool LoadPostData(string postDataKey, NameValueCollection postCollection) {
string presentValue = Text;
string postedValue = postCollection[postDataKey];
if (!presentValue.Equals(postedValue)){
Text = postedValue;
return true;
}
return false;
}
Если состояние элемента управления изменено в результате отправки
данных обратно, LoadPostData возвращает значение true; в противном
случае возвращается false. Оболочка страницы отслеживает все элементы
управления, которые возвращают true, и вызывает
RaisePostDataChangedEvent в них. События Change, если таковые имеются,
вызываются из этого метода. Таким образом, процесс отправки данных
обратно происходит в два этапа - обновление состояния и вызов
регистрации изменений. Это препятствует регистрации изменений во время
загрузки данных, отправленных обратно, т.к. это может неправильно
изменить состояние прежде, чем все элементы управления смогут загрузить
данные, отправленные обратно. В следующем фрагменте кода представлена
реализация RaisePostDataChanged для специального текстового поля.
public virtual void RaisePostDataChangedEvent() {
OnTextChanged(EventArgs.Empty);
}
Существенно, что логика исполнения присваивает UniqueID атрибуту
имени элемента управления. В противном случае оболочка страницы будет
неспособна направить данные, отправленные обратно, к элементу
управления. Если элемент управления содержит множество элементов формы,
по крайней мере один из элементов должен иметь атрибут имени, который
соответствует UniqueID элемента управления. Пример пользовательского
элемента управления, который содержит множественные поля формы, приведен
в разделе MSDN
Composition vs. Rendering. В следующем фрагменте кода UniqueID
присваивается атрибуту имени:
protected override void Render(HtmlTextWriter output) {
output.Write("<INPUT type= text name = "+this.UniqueID +
"value = " + this.Text + " >");
}
Пример специального текстового поля, который участвует в процессе
отправки данных обратно, приведен в разделе MSDN
Postback Data Processing Sample.
Фиксация событий при отправке данных обратно
Чтобы фиксировать событие при отправке данных обратно, элемент
управления должен реализовать интерфейс
System.Web.UI.IPostBackEventHandler. Контракт этого интерфейса позволяет
элементу управления генерировать события на сервере в ответ на отправку
данных от клиента. Интерфейс IPostBackEventHandler содержит один метод.
public interface IPostBackEventHandler{
void RaisePostBackEvent(string eventArgument);
}
При отправке данных обратно, оболочка страницы проверяет посланное
содержимое и определяет, соответствует ли зарегистрированное имя
уникальному UniqueID элемента управления сервера, который реализует
IPostBackEventHandler. Если это так, она вызывает метод
RaisePostBackEvent в этом элемент управления (после вызова события
изменения).
В следующем фрагменте кода приведена реализация RaisePostBackEvent,
которое вызывает событие Click на сервере.
public void RaisePostBackEvent(String eventArgument){
OnClick(EventArgs.Empty);
}
Существенно, что логика исполнения назначает UniqueID атрибуту имени
элемента управления, как в следующем примере. Оболочка страницы
неспособна направить событие при отправке данных обратно к вашему
элементу управления, если атрибут его имени у клиента не соответствует
его UniqueID.
protected override void Render(HtmlTextWriter output) {
output.Write("<INPUT TYPE = submit name = " + this.UniqueID +
" Value = 'Click Me' />");
}
Пример элемента управления, который получает уведомления об отправке
данных обратно и генерирует события на сервере, смотрите в разделе MSDN
Postback Event Sample.
Создание клиентского скрипта для отправки данных обратно
Только два элемента HTML формы (Button и ImageButton) вызывают
формирование события отправки данных обратно. Если пользовательский
элемент управления генерирует элемент HTML, который не вызывает отправку
данных обратно, а Вы хотите, чтобы элемент управления инициализировал
отправку данных, можно программировать это в ASP.NET через архитектуру
события, которое основывается на клиентском скрипте (JScript,
JavaScript).
Чтобы реализовать механизм отправки данных обратно, разработчики
должны сделать несколько небольших изменений в своих элементах
управления. Следующий фрагмент показывает код, который необходимо
добавить к методу Render элемента управления, чтобы инициализировать
отправку данных обратно.
protected override void Render(HtmlTextWriter output) {
output.Write("<a id =\"" + this.UniqueID + "\" href =\"javascript:"
+ Page.GetPostBackEventReference(this) +"\">");
output.Write(" " + this.UniqueID + "</a>");
}
Метод GetPostBackEventReference формирует клиентский скрипт, который
инциирует отправку данных обратно и предоставляет ссылку на элемент
управления, инициализировавший событие отправки данных обратно. Метод
клиентского скрипта используется, чтобы установить скрытые поля и
заставляет форму быть отправленной серверу. Полностью пример смотрите в
разделе MSDN
Postback Using Client-Side Script Sample.
Формирование серверного элемента управления ASP.NET
Формирование относится к процессу создания визуального представления
на экране. В случае Web-запросов, фактическое формирование
осуществляется Web-браузером клиента или другим устройством просмотра.
Задача среды выполнения ASP.NET состоит в том, чтобы послать HTML (или
текст на другом языке разметки, таком как XML или WML) в ответ на
Web-запрос. Задача страницы (и ее дочерних элементов управления) состоит
в выводе содержания разметки выходному потоку.
Формирующие методы класса Control
Базовый класс Control определяет три метода для формирования, они
приведены в Таблица 9. Rendering методы класса Control. Все три метода
принимают экземпляр класса HtmlTextWriter, как единственный параметр.
Этот объект инкапсулирует ответный поток HTTP, в котором элемент
управления будет формировать свои выходные данные.
Таблица 9. Rendering методы класса Control
Метод |
Описание |
protected virtual void Render(HtmlTextWriter
writer) |
Дает возможность элементу управления
сформироваться с помощью написания текста разметки. Чтобы
сформировать содержимое, этот метод необходимо переопределять,
если ваш элемент управления наследуется прямо от класса Control.
|
protected virtual void
RenderChildren(HtmlTextWriter writer)
|
Формирует дочерний элемент управления в
поток, представленный объектом HtmlTextWriter. По умолчанию
дочерние элементы управления формируются в том порядке, в каком
они добавляются в коллекцию Controls элемента управления.
Переопределите этот метод только в том случае, если хотите
заменить логику, используемую по умолчанию. Например, можно
переопределить этот метод, если хотите вставить текст между
дочерними элементами управления. |
public void RenderControl(HtmlTextWriter
writer) |
Формирует элемент управления. Метод Render
является protected, тогда как RenderControl - public и позволяет
классу, использующему элемент управления формировать его.
Например, когда страница формирует дерево элементов управления,
RenderControl вызывается на каждый дочерний элемент управления.
Дизайнер элемента управления также вызывает RenderControl, чтобы
сформировать элемент управления на видимой области разработки.
Как разработчик элемента управления, вы вызовете метод
RenderControl тогда, когда захотите сформировать дочерний
элемент управления. Внутри, RenderControl вызывает Render, когда
значение свойства Visible элемента управления true. |
Класс System.Web.UI.HtmlTextWriter инкапсулирует выходной поток для
того, чтобы сформировать содержание разметки. В самом простом случае,
автор элемента управления может переопределить Render, чтобы передать
HTML (или другое содержание разметки), как строковый параметр к методу
Write экземпляра HtmlTextWriter.
protected override void Render(HtmlTextWriter output) {
output.Write ("<h3> Hello </h3>");
}
HtmlTextWriter поставляет много утилитных методов, которые упрощают
запись HTML. Вы должны использовать эти методы вместо того, чтобы
непосредственно передавать текстовые строки Write, потому что они делают
код более читабельным и пригодным к многократному использованию (и не
требуют, чтобы разработчик был опытным в подробностях синтаксиса HTML).
HtmlTextWriter также поставляет автоматические преобразования между
различными версиями HTML для генерирования низкого или высокого уровня.
Более эффективно делать множественные вызовы к HtmlTextWriter.Write,
чем объединять строки и передавать отдельный строковый параметр методу
Write.
Примечание: Для простоты, в нескольких примерах документации
строки текста передаются непосредственно к HtmlTextWriter.Write. Однако
в своих элементах управления Вы должны использовать сервисные методы
HtmlTextWriter.
Основная реализация метода Render класса Control всегда вызывает
RenderChildren. Если Вы не хотите, чтобы были сгенерированы дочерние
элементы управления, переопределите RenderChildren, как показано в
следующем фрагменте кода:
protected override void RenderChildren(HtmlTextWriter writer) {
// Do nothing so that child controls are not rendered.
}
Формирующие методы класса WebControl
Класс WebControl предоставляет методы, позволяющие модифицировать
тэги, формируемые элементом управления Web, добавлять атрибуты в
сформированные тэги и писать содержимое в сформированных тэгах. В
Таблица 10. Rendering методы класса WebControl представлен список
методов WebControl, которые обеспечивают функциональные возможности
формирования. Так же, как и формирующие методы Control, эти методы имеют
только один параметр - экземпляр класса HtmlTextWriter.
Таблица 10. Rendering методы класса WebControl
Метод |
Описание |
protected virtual void
AddAttributesToRender(HtmlTextWriter writer) |
Обеспечивает возможность элементу управления
Web определять пары имя/значение, которые формируются, как
атрибуты тэга элемента HTML. При переопределении
AddAttributesToRender вы должны вызывать соответствующий метод
базового класса; в противном случае, элемент управления потеряет
важные функциональные возможности. Базовый класс WebControl
формирует многие из свойств элемента управления, как атрибуты
HTML. Например, WebControl формирует свойство ClientID, как HTML
атрибут ID. WebControl также формирует любые свойства стиля,
которые установлены в элементе управления, как CSS значения в
атрибуте стиля HTML. |
public virtual void
RenderBeginTag(HtmlTextWriter writer) |
Обеспечивает возможность элементу управления
Web писать начальный тэг в выходной поток. Вы должны
переопределить этот метод, если хотите обеспечить различную
реализацию начального тэга, например, если хотите генерировать
множественный начальный тэг, такой как <table><tr><td>. Если
элемент управления генерирует одиночный тэг, и вы хотите
переопределить генерируемый WebControl тэг <span>, применяемый
по умолчанию, вместо свойства RenderBeginTag вы должны
переопределить или свойство TagKey, или TagName. |
protected virtual void
RenderContents(HtmlTextWriter writer)
|
Обеспечивает возможность элементу управления
Web формировать содержимое в тэгах элемента управления. По
умолчанию этот метод формирует дерево дочерних элементов
управления. Переопределите этот метод, если хотите писать текст
(внутренний HTML) или любое другое содержимое в тэги вашего
элемента управления. Убедитесь, что активизировали
соответствующий метод базового класса, если хотите использовать
логику, применяемую по умолчанию, для формирования дочерних
элементов управления. |
public virtual void
RenderEndTag(HtmlTextWriter writer) |
Обеспечивает возможность элементу управления
Web писать заключительный тэг в выходной поток. Вам нужно
переопределить этот метод только для того, чтобы обеспечить
согласованный заключительный тэг для тэга, созданного методом
RenderBeginTag (если вы переопределили этот метод в своем
элементе управления). Например, если вы формируете множественные
начальные тэги (<table><tr><td>) в RenderBeginTag, вы должны
формировать заключительные тэги <table><tr><td> в RenderEndTag. |
Чтобы реализовать формирование в классе, наследуемом от WebControl,
необходимо переопределить один или более методов, представленных в
Таблица 10. Rendering методы класса WebControl, вместо того, чтобы
переопределять метод Render, как вы бы делали, если бы наследовались от
Control. Не переопределять метод Render важно потому, что класс
WebControl поставляет возможности формирования тэгов, которые будут
потеряны вашим элементом управления Web в случае переопределения метода
Render. Наилучший способ понять это - изучить реализацию некоторых
формирующих методов в WebControl. WebControl реализует Render, как
показано в следующем примере кода:
protected override void Render(HtmlTextWriter writer) {
RenderBeginTag(writer);
RenderContents(writer);
RenderEndTag(writer);
}
WebControl реализует RenderBeginTag следующим образом:
public virtual void RenderBeginTag(HtmlTextWriter writer) {
AddAttributesToRender(writer);
HtmlTextWriterTag tagKey = TagKey;
if (tagKey != HtmlTextWriterTag.Unknown) {
writer.RenderBeginTag(tagKey);
}
else {
writer.RenderBeginTag(this.TagName);
}
}
И наконец, WebControl реализует RenderContents следующим образом:
protected virtual void RenderContents(HtmlTextWriter writer) {
// This invokes the Render method of the Control class,
// which in turn invokes RenderChildren to render
// child controls.
base.Render(writer);
}
Следующий пример показывает, как элемент управления Button может
записать свои атрибуты в выходной поток. Обратите внимание на вызов
base.AddAttributesToRender, который гарантирует, что атрибуты,
сгенерированные базовым классом, сохраняются.
protected override void AddAttributesToRender(HtmlTextWriter writer) {
writer.AddAttribute(HtmlTextWriterAttribute.Type, "submit");
writer.AddAttribute(HtmlTextWriterAttribute.Name, UniqueID);
writer.AddAttribute(HtmlTextWriterAttribute.Value, Text);
base.AddAttributesToRender(writer);
}
Формирование страницы
Страница является обработчиком HTTP, который отвечает за вывод в
выходной поток. Однако страница - это еще и элемент управления (класс
Page косвенно наследуется от Control) и обладает теми же возможностями
формирования, что и другие элементы управления. Это дает возможность
странице формировать себя и свои дочерние элементы, используя
формирующие методы, описанные в Таблица 9. Rendering методы класса
Control, рекурсивно.
Каждая страница имеет дерево элементов управления, которое
представляет дочерние элементы управления, содержащиеся в странице.
Страница является корнем дерева элементов управления. Чтобы сформировать
дерево элементов управления, страница создает экземпляр класса
HtmlTextWriter, который инкапсулирует ответный поток и передает объект
HtmlTextWriter в метод RenderControl. Метод RenderControl проверяет
значение свойства Visible. Если оно true, RenderControl вызывает метод
Render. Реализация метода Render, применяемая по умолчанию, вызывает
метод RenderChildren, который по умолчанию вызывает метод RenderControl
на каждый дочерний элемент управления. Это приводит к рекурсивному
формированию дерева элементов управления. Каждый элемент управления в
дереве сформирован до тех пор, пока его свойству Visible не будет
присвоено значение false. Если значение свойства Visible элемента
управления false, этот элемент управления и его дети не входят в логику
формирования. Public метод RenderControl необходим в дополнение к методу
Render, потому что метод Render - protected.
Логика формирования страницы лучше всего понимается при проверке
обращения к RenderControl в классе Page и реализации методов
RenderControl, Render и RenderChildren класса Control.
Вызов метода RenderControl в классе Page осуществляется следующим
образом:
RenderControl(new HtmlTextWriter(Response.Output));
Класс Control реализует методы RenderControl, Render, и
RenderChildren следующим образом:
public void RenderControl(HtmlTextWriter writer) {
if (Visible) {
Render(writer);
}
}
protected virtual void Render(HtmlTextWriter writer) {
RenderChildren(writer);
}
protected virtual void RenderChildren(HtmlTextWriter writer) {
foreach (Control c in Controls) {
c.RenderControl(writer);
}
}
Как показано в примере кода, формирующие методы класса Control
настраивают формирующую архитектуру, но не записывают символы в выходной
поток. Содержимое записывается в поток, потому что элементы управления в
дереве элементов управления страницы наследуются от классов (таких как
Button, Label и TextBox), которые переопределяют метод Render (или
формирующие методы класса WebControl), чтобы писать такое содержимое,
как тэги и текст.
Если вы переопределяете методы Render или RenderChildren, необходимо
вызвать соответствующий метод базового класса, чтобы гарантировать, что
дочерний элемент управления вашего элемента управления рекурсивно
сформирован (если только для формирования дочерних элементов управления
не используется логика, предоставляемая по умолчанию). Очень важен
порядок вызова, необходимо вызывать методы базового класса именно там,
где хотите сформировать дочерние элементы управления.
Поддержание состояния в элементе управления
Серверный элемент управления ASP.NET наследует свойство ViewState,
которое позволяет ему свободно участвовать в управлении состоянием, от
Control. Тип ViewState - System.Web.UI.StateBag, который является
колекцией, в которой сохраняются пары имя/значение. Оболочкой страницы
ASP.NET ViewState сохраняется в строковой переменной и пересылается
клиенту и назад, как скрытая переменная. При отправке данных обратно,
оболочка страницы анализирует входную строку из скрытой переменной и
заполняет свойство ViewState каждого элемента управления. Если элемент
управления вместо private поля использует ViewState для данных свойства,
то свойство автоматически будет сохраняться через циклы обработки. (Если
свойство не сохранено во ViewState, хорошей практикой является
возвращать его значение по умолчанию при отправке данных обратно)
В следующем фрагменте кода продемонстрировано свойство, сохраненное
во ViewState:
public String Text {
get {
return (String) ViewState["Text"];
}
set {
ViewState["Text"] = value;
}
}
ViewState обычно используется для того, чтобы сохранить данные формы
на странице сквозь циклы обработки. Не используйте ViewState для
размещения такой информации, как пароли, строки подключения (связи) и
пути к файлу.
Типы, которые могут быть сохранены во ViewState
Во ViewState могут быть сохранены сериализуемые (упорядочиваемые)
типы или типы, которые имеют определенный для них TypeConverter. Однако
сериализуемые типы медленнее и генерируют намного больший ViewState чем
те, у которых есть TypeConverter. ViewState сериализовывается с
использованием ограниченного формата сериализации объекта, который
оптимизирован для примитивных типов и для типов String, ArrayList и
HashTable.
ViewState и производительность
Разработчики элемента управления должны знать, что любые данные во
ViewState автоматически передаются к клиенту и обратно. Поскольку эти
процессы снижают производительность, важно разумно использовать
ViewState. Если существует несколько свойств, зависящих от общих данных,
можно оптимизировать производительность, сохраняя во ViewState только
ключевые элементы. Элемент управления наследует свойство EnableViewState
от Control, что позволяет пользователям элемента управления активировать
или дезактивировать постоянство его ViewState.
Настройка восстановления состояния с помощью ViewState
Для повышения производительности или сохранения пользовательского
типа, который не может быть сохранен по умолчанию во ViewState, элемент
управления может настроить способ сохранения данных свойства во
ViewState. Если элемент управления настраивает хранение данных свойства,
он должен предоставить пользовательскую реализацию восстановления
значения свойства из данных, сохраненных во ViewState. Базовый класс
Control поставляет для этой цели два метода: SaveViewState и
LoadViewState. Эти методы имеют следующие сигнатуры:
protected virtual object SaveViewState();
protected virtual void LoadViewState(object savedState);
Примеры переопределения SaveViewState и LoadViewState представлены в
разделе MSDN
Templated Data-Bound Control Sample.
Стили в серверных элементах управления
Стили определяют визуальный вид элемента управления. Разрабатывая
элемент управления, поддерживающий стили, необходимо наследоваться от
System.Web.UI.WebControls.WebControl, который поставляет стили, как
свойства со строгим контролем типов (такие как Font, Height, Width и так
далее) и предоставляет методы для работы со свойствами стиля.
Класс WebControl поставляет две технологии для поддержки стилей:
- предоставление свойств стиля со строгим контролем типов типа
System.Web.UI.WebControl.Style или типов, наследуемых от
System.Web.UI.WebControl.Style.
- создание стилей, как атрибутов HTML, добавлением пар
имя/значение к коллекции WebControl.Style. Разработчики элементов
управления не должны использовать эту методику. Она заключается в
том, что пользователи элемента управления могут добавить стили,
которые элемент управления не предоставляет, как свойства со строгим
контролем типов. Пользователь элемента управления может установить
стили, используя этот способ, и программно, и декларативно.
Предоставление свойств стиля со строгим контролем типов
Серверный элемент управления Web (элемент управления сервера, который
наследуется от WebControl), наследует свойство WebControl.ControlStyle,
которое определяет типизированный стиль для элемента управления в целом.
Типом ControlStyle является Style, который является классом,
инкапсулирующим связанные со стилем функциональные возможности, как
описано ниже.
- Style имеет свойства, такие как Font, Height, Width, ForeColor,
BackColor и другие, которые соответствуют стилям каскадной таблицы
стилей. WebControl раскрывает эти свойства стиля, как свойства
верхнего уровня, делегируя к подсвойствам свойства ControlStyle.
- Style предоставляет метод (Style.AddAttributesToRender) для
того, чтобы генерировать стили, как атрибуты объекта HtmlTextWriter.
Это позволяет WebControl делегировать генерирование стиля к свойству
ControlStyle.
Примечание: при переопределении метода Render необходимо
гарантировать, что основные функциональные возможности
генерирования, предоставленные WebControl, сохранятся. Например,
если метод Render переопределяется, чтобы непосредственно вызвать
метод Write на HtmlTextWriter экземпляре, теряются функциональные
возможности генерирования стиля, встроенные в WebControl. Пример
рекомендованной методики переопределения Render смотрите в разделе
MSDN
Rendering a Server Control Samples.
- Style предоставляет методы для копирования (Style.CopyFrom) и
объединения (Style.MergeWith) стилей. Пример использования этих
методов смотрите в разделе MSDN
Templated Data-Bound Control Sample.
- Style реализует интерфейс IStateManager, который поставляет
функциональные возможности управления состоянием, таким образом
позволяя элементу управления передавать управление состоянием
свойствам стиля. Как часть контракта IStateManager, Style реализует
такие методы, как LoadViewState, SaveViewState и TrackViewState.
Style также имеет перегруженный конструктор, который использует тип
StateBag, как аргумент. WebControl создает ControlStyle, передавая
ViewState конструктору Style, таким образом давая возможность стилю
своего элемента управления обращаться к режиму отображения. Пример
того, как передавать настроенное управление состоянием свойствам
стиля элемента управления, смотрите в разделе MSDN
Templated Data-Bound Control Sample.
Хотя тип свойства ControlStyle по умолчанию Style, серверный элемент
управления Web может установить стиль элемента управления на любой
класс, который наследуется от Style, переопределяя метод
WebControl.CreateControlStyle, как показано в следующем примере:
protected override Style CreateControlStyle()
{
// Note that the constructor of Style takes ViewState as an
// argument.
TableStyle style = new TableStyle(ViewState);
// Set up default initial state.
style.CellSpacing = 0;
return style;
}
В дополнение к основному типу Style, пространство имен
System.Web.UI.WebControls поставляет другие типы стиля, такие как
TableStyle и TableItemStyle. Дополнительные стили со строгим контролем
типов можно определить, наследуясь от Style (или от класса, который
унаследован от Style) и переопределяя или добавляя члены. Так же, как и
WebControl раскрывает подсвойства Style, как свойства верхнего уровня,
Вы можете раскрыть подсвойства вашего произвольного стиля, как свойства
верхнего уровня. Например, Table устанавливает свой стиль ControlStyle,
как TableStyle, и поставляет подсвойства TableStyle, такие как
CellPadding, CellSpacing и Gridlines, как свойства верхнего уровня.
Изменять ControlStyle элемента управления пользователю элемента
управления позволяют следующие методы WebControl:
- Метод WebControl.ApplyStyle копирует указанный стиль в
ControlStyle и замещает существующие элементы.
- Метод WebControl.MergeStyle объединяет указанный стиль с
ControlStyle, не замещая существующие элементы.
Предоставление дополнительных свойств стиля дочерним элементам
управления
В то время как свойство ControlStyle управляет всем стилем серверного
элемента управления Web, составной серверный элемент управления Web
(тот, который имеет дочерние элементы управления), может также
предоставить дополнительные свойства стиля, применимые к его дочерним
элементам управления. Элемент управления должен реализовать настроенное
управление состоянием, чтобы сохранить эти дополнительные стили через
циклы обработки.
Чтобы предоставить стили дочерним элементам управления:
- Определите одно или более свойств, наследуемых от Style.
Пользовательский элемент управления TemplatedList, описанный в
разделе
Templated Data-Bound Control Sample, предоставляет
дополнительные стили, как показано в следующем фрагменте кода.
Обратите внимание, что в случае, когда новый объект Style создан,
ViewState элемента управления не передается к Style конструктору.
ViewState элемента управления передается к Style конструктору только
при создании свойства ControlStyle элемента управления, а не для
любых других свойств типа Style. Обратите внимание также, что
элемент управления делегирует трэкинг состояния отображения
непосредственно к свойству стиля.
public virtual TableItemStyle AlternatingItemStyle {
get {
if (alternatingItemStyle == null) {
alternatingItemStyle = new TableItemStyle();
if (IsTrackingViewState)
((IStateManager)alternatingItemStyle).TrackViewState();
}
return alternatingItemStyle;
}
}
- Примените стили к дочерним элементам управления. Для примера,
см. метод PrepareControlHierarchy в разделе
Templated Data-Bound Control Sample, в котором также показано,
как объединить стили. Стили к дочерним элементам управления
необходимо применить на этапе генерирования, как показано в примере,
так, чтобы они не были сохранены в состоянии отображения.
- Переопределите метод SaveViewState, чтобы сохранить
дополнительные свойства стиля во ViewState. Так как объекты Style
управляют собственным состоянием, элемент управления должен вызвать
SaveViewState на его Style свойства, чтобы получить объекты, которые
должны быть добавлены к свойству ViewState.
- Переопределите метод LoadViewState, чтобы настроить
восстановление дополнительных свойств стиля из ViewState.
Дополнительные объекты, которые были добавлены к ViewState, должны
теперь быть получены в том же порядке, в котором они были добавлены.
Пример элемента управления, который предоставляет свойства стиля,
применяемые к его дочерним элементам управления, смотрите в разделе MSDN
Templated Data-Bound Control Sample.
Разработка элемента управления проверки правильности
В этом разделе рассматривается проверка правильности в оболочке
страницы ASP.NET и показано, как можно разработать собственную
библиотеку элементов управления проверки правильности.
Проверка правильности обычно касается проверки данных формы,
введенных пользователем. Однако она может также проводить другие виды
проверки ошибок. Чтобы обеспечить абстрактность, охватывающую различные
сценарии проверки правильности, .NET Framework определяет интерфейс
System.Web.UI.IValidator, в который входят следующие члены:
public interface IValidator
{
void Validate();
string ErrorMessage {get; set;}
bool IsValid {get; set;}
}
Метод Validate осуществляет логику проверки ошибок и устанавливает
свойство IsValid. Свойство ErrorMessage содержит строку ошибки,
предоставленную разработчиком страницы.
Класс Page предоставляет свойство Validators, которое является
списком всех элементов типа IValidator на странице. Считается, что любой
класс, реализующий интерфейс IValidator, является объектом проверки
правильности и может быть добавлен к свойству Validators содержащей его
страницы. Страница следит за статусом ошибки ее объектов для проверки
правильности и обновляет собственный общий статус ошибки,
соответственно.
Самые общие типы объектов для проверки правильности - элементы
управления проверки правильности, которые генерируют интерфейс у
клиента, чтобы пометить входные ошибки. Следующий список суммирует
характеристики элементов управления проверки правильности в .NET
Framework и будет полезен при построении библиотеки элементов управления
проверки правильности:
- элементы управления проверки правильности отделены от элементов
управления, правильность которых они проверяют. (Чтобы участвовать в
проверке правильности, элемент управления сервера должен открыть
свойство для проверки правильности, помечая его
ValidationPropertyAttribute.)
- множество элементов управления проверки правильности могут быть
связаны с одним элементом управления ввода данных.
- вид и сообщение об ошибках элемента управления проверки
правильности могут настраиваться разработчиком страницы.
- элементы управления проверки правильности поддерживаются
скриптом и выполняют ту же самую проверку ошибок (высокого уровня) у
клиентов, как на сервере.
- у клиентов высокого уровня элементы управления проверки
правильности отображают входные ошибки без цикла обработки сервера.
Ошибки проверки правильности отдельного поля отображаются у клиента,
как только пользователь переходит к другому полю, необходимые ошибки
проверки правильности поля и резюме ошибок отображаются, когда
пользователь отправляет форму.
- разработчики страницы могут обеспечить клиентский скрипт,
который дополняет или заменяет клиентскую проверку ошибок,
выполняемую элементом управления проверки правильности.
Общие функциональные возможности проверки правильности реализованы
абстрактным классом System.Web.UI.WebControls.BaseValidator, который
реализует IValidator, регистритует клиентский скрипт со страницей и
предоставляет основу, выполняющую логику. Конкретные классы объекта для
проверки правильности, такие как RequiredFieldValidator,
RegularExpressionValidator и CustomValidator наследуются от
BaseValidator и добавляют собственные специфические функциональные
возможности. Полный список сообщений об ошибках проверки правильности
предоставляется элементом управления ValidationSummary. Сам
ValidationSummary не является объектом для проверки правильности (он не
реализует IValidator) и наследуется от WebControl. ValidationSummary
обращается к коллекции Validators страницы, чтобы получить сообщение об
ошибках каждого объекта для проверки правильности, зарегистрированного
со страницей.
Если.NET Framework SDK или Visual Studio .NET установлены, библиотеку
скрипта, которая выпускается BaseValidator, можно увидеть, посмотрев
файл в виртуальном внедренном каталоге aspnet_client/system_web / <
version of SDK installed >. О выпуске клиентского сценария см. в разделе
Жизненный цикл выполнения элемента управления
Далее описаны ключевые шаги разработки элемента управления проверки
правильности, который поставляет основные функциональные возможности
проверки правильности. Следуйте этим шагам, если хотите разработать
основной элемент управления проверки правильности для пользовательской
библиотеки объекта проверки правильности или пользовательского элемента
управления проверки правильности, который предоставляет все необходимые
функциональные возможности проверки правильности. Не надо реализовать
эти шаги, если Вас интересует более простая задача, например, расширение
возможностей существующего объекта для проверки правильности. Этот
случай описан в разделе MSDN
Validator Control Samples.
Примечание: Шаги 1-4 общие для всех объектов проверки
правильности, включая серверные классы объекта для проверки
правильности, которые не являются элементами управления. Шаги 5-10 - для
элементов управления, которые предоставляют пользовательский интерфейс и
выпускают клиентский скрипт.
Чтобы реализовать объект для проверки правильности:
- Определите класс, который реализует IValidator. Если объект для
проверки правильности - элемент управления, он должен дополнительно
наследоваться прямо или косвенно от Control. Элементы управления
проверки правильности в .NET Framework наследуются от
System.Web.UI.WebControls.Label:
public abstract class BaseDomValidator : Label, IValidator {...}
Примечание: Если только он не базовый класс, ваш объект
для проверки правильности не должен быть абстрактен.
- Реализуйте контракт IValidator.
- Добавьте объект для проверки правильности к свойству Validators
содержащей его страницы, когда страница инициализирована,
переопределяя метод OnInit следующим образом:
protected override void OnInit(EventArgs e) {
base.OnInit(e);
Page.Validators.Add(this);
}
Примечание: если Ваш объект для проверки правильности не элемент
управления, этот шаг должен быть выполнен разработчиком страницы в
методе Page_Load.
- Удалите объект для проверки правильности из свойства Validators
содержащей его страницы, когда страница выгружается, переопределяя
метод OnUnload следующим образом:
protected override void OnUnload(EventArgs e) {
if (Page != null) {
Page.Validators.Remove(this);
}
base.OnUnload(e);
}
Примечание: если ваш объект для проверки правильности не
элемент управления, этот шаг должен быть выполнен разработчиком
страницы в методе Page_Unload.
- Определите свойство, которое позволяет пользователю связывать
элемент управления ввода данных для проверки правильности. В
элементах управления проверки правильности ASP.NET это свойство
называют ControlToValidate.
- Определите свойство, которое позволяет пользователю выбирать,
должен ли элемент управления выпустить клиентский скрипт. В
элементах управления проверки правильности ASP.NET, это свойство
называют EnableClientScript.
- Подготовите клиентскую библиотеку скрипта, которая будет
упакована с вашим элементом управления проверки правильности и
разместите его в нужную директорию на сервере таким образом, чтобы
он мог использоваться другими приложениями и не создавать конфликты
версий.
- Вызовите методы Page, которые выпускают клиентский скрипт. Эти
методы создания сценария вызваны из методов PreRender и Render.
- Реализуйте логику исполнения, переопределяя методы
AddAttributestoRender и Render.
Для получения дополнительной информации см. раздел
Жизненный цикл выполнения элемента управления.
Все шаги, приведенные здесь, реализованы в
Base Validator Control Sample.
Синтаксический анализ элемента управления, ParseChildrenAttribute и
построители элементов управления
Элемент управления может предоставлять пользовательские метаданные,
которые указывают синтаксическому анализатору страницы ASP.NET, как
интерпретировать вложенные (дочерние) элементы в пределах тэгов элемента
управления, когда элемент управления используется декларативно на
странице ASP.NET. .NET Framework предоставляет два класса атрибута для
этой цели:
- Класс ParseChildrenAttribute определяет будут ли вложенные
(дочерние) элементы в пределах тэгов элемента управления
интерпретироваться как свойства.
- Класс ControlBuilderAttribute позволяет реализовать более
сложную логику синтаксического анализа, связывая класс построителя
элементов управления с элементом управления.
Эти два атрибута не являются взаимоисключающими. И
ParseChildrenAttribute, и ControlBuilderAttribute можно применять к
одному и тому же элементу управления.
Использование ParseChildrenAttribute
ParseChildrenAttribute - атрибут метаданных .NET Framework, который
применяется на уровне класса. Он позволяет элементу управления
определить, как синтаксический анализатор страницы интерпретирует
вложенные элементы в пределах тегов элемента управления, когда элемент
управления используется декларативно на странице ASP.NET.
ParseChildrenAttribute может быть создан, используя поименованные
параметры, которые соответствуют логическому флажку и свойству по
умолчанию элемента управления.В следующем примере показано, как
применять ParseChildrenAttribute к элементу управления.
[ ParseChildren(ChildrenAsProperties = true)]
// Or apply as [ ParseChildren(true)].
public class TemplatedFirstControl : Control, INamingContainer {...}
Пример элемента управления, имеющего ParseChildrenAttribute, смотрите
в разделе MSDN
Templated Control Sample. Когда TemplatedFirstControl используется
на странице декларативно, дочерние элементы (непосредственные дети) в
пределах тегов ее элемента управления должны соответствовать свойствам
TemplatedFirstControl, как показано в следующем примере:
<%-- FirstTemplate is a property of TemplatedFirstControl. --%>
<%-- Custom is the tag prefix for TemplatedFirstControl on
the page as defined in the <%@ Register %> directive. --%>
<Custom:TemplatedFirstControl id = "First" Text= "The time on
the server is " runat=server>
<FirstTemplate>
<h3><font face="Verdana" color = "red"><%#
Container.Text %> <%# Container.DateTime %>
</font></h3>
</FirstTemplate>
</Custom:TemplatedFirstControl>
Следующая таблица описывает образец использования
ParseChildrenAttribute:
Таблица 11
Использование атрибута |
Описание |
ParseChildrenAttribute(ChildrenAsProperties =
true)
или
ParseChildren(true) |
Вложенные (дочерние) элементы должны
соответствовать свойствам элемента управления. Другие
(nonproperty) элементы и обычный текст между тегами элемента
управления генерируют ошибку синтаксического анализатора.
Примеры: Repeater и другие связанные с данными элементы
управления. |
ParseChildrenAttribute(ChildrenAsProperties =
false)
или
ParseChildrenAttribute(false)
или
ParseChildrenAttribute не применяется к элементу управления. |
Вложенные (дочерние) элементы должны
соответствовать серверным элементам управления ASP.NET.
Синтаксический анализатор страницы создает дочерние элементы
управления и вызывает IParserAccessor.AddParsedSubObject на
элементе управления. Реализация по умолчанию AddParsedSubObject
добавляет дочерние элементы управления к коллекции Controls
элемента управления. Синтаксический анализатор страницы
анализирует обычный текст между тегами как экземпляры
LiteralControl.
Пример: Panel. |
ParseChildrenAttribute (ChildrenAsProperties
= true, DefaultProperty = "PropertyName")
или
ParseChildrenAttribute(true, "PropertyName") |
Элемент управления должен определить public
свойство по имени PropertyName. Вложенные (дочерние) элементы
должны соответствовать дочерним элементам свойства PropertyName
или другим свойствам элемента управления.
Примеры: HtmlTable, HtmlTableRow.
Для примера, смотрите раздел MSDN ParseChildrenAttribute Sample. |
ParseChildrenAttribute(false, "PropertyName") |
Синтаксический анализатор страницы
приравнивает это использование к ParseChildrenAttribute(false). |
System.Web.UI.WebControls.WebControl отмечен, как ParseChildren
(true). Пользовательский элемент управления, который происходит от
WebControl, наследует эту маркировку атрибута. Вы можете переопределить
унаследованный атрибут с другими параметрами.
Если ControlBuilderAttribute применен в дополнение к
ParseChildrenAttribute, он может изменить логику синтаксического
анализа, описанную в таблице.
Краткий обзор построителя элементов управления
Построитель элементов управления - это класс, который управляет
синтаксическим анализом элемента управления сервера, когда он
используется на странице ASP.NET декларативно. По умолчанию каждый
элемент управления ASP.NET связан с построителем элементов управления
(System.Web.UI.ControlBuilder). По ходу синтаксического анализа страницы
построитель элементов управления, примененный по умолчанию, проверяет,
истинно ли значение атрибута ParseChildren элемента управления. Если это
так, вложенные элементы в пределах открывающих и закрывающих тэгов
элемента управления должны соответствовать свойствам элемента
управления. Любые другие вложенные элементы генерируют ошибку
синтаксического анализатора. Подробнее см. в разделе
Использование ParseChildrenAttribute. Если значение атрибута
ParseChildren не true, построитель элемента управления создает дочерние
элементы управления и вызывает IParserAccessor.AddParsedSubObject на
элементе управления. Реализация по умолчанию AddParsedSubObject
добавляет дочерние элементы управления к коллекции Controls элемента
управления. Любой обычный текст между тэгами вложенного элемента
управления анализируется как LiteralControl.
Если Вы хотите предоставить другую (пользовательскую) логику
синтаксического анализа для вашего элемента управления, можно создать
собственный построитель элемента управления, наследуясь от
ControlBuilder и переопределяя методы базового класса. Вы должны связать
пользовательский построитель элемента управления с вашим элементом
управления, отмечая элемент управления
System.Web.UI.ControlBuilderAttribute, как сделано в следующем примере:
// Define a custom control builder.
public class MyControlBuilder : ControlBuilder {...}
// Associate it with your control by applying a
// ControlBuilder attribute.
[ControlBuilder(typeof(MyControlBuilder))]
public class MyControl : Control {...}
В Таблица 12 перечислены некоторые из методов ControlBuilder, которые
Вы, вероятно, переопределите при создании пользовательского построителя
элемента управления. Полный список членов ControlBuilder см. в разделе
.Net Framework Class Library->ControlBuilder Class.
Таблица 12
Метод ControlBuilder |
Что он делает |
AllowWhiteSpaceLiterals |
Определяет, добавлены ли промежуточные
незаполненные пространства между тегами вложенных дочерних
элементов управления к коллекции Controls как экземпляры
LiteralControl. |
AppendSubBuilder |
Добавляет построитель элементов управления
для дочерних элементов управления. |
GetChildControlType |
Возвращает Type дочернего элемента
управления, основанный на имени его тега. Используйте этот
метод, чтобы фильтровать дочерние элементы управления, которые
добавляются к коллекции Controls. |
Пример построителя элементов управления см. в разделе MSDN
Custom Control Builder Sample.
Другой способ переопределения логики синтаксического анализа состоит
в том, чтобы переопределить метод AddParsedSubObject, унаследованный от
Control. Обратите внимание, что синтаксический анализ построителем
элементов управления производится во время компиляции, в то время как
метод AddParsedSubObject выполняется во время выполнения (могут
возникнуть потери производительности).
Расширение поддержки режима разработки
Функциональные возможности режима разработки касаются отображения и
поведения компонента или элемента управления в визуальном дизайнере.
Задачи режима разработки включают показ компонента или элемента
управления на панеле инструментов, отображение компонента на поверхности
разработки, отображение свойств и событий в инспекторе свойств,
автоматическое генерирование кода и так далее. В .NET Framework режим
разработки не поддерживается средой проектирования, такой как Visual
Studio .NET, но возможен через богатую встроенную архитектуру режима
разработки.
Архитектура режима разработки .NET Framework гибка, расширить и
настроить основные функциональные возможности режима разработки весьма
просто. Например, если Вы определяете пользовательское свойство в
элементе управления, можно легко предоставить пользовательский редактор
для редактирования этого свойства в инспекторе свойств. Или, если хотите
отобразить элемент управления особым образом во время разработки, можно
использовать специальные классы, называемые дизайнерами.
В то время, как многие из расширений режима разработки, обсуждаемые в
этом разделе, применяются к компонентам вообще, некоторые специфичны для
Web Forms или для Windows Forms. Есть различия режима разработки между
ASP.NET Web Forms и Windows Forms, потому что механизм исполнения
серверных элементов управления ASP.NET отличается от механизма
исполнения элементов управления Windows Forms. Серверный элемент
управления ASP.NET посылает HTML (или другой язык разметки) клиенту,
который сгенерирован браузером клиента или другим устройством просмотра.
Элемент управления Windows Forms, с другой стороны, делает собственную
отрисовку, используя GDI+ (новая библиотека графики Windows).
Архитектура режима разработки
Чтобы создать разработчику страницы впечатление быстрой разработки
приложения (RAD), в .NET Framework конструируемые элементы управления
входят в богатую и наращиваемую архитектуру времени разработки.
Конструируемые элементы управления - это класс, реализующий интерфейс
IComponent, или наследуемый прямо или косвенно от класса
System.ComponentModel.Component, реализующего этот интерфейс. Для
краткости в этой главе в случаях, где не возникает никакой
неоднозначности, вместо термина конструктивные элементы управления мы
будем использовать просто элементы управления. Т.к. базовый класс
Control реализует IComponent, все серверные элементы управления являются
конструируемыми и автоматически входят в архитектуру времени разработки
.NET Framework.
Важной особенностью архитектуры времени разработки .NET является то,
что вы реализуете возможности времени разработки не в пределах элемента
управления, а в классах вне его. Затем эти классы через атрибуты
метаданных ассоциируются с вашим элементом управления. Такое разделение
реализации времени разработки с реализацией времени выполнения делает
архитектуру времени разработки гибкой и наращиваемой. Это также защищает
функциональность времени разработки от влияния поведения элемента
управления во время выполнения.
Далее представлены четыре главные категории классов, обеспечивающих
расширенные функциональные возможности времени разработки:
- Конвертер типа
Класс, ассоциированный с типом или свойством, и осуществляющий как
string-to-value преобразования, так и преобразования между типом, с
которым он ассоциирован, и другим типом. Инспектор свойств
использует конвертеры типов для того, чтобы отображать значения
свойств, как текстовые строки, и чтобы преобразовывать введенные
пользователем текстовые строки в объекты, представляющие значения
свойств.
- Дизайнер
Класс, ассоциированный с элементом управления, управляющий
отображением и поведением элемента управления на поверхности
разработки. Дизайнер обеспечивает возможность отображать элемент
управления во время разработки и во время выполнения по-разному.
Например, элемент управления DataList использует класс дизайнера,
чтобы предложить редактирование шаблона WYSIWIG. Обратите внимание,
что в этом контексте термин дизайнер имеет значение, отличное от
значения термина визуальный дизайнер, который обращается к среде
визуального проектирования.
- Редактор типа UI
Класс, ассоциированный с типом или свойством, и обеспечивающий
специализированный интерфейс пользователя для редактирования
свойства. Редактор типа UI запускается из инспектора свойств и
полезен в том случае, когда простой ввод текста на UI,
обеспечиваемый инспектором свойств, не годится для редактирования
свойства. Например, выпадающий список для выбора цвета, который
позволяет разработчику страницы выбирать цвет для свойств BackColor
или ForeColor элемента управления Web в режиме WYSIWYG, - это
редактор типа UI.
- Редактор компонента
Класс, ассоциированный с элементом управления, который предоставляет
UI для редактирования свойств элемента управления. Редакторы
компонентов обеспечивают возможности, похожие на возможности,
предоставляемые страницами свойств элементов управления Microsoft
ActiveX. Редактор компонента - это не альтернатива инспектору
свойств, но он обеспечивает более удобный UI для редактирования
обычно используемых свойств элементов управления. Например, элемент
управления DataList ассоциирован с редактором компонента, который
запускается, когда разработчик страницы выбирает команду Property
Builder контекстного меню.
Следующая иллюстрация представляет краткий обзор архитектуры режима
разработки в .NET Framework:
Рисунок 6. Архитектура режима разработки
.NET Framework дает возможность разработчику компонента или элемента
управления предоставить различные уровни поддержки режима разработки для
компонента или элемента управления. На диаграмме архитектуры сложность
программирования режима разработки разделена на три уровня.
Базовый
.NET Framework поставляет много классов, которые предоставляют
функциональные возможности режима разработки. На самом простом уровне
разработчик компонента может связать эти классы с компонентом и его
членами, применяя пользовательские атрибуты, которые предоставляют
метаданные хосту режима разработки. В этом случае, разработчик
компонента не реализует функциональные возможности режима разработки, но
использует встроенную реализацию режима разработки.
Средний
Разработчик компонента может реализовать классы, которые позволяют
преобразование типов (такое как преобразование string в value), а также
классы, которые поставляют пользовательский интерфейс (UI) для
редактирования свойств. Разработчик связывает эти классы с компонентом,
применяя пользовательские атрибуты.
Расширенный
Разработчик компонента может реализовать классы, называемые
дизайнерами, которые настраивают представление режима разработки
компонента. Разработчик связывает дизайнер с компонентом, применяя
пользовательский атрибут. Дизайнеры предлагают расширенные возможности,
такие как фильтрация метаданных (скрывающиеся или изменяющиеся во время
разработки свойства и другие члены) и команды дизайнера (команды режима
разработки). Дизайнеры в свою очередь используют расширенные классы,
которые предоставляют службы дизайнера. Подробнее о дизайнерах
рассказывается в разделе Пользовательские дизайнеры.
Конвертеры типа и редакторы типа пользовательского интерфейса
используются и во время разработки, и во время выполнения, в то время
как дизайнеры используются только во время разработки. В диаграмме
архитектуры это различие обозначено сплошными стрелками от
пользовательских атрибутов к конвертерам типа и редакторам типа
пользовательского интерфейса и пунктирными стрелками от пользовательских
атрибутов к дизайнерам.
На Рисунок 7. Архитектура режима разработки .NET Framework
представлен пример архитектуры времени разработки .NET Framework.
Рисунок 7. Архитектура режима разработки .NET Framework
Чтобы продемонстрировать взаимоотношения между элементом управления и
классами, обеспечивающими функциональные возможности времени разработки,
на Рисунок 7. Архитектура режима разработки .NET Framework используется
гипотетический специальный элемент управления Web, названный MyControl:
- Класс System.Drawing.ColorConverter - это конвертер типа,
который обеспечивает возможность инспектору свойств отображать
значение свойства MyColor элемента управления MyControl, как
текстовую строку. ColorConverter наследуется от класса
System.ComponentModel.TypeConverter. ColorConverter ассоциирован с
типом System.Drawing.Color свойства MyColor через атрибут метаданных
System.ComponentModel.TypeConverterAttribute. Конвертеры типов
косвенно ассоциированы с элементом управления через типы свойств
элемента управления.
- Класс MyControlDesigner - это дизайнер, который управляет
отображением и поведением элемента управления MyControl на
поверхности разработки. MyControlDesigner наследуется от
System.Web.UI.Design.ControlDesigner и ассоциирован с MyControl
через атрибут метаданных System.ComponentModel.DesignerAttribute.
- Класс System.Drawing.Design.ColorEditor - это редактор типа UI,
обеспечивающий возможность инспектору свойств предлагать специальный
UI в форме выпадающего списка выбора цветов, когда разработчик
страницы выбирает в браузере свойств свойство MyColor. ColorEditor
наследуется от класса System.Drawing.Design.UITypeEditor и
ассоциирован с типом Color через атрибут метаданных
System.ComponentModel.EditorAttribute. Редакторы типа UI косвенно
ассоциированы с элементом управления через типы свойств элемента
управления.
- Класс MyControlComponentEditor - это редактор компонента,
обеспечивающий UI для редактирования часто используемых свойств
MyControl. MyControlComponentEditor наследуется от
System.Windows.Forms.Design.WindowsFormsComponentEditor и
ассоциирован с MyControl через атрибут метаданных EditorAttribute.
Хотя архитектура режима разработки была описана здесь в терминах
элементов управления, основная архитектура, представленная на Рисунок 7.
Архитектура режима разработки .NET Framework, применима ко всем
конструируемым компонентам. На этом рисунке, если заменить класс Control
интерфейсом IComponent и класс ControlDesigner интерфейсом IDesigner,
полученный рисунок будет представлять более общую архитектуру режима
разработки .NET Framework.
Атрибуты и поддержка режима разработки
Атрибуты связывают функциональные возможности режима разработки с
компонентом. В .NET Framework функциональные возможности режима
разработки реализованы не в компоненте, а вне компонента, и связаны с
компонентом через метаданные, предоставленные пользовательскими
атрибутами. Это делает модель режима разработки очень гибкой, так как
разработчик компонента может легко изменить поведение режима разработки
компонента, переопределяя существующие атрибуты или применяя
дополнительные атрибуты.
В .NET Framework только классы, которые прямо или косвенно реализуют
System.ComponentModel.IComponent, могут быть добавлены к панели
инструментов визуального дизайнера. Пользовательские атрибуты широко
используются в .NET Framework, но этот раздел описывает только атрибуты
режима разработки. Атрибуты режима разработки применяются к свойствам,
событиям, классам и даже к сборкам.
В соответствии с соглашением, классы атрибута называют
AttributeNameAttribute. Пространство имен System.ComponentModel содержит
много основных классов атрибута.
Атрибуты режима разработки и наследование
При наследовании компонента или элемента управления от основного
компонента, который имеет атрибуты режима разработки, ваш компонент
наследует функциональные возможности режима разработки базового класса.
Если основные функциональные возможности отвечают вашим целям, Вы не
должны повторно применять атрибуты. Однако, всегда можно переопределить
атрибуты или применить дополнительные атрибуты к производному
компоненту. Следующий фрагмент кода показывает пользовательский элемент
управления, который переопределяет свойство Text, унаследованное от
Control, переопределяя атрибут BrowsableAttribute, примененный в базовом
классе.
public class MyControl : Control {
// The base class has [Browsable(true)] applied to the Text property.
[Browsable(false)]
public override string Text {...}
...
}
Применение конвертера типа, редактора типа пользовательского
интерфейса или атрибута дизайнера
Конвертер типа (или редактор типа пользовательского интерфейса)
обычно связан с типом. Когда конвертер типа или атрибут редактора
применяются к типу, он становится конвертером по умолчанию или
редактором для свойств того типа. Чтобы связывать конвертер типа (или
редактор типа пользовательского интерфейса) с типом, примените атрибут
на уровне класса, как показано в следующем примере:
[ TypeConverter(typeof(MyColorConverter))]
[ Editor(typeof(MyColorEditor), typeof(UITypeEditor))]
struct MyColor {...}
Если тип свойства не имеет конвертера или редактора, связанного с
ним, или если Вы хотите переопределить конвертер, примененный по
умолчанию, или редактор, связанный с типом свойства, можете применить
конвертер типа или атрибут редактора типа пользовательского интерфейса к
свойству непосредственно. Чтобы связать конвертер типа со свойством,
примените TypeConverterAttribute к свойству, как показано в следующем
примере:
[ TypeConverter(typeof(PointConverter))]
public Point MyLocation {...}
Чтобы связать редактор типа пользовательского интерфейса со
свойством, примените EditorAttribute к свойству, как показано в
следующем примере:
[ Editor(typeof(FlashTrackBarDarkenByEditor), typeof(UITypeEditor))]
public byte DarkenBy {...}
Дизайнер всегда связан с компонентом, как целое. Чтобы связывать
дизайнер с компонентом, примените DesignerAttribute на уровне класса,
как показано в следующем примере:
[Designer(typeof(HelpLabel.HelpLabelDesigner))]
public class HelpLabel : System.Windows.Forms.Control,
System.ComponentModel.IExtenderProvider {...}
В этих примерах конструкторы для классов TypeConverterAttribute,
EditorAttribute и DesignerAttribute принимают в качестве параметров
объекты соответствующего типа времени выполнения. Это использование
атрибутов работает, если компонент находится в той же самой сборке, что
и классы режима разработки. Если классы режима разработки находятся в
другой сборке, то необходима другая форма конструктора атрибута
(называемая определенным сборкой форматом), как показано в следующем
примере:
[Designer("System.Windows.Forms.Design.DocumentDesigner, System.Design")]
public class MyForm : Form {...}
Атрибут режима разработки уровня сборки
ASP.NET предоставляет атрибут уровня сборки
System.Web.UI.TagPrefixAttribute, который предоставляет префикс тэга,
применяемый дизайнером к элементу управления при перемещении его из
панели инструментов на поверхность разработки.. Префикс тега
автоматически введен Visual Studio .NET в директиве Register для
элемента управления таким образом, чтобы элемент управления мог
использоваться декларативно на странице с предуказанным префиксом тега
(<tagprefix:controlname … runat = server/>). Если этот атрибут не
применяется, дизайнер генерирует префикс тэга на странице по умолчанию,
например, cc1. Т.к. TagPrefixAttribute является атрибутом на уровне
сборки, он не применяется непосредственно к любому отдельному элементу
управления. Вместо этого он объявляется в отдельном файле, который
компилируется в ту же сборку как элемент управления.
Примечание: TagPrefixAttribute работает только в визуальных
дизайнерах. Если страницы ASP.NET создаются в текстовом редакторе, таком
как Notepad, необходимо самостоятельно определить префикс тега и
пространство имен в директиве Register для элемента управления.
Следующий пример показывает, как применить TagPrefixAttribute. Первый
параметр к конструктору атрибута определяет пространство имен и второй
определяет префикс тега.
[ assembly:TagPrefix("SimpleControls", "simple") ]
namespace SimpleControls {
[
Designer("SimpleControl.Design.SimpleDesigner, SimpleControl")
]
public class SimpleControl : System.Web.UI.WebControls.WebControl {}
}
Атрибуты режима разработки для компонентов
Поскольку элементы управления могут быть отображены в дизайнере,
таком как Visual Studio .NET, им необходимы атрибуты, которые
предоставляют метаданные инструментальным средствам режима разработки. В
этом разделе перечислены и описаны обычно используемые атрибуты режима
разработки.
Общие атрибуты для свойств и событий
В Таблица 13 перечислены атрибуты, которые обычно применяются к
свойствам и событиям. Обратите внимание, что атрибуты времени разработки
применяются только к public свойствам, потому что не public свойства
никогда не видимы в дизайнере.
Таблица 13
Атрибут |
Применяется к |
Описание |
BrowsableAttribute |
Свойствам и событиям |
Определяет, должны ли свойство или событие
быть отображены в браузере свойств.
По умолчанию public свойство всегда отображается в инспекторе
свойств. Применяйте этот атрибут, как Browsable (false), только
если не хотите отображать свойство. Вообще read-only свойства
необходимо обозначать, как Browsable (false). |
CategoryAttribute |
Свойствам и событиям |
Определяет имя категории, в которую
группировать свойство или событие. При выборе категории этот
атрибут делает возможным организовать свойства и события в
логические группы инспектора свойств, такие как Appearance,
Behavior, Data и Layout. |
DescriptionAttribute |
Свойствам и событиям |
Обеспечивает краткое описание свойства,
которое отображается в нижней части инспектора свойств, когда
разработчик страницы выбирает свойство или событие. |
BindableAttribute |
Свойствам |
Определяет, можно ли связывать свойство с
данными. |
DefaultPropertyAttribute |
Свойствам (Вставьте этот атрибут перед
объявлением класса.) |
Определяет для компонента свойство по
умолчанию. Это свойство выбрано в инспекторе свойств, когда
пользователь выделяет элемент управления. |
DefaultValueAttribute |
Свойствам |
Определяет для свойства значение по
умолчанию, которое присваивается инспектором. Значение, которое
передается в этот атрибут, должно быть таким же, как значение,
возвращаемое свойством по умолчанию. |
EditorAttribute |
Свойствам |
Определяет редактор, чтобы использовать для
изменения свойства в визуальном дизайнере. |
LocalizableAttribute |
Свойствам |
Определяет, что свойство может быть
локализовано. Любые свойства, которые имеют этот атрибут,
автоматически сохраняются в файл ресурсов, когда пользователь
хочет локализовать форму. |
DesignerSerializationVisibilityAttribute |
Свойствам |
Определяет, должно ли и как должно свойство
преобразовываться в код. Например, read-only свойства должны
быть помечены, как DesignerSerializationVisibility
(DesignerSerializationVisibility.Hidden), таким образом
указывая, что свойство исключено из механизма сериализации
дизайнера. |
TypeConverterAttribute |
Свойствам |
Определяет конвертер типа для преобразования
типа свойства к другому типу данных. |
DefaultEventAttribute |
Событиям
(Вставьте этот атрибут перед объявлением класса.) |
Определяет событие по умолчанию для
компонента. Это событие, выделяемое в инспекторе свойств, когда
пользователь выбирает компонент. |
Если иначе не заявлено, атрибуты свойств и событий помещены в код
непосредственно перед свойством или объявлением события, как показано в
следующем примере:
// To apply CategoryAttribute to the BorderColor
// property, place it immediately before the declaration
// of the BorderColor property.
[Category("Appearance")]
public Color BorderColor;
// To apply DescriptionAttribute to the Click event,
// place it immediately before the declaration
// of the Click event.
[Description("The Click event of the button")]
public event EventHandler Click;
Об атрибутах режима разработки, которые связывают дизайнеры с
компонентами и элементами управления, см. в разделе
Расширение поддержки режима разработки.
В дополнение к использованию классов атрибутов, определенных в
Библиотеке классов .NET Framework, можно определить собственные классы
атрибутов. Для получения дополнительной информации, обратитесь к
документации языка программирования, на котором работаете, или см.
раздел Написание специальных атрибутов (Writing
Custom Attributes).
Атрибуты и поддержка дизайнера
В следующем фрагменте кода атрибут CategoryAttribute обеспечивает
возможность инспектору свойств отобразить свойство TextAlignment в
категории Alignment. Атрибут DescriptionAttribute позволяет инспектору
свойств предоставлять краткое описание свойства, когда пользователь
выделяет его.
[
Category("Alignment"),
Description("Specifies the alignment of text.")
]
public ContentAlignment TextAlignment { //... }
Примечание для пользователей C# и Visual Basic .NET: В C# и
Visual Basic .NET на класс атрибута AttributeNameAttribute можно
сослаться просто, как на AttributeName в синтаксисе атрибута.
Некоторые атрибуты режима разработки применяются на уровне класса.
Атрибут DesignerAttribute применяется на уровне класса и сообщает
дизайнеру форм, какой класс дизайнера использовать для отображения
элемента управления. Компоненты связаны с дизайнером по умолчанию
(System.ComponentModel.Design.ComponentDesigner), и Windows Forms, и
серверные элементы управления ASP.NET связаны с их собственными
дизайнерами по умолчанию. Применяйте DesignerAttribute, только если
определяете пользовательский дизайнер для компонента или элемента
управления.
//Associates the designer class SimpleControl.Design.SimpleDesigner
//with Simple.
[ Designer(typeof(SimpleControl.Design.SimpleDesigner))]
public class Simple : WebControl { //... }
Реализация конвертера типа
Конвертер типа конвертирует данный тип данных к другому типу данных.
Конвертеры типа прежде всего используются для преобразований
строки-к-значению (string-to-value) и для проверки правильности во время
разработки и во время выполнения. В хосте, таком как инспектор свойств в
дизайнере форм, конвертеры типа позволяют представить пользователю
значение свойства как текст, они могут конвертировать введенный
пользователем текст в соответствующий тип данных.
Большинство встроенных типов данных (Int32, String, типы перечисления
и другие) имеют конвертеры типа по умолчанию, которые обеспечивают
преобразование строки-к-значению и выполняют проверки правильности.
Конвертеры типа по умолчанию находятся в пространстве имен
System.ComponentModel и называются TypeConverterNameConverter. Если
функциональные возможности по умолчанию не отвечают вашим целям, можно
расширить конвертер типа или реализовать пользовательский конвертер
типа, если Вы определяете пользовательский тип, который не имеет
ассоциированного с ним конвертера типа. Поддержка режима разработки
может быть предоставлена пользовательскому типу, только если для него
определен конвертер типа.
Реализация конвертера типа независима от функциональных возможностей
пользовательского интерфейса. Следовательно, один и тот же конвертер
типа может быть применен и в Windows Forms, и в Web-формах.
Чтобы реализовать простой конвертер типа:
- Определите класс, который наследуется от
System.ComponentModel.TypeConverter.
- Переопределите метод CanConvertFrom, который определяет, какой
тип конвертера, может конвертировать из заданного типа.
- Переопределите метод ConvertFrom, который реализует
преобразование.
- Переопределите метод CanConvertTo, который определяет, какой тип
конвертера может преобразовать к заданному типу. Нет необходимости
переопределить этот метод для преобразования к строковому типу.
- Переопределите метод ConvertTo, который реализует
преобразование.
- Переопределите метод IsValid, который выполняет проверку
правильности.
Обобщенное преобразование типов
.NET Framework предоставляет два механизма для преобразования
определяемых пользователем типов данных (пользовательские типы) к другим
типам данных:
- определение конвертера типа для пользовательского типа методом
расширения класса System.ComponentModel.TypeConverter и связывания
конвертера типа с заданным типом через атрибут
System.ComponentModel.TypeConverterAttribute.
- реализация интерфейса System.IConvertible в пользовательском
типе.
В таблице 14 указаны различия между этими двумя механизмами.
Таблица 14
Преобразование с
использованием TypeConverter |
Преобразование с
использованием IConvertible |
Может использоваться, как во время
разработки, так и во время выполнения. |
Может использоваться только во время
выполнения. |
Использует reflection ; поэтому медленнее чем
преобразование, производимое IConvertible. |
Не использует reflection. |
Позволяет двухсторонние преобразования типов
от пользовательского типа к другим типам данных и наоборот.
Например, TypeConverter, определенный для MyType, позволяет
преобразования из MyType к String и из String в MyType. |
Позволяет преобразование от пользовательского
типа к другим типам данных, но не из других типов данных к
пользовательскому типу. |
Примечание: TypeConverter для типа реализован
вне типа и связан с типом атрибутом TypeConverterAttribute. |
Примечание: IConvertible реализован
пользовательским типом. Чтобы конвертировать тип, пользователь
типа вызывает конверсионный метод (контракта IConvertible). |
Для получения дополнительной информации об использовании конвертеров
типа для осуществления преобразований см.
System.ComponentModel.TypeConverter.
Реализация редактора типа пользовательского интерфейса
В некоторых ситуациях простое преобразование значения-к-строке
(value-to-string), которое позволяет свойству быть отображенным как
текст в инспекторе свойств, не подходит. Например, для свойства цвета
(color) более желательно визуальное представление. Редактор типа
пользовательского интерфейса обеспечивает такое представление и
предназначен для использования с инспекторами свойств и другими
инспекторами режима разработки. Реализация редактора типа
пользовательского интерфейса зависит от хоста, который поддерживает его.
Чтобы реализовать специальный редактор типа пользовательского
интерфейса для Windows Forms:
- Определите класс, который наследуется от
System.Drawing.Design.UITypeEditor.
- Переопределите метод EditValue, чтобы установить свойства
пользовательского интерфейса.
- Переопределите метод GetEditStyle, чтобы сообщить браузеру
свойств о поддержке редактирования, которую Вы предоставите.
Полный пример приведен в разеле MSDN
UI Type Editor for Windows Forms Sample.
Реализация провайдера расширителя
Провайдеры расширителя добавляют свойства к другим элементам
управления. Элементы управления ActiveX также использовали понятие
провайдеров расширителя, но они требовали специальной поддержки языка
программирования. В .NET Framework провайдеры расширителя не требуют
никакой специальной поддержки. В исходном коде свойство провайдера
расширителя существует в объекте провайдера расширителя. При установке
значения свойства на другом объекте необходимо знать две вещи: объект,
на котором устанавливается значение, и новое значение свойства.
Например, Windows Forms имеют компонент ToolTip, который предлагает
свойство расширителя другим элементам управления. Свойство, которое он
устанавливает на других объектах - это строка, которая представляет
собой всплывающую подсказку, появляющуюся, когда курсор мыши проходит
над элементом управления. Следующий пример показывает, как установить
свойство ToolTip:
tooltip1.SetToolTip(button1, "The tooltip text");
Во время разработки свойства расширителя появляются в испекторе
свойств, как свойства на объектах, которые они расширяют, а не на
фактическом объекте расширителя. В предыдущем примере, свойство ToolTip
появляется на button1, а не на tooltip1.
Чтобы реализовывать провайдер расширителя:
- Определите компонент, реализующий интерфейс
System.ComponentModel.IExtenderProvider.
public class MyExtender : IExtenderProvider {...}
IExtenderProvider определяется следующим образом:
public interface IExtenderProvider {
bool CanExtend(object extendee);
}
- Реализуйте метод CanExtend так, чтобы он возвращал true для
каждого компонента или элемента управления, для которого ваш
расширитель предоставляет свойства.
- Определите набор свойств, которые ваш расширитель сможет
предоставить другим компонентам. Свойства - это в действительности
методы, так как у них есть дополнительный параметр, определяющий
компонент, к которому применяется свойство.
Класс провайдера расширителя должен быть отмечен
ProvidePropertyAttribute. Конструктор ProvidePropertyAttribute содержит
два параметра: во-первых, строку, которая описывает свойство, и,
во-вторых, тип свойства.
[ProvideProperty("HelpText", typeof(string))]
class MyExtender : IExtenderProvider {}
Хотя провайдер расширителя может предоставить свойства любому
компоненту, реализация обычно включает возможности, которые делают его
пригодным для использования только с определенной категорией
компонентов.
Реализация провайдера расширителя для элементов управления Windows
Forms и для серверных элементов управления ASP.NET различна.
Пример полностью см. в разделе MSDN
Windows Forms Extender Provider Sample.
Пользовательские дизайнеры
Дизайнеры - это классы, которые предоставляют возможность изменять
внешний вид и поведение компонентов и элементов управления в режиме
разработки. Хотя обычная цель любого WYSIWYG дизайнера форм -
минимизировать различия между отображением в режиме разработки и во
время выполнения, время от времени необходимы специальные команды вызова
в режиме разработки. Например, объект System.Windows.Forms.Panel мог бы
не иметь видимой границы во время выполнения. Однако без границы панель
невидима для разработчика, проектирующего форму, которая содержит
панель. Поэтому, дизайнер для объекта System.Windows.Forms.Panel рисует
границу вокруг панели пунктиром.
В .NET Framework дизайнер - это просто объект, который связан с
компонентом во время разработки. Хост дизайнера позволяет объекту
дизайнера изменять поведение компонента во время разработки. Ранее
поддержка логики такого типа была встроена в дизайнер формы, и это был
дизайнер формы, а не объект, ответственный за возможности режима
разработки. В мире ActiveX, например, различные инструментальные
средства предлагают собственные дизайнеры форм с различными
пользовательскими интерфейсами и возможностями. Поскольку фиксированный
набор свойств продукта не может учесть потребности всех элементов
управления, элементы управления ActiveX имели только ограниченный
уровень пользовательской поддержки режима разработки.
Поддержка режима разработки для компонентов в .NET Framework, однако,
поступает не от средства разработки, такого как Visual Studio .NET, а
непосредственно от самих компонентов. Инструментальные средства, такие
как Visual Studio .NET, предоставляют набор основных сервисов для
использования дизайнерами, но поддержка дизайнеров - основная часть
платформы. Пространство имен System.ComponentModel.Design предоставляет
интерфейсы и базовые классы для поддержки режима разработки. Основные
функциональные возможности могут быть расширены, чтобы обеспечить
различные уровни поддержки дизайнера.
Поддержка режима разработки для Web-форм
В этом разделе представлены темы, описывающие, как реализовать
дизайнеры для серверных элементов управления ASP.NET.
Примечание по терминологии: Термины страница Web Forms и
страница ASP.NET взаимозаменяемы. Термин Web Forms обычно применяется к
технологии режима разработки, которая обеспечивает создание страниц
ASP.NET в визуальном дизайнере. Термин серверный элемент управления Web
ссылается на класс в пространстве имен System.Web.UI.WebControls.
ASP.NET предоставляет следующие основные классы дизайнера в
пространстве имен System.Web.UI.Design:
- System.Web.UI.Design.ControlDesigner предоставляет основные
функциональные возможности режима разработки для серверных элементов
управления ASP.NET.
- System.Web.UI.Design.ReadWriteControlDesigner наследуется от
ControlDesigner и предоставляет основные функциональные возможности
режима разработки для читающих/записывающих элементов управления,
таких как System.Web.UI.WebControls.Panel.
- System.Web.UI.Design.TemplatedControlDesigner наследуется от
ControlDesigner и предоставляет основные функциональные возможности
режима разработки для шаблонных элементов управления, таких как
System.Web.UI.WebControls.DataList.
Классы дизайнера для серверных элементов управления Web наследуются
прямо или косвенно от этих базовых классов и находятся в пространстве
имен System.Web.UI.Design.WebControls. Например,
System.Web.UI.Design.WebControls.ButtonDesigner - это дизайнер для
элемента управления System.Web.UI.WebControls.Button,
System.Web.UI.Design.WebControls.CalendarDesigner - это дизайнер для
элемента управления System.Web.UI.WebControls.Calendar и так далее.
Следующие замечания не имеют отношения к классам дизайнера, но они
приведены здесь, потому что применяются ко всем функциональным
возможностям режима разработки в Web Forms:
- Дизайнер Web Forms не поддерживает методы
ShouldSerializePropertyName и ResetPropertyName, которые выборочно
предоставляются элементами управления Windows Forms для сохранения
свойства. Серверные элементы управления сериализованы с помощью
методов, предоставленных классом ControlPersister. Реализация этих
методов управляется атрибутами метаданных, такими как
DefaultValueAttribute, PersistenceModeAttribute и
DesignerSerializationVisibilityAttribute.
- Если элемент управления предоставляет свойства, которые имеют
подсвойства (т.е. свойства, типами которых являются классы, которые,
в свою очередь, предоставляют свойства), применяйте
NotifyParentPropertyAttribute к подсвойствам, если хотите, чтобы
родительское свойство получило уведомление об изменениях значений
подсвойства.
Реализация простого дизайнера элемента управления Web Forms
Класс дизайнера Web Forms управляет представлением элемента
управления в режиме разработки, поставляя HTML режима разработки хосту
дизайнера. .NET Framework поставляет основной класс дизайнера,
System.Web.UI.ControlDesigner, который предоставляет основные
функциональные возможности для дизайнера серверного элемента управления.
ControlDesigner определяет следующие ключевые методы для генерирования
HTML режима разработки:
public class ControlDesigner : HtmlControlDesigner {
public virtual string GetDesignTimeHtml() {...}
protected virtual string GetEmptyDesignTimeHtml() {...}
protected virtual string GetErrorDesignTimeHtml(Exception e) {...}
// The helper method that provides default placeholder HTML
// cannot be overridden. Invoke this method when it is not possible to
// render a meaningful representation of the control at design time.
protected string CreatePlaceHolderDesignTimeHtml(string s) {...}
}
Метод GetDesignTimeHtml возвращает HTML режима разработки для
элемента управления. Основная реализация GetDesignTimeHtml вызывает
метод Render элемента управления, таким образом генерируя во время
разработки тот же самый HTML, что и во время выполнения. Задача
дизайнера состоит в том, чтобы переопределить GetDesignTimeHtml таким
образом, чтобы выполнить HTML, который более уместен во время
разработки.
Метод GetEmptyDesignTimeHtml поставляет строку HTML для выполнения во
время разработки, если строка HTML во время выполнения, сгенерированная
элементом управления, пуста, так же как и в случае, когда свойства не
установлены. Основная реализация метода GetEmptyDesignTimeHtml
возвращает полностью определенное имя и ID элемента управления. Дизайнер
может переопределить этот метод и вызвать
CreatePlaceHolderDesignTimeHtml или он может возвратить дополнительный
HTML.
Метод GetErrorDesignTimeHtml предоставляет HTML, используемый для
представления в режиме разработки элемента управления при выявлении
ошибки.
Чтобы реализовать простой дизайнер элементов управления Web Forms:
- Определите класс, наследуемый прямо или косвенно от
System.Web.UI.ControlDesigner.
- (Необязательно) Переопределите метод GetDesignTimeHtml,
унаследованный от ControlDesigner.
- (Необязательно) Переопределите метод GetEmptyDesignTimeHtml,
унаследованный от ControlDesigner.
- (Необязательно) Переопределите метод GetErrorDesignTimeHtml,
унаследованный от ControlDesigner.
Раздел MSDN
Web Forms Templated Data-Bound Control Designer Sample содержит
пример нетривиального дизайнера, который переопределяет эти методы.
Пример обычного дизайнера (SimpleDesigner) для простого серверного
элемента управления ASP.NET (Simple) приведен в разделе MSDN
Implementing a Simple Web Forms Control Designer
Реализация дизайнера элемента управления Web Forms, связанного с
данными
Основная задача дизайнера связанного с данными элемента управления
состоит в том, чтобы обеспечить возможность элементу управления
обращаться к типовому источнику данных режима разработки, когда элемент
управления находится в дизайнере. .NET Framework предоставляет интерфейс
IDataSourceProvider, который определяет контракт, разрешающий доступ к
источнику данных. Дизайнер для связанного с данными серверного элемента
управления должен реализовать интерфейс IDataSourceProvider. Вот
ключевые шаги для реализации такого дизайнера.
Чтобы реализовать дизайнер связанного с данными элемента
управления:
- Определите класс, который наследуется прямо или косвенно от
ControlDesigner и реализует интерфейс IDataSourceProvider, как
показано в следующем фрагменте кода:
public class DataboundControlDesigner : ControlDesigner, IDataSourceProvider {...}
- Предоставьте свойство (строкового типа) DataSource, которое
определяет источник данных режима разработки. Свойство DataSource
дизайнера работает поверх коллекции DataBindings элемента
управления.
- Переопределите метод GetDesignTimeHtml и установите источником
данных элемента управления (который связан с вашим дизайнером)
источник данных режима разработки.
- Переопределите метод PreFilterProperties, чтобы заменить
свойство DataSource элемента управления свойством DataSource режима
разработки. Метод PreFilterProperties принадлежит интерфейсу
IDesignerFilter, который позволяет дизайнеру заменять или создавать
свойства и события во время разработки. IDesignerFilter реализован
ControlDesigner.
- Реализуйте методы интерфейса IDataSourceProvider. Этот интерфейс
имеет два метода - GetResolvedSelectedDataSource и
GetSelectedDataSource.
Эти шаги описывают конкретный пример, приведенный в разделе MSDN
Web Forms Templated Data-Bound Control Designer Sample. Данная
реализация может применяться к элементу управления с источником данных
типа объект, который принимает ссылку IEnumerable.
Реализация редактора шаблона Web-форм
Редактор шаблона - дизайнер специального назначения, который
поддерживает создание шаблона и редактирование для шаблонного серверного
элемента управления ASP.NET. Элемент управления с редактором шаблона
представляет собой поверхность для разработки, на которую разработчик
страницы методом drug-and-drop может поместить элементы управления из
панели инструментов для создания встроенных шаблонов. Элементы
управления сервера DataList и DataGrid в .NET Framework SDK - это
примеры элементов управления, поддерживающих редактирование шаблона.
Создание шаблонного элемента обсуждалось в разделе
Разработка шаблонного элемента управления.
.NET Framework предоставляет абстрактный базовый класс (System.Web.UI
.Design.TemplatedControlDesigner), который наследуется от
ControlDesigner и предоставляет свойства и методы, необходимые для
редактирования шаблона. Редактор шаблона должен наследоваться от
TemplatedControlDesigner. Далее перечислены ключевые шаги при реализации
редактора шаблона.
Чтобы реализовать редактор шаблона:
- Определите класс, который наследуется от
TemplatedControlDesigner.
- Переопределите свойство AllowResize, унаследованное от
ControlDesigner, чтобы позволить изменения элемента управления.
Рекомендуется предоставить возможность изменять размеры элемента
управления в режиме шаблона, даже если в обычном режиме изменение
размеров невозможно.
- Переопределите метод GetCachedTemplateEditingVerbs. Элемент
управления переопределяет этот метод, чтобы возвратить список
редактирующих команд шаблона, применимых к элементу управления.
- Переопределите метод CreateTemplateEditingFrame. Этот метод
берет экземпляр TemplateEditingVerb, как параметр.
TemplateEditingVerb - это команда дизайнера (класс, наследуемый от
DesignerVerb), который позволяет редактору шаблона добавлять команду
к элементу управления во время разработки.
- Переопределите метод GetTemplateContent, который получает
содержимое шаблона.
- Переопределите метод SetTemplateContent, который устанавливает
содержимое шаблона.
- Как с любым другим дизайнером, переопределите методы
GetDesignTimeHtml, GetEmptyDesignTimeHtml и GetEmptyDesignTimeHtml,
унаследованные от класса ControlDesigner. Если шаблонный элемент
управления связан с данными, переопределенный метод
GetDesignTimeHtml должен установить источник данных элемента
управления в источник данных режима разработки, как описано в
разделе Реализация дизайнера элемента управления Web
Forms, связанного с данными.
Если шаблонный элемент управления связан с данными, необходимо также
реализовать шаги, описанные в разделе Реализация
дизайнера элемента управления Web Forms, связанного с данными, и
переопределить следующие дополнительные методы
TemplatedControlDesigner (описанные в шагах 8 и 9).
- Переопределите метод GetTemplateContainerDataSource, чтобы
предоставить источник данных контейнера шаблона. Он вызывается из
диалога привязки данных при редактировании связываний данных
элементов управления, помещенных в пределах шаблона.
- Переопределите метод GetTemplateContainerDataItemProperty, чтобы
обратиться к имени свойства DataItem контейнера шаблона.
Пример редактора шаблона см. в разделе MSDN
Web Forms Templated Data-Bound Control Designer Sample.
Заключение
Данная статья является обзором типов серверных элементов управления и
критериев их выбора. Автор не ставил перед собой цели подробно решать
конкретные задачи реализации или углубленного рассмотрения тех или иных
вопросов теории. В статье приведены и кратко описаны многие, но далеко
не все аспекты создания серверных элементов управления.