Опубликован: 13.07.2010 | Уровень: специалист | Доступ: платный
Самостоятельная работа 31:

Стилевое оформление страниц

Первый способ
  • Двойным щелчком на свободном от элементов управления месте мастер-страницы Title.master в режиме Design (или вручную) создайте блок скриптов, который заполните так
<%@ Master Language="C#" %>
  
<script runat="server">
  
    protected void Page_Load(object sender, EventArgs e)
    {

    }
  
    public string MyHeader1
    {
        get { return Header1.InnerText; }
        set { Header1.InnerText = value; }
    }
</script>
  
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Мастер-страница Title.master</title>
</head>
<body>
    <form id="form1" runat="server">
        <div style="text-align: center">
            <asp:Image ID="ImageBanner" runat="server" ImageUrl="~/MasterPages/Banner.png" />
            <h1 style="color: Green" runat="server" id="Header1">
                Мы приветствуем Вас на нашем сайте!!!
            </h1>
      .............................................
        </div>
    </form>
</body>
</html>
Листинг 31.54. Добавление свойства доступа к дескриптору <h1> в файле Title.master
  • Дополните контекстную страницу SatelliteExt.aspx следующим кодом
<%@ Page Language="C#" MasterPageFile="~/MasterPages/Title.master" 
    Title="Декларативный заголовок SatelliteExt.aspx" %>
    
<%@ MasterType VirtualPath="~/MasterPages/Title.master" %>
    
<script runat="server">
    
    protected void Page_Load(object sender, EventArgs e)
    {
        // Получаем ссылку на мастер-страницу
        Page masterPage = Master.Page;
        
        // Переопределяем строку заголовка
        masterPage.Header.Title = "Программный заголовок SatelliteExt.aspx";
        
        // Управляем дескриптором h1
        Master.MyHeader1 = "Это программно подмененный"
                         + " заголовок мастер-страницы (способ 1)!";
   }
</script>
Листинг 31.55. Дополненный код контекстной страницы SatelliteExt.aspx
  • Исполните контекстную страницу SatelliteExt.aspx и убедитесь, что дескриптор <h1>, относящийся к заполнителю мастер-страницы, подменяется нашим текстом


Директива @ MasterType указывает ASP.NET виртуальный путь к соответствующему файлу мастер-страницы. Без нее доступ к объявленному свойству будет неопределен.

Второй способ
  • Измените контекстную страницу SatelliteExt.aspx следующим образом
<%@ Page Language="C#" MasterPageFile="~/MasterPages/Title.master" 
    Title="Декларативный заголовок SatelliteExt.aspx" %>
    
<%@ MasterType VirtualPath="~/MasterPages/Title.master" %>
    
<script runat="server">
    
    protected void Page_Load(object sender, EventArgs e)
    {
        // Получаем ссылку на мастер-страницу
        Page masterPage = Master.Page;
        
        // Переопределяем строку заголовка
        masterPage.Header.Title = "Программный заголовок SatelliteExt.aspx";
        
        // Управляем дескриптором h1
//        Master.MyHeader1 = "Это программно подмененный"
//                         + " заголовок мастер-страницы (способ 1)!";
    
        HtmlGenericControl h1 = Master.FindControl("Header1") as HtmlGenericControl;
        if (h1 != null)
        {
            h1.InnerText = "Это программно подмененный"
                         + " заголовок мастер-страницы (способ 2)!";
        }
   }
</script>
Листинг 31.56. Измененный код контекстной страницы SatelliteExt.aspx

Метод FindControl() ищет указанный элемент на мастер-странице среди элементов заданного типа, и если находит, то возвращает контекстной странице ссылку на этот элемент. Затем уже мы обращаемся к конкретным свойствам этого элемента.

  • Исполните страницу SatelliteExt.aspx и убедитесь, что дескриптор <h1>, относящийся к заполнителю мастер-страницы, подменяется нашим текстом


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

Упражнение 16. Программное переключение мастер-страниц

Иногда может возникнуть необходимость дать возможность пользователю подключать необходимые мастер-страницы. Мы уже рассматривали подобный способ, когда говорили о динамическом подключении тем. Там мы программно определяли значение атрибута Page.Theme в директиве @Page. Теперь нам нужно программно менять атрибут Page.MasterPageFile в коде контекстной страницы.

