Мастер-страницы

167

Мастер-страницы - это средство ASP.NET, разработанное специально для стандартизации компоновки веб-страниц. Мастер-страницы представляют собой шаблоны веб-страниц, которые могут определять фиксированное содержимое и объявлять часть веб-страницы, куда можно помещать нестандартное содержимое. При использовании одной и той же мастер-страницы во всем веб-сайте компоновка гарантированно будет одинаковой. Более того, если изменить определение мастер-страницы после ее применения, то все использующие ее веб-страницы автоматически воспримут это изменение.

Чтобы можно было обеспечить практическое и гибкое решение для создания шаблонов страниц, должен быть удовлетворен ряд требований:

Мастер-страницы соответствуют всем этим требованиям. Они обеспечивают систему для повторного использования шаблонов, способ ограничения модификации шаблонов и широкую поддержку во время проектирования.

Чтобы это все работало, в ASP.NET определены два новых типа страниц: мастер-страницы и страницы содержимого. Мастер-страница представляет собой шаблон страницы. Как и обычная веб-страница ASP.NET, она может содержать любую комбинацию HTML, веб-элементов управления и даже кода. Кроме того, мастер-страницы могут включать заполнители содержимого - определенные модифицируемые области. Каждая страница содержимого ссылается на одну мастер-страницу и получает ее компоновку и содержимое. Также страница содержимого может добавлять характерное для страницы содержимое в любые заполнители. Другими словами, страница содержимого заполняет отсутствующие части, которые не определены в мастер-странице.

Например, на обычном веб-сайте мастер-страница может включать фиксированный элемент, например, верхний колонтитул, и заполнитель содержимого для остальной части страницы. Страница содержимого получает этот колонтитул и предоставляет дополнительное содержимое.

Чтобы лучше понять, как это работает, давайте рассмотрим пример.

Простая мастер-страница

Чтобы создать мастер-страницу в Visual Studio, выберите пункт меню WebSite --> Add New Item. Затем укажите элемент Master Page, назначьте имя (например, SiteTemplate.master) и щелкните на кнопке Add.

Мастер-страница подобна обычной веб-форме ASP.NET. Как и веб-форма, мастер-страница может включать HTML-разметку, веб-элементы управления и код (встроенный блок сценария либо отдельный файл). Одно различие между ними заключается в том, что веб-формы начинаются с директивы Page, а мастер-страница - с директивы Master, определяющей ту же информацию:

<%@ Master Language="C#" AutoEventWireup="true" CodeFile="SiteTemplate.master.cs"
	Inherits="SiteTemplate" %>

Другое различие между мастер-страницами и обычными веб-формами состоит в том, что мастер-страницы могут использовать элемент управления ContentPlaceHolder, а обычные страницы - нет, ContentPlaceHolder является частью страницы, в которую страница содержимого может вставлять содержимое.

При создании новой мастер-страницы в Visual Studio процесс начинается с создания пустой страницы, включающей два элемента управления ContentPlaceHolder. Один определен в разделе <head>, благодаря чему страницы содержимого получают возможность добавлять метаданные страницы, такие как ключевые слова поиска или ссылки на таблицы стилей.

Второй, более важный, элемент управления ContentPlaceHolder определен в разделе <body> и представляет видимое содержимое страницы. Чтобы создать более сложные компоновки страницы, можно добавить дополнительную разметку и элементы управления ContentPlaceHolder.

Элемент управления ContentPlaceHolder не имеет никаких примечательных свойств. Ниже приведен пример кода, в котором создается мастер-страница со статическим баннером, элементом управления ContentPlaceHolder и нижним колонтитулом:

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
    <asp:ContentPlaceHolder id="head" runat="server">
    </asp:ContentPlaceHolder>
