Средства форматирования страниц
Мастер-страницы
Очень часто требуется зафиксировать структуру страницы так, чтобы одни и те же элементы управления пользовательского интерфейса размещались в одном и том же месте как на одной странице, так и на множестве страниц сайта. Это позволяют сделать специальные страницы - шаблоны, которые существуют независимо, но подключаются к страницам по мере необходимости. Любая исполнимая страница может подключать только один из заранее заготовленных шаблонов.
Шаблон размечает именованные области визуализации на исполнимой странице, в которых будут размещены либо прикрепленные группы элементов страницы, либо содержимое именованной области самого шаблона. Группы элементов управления страницы, закрепленные за именованными областями шаблона, называются содержимым (content). Страница, к которой подключен шаблон, должна содержать группы, каждая из которых может ссылаться только на одну именованную область шаблона. Если для какой-то именованной области шаблона прикрепленной группы нет, то на странице появится содержимое именованной области.
Именованные области шаблона, подключенного к странице, как бы проецируются на страницу и структурируют ссылающиеся на них группы содержимого страницы или своего собственного содержимого. Часть шаблона, находящаяся вне именованных областей, может иметь содержимое, которое называется заполнителем шаблона. Именованные области шаблона и заполнитель шаблона могут содержать любую комбинацию HTML-кода, серверных элементов управления или кода программной поддержки (внутристрочный или в отдельном файле) - все то, что используется на обычной странице.
Перекомпоновка подключенного шаблона или его содержимого, а также подключение к исполнимой странице нового шаблона на этапе проектирования или программно приводит к немедленному изменению ее структуры и содержимого шаблонной части.
Исполнимая страница получает от используемого шаблона его компоновку, содержимое и заполнитель. Заполнитель шаблона проецируется на исполнимую страницу полностью. Если на исполнимой странице есть хотя-бы одна группа, прикрепленная к соответствующей именованной области шаблона, то именованная область заполняется содержимым этой группы. Если такой группы на странице нет, то проецируется содержимое самой именованной области шаблона. Если прикрепленная группа есть, но она пустая, то именованная область не проецируется.
Шаблоны представляют собой обычные страницы, за исключением нескольких отличий:
- Шаблоны нельзя выполнить напрямую, они могут только поддерживать ссылающиеся на них исполнимые страницы
- Файлы шаблонов имеют расширение .master, а файлы кода поддержки для них на языке C# - .master.cs
- В декларации дескрипторного представления шаблона вместо директивы @Page используется директива @Master
- Класс кода поддержки шаблона наследует базовый класс System.Web.UI.MasterPage, вместо базового класса System.Web.UI.Page для обычной исполнимой страницы
- Элемент управления ContentPlaceHolder используется для резервирования именованных областей содержимого только в шаблонах. Если раскрыть вкладку Standard панели Toolbox при редактировании обычной страницы, то мы этого элемента просто не увидим , поскольку оболочка его скрывает до момента редактирования шаблона
- Элементы ContentPlaceHolder можно размещать в ячейках таблицы. При выполнении эти ячейки заполнятся присоединенным содержимым, а остальные ячейки можно занять заполнителем, который будет виден и доступен на любой странице, использующей этот шаблон
- При загрузке страницы во время выполнения вначале создается объект мастер-страницы, а потом сама страница с содержимым
- Во время выполнения именованные области мастер-страницы сжимаются до минимального размера содержимого
Генерация заготовки мастер-страницы
Шаблон представляет собой полноценную страницу, готовую к выполнению, но не самостоятельно, а через исполнимую страницу содержимого. Оболочка имеет мастер для создания заготовок шаблонов, как и обычных страниц. Для примера, давайте создадим простую эталонную шаблон-страницу и пустую (вначале) исполнимую страницу, чтобы испытать шаблон.
- Добавьте к проекту командой Website/Add New Item шаблон с именем FirstMasterPage.master с разделяемым кодом
Вот какой дескрипторный код создал мастер
<%@ Master Language="C#" AutoEventWireup="true" CodeFile="FirstMasterPage.master.cs" Inherits="FirstMasterPage" %> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <title>Untitled Page</title> </head> <body> <form id="form1" runat="server"> <div> <asp:contentplaceholder id="ContentPlaceHolder1" runat="server"> </asp:contentplaceholder> </div> </form> </body> </html>
Этот код почти такой-же, как и у заготовки обычной исполнимой страницы .aspx
- Удалите дескриптор ContentPlaceHolder и через контекстное меню редактора выполните команду Add Content Page (добавить страницу содержимого)
Оболочка сгенерирует такую заготовку файла Default.aspx
<%@ Page Language="C#" MasterPageFile="~/FirstMasterPage.master" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" Title="Тестовая страница содержимого" %> <%-- Add content controls here --%>
Пустой файл, - только директива @Page страницы со стандартными атрибутами страницы и атрибутом подключения шаблона.
Оболочка прописала значение атрибута подключения MasterPageFile с путем относительно корня Web-дерева нашего сайта (ну, там наш шаблон и находится). Если уже подключенный к каким-то страницам шаблон переместить из корня в другую папку Web-дерева, то оболочка не меняет автоматически путь атрибута MasterPageFile во всех ссылающихся на шаблон страницах, это придется делать вручную (ох и работы, можно запутаться!). Поэтому нужно заранее спланировать, будем ли мы использовать мастер-страницы и где они будут находиться.
- Создайте в панели Solution Explorer через контекстное меню для Web-корня подпапку MasterPages командой New Folder
- Переместите мышью в папку MasterPages шаблон FirstMasterPage.master
Теперь давайте откорректируем дескрипторный код исполнимой страницы.
- В панели Solution Explorer дайте файлу Default.aspx новое имя FirstMasterPage.aspx
-
Откорректируйте дескрипторный код страницы FirstMasterPage.aspx так
<%@ Page Language="C#" MasterPageFile="~/MasterPages/FirstMasterPage.master" AutoEventWireup="true" CodeFile="FirstMasterPage.aspx.cs" Inherits="FirstMasterPage" Title="Тестовая страница содержимого" %> <%-- Add content controls here --%>
- Откройте файл FirstMasterPage.aspx.cs командой View Code контекстного меню страницы и измените имя _Default класса страницы на FirstMasterPage
- Назначьте страницу FirstMasterPage.aspx стартовой и исполните ее, чтобы убедиться в отсутствии ошибок
Появилась пустая страница с фоном, определенным темой по умолчанию MyTheme, включенной нами ранее в конфигурационный файл Web.Config
<configuration> <system.web> <pages theme="MyTheme" /> </system.web> </configuration>
Никакого содержимого пока страница не имеет.
- Добавьте в файл FirstMasterPage.aspx произвольный текст, например
<%@ Page Language="C#" MasterPageFile="~/MasterPages/FirstMasterPage.master" AutoEventWireup="true" CodeFile="FirstMasterPage.aspx.cs" Inherits="FirstMasterPage" Title="Тестовая страница содержимого" %> <%-- Add content controls here --%> <h1>Привет студентам из "страницы содержимого1"!</h1>
Если сейчас исполнить такую страницу, то мы получим ошибку времени выполнения! Отсюда мораль:
<%@ Page Language="C#" MasterPageFile="~/MasterPages/FirstMasterPage.master" AutoEventWireup="true" CodeFile="FirstMasterPage.aspx.cs" Inherits="FirstMasterPage" Title="Тестовая страница содержимого" %> <%-- Add content controls here --%> <asp:Content> <h1>Привет студентам из "страницы содержимого1"!</h1> </asp:Content>
Опять ошибка, опять мораль:
- Откройте на редактирование файл ~/MasterPages/FirstMasterPage.master в режиме Design и поместите на шаблон из вкладки Standard элемент управления ContentPlaceHolder, экземпляр которого мы ранее удалили
-
Перейдите в режим Source на странице FirstMasterPage.master и поместите внутрь созданного контейнера ContentPlaceHolder1 текстовую строку, чтобы дескрипторный код стал таким
<%@ Master Language="C#" AutoEventWireup="true" CodeFile="FirstMasterPage.master.cs" Inherits="FirstMasterPage" %> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <title>Untitled Page</title> </head> <body> <form id="form1" runat="server"> <div> <asp:ContentPlaceHolder ID="ContentPlaceHolder1" runat="server"> <h1>Привет студентам из "именованной области1" мастер-страницы!</h1> </asp:ContentPlaceHolder> </div> </form> </body> </html>
- Перейдите в режим Design и убедитесь, что именованная область шаблона приобрела свое собственное содержимое
Теперь нужно настроить страницу содержимого, использующую шаблон.
- Откройте на редактирование в режиме Source страницу содержимого FirstMasterPage.aspx, к которой подключен шаблон, и определите в контейнере <asp:Content> атрибуты ID самого дескриптора страницы и ContentPlaceHolderID подключенного шаблона, на которую будет ссылаться дескриптор содержимого. Пользуйтесь услугами подсказчика кода IntelliSense, который вызывается каждый раз при добавлении пробела внутри дескриптора.
Идентификатор ID самого контейнера <asp:Content> в странице содержимого определять необязательно, если только мы не планируем управлять им программно. Измененный код будет таким
<%@ Page Language="C#" MasterPageFile="~/MasterPages/FirstMasterPage.master" AutoEventWireup="true" CodeFile="FirstMasterPage.aspx.cs" Inherits="FirstMasterPage" Title="Тестовая страница содержимого" %> <%-- Add content controls here --%> <asp:Content ContentPlaceHolderID="ContentPlaceHolder1" runat="server"> <h1>Привет студентам из "страницы содержимого1"!</h1> </asp:Content>
- Исполните страницу FirstMasterPage.aspx, должен получиться такой результат
-
Поместите на шаблон еще один контейнер шаблона ContentPlaceHolder и наполните его таким кодом
<%@ Master Language="C#" AutoEventWireup="true" CodeFile="FirstMasterPage.master.cs" Inherits="FirstMasterPage" %> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <title>Untitled Page</title> </head> <body> <form id="form1" runat="server"> <div> <asp:ContentPlaceHolder ID="ContentPlaceHolder1" runat="server"> <h1>Привет студентам из "именованной области1" мастер-страницы!</h1> </asp:ContentPlaceHolder> <br /> <asp:ContentPlaceHolder ID="ContentPlaceHolder2" runat="server"> <h1>Привет студентам из "именованной области2" мастер-страницы!</h1> </asp:ContentPlaceHolder> </div> </form> </body> </html>
- Перейдите на страницу содержимого FirstMasterPage.aspx в режиме Design, которая станет такой
Мы видим, что мастер-страница задает схему размещения на итоговой странице. Причем, если на итоговой странице есть контейнер с содержимым, привязанный к именованной области шаблона, то на место именованной области вставляется его содержимое. Если контейнер с содержимым есть, но пустой, то ничего не вставляется. Но если контейнера с содержимым вообще нет, то на итоговой странице появляется содержимое именованной области.
Мы можем рассматривать итоговую страницу пользователя как двухслойную. На нижнем слое находится мастер-страница с заготовленными контейнерами, которые верхний слой потенциально готов переопределить. На верхнем прозрачном слое находятся контейнеры переопределяющего содержимого, закрывающие закрепленные за ними контейнеры нижнего слоя.
Иными словами, именованная область шаблона спроецируется на итоговую страницу всегда: либо с содержимым (в том числе пустым) прикрепленного к ней контейнера, либо со своим содержимым (при его наличии), если для нее нет прикрепленного контейнера. О пустых полях внизу областей беспокоиться не нужно. На этапе выполнения они исчезнут. Просто, оболочка выделяет минимально необходимую площадь для их заполнения нужными элементами управления на этапе проектирования.
Обратите внимание на заголовки именованных областей и их фон. Там, где подставлено содержимое прикрепленного контейнера, фон светлее и в скобках надпись ( Custom ) - заказной. Там, где проявилось содержимое мастер-страницы, фон темнее и надпись ( Master ).
Давайте добавим контейнер содержимого к нижней именованной области и наполним его.
- На странице FirstMasterPage.aspx в режиме Design вызовите контекстное меню для нижней именованной области и выполните команду Create Custom Content
Оболочка добавит пустой контейнер содержимого и автоматически свяжет его в дескрипторном коде с именованной областью. Отсюда еще одна мораль:
После того, как на вновь созданной странице с содержимым проявятся именованные области шаблона, их можно переопределить и заполнить, создав командой контекстного меню Create Custom Content контейнеры индивидуального содержимого. Удалить контейнер содержимого можно командой Default to Master's Content.
Если именованные области шаблона пустые и их не переопределять индивидуальным содержимым для какой-то страницы, то на этапе выполнения эта область не будет видна.