Здесь нужно иметь ввиду следующее:

  1. Контекстная страница, динамически меняющая шаблон, не должна иметь контекстных блоков, ссылающихся на несуществующие в новой мастер-странице контейнеры ContentPlaceHolderID
  2. Замена шаблона должна выполняться в обработчике события Page.PreInit контекстной страницы. Если это сделать в обработчиках более поздних событий, то будет сгенерировано исключение
  3. Все контекстные страницы, на которые имеются ссылки в текущей странице содержимого, должны иметь код подключения выбранного шаблона
  4. Для межстраничной передачи информации о выбранном шаблоне ее нужно где-то сохранять. Как и в случае с темами, это могут быть скрытые поля состояния вида (VIEWSTATE), глобальные объекты уровня сессии или приложения

Учитывая, что наши страницы Satellite.aspx, Satellite1.aspx, Satellite2.aspx, Satellite3.aspx согласованы с Title.master, сделаем их копии и настроим под динамическую смену мастер-страниц.

  • В панели Solution Explorer создайте копию файла Title.master с именем Title1.master
  • Откройте мастер-страницу Title1.master и добавьте выше объекта ImageBanner (после открывающего дескриптора <div> ) элемент ContentPlaceHolder с именем ContentPlaceHolderList
  • Добавьте к гиперссылкам HyperLink еще одну с названием " Автошкола в целом "
  • Сохраните и закройте файл Title1.master
  • В панели Solution Explorer создайте копию файла Title1.master с именем Title2.master
  • Сделайте копии файлов контекстных страниц Satellite?.aspx и присвойте им имена SatelliteDynamic.aspx, SatelliteDynamic1.aspx, SatelliteDynamic2.aspx, SatelliteDynamic3.aspx и SatelliteDynamic4.aspx соответственно
  • Определите страницу SatelliteDynamic.aspx стартовой
  • Откройте мастер-страницу Title2.master и выполните в ней следующее:
    • Полностью удалите контейнер скриптов <script runat="server">...</script>
    • Удалите объект ImageBanner и внесите еще какие-нибудь визуально отличимые изменения
    • Заполните свойство NavigateUrl гиперссылок адресами новых контекстных страниц, а ссылку " Автошкола в целом " адресуйте на SatelliteDynamic.aspx
  • В панели Solution Explorer выделите сразу все контекстные страницы SatelliteDynamic?.aspx и откройте их в режиме Source, чтобы вручную заменить атрибут MasterPageFile="~/MasterPages/Title.master" на MasterPageFile="~/MasterPages/Title1.master"
  • Откройте контекстную страницу SatelliteDynamic.aspx и в режиме Design создайте контейнер содержимого для шаблона ContentPlaceHolderList, выполнив на нем команду Create Custom Content контекстного меню
  • Добавьте в контейнер контекста предваряющий текст " Выберите мастер-страницу:" и элемент DropDownList с именем lstMasterPages
  • Заполните раскрывающийся список lstMasterPages так
  Text Value
Items[0] Мастер-страница с баннером ~/MasterPages/Title1.master
Items[1] Мастер-страница без баннера ~/MasterPages/Title2.master
  • Установите для списка lstMasterPages свойство AutoPostBack="True"
  • В режиме Design двойным щелчком на тускло просвечивающемся заполнителе мастер-страницы создайте контейнер для скриптов, обработчик Page_Load() и переименуйте его в Page_Init()
  • Скопируйте заготовку обработчика Page_Init() и переименуйте копию в Page_PreInit(), поскольку все они имеют одинаковую сигнатуру
  • В режиме Design двойным щелчком на объекте lstMasterPages списка создайте обработчик изменения выбора опции с именем lstMasterPages_SelectedIndexChanged()
  • Заполните обработчики, чтобы окончательный код страницы был таким
<%@ Page Language="C#" MasterPageFile="~/MasterPages/Title1.master" Title="Untitled Page" %>
    
<script runat="server">
    
    protected void Page_Init(object sender, EventArgs e)
    {
        if (Session["index"] != null)
        {
            // Восстанавливаем новое состояние списка
            lstMasterPages.SelectedIndex = (int)Session["index"];
        }
    }
    
    protected void Page_PreInit(object sender, EventArgs e)
    {
        if (Session["Master"] != null)
        {
            // Был ранее сохраненный выбор, включить его
            Page.MasterPageFile = (string)Session["Master"];
        }
    }
    
    protected void lstMasterPages_SelectedIndexChanged(object sender, EventArgs e)
    {
        // Запоминаем новый выбор пользователя
        Session["Master"] = lstMasterPages.SelectedValue;
        // Запоминаем новое состояние списка
        Session["index"] = lstMasterPages.SelectedIndex;
    
        // Обновляем страницу без полного цикла,
        // чтобы новая мастер-страница вступила в действие
        Server.Transfer(Request.FilePath);
    }