</head>
<body>
    <form id="form1" runat="server">
        <div>
            <div style="background: black; height: 87px; font-weight: bold; font-size: 20px; color: white; font-family: Verdana">

                <img style="float:left" src="headerleft.jpg" />
                <img style="float:right" src="headerright.jpg" />
                <br />
                <asp:ContentPlaceHolder ID="TitleContent" runat="server">
                    <h1 style="font-size:20px; text-align:center">My Site</h1>
                </asp:ContentPlaceHolder>
            </div>

            <br /> <br />
            <asp:ContentPlaceHolder ID="ContentPlaceHolder1" runat="server">
            </asp:ContentPlaceHolder>
            <br />
            <em>Copyright © 2012.</em>
        </div>
    </form>
</body>
</html>
Мастер-страница во время проектирования

Мастер-страницу нельзя запрашивать напрямую. Чтобы использовать мастер-страницу, потребуется создать связанную страницу содержимого.

Простая страница содержимого

Для использования созданной мастер-страницы в другой веб-странице необходимо добавить атрибут MasterPageFile к директиве Page. Этот атрибут показывает имя файла требуемой мастер-таблицы:

<%@ Page Language="C#" MasterPageFile="~/SiteTemplate.master" ... >

Обратите внимание, что атрибут MasterPageFile начинается с пути ~/, который указывает корневую папку веб-сайта.

Установки атрибута MasterPageFile недостаточно для преобразования простой страницы в страницу содержимого. Проблема в том, что единственная обязанность страницы содержимого - определение содержимого, которое будет вставлено в один или несколько элементов управления ContentPlaceHolder (а также запись любого необходимого для этих элементов управления кода). Страница содержимого не определяет страницу, поскольку внешняя оболочка уже предоставлена мастер-страницей. В результате попытка включения таких элементов, как <html>, <head> и <body>, ни к чему не приведет, поскольку они уже определены на мастер-странице.

Чтобы предоставить содержимое для элемента управления ContentPlaceHolder, используется другой специализированный элемент управления, называемый Content. Элемент управления ContentPlaceHolder и Content связаны отношением "один к одному". Для каждого элемента управления ContentPlaceHolder в мастер-странице страница содержимого предоставляет соответствующий элемент управления Content (если для данной области вообще должно предоставляться содержимое).

ASP.NET связывает элемент управления Content с соответствующим элементом управления ContentPlaceHolder, сопоставляя ID элемента ContentPlaceHolder со свойством Content.ContentPlaceHolderID соответствующего элемента управления Content. Если создать элемент управления Content, который будет ссылаться на несуществующий элемент управления ContentPlaceHolder, во время выполнения возникнет ошибка.

Среда Visual Studio позволяет дополнительно облегчить создание новой страницы содержимого. Выберите пункт меню Website --> Add New Item. Укажите элемент Web Form, установите флажок Select Master Page (Выбрать мастер-страницу) и щелкните на кнопке OK. Visual Studio предложит выбрать файл мастер-страницы из текущего веб-проекта. После этого Visual Studio автоматически создаст элемент управления Content для каждого элемента управления Content PlaceHolder на мастер-странице.

Таким образом, чтобы создать полную страницу содержимого, которая использует мастер-страницу SiteTemplate, нужно просто заполнить содержимое для элемента управления ContentPlaceHolder с идентификатором ContentPlaceHolder1. Ниже приведен код всей страницы:

<%@ Page Language="C#" MasterPageFile="~/SiteTemplate.master" Title="Основы ASP.NET"
     AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default"  %>

<asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">
    <span style="font-size: 14px">
        Заполнение контентом страницы-содержимого Default.aspx
    </span>
    <br />
</asp:Content>

В этом примере директива Page задает атрибуты MasterPageFile и Title. Атрибут Title позволяет указать заголовок страницы содержимого, заменяя таким образом заголовок, который задан в мастер-странице. Этот вариант будет работать в том случае, если в дескрипторе <head> задан атрибут runat="server" (это делается по умолчанию).

Как видите, страницы содержимого совершенно пусты, поскольку они не включают никаких деталей, определенных в мастер-странице. Более того, это облегчает обновление веб-сайта. Для этого достаточно модифицировать одну мастер-страницу. До тех пор пока сохраняются одни и те же элементы управления ContentPlaceHolder, существующие страницы содержимого будут работать, и будут вставляться в любую указанную новую компоновку. Этот пример страницы содержимого показан на рисунке ниже:

Страница содержимого во время выполнения

Для динамического конфигурирования мастер-страницы или страницы содержимого можно воспользоваться событием Page.Load в любом классе. В некоторых случаях код инициализации придется помещать и в мастер-страницу, и в страницу содержимого. В таких ситуациях важно понять порядок, в котором генерируются соответствующие события.

Сначала ASP.NET создает элементы управления для мастер-страницы, а затем дочерние элементы управления для страницы содержимого. После этого генерируется событие Page.Init для мастер-страницы, а за ним - такое же событие для страницы содержимого. Аналогичная последовательность имеет место и для события Page.Load. Таким образом, в случае возникновения конфликта настройки, выполняемые в странице содержимого (например, изменение заголовка страницы), получают преимущество по сравнению с изменениями, которые выполняются на этом же этапе в мастер-странице.

Мастер-страницы и форматирование

Мастер-страницы предлагают несколько интересных возможностей для стандартизации форматирования. Например, можно подключиться к мастер-странице, не используя темы, за счет добавления элемента <link> в раздел <head> мастер-страницы. Таким образом, таблица стилей будет автоматически применяться ко всем страницам содержимого, которые используют данную мастер-страницу.

Можно также воспользоваться более детальной моделью, благодаря которой мастер-страница поможет применять различное форматирование к разным разделам страницы содержимого. Для этого достаточно установить подходящие цвета фона и переднего плана, шрифты и опции выравнивания с помощью дескрипторов контейнера в мастер-странице. Например, их можно задать в таблице, ячейке таблицы, дескрипторе <div> или в элементе управления Panel. После этого информация из страницы содержимого может беспрепятственно передаваться в эти контейнеры, автоматически принимая подходящие атрибуты стилей.

Содержимое по умолчанию

Когда мастер-страница определяет элемент управления ContentPlaceHolder, она может также включать содержимое по умолчанию - содержимое, которое будет использовано только в том случае, если страница содержимого не предоставит соответствующий элемент управления Content.

Чтобы добиться этого эффекта, достаточно поместить соответствующие элементы управления HTML или веб-элементы управления в дескриптор ContentPlaceHolder. (Это можно сделать либо вручную в разметке .aspx, либо перетащить элементы управления в ContentPlaceHolder.)

В предыдущем примере содержимое по умолчанию добавляется к тексту баннера, который мы не переопределяли в странице содержимого:

<asp:ContentPlaceHolder ID="TitleContent" runat="server">
	<h1 style="font-size:20px; text-align:center">My Site</h1>
</asp:ContentPlaceHolder>

Если страница содержимого создается в Visual Studio, вы не заметите никаких немедленных изменений. Дело в том, что Visual Studio автоматически создает дескриптор <Content> для каждого элемента управления ContentPlaceHolder. Если страница содержимого включает дескриптор <Content>, он автоматически замещает содержимое, используемое по умолчанию. Однако если дескриптор <Content> удалить, отобразится содержимое по умолчанию - новый текст баннера "My Site".

Страницы содержимого не могут использовать только часть содержимого по умолчанию, а также изменять его. Это невозможно, потому что содержимое по умолчанию хранится только в мастер-странице, а не в странице содержимого. В результате придется либо использовать содержимое по умолчанию в том виде, каком оно существует, либо заменять его полностью.

Мастер-страницы и относительные пути

Ничего не подозревающий разработчик может быть ошеломлен тем, каким образом мастер-страницы обрабатывают относительные пути. При использовании только статического текста это не имеет значения. Однако при добавлении дескрипторов <img> или любого другого HTML-дескриптора, указывающего на другой ресурс, может возникнуть проблема.