</script>
    
<asp:Content ID="Content1" runat="server" ContentPlaceHolderID="ContentPlaceHolderList">
    Выберите мастер-страницу:
    <asp:DropDownList ID="lstMasterPages" runat="server" 
            OnSelectedIndexChanged="lstMasterPages_SelectedIndexChanged" 
            AutoPostBack="True">
        <asp:ListItem Value="~/MasterPages/Title.master">Мастер-страница с баннером
        </asp:ListItem>
        <asp:ListItem Value="~/MasterPages/Title1.master">Мастер-страница без баннера
        </asp:ListItem>
    </asp:DropDownList>
</asp:Content>
Листинг 31.57. Окончательный код страницы SatelliteDynamic.aspx
  • Откройте поочередно файлы SatelliteDynamic1.aspx, SatelliteDynamic2.aspx, SatelliteDynamic3.aspx , SatelliteDynamic4.aspx и добавьте в каждый из них после директивы @Page такой блок скриптов
<script runat="server">
  
    protected void Page_PreInit(object sender, EventArgs e)
    {
        if (Session["Master"] != null)
        {
            // Был ранее сохраненный выбор, включить его
            Page.MasterPageFile = (string)Session["Master"];
        }
    }
</script>
Листинг 31.58. Скрипты остальных контекстных страниц для загрузки выбранного шаблона
  • Исполните начальную страницу SatelliteDynamic.aspx и убедитесь, что мы дали пользователю полнофункциональный механизм выбора шаблонов форматирования

Упражнение 17. Многослойное подключение мастер-страниц

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

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

  • Создайте в корне Web-дерева мастер-страницу нижнего уровня с именем MasterPageLevel1.master

  • Код страницы сделайте таким
<%@ Master Language="C#" %>
    
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Untitled Page</title>
</head>
<body>
    <form id="form1" runat="server">
        <div style="text-align: center; color: #0000ff">
            <h1>
                Мастер-страница уровня Level1 - наибольшей глубины. 
                Ее можно использовать как верхний колонтитул
            </h1>
            <asp:ContentPlaceHolder ID="ContentPlaceHolderLevel1" runat="server">
            </asp:ContentPlaceHolder>
        </div>
    </form>
</body>
</html>
Листинг 31.59. Дескрипторный код страницы MasterPageLevel1.master
  • Теперь также в корне Web-дерева создайте мастер-страницу верхнего уровня с именем MasterPageLevel0.master и подключите к ней предыдущую через атрибут MasterPageFile директивы @Master

Код страницы должен выглядеть так

<%@ Master Language="C#" MasterPageFile="~/MasterPageLevel1.master" %>
    
<asp:Content ID="ContentLevel0" ContentPlaceHolderID="ContentPlaceHolderLevel1" runat="server">
    <table style="background-color: #ccff00" border="1">
        <tr>
            <td colspan="2">
                <h2>
                    Мастер-страница уровня Level0 - верхнего уровня
                </h2>
            </td>
        </tr>
        <tr>
            <td style="width: 200px">
            <strong>Здесь можно расположить элементы навигации</strong>
            </td>
            <td style="background-color: #ccffff">
                <asp:ContentPlaceHolder ID="ContentPlaceHolderLevel0" runat="server">
                </asp:ContentPlaceHolder>
            </td>
        </tr>
    </table>
</asp:Content>
Листинг 31.60. Дескрипторный код страницы MasterPageLevel0.master

Обратите внимание, что при попытке переключить созданную мастер-страницу в режим Design оболочка выдает предупреждение

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

  • Выполните для страницы MasterPageLevel0.master команду Website/Add Content Page, чтобы создать для нее контекстную страницу
  • Измените имя по умолчанию Default.aspx созданной контекстной страницы на TestMasterPageLevel.aspx и отредактируйте ее так
<%@ Page Language="C#" MasterPageFile="~/MasterPageLevel0.master" Title="Untitled Page" %>
    
<asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolderLevel0" runat="Server">
    <strong>Это есть заменяющее содержимое для мастер-страницы верхнего уровня Level0</strong>
</asp:Content>
Листинг 31.61. Дескрипторный код страницы TestMasterPageLevel.aspx
  • Назначьте контекстную страницу TestMasterPageLevel.aspx стартовой и исполните ее

Результат будет таким

Итак, все работает, но нам это не надо - слишком запутано и посмотреть в Design нельзя!

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

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

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

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


Александр Очеретяный
Александр Очеретяный
Украина, Киев
Анастасия Балыбердина
Анастасия Балыбердина
Украина, Киев, НТУУ КПИ