Проблема проявится в том случае, если мастер-страница и использующая ее страница содержимого оказываются в разных каталогах. Такую практику рекомендуется применять для крупных веб-сайтов. В Microsoft предлагается предусмотреть специальную папку для хранения всех мастер-страниц. Однако при невнимательном использовании этот подход может породить проблемы, связанные с относительными путями.

Предположим, например, что мастер-страница была помещена в подпапку по имени MasterPages и в мастер-страницу был добавлен следующий дескриптор <img>:

<img src="banner.jpg" />

Если файл \MasterPages\banner.jpg существует, то, на первый взгляд, все работает нормально. Изображение будет даже появляться в среде проектирования Visual Studio. Однако если создать страницу содержимого в другой подпапке, то путь будет интерпретироваться по отношению к этой папке. Если в ней не будет этого файла, то вместо изображения вы получите неработающую ссылку. Более того, если какое-то изображение будет храниться в файле с таким же именем, вы получите еще и неверное изображение.

Эта проблема возникает из-за того, что дескриптор <img> является обычным HTML-дескриптором. В результате ASP.NET даже не обратится к нему. К сожалению, когда ASP.NET создает вашу страницу содержимого, этот дескриптор не имеет никакого значения. Та же проблема возникает с дескрипторами <a>, которые содержат относительные ссылки на другие страницы, и с элементом <link>, который можно использовать для подключения мастер-страницы к таблице стилей.

Для решения этой проблемы можно попробовать заранее записать свой URL-адрес относительно страницы содержимого, в которой его нужно использовать. Но это создает путаницу, ограничивает места использования мастер-страницы и оказывает нежелательный побочный эффект неправильного отображения мастер-страницы в среде проектирования.

Еще один быстрый способ решения этой проблемы - преобразование дескриптора изображения в элемент управления серверной стороны, в результате чего ASP.NET исправит ошибку:

<img src="banner.jpg" runat="server" />

Это работает потому, что ASP.NET применяет данную информацию для создания серверного элемента управления HtmlImage. Этот объект создается после создания экземпляра объекта Page для мастер-страницы. С этого момента ASP.NET будет интерпретировать все пути относительно местонахождения мастер-страницы. Такой прием можно использовать и для корректировки дескрипторов <a>, содержащих относительные ссылки на другие страницы.

Можно также применять синтаксис указания пути к корневому каталогу и начинать URL-адрес с символа тильды (~) или точки. Например, следующий дескриптор <img> однозначно указывает на файл baner.jpg в подпапке MasterPages веб-сайта:

<img src="./MasterPages/banner.jpg" runat="server" />

К сожалению, этот синтаксис подходит только для элементов управления серверной стороны. Если вы хотите получить аналогичный эффект с обычным элементом HTML, понадобится изменить ссылку на полный относительный путь, включающий доменное имя. В результате получается громоздкая и непереносимая HTML-разметка, поэтому данный способ применять не рекомендуется.

Применение мастер-страниц через конфигурационный файл

Следует отметить, что мастер-страницу можно применить также сразу ко всем страницам веб-сайта с помощью файла web.config. Для этого достаточно добавить атрибут <pages> и задать его атрибут masterPageFile:

<configuration>
	<system.web>
		<pages masterPageFile="~/SiteTemplate.master"/>
	</system.web>
</configuration>

Проблема в том, что этот подход недостаточно гибок. Любая существующая веб-страница, которая "играет не по правилам" (например, содержит корневой дескриптор <html> или определяет область содержимого, которая не соответствует элементу управления ContentPlaceHolder), автоматически окажется разрушенной. Если требуется использовать именно такой вариант, не применяйте его ко всему сайту. Вместо этого создайте подпапку для страниц содержимого, а в ней файл web.config для применения мастер-страницы.

Даже если мастер-страница применяется через файл web.config, нет никакой гарантии, что отдельная страница не переопределит настройки, устанавливая атрибут MasterPageFile в директиве Page. И если атрибут MasterPageFile указан с пустой строкой, страница вообще не будет иметь никакой мастер-страницы, независимо от того, что задано в файле web.config.

Пройди тесты
Лучший чат для C# программистов