Практикум прикладного программирования на C# в среде VS.NET 2005

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

Разбить на страницы
Показывать лекцию целиком

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

Файлы к лабораторной работе Вы можете скачать здесь.

Единообразие в оформлении множества отдельных страниц сайта является важной его характеристикой. Visual Studio 2005 имеет три возможности для поддержки стилевого оформления (форматирования) сайта:

  • Каскадные таблицы стилей ( CSS - Cascading Style Sheet) - типовое средство языка HTML для управления стилями тегов
  • Темы (theme) - специфическое средство ASP.NET, позволяющее определить детали оформления отдельных элементов управления и воспроизводить их на множестве страниц, где есть связанные с темой элементы. Можно заранее создать несколько разных тем и переключать их, если необходимо изменить стилевое оформление страниц, использующих темы
  • Эталонные страницы (master page) - представляют собой многократно используемые шаблоны страниц. Их еще называют мастер-страницами. В шаблоне можно определить необходимые детали оформления, такие как заголовки, панели меню, банеры и связать их с обычными страницами. После этого все использующие шаблон страницы будут генерировать одинаковый дизайн
  • Каскадные таблицы стилей

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

  • Внутренняя (inline) - когда отдельный тег HTML имеет свои индивидуальные настройки стиля, областью действия которых является только этот тег
  • Встроенная (embedded) - когда таблица стилей находится на странице внутри блока <head> и имеет описание стилей общее для тех дескрипторов страницы, на которые она должна действовать. Это индивидуальная настройка стилей для страницы и бластью их действия являются теги только одного документа
  • Внешняя (linked) - когда описание стилей находится в отдельном файле и действуют они на те страницы сайта, к которым подключен этот файл
  • По отношению к этим трем типам CSS также соблюдается свойство каскадности, т.е. стиль внутреннего каскада имеет более высокий приоритет и перекрывает действие внешнего стиля.

    Кратко рассмотрим все перечисленные типы CSS и их поддержку оболочкой Visual Studio 2005.

    Упражнение 1. Внутренние стили inline

    Стиль отдельного дескриптора определяется атрибутом style. Синтаксис применения внутреннего стиля на примере дескриптора <h1> будет таким

    Значением стилевого атрибута является строка, представляющая пары ' стиль:значение ', отделенные друг от друга разделителем 'точка с запятой' или пробелом. Определенный в дескрипторе внутренний стиль будет действовать только на свой дескриптор и перекрывать все определения внешних стилей.

  • Командой главного меню оболочки File/New/Website создайте новый пустой проект с именем PageStyles
  • Командой главного меню оболочки Website/Add New Item добавьте в корневой узел проекта конфигурационный файл Web.config, который будет действовать на уровне всего сайта
  • Запустите утилиту WAT (Web Site Administration Tool) командой меню Website/ASP.NET Configuration (то же самое можно сделать в панели Solution Explorer, щелкнув на пиктограмме ASP.NET Configuration )
  • В открывшемся окне броузера на странице WAT перейдите на вкладку Application, щелкните на ссылке Configure debugging and tracing и в появившейся странице включите флажок Enable debugging
  • Выполненное действие установит в конфигурационном файле элемент debug в значение true.

    <configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
        <appSettings/>
        <connectionStrings/>
        <system.web>
            <compilation debug="true" />
            <authentication mode="Windows" />
        </system.web>
    </configuration>
  • Добавьте к проекту пассивную страницу Page1.htm
  • Поместите внутрь дескриптора <body> заголовочный дескриптор с внутренними стилевыми настройками, дескриптор горизонтальной линии и ссылку на следующую страницу
  • <h1 style="color: red; text-align: center">
            Этот заголовочный текст будет центрирован в 
            сгенерированном документе и иметь красный цвет!
        </h1>
        <hr />
        <a href=Page2.htm>Вперед</a>
  • Запустите текущую страницу и убедитесь, что внутренний стиль дескриптора действует
  • Упражнение 2. Встроенные стили embedded

    Определение внутренних стилей отдельных дескрипторов используется редко. Чаще применяются встроенные стили, которые действуют в пределах всей страницы. Встроенные стили не разбросаны по странице, а размещаются внутри дескрипторного блока <head>...</head> в специальном стилевом контейнере <style>...</style>. Синтаксис встроенных стилей следующий

    Атрибут type="text/css" дескриптора стиля указывает броузеру, что ниже перечислены стили, которые нужно применить к тексту при его отображении. Внутри стилевого блока описываются именованные группы пар 'стиль:значение' с разделителем 'точка с запятой', которые называются селекторами.

    Имена селекторов могут совпадать с именами дескрипторов, например, в приведенном выше примере селекторы body и h1 совпадают с именами дескрипторов HTML. В этом случае дополнительных указаний внутри дескрипторов не требуется.

    Если используется список селекторов с определенными идентификаторами, то описанный в списке стиль применяется к элементам страницы с указанными идентификаторами и дополнительных указаний внутри именованных дескрипторов не требуется.

    В качестве универсальных меток стилей могут использоваться селекторы классов " .имя_класса ". Тогда в тех дескрипторах документа, где необходимо применить определенный в классе стиль, должен присутствовать атрибут class=" имя_класса". Классы для конкретного вида тегов " тег.имя_класса " используются только в тегах заданного типа по той же самой схеме class=" имя_класса". Имена классов " .имя_класса " выбираются произвольно.

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

  • В панели Solution Explorer вызовите контекстное меню для корневого узла проекта и командой New Folder добавьте папку с именем Resources
  • В панели Solution Explorer вызовите контекстное меню для узла Resources и командой Add Existing Item скопируйте в папку Resources из прилагаемого к работе каталога Source файлы BackgroundImage.bmp и Web.jpg
  • Добавьте к проекту пассивную HTML-страницу с именем Page2.htm и заполните ее следующим кодом
  • <html xmlns="http://www.w3.org/1999/xhtml" >
    <head>
        <title>Документ со встроенной CSS</title>
        <meta content="text/html; charset=windows-1251" http-equiv="Content-Type">
        <style type="text/css">
            body
            {
              background:url(Resources/Web.jpg);
              font-family:Arial, Helvetica, sans-serif;
            }
      
            h1
            {
              color:#FF0000;
              text-align:center;
            }
      
            #TextBox1
            {
              font-family:"Courier New", Courier, mono;
              font-size:24px;
              font:bold italic;
              background-color:#0000FF;
              color:yellow;
              width:400;
            }
      
            .btnResult
            {
              background-image:url(Resources/BackgroundImage.bmp);
              color:white;
            }
        </style>
    </head>
    <body>
        <h1>
            Заполните анкету!
        </h1>
        <form id="form1" action="Page2.htm">
            <p>
                Введите ФИО:
                <input id="TextBox1" name="TextBox1" type="text">
            </p>
            <p>
                Введите ИНН:
                <input id="TextBox2" name="TextBox2" type="text">
            </p>
            <center>
                <input id="Button1" class="btnResult" name="Button1" 
                       type="button" value="Отослать">
            </center>
        </form>
    </body>
    </html>
  • Запустите страницу Page2.htm и проверьте результат
  • Упражнение 3. Внешние стили linked

    Внешними считаются стили, которые хранятся в отдельном файле текстового формата (чаще, для узнаваемости, - с расширением .css, но будет работать и любое другое расширение). Содержимое стиля в отдельном файле отличается от встроенного стиля:

  • Во внешнем стиле не должно быть дескрипторных скобок <style></style>
  • Подключение стилевого файла к HTML-документу выполняется добавлением в блок <head>...</head> дескриптора <link> или директивы @impot
  • Пусть имеется два файла с внешними стилями и произвольными именами, например, header.css и default.css. Варианты их подключения в HTML-документ Page3.htm ( обязательно в заголовочный блок <head>...</head> ) приведены в таблице:

    Варианты подключения CSS к HTML-документу
    Вариант В блоке <head> файла Page3.htm Замечания
    Подключается одна CSS <link rel="stylesheet" href="header.css">  
    Подключается две CSS
    <link rel="stylesheet" href="header.css">
    <link rel="stylesheet" href="default.css">
     
    Подключается одна CSS
    <style type="text/css">
        @import url(header.css);
    </style>
    Не поддерживается Netscape Navigator 4
    Подключается две CSS
    <style type="text/css">
        @import url(header.css);
        @import url(default.css); 
    </style>
    Не поддерживается Netscape Navigator 4
    Подключается две CSS ( в страницу импортируется цепочка CSS)
    <style type="text/css">
        @import url(header.css);
    </style>

    В самом начале файла header.css имеется голая строка прямого подключения: @import url(default.css)

    Подключается две CSS
    <style type="text/css">
        @import url(header.css);
    </style>
    <link rel="stylesheet" href="default.css">
    Порядок следования не влияет
    Подключается две CSS
    <link rel="stylesheet" href="default.css">
    <style type="text/css">
        @import url(header.css);
    </style>
    Порядок следования не влияет
    Подключается три CSS
    <link href="default.css" rel="stylesheet">
    <style type="text/css">
        
        @import url(header.css);
        
        body
        {
            background:white url(Web.jpg);
            font-family:Arial, Helvetica, sans-serif;
        }
          
        h1
        {
            color:#FF0000;
            text-align:center;
        }
          
        #TextBox1
        {
            font-family:"Courier New", Courier, mono;
            font-size:24px;
            font:bold italic;
            background-color:#0000FF;
            color:yellow;
            width:400;
        }
          
        .btnResult
        {
            background-image:url(BackgroundImage.bmp);
            color:white;
        }
        
    </style>

    Подключается две внешних таблицы в дополнение к встроенной.

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

    Порядок следования не влияет

    Подключается три CSS
    <style type="text/css">
        
        body
        {
            background:white url(Web.jpg);
            font-family:Arial, Helvetica, sans-serif;
        }
          
        h1
        {
            color:#FF0000;
            text-align:center;
        }
          
        #TextBox1
        {
            font-family:"Courier New", Courier, mono;
            font-size:24px;
            font:bold italic;
            background-color:#0000FF;
            color:yellow;
            width:400;
        }
          
        .btnResult
        {
            background-image:url(BackgroundImage.bmp);
            color:white;
        }
        
        @import url(header.css);
        
    </style>
      
    <link href="default.css" rel="stylesheet">

    Подключается две внешних таблицы в дополнение к встроенной.

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

    Порядок следования не влияет

    Применим рассмотренный материал на практике.

  • В панели Solution Explorer найдите страницу Page2.htm и сделайте из нее две копии с именами Page3.htm и ExternalCSS.css
  • Откройте файл Page3.htm и замените в заголовке <head> встроенный стиль на строку подключения внешней каскадной таблицы стилей
  • <link rel="stylesheet" href=" ExternalCSS.css">

    <html xmlns="http://www.w3.org/1999/xhtml" >
    <head>
        <title>Документ со встроенной CSS</title>
        <meta content="text/html; charset=windows-1251" http-equiv="Content-Type">
        <link href="ExternalCSS.css" rel="stylesheet">
    </head>
    <body>
        <h1>
            Заполните анкету!
        </h1>
        <form id="form1" action="Page3.htm">
            <p>
                Введите ФИО:
                <input id="TextBox1" name="TextBox1" type="text">
            </p>
            <p>
                Введите ИНН:
                <input id="TextBox2" name="TextBox2" type="text">
            </p>
            <center>
                <input id="Button1" class="btnResult" name="Button1" 
                       type="button" value="Отослать">
            </center>
        </form>
    </body>
    </html>
  • Откройте файл ExternalCSS.css и оставьте только чистый код селекторов и определений стилей
  • body
            {
              background:url(Resources/Web.jpg);
              font-family:Arial, Helvetica, sans-serif;
            }
      
            h1
            {
              color:#FF0000;
              text-align:center;
            }
      
            #TextBox1
            {
              font-family:"Courier New", Courier, mono;
              font-size:24px;
              font:bold italic;
              background-color:#0000FF;
              color:yellow;
              width:400;
            }
      
            .btnResult
            {
              background-image:url(Resources/BackgroundImage.bmp);
              color:white;
            }
  • Откройте страницу Page3.htm в броузере и убедитесь, что получился тот же результат, что и при использовании встроенных стилей в странице Page2.htm
  • Опробуйте другие варианты подключения внешней CSS, приведенные ранее в таблице
  • Упражнение 4. Управление внешними стилями средствами оболочки

    Итак, внешняя CSS, это обычный текстовый файл, которому традиционно назначают расширение .css для лучшей узнаваемости, хотя может использоваться любое другое расширение. Его можно создавать отдельно в любом текстовом редакторе, а затем использовать в Web-проекте. Но Visual Studio 2005 имеет свои встроенные средства создания CSS. Рассмотрим их.

  • Добавьте в корневой узел сайта командой Website/Add New Item новый файл типа Style Sheet с именем StyleSheet.css (по умолчанию)
  • Оболочка добавит в проект новый файл каскадной таблицы стилей и покажет панель инструментов Style Sheet, помогающую редактировать этот файл. Здесь, правда, возможностей не так много, как в специализированных редакторах стилей, но основные средства все-таки имеются.

    Наша главная задача, как программистов, - понимать и правильно применять CSS, а на чем она подготовлена или кем (пусть у дизайнеров голова болит), не очень важно. Но если приспичит, можно порекомендовать специализированный редактор CSS, который позволяет кодировать вручную или использовать удобный интерфейс. Это редактор StyleMaster, информацию о котором можно найти в Internet по ссылке www.westciv.com/style_master

    Не будем использовать созданную оболочкой заготовку StyleSheet.css, а научимся подключать к странице через оболочку свою уже готовую таблицу стилей ExternalCSS.css.

  • Удалите созданную оболочкой заготовку StyleSheet.css
  • Создайте копию статической страницы Page3.htm и присвойте копии имя Page4.htm
  • Назначьте страницу Page4.htm стартовой (команда Set As Start Page ) и запустите ее на выполнение, чтобы убедиться в ее работоспособности
  • Если страница не содержит серверных элементов с атрибутом runat="server", то система ASP.NET просто отсылает клиенту код этой страницы в неизменном виде. Если посмотреть на исходный HTML-код присланной броузеру страницы (команда View Source контекстного меню окна броузера), то он остался тем же самым, что и в генерирующей странице Page4.htm

    <html xmlns="http://www.w3.org/1999/xhtml" >
    <head>
        <title>Документ со встроенной CSS</title>
        <meta content="text/html; charset=windows-1251" http-equiv="Content-Type">
        <link href="ExternalCSS.css" rel="stylesheet">
    </head>
    <body>
        <h1>
            Заполните анкету!
        </h1>
        <form id="form1" action="Page3.htm">
            <p>
                Введите ФИО:
                <input id="TextBox1" name="TextBox1" type="text">
            </p>
            <p>
                Введите ИНН:
                <input id="TextBox2" name="TextBox2" type="text">
            </p>
            <center>
                <input id="Button1" class="btnResult" name="Button1" 
                       type="button" value="Отослать">
            </center>
        </form>
    </body>
    </html>
    <html xmlns="http://www.w3.org/1999/xhtml" >
    <head>
        <title>Документ со встроенной CSS</title>
        <meta content="text/html; charset=windows-1251" http-equiv="Content-Type">
        <link href="ExternalCSS.css" rel="stylesheet">
    </head>
    <body>
        <h1>
            Заполните анкету!
        </h1>
        <form id="form1" action="Page4.htm">
            <p>
                Введите ФИО:
                <input id="TextBox1" name="TextBox1" type="text">
            </p>
            <p>
                Введите ИНН:
                <input id="TextBox2" name="TextBox2" type="text">
            </p>
            <center>
                <input id="Button1" class="btnResult" name="Button1" 
                       type="button" value="Отослать">
            </center>
        </form>
    </body>
    </html>

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

    Чтобы успокоить редактор, нужно сообщить ему, чтобы он проверял синтакис чистого HTML, а не XHTML.

  • Выполните команду Tools/Customize и отметьте флажок HTML Source Editing
  • В верхней части каркаса оболочки появится панель инструментов настройки HTML-редактора, в которой нужно установить через раскрывающийся список опцию HTML 4.01. Через некоторое время, после очередного сканирования синтаксиса документа HTML-редактор успакаивается. Может остаться подчеркнутым открывающий HTML-дескриптор <form>. Чтобы избавиться и от этого замечания редактора, нужно добавить атрибут action с целевой страницей, которая будет обрабатывать обратную отсылку (постинг) на сервер, в нашем случае

    <form action="Page4.htm">

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

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

  • Вызовите контекстное меню щелчком правой кнопки мыши на свободном месте верхней части каркаса оболочки и сбросьте флажок HTML Source Editing, чтобы закрыть одноименную панель настройки редактора
  • Удалите из заголовочного блока страницы Page4.htm код подключения внешней таблицы стилей , а именно, удалите строку
  • <link rel="stylesheet" href="ExternalCSS.css">

  • Переведите страницу в режим Design и посмотрите, какая она сразу стала некрасивая
  • Захватите мышью в панели Solution Explorer файл ExternalCSS.css и перенесите его на страницу, которая сразу преобразится уже на этапе проектирования
  • Вернитесь в режим Source и убедитесь, что оболочка добавила нужный дескриптор подключения CSS в нужное место
  • Опять удалите дескриптор подключения CSS
  • Перейдите в режим Design, откройте панель Properties и через раскрывающийся список в ее верхней части выберите объект DOCUMENT
  • Найдите свойство StyleSheet и подключите к нему таблицу ExternalCSS.css через диалоговое окно. Оболочка опять добавит нужный дескриптор подключения CSS в нужное место
  • Очистите поле свойства StyleSheet, оболочка вновь удалит дескриптор подключения
  • Таблицы CSS применяются не только к простым дескрипторам HTML, но и к дескрипторам серверных элементов управления. Проверим это, поместив на страницу серверный элемент управления, например, Label.

  • Откройте панель Toolbox
  • Мы видим, что доступной является только вкладка HTML, потому, что наша тестовая страница Page4.htm имеет расширение .htm.

  • Измените в панели Solution Explorer расширение страницы на Page4.aspx и сделайте страницу принимающей, исправив дескриптор <form>
  • <form action="Page4.aspx" runat="server">

    Вкладка Standard в панели Toolbox все равно не появилась, потому что редактор оболочки все еще считает страницу статической.

  • Для изменения мнения редактора о типе страницы закройте редактирование страницы Page4.aspx, затем вновь откройте
  • Поместите на страницу между текстовым полем и кнопкой из вкладки Standard текстовую метку Label и в панели Properties определите
  • CssClass=label
  • Text=Стиль класса label
  • Откройте через Solution Explorer файл ExternalCSS.css и добавьте стилевое правило для класса label
  • .label
            {
                background-color:Green;
                color:White;
                font:bold;
                font-size:x-large;
                font-family:Courier New;
            }    
            
            body
            {
              background:white url(Web.jpg);
              font-family:Arial, Helvetica, sans-serif;
            }
      
            h1
            {
              color:#FF0000;
              text-align:center;
            }
      
            #TextBox1
            {
              font-family:"Courier New", Courier, mono;
              font-size:24px;
              font:bold italic;
              background-color:#0000FF;
              color:yellow;
              width:400;
            }
      
            .btnResult
            {
              background-image:url(BackgroundImage.bmp);
              color:white;
            }
  • Переведите страницу в режим Design, захватите мышью в панели Solution Explorer файл ExternalCSS.css и перенесите его на страницу
  • Выполните страницу, которая в броузере станет такой
  • Краткое знакомство с CSS и их поддержкой в Visual Studio 2005 закончили!

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

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

    Темы

    Как и CSS, темы определяют набор стилей, которые называют оформлениями и которые можно применять на многих страницах ASP.NET. Особенности тем состоят в следующем:

  • Стилевые параметры тем преобразуются элементами управления во внутренние стили HTML-дескрипторов при генерации HTML-кода и не могут быть реализованы напрямую на стороне броузера
  • Темы могут применяться вместе с таблицами стилей, тогда клиенту отсылается комбинированный стиль, определяемый CSS и сгенерированным стилем темы
  • CSS можно использовать как часть темы
  • Темы могут подключаться к множеству страниц через конфигурационные файлы
  • К странице одновременно может применяться только одна тема, темы друг с другом не объединяются. Темы для одной и той же страницы можно менять только программно при каждой новой загрузке страницы
  • Приоритетом внутреннего стиля элемента управления и его стиля, определенного в теме, можно управлять
  • Разработчик должен создавать свои темы с нуля или может загружать готовые темы с других Web-сайтов, например, http:// www.asp.net. Но если тема используется, то для обеспечения гибкости и автономности она должна физически находиться в каталоге приложения.
  • Все темы помещаются в каталог с зарезервированным именем App_Themes, который можно добавить к приложению в панели Solution Explorer, выполнив команду Add ASP.NET Folder/Theme контекстного меню для узла приложения

    Каждая тема должна храниться в своей одноименной папке и может состоять из нескольких файлов текстового формата с произвольным именем и обязательным расширением .skin (skin - кожа). Эти файлы называются файлами оформлений и добавляются в папку App_Themes командой Website/Add New Item через соответствующее диалоговое окно

    Папку App_Themes отдельно можно и не создавать, потому, что при попытке добавления нового файла типа Skin File оболочка предложит автоматически создать папку App_Themes

    Для темы можно все стилевые настройки элементов управления разместить в одном файле оформленийscin, а можно и в нескольких. Иногда просто удобнее группировать оформления по отдельным категориям элементов управления в соответствующих файлах. Для ASP.NET это не имеет никакого значения, поскольку она все равно считывает все файлы оформлений в каталоге указанной темы как неделимое целое определения одной темы.

    Структура папки App_Themes может иметь следующий вид

    Проверим сказанное на практике.

    Упражнение 5. Простое стилевое оформление страницы с помощью темы

  • Добавьте к проекту новую страницу Page5.aspx с отделенным кодом
  • Добавьте на страницу из вкладки Standard следующие элементы управления
  • BulletedList
  • Label
  • CheckBox
  • Button
  • Назначьте свойства Text этих элементов (для BulletedList - коллекция Items ) и скомпонуйте их на странице так, чтобы их внешний вид на этапе проектирования был следующим
  • В панели Solution Explorer создайте копию файла Page5.aspx и присвойте ей имя FirstSkin.aspx. Из него мы в дальнейшем сделаем файл оформлений
  • Удалите файл кодовой поддержки FirstSkin.aspx.cs
  • Настройте декларативно в панели Properties стилевые свойства объектов страницы FirstSkin.aspx
  • Таблица свойств элементов управления страницы FirstSkin.aspx
    Элемент Свойство Значение
    BulletedList ID BulletedList1
      BackColor Yellow
      BorderColor Red
      BorderStyle Ridge
      BorderWidth 10px
      BulletStyle Numbered
      ForeColor Blue
    Label ID Label1
      BackColor Blue
      ForeColor Yellow
      ToolTip Текстовая метка
    CheckBox ID CheckBox1
      BackColor Red
      BorderStyle Groove
      Font/Bold True
      ForeColor White
    Button ID Button1
      BackColor #FFC0C0
      BorderColor Blue
      BorderStyle Solid
      BorderWidth 2px
      Font/Bold True
      ForeColor Blue

    Внешний вид элементов управления после проведенных стилевых настроек должен быть таким

  • Перейдите в режим Source и удалите из файла FirstSkin.aspx весь код, кроме открывающих дескрипторов элементов управления, в которых тоже удалите атрибуты, не относящиеся к настройкам стиля, включая атрибут ID
  • В файле FirstSkin.aspx должно остаться следующее

    <asp:BulletedList runat="server" 
                BackColor="Yellow" BorderColor="Red" 
                BorderStyle="Ridge" BorderWidth="10px" 
                BulletStyle="Numbered" ForeColor="Blue" />
            <asp:Label runat="server" 
                BackColor="Blue" ForeColor="Yellow" 
                ToolTip="Текстовая метка" />
            <asp:CheckBox runat="server" 
                BackColor="Red" BorderStyle="Groove" 
                Font-Bold="True" ForeColor="White" />
            <asp:Button runat="server"
                BackColor="#FFC0C0" BorderColor="Blue" 
                BorderStyle="Solid" BorderWidth="2px" 
                Font-Bold="True" ForeColor="Blue" />
  • Присвойте файлу FirstSkin.aspx новое расширение FirstSkin.skin
  • Создайте в приложении папку с зарезервированным именем App_Themes и определите в ней подпапку с именем MyTheme, в которой будут храниться файлы оформлений этого упражнения (точнее, будет всего один файл FirstSkin.skin )
  • В панели Solution Explorer захватите мышью файл FirstSkin.skin и переместите его в папку MyTheme
  • Тема подключается к странице с помощью атрибута Theme директивы @Page. Так, например, при применении темы с именем каталога MyTheme к странице упражнения Page5.aspx директива будет выглядеть так

    <%@ Page Language="C#" AutoEventWireup="true" 
             CodeFile="Page5.aspx.cs" Inherits="Page5"
             Theme="MyTheme" %>

    Тему можно подключить к странице и в панели Properties через свойство Theme объекта DOCUMENT. В этом случае оболочка автоматически добавит атрибут Theme к директиве @Page с выбранным значением.

  • В раскрывающемся списке панели Properties для страницы Page5.aspx выделите объект DOCUMENT и установите свойство Theme="MyTheme"
  • Назначьте страницу Page5.aspx стартовой и выполните ее
  • Представление рендеринга на стороне клиента, сгенерированного активной страницей Page5.aspx, с учетом присоединенных через тему стилевых настроек элементов управления будет иметь вид

    Естественно, что переопределяются только те стилевые настройки элементов управления страницы, которые указаны в подключенной теме. Остальные настройки не переопределяются.

    Упражнение 6. Приоритеты тем и CSS

    При подключении CSS стили начинают действовать на отображение элементов немедленно, на этапе проектирования и мы можем их подбирать, сразу видя результат. При определении свойства Theme действие стилей мы увидим только на этапе выполнения. Механизм тем реализует стилевые способности генерирующих элементов управления только при выполнении в соответствии с настройками, хранящимися отдельно в одном или нескольких файлах оформлений каталога темы.

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

  • В панели Solution Explorer получите копию страницы Page5.aspx и присвойте ей имя Page6.aspx
  • Установите прямо в настройках страницы Page6.aspx для элемента CheckBox1 атрибуты
  • BackColor="Black"
  • ForeColor="Yellow"
  • Мы хотим, чтобы элемент CheckBox1 имел черный фон и желтый текст, т.е. ждем его таким

  • Выполните страницу и убедитесь, что элемент CheckBox1 попрежнему будет иметь цвет фона красный, а цвет текста белый, определенные в подключенной к странице теме Theme="MyTheme"
  • Объект DOCUMENT страницы имеет еще одно свойство StyleSheetTheme, к которому также можно подключить внешнюю тему. В этом случае приоритет будут иметь индивидуальные настройки элементов управления, которые в них были явно определены. Но те настройки по умолчанию, которые мы не определяли на странице, но которые содержатся в теме, на свойства по умолчанию действуют.

  • Для страницы Page6.aspx в панели Properties выделите объект DOCUMENT, очистите поле его свойства Theme и подключите тему MyTheme к свойству StyleSheetTheme
  • Выполните страницу и убедитесь, что индивидуальная настройка элемента CheckBox1 имеет приоритет
  • Любой вариант влияния темы на элемент управления можно отключить, если установить в этом элементе свойство EnableTheming="False". Влияние настроек темы на всю страницу можно отключить, если в директиве @Page страницы установить атрибут EnableTheming="False" (по умолчанию это свойство страницы имеет значение True ).
  • Для страницы Page6.aspx в панели Properties выделите объект DOCUMENT, очистите поле его свойства StyleSheetTheme и подключите тему MyTheme к свойству Theme
  • Выделите объект CheckBox1 с индивидуальными настройками BackColor="Black" ; ForeColor="Yellow" и отключите для него влияние темы установкой значения свойства
  • EnableTheming="False"
  • Выполните страницу и убедитесь, что настройки темы отключены и действуют только настройки по умолчанию и индивидуальные настройки элемента
  • Выделите объект CheckBox1 и верните для него EnableTheming="True"
  • Упражнение 7. Именованные оформления элементов управления в теме

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

    Если просто определить в теме несколько вариантов настроек для одного и того же элемента управления, то ASP.NET выдаст ошибку об обнаружении неоднозначности. В этом случае в теме нужно применять именованные оформления с помощью атрибута SkinID. Стиль элемента управления в файле оформлений будет иметь метку SkinID, и на странице наряду с общим подключением темы с помощью свойства Theme нужно будет определить дополнительные свойства SkinID для отдельных элементов управления.

    Значения SkinID в определениях темы должно быть уникальным только в группе однотипных элементов. Это значит, что мы можем создать несколько групп настроек разнотипных элементов с одинаковыми значениями атрибута SkinID и подключать из одной темы, по мере необходимости, к каждому элементу управления свои индивидуальные настройки.

  • Скопируйте страницу Page5.aspx в страницу Page7.aspx
  • Добавьте в страницу Page7.aspx элемент Table из вкладки HTML панели Toolbox. Таблицу расположите между кнопкой и предшествующими элементами. Она будет компоновать расположение части элементов управления на странице и мы ее будем называть далее компоновочной
  • Для нашего упражнения таблица должна иметь три столбца и пять строк. Оболочка не обладает развитыми средствами редактирования таблиц, поэтому проще настроить таблицу в режиме Source.

  • Поместите из вкладки Standard панели Toolbox в средний столбец таблицы пять серверных элементов Label, а в правый - пять серверных элементов CheckBox
  • Компоновочная таблица
    SkinID не подключен: действуют общие настройки по умолчанию Label2
    Именованные настройки подключены: SkinID="Variant3" Label3
    Именованные настройки подключены: SkinID="Variant4" Label4
    SkinID="Variant5" подключен, но тема подавлена: EnableTheming="False" Label5
    Подключен несуществующий SkinID="XXX": настройки темы не действуют Label6
  • В панели Solution Explorer щелкните на узле MyTheme и добавьте к проекту новый файл оформлений с именем FirstSkinID.skin
  • Очистите от комментариев файл оформленийFirstSkinID.skin и дополните его индивидуальными именованными настройками стилей, чтобы общий код его стал таким
  • <asp:Label runat="server" BackColor="Blue" ForeColor="Yellow" SkinID="Variant2" />
    <asp:CheckBox runat="server" BackColor="Red" BorderStyle="Groove"
        Font-Bold="True" ForeColor="White" SkinID="Variant2" />
    <asp:Label runat="server" ForeColor="Blue" SkinID="Variant3" />
    <asp:CheckBox runat="server" BorderStyle="Dotted" Font-Bold="True" 
        ForeColor="Red" SkinID="Variant3" />
    <asp:Label runat="server" BackColor="Yellow" ForeColor="Red" SkinID="Variant4" />
    <asp:CheckBox runat="server" BackColor="Red" BorderStyle="Solid"
        ForeColor="Yellow" SkinID="Variant4" />
    <asp:Label runat="server" BackColor="Blue" ForeColor="Yellow" SkinID="Variant5" />
    <asp:CheckBox runat="server" BackColor="Red" BorderStyle="Groove"
        Font-Bold="True" ForeColor="White" SkinID="Variant5" />
    <asp:Label runat="server" BackColor="Blue" ForeColor="Yellow" SkinID="Variant6" />
    <asp:CheckBox runat="server" BackColor="Red" BorderStyle="Groove"
        Font-Bold="True" ForeColor="White" SkinID="Variant6" />
  • Установите в добавленных объектах свойство SkinID в соответствии с первым столбцом компоновочной таблицы, приведенной ранее
  • Выполните страницу Page7.aspx, результат должен быть примерно таким
  • Вспомним, что активизируются все файлы оформлений, соответствующие указанной теме и расположенные в соответствующем одноименном каталоге. И в нашем упражнении часть элементов страницы оформляется с помощью неименованных атрибутов, расположенных в файле App_Themes\MyTheme\ FirstSkin.skin, а часть - с помощью именованных, расположенных в файле App_Themes\MyTheme\ FirstSkinID.skin.

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

    Окончательный код интерфейсной части страницы Page7.aspx будет следующим

    <%@ Page Language="C#" AutoEventWireup="true" 
             CodeFile="Page7.aspx.cs" Inherits="Page7"
             Theme="MyTheme" %>
        
    <html xmlns="http://www.w3.org/1999/xhtml" >
    <head runat="server">
        <title>Untitled Page</title>
    </head>
    <body>
        <form id="form1" runat="server">
            <div>
                <asp:BulletedList ID="BulletedList1" runat="server">
                    <asp:ListItem>Туризм</asp:ListItem>
                    <asp:ListItem>Спорт</asp:ListItem>
                    <asp:ListItem>Музыка</asp:ListItem>
                    <asp:ListItem Value="Танцы"></asp:ListItem>
                </asp:BulletedList>
                <asp:Label ID="Label1" runat="server" Text="Текстовая метка"></asp:Label>
                <asp:CheckBox ID="CheckBox1" runat="server" Text="Флажок" />
                <br />
                <br />
                <table bgcolor="#FFFFFF">
                    <tr>
                        <td>
                            SkinID не подключен: действуют общие настройки по умолчанию
                        </td>
                        <td>
                            <asp:Label ID="Label2" runat="server" Text="Label2"></asp:Label></td>
                        <td style="height: 26px">
                            <asp:CheckBox ID="CheckBox2" runat="server" Text="CheckBox2" /></td>
                    </tr>
                    <tr>
                        <td>
                            Именованные настройки подключены: SkinID="Variant3"</td>
                        <td>
                            <asp:Label ID="Label3" runat="server" SkinID="Variant3" Text="Label3"></asp:Label></td>
                        <td>
                            <asp:CheckBox ID="CheckBox3" runat="server" SkinID="Variant3" Text="CheckBox3" /></td>
                    </tr>
                    <tr>
                        <td>
                            Именованные настройки подключены: SkinID="Variant4"</td>
                        <td>
                            <asp:Label ID="Label4" runat="server" SkinID="Variant4" Text="Label4"></asp:Label></td>
                        <td>
                            <asp:CheckBox ID="CheckBox4" runat="server" SkinID="Variant4" Text="CheckBox4" /></td>
                    </tr>
                    <tr>
                        <td>
                            SkinID="Variant5" подключен, но тема подавлена: EnableTheming="False"</td>
                        <td>
                            <asp:Label ID="Label5" runat="server" EnableTheming="False" SkinID="Variant5" 
                                       Text="Label5"></asp:Label></td>
                        <td>
                            <asp:CheckBox ID="CheckBox5" runat="server" EnableTheming="False" SkinID="Variant5"
                                Text="CheckBox5" /></td>
                    </tr>
                    <tr>
                        <td>
                            Подключен несуществующий SkinID="XXX": настройки темы не действуют</td>
                        <td>
                            <asp:Label ID="Label6" runat="server" SkinID="XXX" Text="Label6"></asp:Label></td>
                        <td>
                            <asp:CheckBox ID="CheckBox6" runat="server" SkinID="XXX" Text="CheckBox6" /></td>
                    </tr>
                </table>
                <br />
                <asp:Button ID="Button1" runat="server" Text="Отправить" />
            </div>
        </form>
    </body>
    </html>

    Упражнение 8. Оформления сложных элементов управления

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

  • Создайте новую страницу со встроенным кодом поддержки и с именем Page8.aspx
  • Назначьте страницу стартовой и поместите на нее из вкладки Standard элемент управления Calendar
  • Определите для объекта Calendar1 следующие стилевые настройки
  • <asp:Calendar ID="Calendar1" runat="server" 
                BackColor="white" ForeColor="black" BorderColor="black"
                BorderStyle="solid" CellSpacing="1" Font-Names="Vernada" 
                Font-Size="9pt" Height="250px" Width="500px" 
                NextPrevFormat="ShortMonth" SelectionMode="day">
                <SelectedDayStyle BackColor="darkorange" ForeColor="white" />
                <DayStyle BackColor="orange" Font-Bold="true" ForeColor="white" />
                <NextPrevStyle Font-Bold="true" Font-Size="8pt" ForeColor="white" />
                <DayHeaderStyle Font-Bold="true" Font-Size="8pt" 
                    ForeColor="#333333" Height="8pt" />
                <TitleStyle BackColor="firebrick" BorderStyle="none" 
                    Font-Bold="true" Font-Size="12pt" 
                    ForeColor="white" Height="12pt" />
                <OtherMonthDayStyle BackColor="navajowhite" 
                    Font-Bold="false" ForeColor="darkgray" />
            </asp:Calendar>
  • В панели Solution Explorer вызовите контекстное меню для папки MyTheme и командой Add New Item создайте новый файл оформлений с именем Calendar.skin
  • Запомните, как пишутся комментарии в интерфейсных файлах ASP.NET и очистите автоматически сгенерированный текст в файле оформленийCalendar.skin
  • Скопируйте из страницы Page8.aspx в файл оформленийCalendar.skin весь дескрипторный код объекта Calendar1 вместе с его стилевыми настройками и удалите в копии атрибут ID="Calendar1", оставив только атрибуты стилевых настроек
  • <asp:Calendar runat="server"
                BackColor="white" ForeColor="black" BorderColor="black"
                BorderStyle="solid" CellSpacing="1" Font-Names="Vernada" 
                Font-Size="9pt" Height="250px" Width="500px" 
                NextPrevFormat="ShortMonth" SelectionMode="day" >
                <SelectedDayStyle BackColor="darkorange" ForeColor="white" />
                <DayStyle BackColor="orange" Font-Bold="true" ForeColor="white" />
                <NextPrevStyle Font-Bold="true" Font-Size="8pt" ForeColor="white" />
                <DayHeaderStyle Font-Bold="true" Font-Size="8pt" ForeColor="#333333" Height="8pt" />
                <TitleStyle BackColor="firebrick" BorderStyle="none" Font-Bold="true" 
                    Font-Size="12pt" ForeColor="white" Height="12pt" />
                <OtherMonthDayStyle BackColor="navajowhite" Font-Bold="false" ForeColor="darkgray" />    
            </asp:Calendar>
  • Удалите в дескрипторном коде страницы Page8.aspx код стилевых настроек объекта Calendar1, чтобы от объекта осталось только простое описание
  • <asp:Calendar ID="Calendar1" runat="server"> 
        </asp:Calendar>
  • Через панель Properties выделите объект DOCUMENT и поключите к странице тему MyTheme в свойстве Theme
  • Выполните страницу и убедитесь, что вынесенный со страницы в тему большой блок стилевых настроек элемента Calendar работает также, как если бы он находился на самой странице
  • Ниже приведены два варианта представления объекта Calendar1: без темы и с подключенной темой

    Тема с настройками не подключена Внешние стилевые настройки работают

    Упражнение 9. Стилевое оформление рисунков

    С помощью внешних файлов оформлений и тем можно подключать стилевые компоненты рисунков. Пусть мы хотим использовать несколько кнопок элемента управления ImageButton с разными рисунками в зависимости от контекста применения. Пусть для кнопок OK будет использован один рисунок, а для кнопок Cancel - другой. Темы позволяют легко решить эту задачу.

  • В панели Solution Explorer через контекстное меню на папке MyTheme командой New Folder создайте подпапку ButtonImages для хранения рисунков кнопок
  • В панели Solution Explorer через контекстное меню на новой папке ButtonImages командой Add Existing Item вызовите диалоговое окно копирования и из прилагаемого каталога Source скопируйте рисунки ButtonCancel.png и ButtonOK.png
  • Создайте из страницы Page8.aspx копию с именем Page9.aspx и назначьте ее стартовой
  • Добавьте на страницу Page9.aspx из вкладки Standard панели Toolbox две кнопки типа ImageButton и настройте через одноименные свойства в панели Properties их дескрипторы так
  • <asp:ImageButton ID="ImageButton1" runat="server" 
        ImageUrl="~/App_Themes/MyTheme/ButtonImages/ButtonOK.png" 
        SkinID="OKButton" />
    <asp:ImageButton ID="ImageButton2" runat="server" 
        ImageUrl="~/App_Themes/MyTheme/ButtonImages/ButtonCancel.png" 
        SkinID="CancelButton" />
  • В контекстном меню на папке MyTheme выполните команду Add New Item, чтобы добавить в тему новый файл оформленияButtons.skin
  • Скопируйте настроенные дескрипторы элементов ImageButton в файл Buttons.skin, предварительно очистив его от автоматически сгенерированных инструкций
  • Удалите из файла оформлений Buttons.skin атрибуты ID, чтобы код стал выглядеть так
  • <asp:ImageButton runat="server" 
        ImageUrl="ButtonImages/ButtonOK.png" 
        SkinID="OKButton" />
    <asp:ImageButton runat="server" 
        ImageUrl="ButtonImages/ButtonCancel.png" 
        SkinID="CancelButton" />

    Обратите внимание, что мы в атрибуте ImageUrl указали путь только относительно каталога темы. Путь до корня виртуального каталога сайта " ~/App_Themes/MyTheme/ " добавит ASP.NET при компиляции этого кода и автоматически преобразует его в абсолютный путь файловой системы сервера.

  • Откорректируйте дескрипторы ImageButton в файле Page9.aspx, чтобы от них осталось только следующее
  • <asp:ImageButton ID="ImageButton1" runat="server" SkinID="OKButton" />
    <asp:ImageButton ID="ImageButton2" runat="server" SkinID="CancelButton" />
  • Исполните страницу Page9.aspx и получите следующий результат с внешними оформлениями в темах сложного календаря и рисуночных кнопок
  • На интерфейсной странице находится дескрипторный код только создания сложных элементов управления, а большой код тонких настроек вынесен в файлы темы. Это очень удобно и для централизованного изменения стилевого оформления элементов управления, и для читабельности самой страницы.

    Упражнение 10. Комбинирование темы и внешних CSS

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

  • Скопировать файл внешней CSS в каталог темы
  • Включить тему в свойстве Theme объекта DOCUMENT страницы
  • Если CSS содержит классы, подключить их в свойстве CssClass элементов управления страницы
  • Вместо дескриптора <link>, используемого при традиционном способе подключения к странице, проследить, чтобы заголовочный дескриптор страницы был объявлен как серверный ( <head runat="server"> ). В этом случае ASP.NET сама при запросах страницы будет генерировать столько дескрипторов <link> в выходном HTML-коде, сколько файлов CSS будет находиться в подключенной к странице теме
  • Проверим сказанное. В панели Solution Explorer оболочки выполните следующие действия:

  • Скопируйте файл Page4.aspx, присвойте копии имя Page10.aspx и назначьте новую страницу стартовой
  • Откройте файл Page5.aspx, скопируйте из него на страницу Page10.aspx (ниже кнопки) интерфейсный код серверных элементов управления BulletedList, Label, CheckBox
  • После этого вид страницы Page10.aspx без стилевого оформления на этапе проектирования должен быть таким

  • Скопируйте таблицу стилей ExternalCSS.css в папку MyTheme проекта. Копирование выполните в панели Solution Explorer так: щелкните правой кнопкой мыши на файле ExternalCSS.css и выполните команду Copy, затем щелкните правой кнопкой мыши на папке MyTheme и выполните команду Paste. Назначьте копии имя ThemeCSS.css
  • Подобным же образом скопируйте из каталога Resources в каталог MyTheme два файла BackgroundImage.bmp и Web.jpg, чтобы они оказались в одном каталоге с файлом ThemeCSS.css
  • Откройте страницу Page10.aspx на редактирование, удалите в ней дескриптор <link> подключения внешней таблицы стилей и добавьте в заголовочный дескриптор <head> атрибут runat="server", чтобы он стал обрабатываться средой ASP.NET
  • Откройте файл ThemeCSS.css и замените пути доступа к рисункам, а именно
  • url(Resources/Web.jpg) ---> url(Web.jpg)
    url(Resources/BackgroundImage.bmp) ---> url(BackgroundImage.bmp)
  • Подключить тему MyTheme к странице Page10.aspx, добавив в самое ее начало директиву
  • <%@ Page Language="C#" Theme="MyTheme" %>

  • Выполните страницу Page10.aspx и убедитесь, что стили из внешней таблицы ThemeCSS.css работают
  • Результат выполнения страницы будет таким

    На данной странице Page10.aspx таблица стилей CSS и файлы оформления темы действуют вместе. Система ASP.NET на стороне сервера трансформирует серверные элементы управления в HTML-дескрипторы с уже готовыми внутренними стилями, согласно оформлениям темы. На заключительном этапе, перед отсылкой сформированной HTML-страницы на броузер, среда подключает с помощью дескриптора <link> ссылку на внешнюю CSS. Броузер, получив ссылку на внешнюю CSS, проверяет в соответствии с правилом приоритета стилей возможность их применения только в тех дескрипторах HTML, в которых нет своих внутренних стилей, сгенерированных серовером на основании темы.

    Иными словами

    Теперь осталось проверить тот факт, что ASP.NET подключает к странице все таблицы стилей, помещенные в тему.

  • В каталоге Theme сделайте еще три копии файла ThemeCSS.css с именем ThemeCSS1.css, ThemeCSS2.css, ThemeCSS3.css
  • Выполните страницу Page10.aspx и в появившемся окне броузера выполните команду контекстного меню View Source для присланной с сервера страницы-отклика
  • Исследуем блок кода заголовочного дескриптора <head>. Он будет содержать такие строки

    <head>
        <title>Документ с внешней CSS</title>
        <meta content="text/html; charset=windows-1251" http-equiv="Content-Type" />
        <link href="App_Themes/MyTheme/ThemeCSS.css" type="text/css" rel="stylesheet" />
        <link href="App_Themes/MyTheme/ThemeCSS1.css" type="text/css" rel="stylesheet" />
        <link href="App_Themes/MyTheme/ThemeCSS2.css" type="text/css" rel="stylesheet" />
        <link href="App_Themes/MyTheme/ThemeCSS3.css" type="text/css" rel="stylesheet" />
    </head>

    Мы видим, что среда исполнения ASP.NET автоматически вставляет в код столько дескрипторов подключения CSS, сколько файлов с таблицами находится в каталоге темы. Обратите внимание, что пути в ссылках прописываются относительно корня виртуального каталога, в котором находится сама страница.

  • Удалите лишние файлы ThemeCSS1.css, ThemeCSS2.css и ThemeCSS3.css
  • Упражнение 11. Применение общей темы в конфигурационном файле

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

    Ранее мы говорили, что при подключении к странице темы (в наших примерах - MyTheme ) через свойство Theme объекта DOCUMENT оболочка автоматически вставляет соответствующий атрибут в директиву @Page этой страницы, тем самым подключая к странице индивидуальную тему.

    Например

    <% @Page Language="C#" Theme="MyTheme" %>

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

  • Откройте страницы наших примеров, использующих темы, а именно: Page5.aspx, Page8.aspx, Page10.aspx, и скопируйте их в файлы с именами TestTheme.aspx, TestCalendar.aspx, ThemeCSS.aspx соответственно
  • Удалите в них из директивы @Page атрибут Theme="MyTheme"
  • Вызывая для каждой страницы контекстное меню и выполняя команду View in Browser, исполните их по очереди, чтобы убедиться, что отключенная тема в них не действует. Пока получилась грустная, унылая картина
  • Откройте на редактирование файл web.config и добавьте в секцию <system.web> дескриптор с указанием общей темы
  • <?xml version="1.0"?>
    <configuration>
      <system.web>
        <compilation debug="true"/>
        <authentication mode="Windows"/>
        <pages theme="MyTheme" />
      </system.web>
    </configuration>
  • Вновь выполняя для каждой страницы команду View in Browser, исполните файлы по очереди, чтобы убедиться, что тема опять заработала так, как будто-бы она была индивидуально подключена к каждой странице
  • Если нужно сделать так, чтобы общая тема действовала по умолчанию на множестве страниц, но не перезаписывала на них индивидуальные стилевые настройки элементов управления, то конфигурационный файл должен быть таким

    <?xml version="1.0"?>
    <configuration>
      <system.web>
        <compilation debug="true"/>
        <authentication mode="Windows"/>
        <pages  styleSheetTheme="MyTheme" />
      </system.web>
    </configuration>

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

  • Установить для страницы индивидуальную тему, как это мы делали раньше, вставив в директиву @Page атрибут Theme=" Другая_тема "
  • Вообще отключить тему, чтобы действовали индивидуальные стиливые свойства элементов управления, назначенные нами или установленные по умолчанию библиотечными значениями. Для этого в директиву @Page страницы нужно вставить атрибут EnableTheming="false"
  • Следует заметить, что если к странице явно подключена CSS дескриптором <link> или в конфигурационном файле каталога, где размещена страница, установлена тема по умолчанию, содержащая хотя-бы одну CSS, то отключение темы по умолчанию атрибутом EnableTheming="false" отключает только skin -файлы, CSS все равно останется подключенной.

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

    В развитом Web-дереве применение общей темы по умолчанию является удобным способом стилизации страниц. В каждом каталоге можно разместить отдельный файл web.config со своей общей темой и все страницы этого каталога будут использовать эту тему по умолчанию.

    Упражнение 12. Переключение тем по выбору пользователя

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

    Все, что нужно сделать, это предусмотреть на странице интерфейс выбора темы, а в коде страницы динамически присвоить свойству Page.Theme или Page.StyleSheetTheme выбранное пользователем значение. При этом важно понимать, что тема настраивается перед инициализацией страницы, поэтому соответствующий код нужно поместить в обработчик Page_PreInit(). Если это сделать позже, в обработчиках других событий, то попытка присвоить новое значение свойству Page.Theme или Page.StyleSheetTheme вызовет ошибку компиляции.

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

  • Прочитать выбор пользователя, когда это станет возможным после полной загрузки страницы
  • Принудительно перезапустить страницу на сервере, чтобы в обработчике Page_PreInit() назначить новую тему
  • При перезапуске страницы она должна быть переадресована на саму себя. Такую операцию можно выполнить либо методом Response.Redirect(), либо методом Server.Transfer(). Первый метод реализует полный цикл с отправкой на броузер и автоматическим возвращением на сервер. Он тратит лишнее время на пересылку. Второй метод перезапускает страницу сразу на сервере без цикла через клиента. Но есть одна существенная особенность: оба метода сбрасывают состояние вида всех элементов управления и запускают страницу как в при первом запросе. Поскольку выбирать не из чего, то мы применим второй метод Server.Transfer().

    Рассмотрим все это на примере. Прежде всего, нужно создать несколько тем, чтобы было из чего пользователю выбирать.

  • В панели Sulution Explorer вызовите контекстное меню для папки MyTheme и выполните команду Copy
  • В панели Sulution Explorer вызовите контекстное меню для папки App_Themes, где должны храниться все темы приложения, и выполните на ней команду Paste два раза, чтобы создать еще две темы
  • Присвойте копиям имена, например, MyTheme1 и MyTheme2
  • Обратите внимание, что новые темы имеют старое содержание, которое было-бы желательно подправить, чтобы наглядно видеть влияние выбранной темы на оформление страницы. Мы это сделаем попозже.

    Учитывая, что протокол HTTP не поддерживает сохранение состояний и что страница существует только на время обработки обратной отсылки, выбранную пользователем тему нужно где-то сохранять. Это могут быть cookie -наборы ( куки -файлы) на компьютере клиента, которые мы туда запишем без его ведома, если он забыл выключить их поддержку. Это могут быть скрытые поля состояния вида (VIEWSTATE), которые мы будем пересылать туда-сюда. Это могут быть глобальные объекты уровня сеанса или приложения, которые существуют на сервере, пока не закончится сеанс с пользователем или не закроется приложение. Выберем объект уровня сеанса Session.

  • Через панель Solution Explorer скопируйте в корневой виртуальный каталог нашего сайта файл TestTheme.aspx и назовите его TestSwitchThemes.aspx
  • Определите новую страницу TestSwitchThemes.aspx стартовой
  • Откройте файл TestSwitchThemes.aspx на редактирование и добавьте в верхнюю часть страницы из вкладки Standard элемент списока DropDownList для выбора темы
  • Переименуйте ID списка в lstSwitchThemes
  • Через панель Properties в режиме Events создайте для списка заготовку обработчика события SelectedIndexChanged, которое поступит на сервер с обратной отсылкой, если пользователь изменит выбор в списке
  • Добавьте на страницу перед списком серверный элемент Label с именем lblFirstLoad
  • Интерфейсная часть страницы TestSwitchThemes.aspx примет следующий вид

    Код интерфейсной части страницы TestSwitchThemes.aspx следующий

    <%@ Page AutoEventWireup="true" CodeFile="TestSwitchThemes.aspx.cs" 
        Inherits="TestSwitchThemes" 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>
            <asp:Label ID="lblFirstLoad" runat="server" Text="Label"></asp:Label>
            <br />
            <asp:DropDownList ID="lstSwitchThemes" runat="server"
                              OnSelectedIndexChanged="lstSwitchThemes_SelectedIndexChanged">
            </asp:DropDownList>
            <br />
            <asp:BulletedList ID="BulletedList1" runat="server">
                <asp:ListItem>Туризм</asp:ListItem>
                <asp:ListItem>Спорт</asp:ListItem>
                <asp:ListItem>Музыка</asp:ListItem>
                <asp:ListItem Value="Танцы"></asp:ListItem>
            </asp:BulletedList>
            <asp:Label ID="Label1" runat="server" Text="Текстовая метка"></asp:Label>
            <asp:CheckBox ID="CheckBox1" runat="server" Text="Флажок" /><br />
            <br />
            <asp:Button ID="Button1" runat="server" Text="Отправить" /></div>
        </form>
    </body>
    </html>
  • Откройте на редактирование файл кода TestSwitchThemes.aspx.cs и заполните его так
  • using System;
        
    public partial class TestSwitchThemes : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!Page.IsPostBack)
            {
                lblFirstLoad.Text = "Первая загрузка страницы";
          
                // Заполняем список именами каталогов тем
                System.IO.DirectoryInfo themeDir =
                    new System.IO.DirectoryInfo(Server.MapPath("App_Themes"));
                lstSwitchThemes.DataTextField = "Name";
                lstSwitchThemes.DataSource = themeDir.GetDirectories();
                lstSwitchThemes.DataBind();
            }
            else
                lblFirstLoad.Text = "Повторная загрузка страницы";
        }
          
        protected void Page_PreInit(object sender, EventArgs e)
        {
            if (Session["Theme"] != null)
            {
                // Был ранее сохраненный выбор, включить его
                Page.Theme = (string)Session["Theme"];
          
                // Восстанавливаем новое состояние списка
                lstSwitchThemes.SelectedIndex = (int)Session["index"];
                // Восстанавливаем текущее состояние "Флажка" 
                CheckBox1.Checked = (bool)Session["Флажок"];
            }
        }
        
        protected void lstSwitchThemes_SelectedIndexChanged(object sender, EventArgs e)
        {
            // Запоминаем новый выбор пользователя
            Session["Theme"] = lstSwitchThemes.SelectedValue;
            // Запоминаем новое состояние списка
            Session["index"] = lstSwitchThemes.SelectedIndex;
            // Сохраняем текущее состояние "Флажка"
            Session["Флажок"] = CheckBox1.Checked;
          
            // Первый вариант:
            // Обновляем страницу с полным циклом,
            // чтобы новая тема вступила в действие
            //Response.Redirect(Request.Url.ToString());
          
            // Второй вариант:
            // Обновляем страницу без полного цикла,
            // чтобы новая тема вступила в действие
            Server.Transfer(Request.FilePath);
        }
    }

    Сделаем некоторые пояснения по кодовой части страницы:

  • В коде используется подключение только одного пространства имен System, потому что:
  • объекты пользовательского интерфейса определены в интерфейсной части страницы и не требуют явного указания пространства имен
  • класс System.IO. DirectoryInfo прописан с полным путем и компилятор его увидит
  • объекты Session, Server, Response, Request являются общедоступными свойствами класса System.Web.UI.Page, который наследуется нашим классом TestSwitchThemes и явно приобретает все эти свойства
  • Оставлено объявление пространства имен System только потому, что в коде имеются ключевые слова string, int, bool, используемые при явном приведении типов. Но и здесь можно было бы использовать полные имена библиотечных типов System. string, System. int, System. bool
  • При принудительной перезагрузке страницы полностью сбрасывается состояние ее элементов управления в начальное, поэтому мы вынуждены его сохранять и восстанавливать в объекте сеанса
  • Язык C# допускает использование в идентификаторах символов кириллицы при условии соблюдения общепринятых правил: должны состоять из букв цифр и знаков подчеркивания, не должны начинаться с цифры, и т.д.
  • При внутренней обработке кода страницы можно (в разумных пределах) использовать весь арсенал библиотеки .NET Framework, а не только специализированные Web-средства
  • Теперь осталось внести какое-либо видимое различие в оформления тем, чтобы мы могли убедиться, что программно была подключена действительно другая тема. Для экономии своих сил внесем минимальные изменения, например, сделаем разными цвета фона страницы.

  • Вызовите на редактирование файл App_Themes/MyTheme1/ThemeCSS.css и поменяйте в селекторе body определение стиля фона background, чтобы фон страницы стал зеленым
  • body
            {
              background:green;
              font-family:Arial, Helvetica, sans-serif;
            }
  • Вызовите на редактирование файл App_Themes/MyTheme2/ThemeCSS.css и поменяйте в селекторе body определение стиля фона background, чтобы фон страницы стал красным
  • body
            {
              background:red;
              font-family:Arial, Helvetica, sans-serif;
            }
  • Исполните страницу и убедитесь в том, что пользователь может сам выбирать варианты стилевого оформления страниц подключением соответствующих тем
  • Подобным образом программно можно менять и другие стилевые свойства страницы и ее элементов, например, именованные оформления элементов управления, определенные атрибутом SkinID. Не обязательно давать пользователю возможность настройки стилевого оформления напрямую. Можно сделать оформление страниц и элементов косвенно зависимым от выбора пользователя.

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

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

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

    Шаблон размечает именованные области визуализации на исполнимой странице, в которых будут размещены либо прикрепленные группы элементов страницы, либо содержимое именованной области самого шаблона. Группы элементов управления страницы, закрепленные за именованными областями шаблона, называются содержимым (content). Страница, к которой подключен шаблон, должна содержать замещающие группы, каждая из которых может ссылаться только на одну именованную область шаблона. Если для какой-то именованной области шаблона прикрепленной группы нет, то на странице появится содержимое именованной области самого шаблона.

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

    Перекомпоновка подключенного шаблона или его содержимого, а также подключение к исполнимой странице нового шаблона декларативно или программно приводит к немедленному изменению ее структуры и содержимого шаблонной части.

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

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

    <asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
    </asp:Content>

    Упражнение 13. Простая мастер-страница

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

  • Добавьте к проекту командой 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. Шаблоны представляют собой обычные страницы, за исключением нескольких отличий:

  • Шаблоны нельзя выполнить напрямую, они могут только поддерживать ссылающиеся на них исполнимые страницы
  • Файлы шаблонов имеют расширение .master, а файлы кода поддержки для них на языке C# - .master.cs
  • В декларации интерфейсного представления шаблона вместо директивы @Page используется директива @Master
  • Класс кода поддержки шаблона наследует базовый класс System.Web.UI.MasterPage, вместо базового класса System.Web.UI.Page для обычной исполнимой страницы
  • Элемент управления ContentPlaceHolder используется для резервирования именованных контекстных областей только в шаблонах. Если раскрыть вкладку Standard контекстно-зависимой панели Toolbox при редактировании обычной страницы, то мы этого элемента просто не увидим, поскольку оболочка его скрывает до случая редактирования шаблона
  • Элементы ContentPlaceHolder ( держатель места для содержимого ) шаблонной страницы можно размещать в ячейках таблицы. При выполнении эти ячейки заполнятся присоединенным содержимым, а остальные ячейки можно занять заполнителем, который будет виден и доступен на любой странице, использующей этот шаблон
  • При загрузке страницы во время выполнения вначале создается объект мастер-страницы, а потом сама страница с содержимым
  • Во время выполнения именованные области мастер-страницы сжимаются до минимального размера фактического содержимого
  • Удалите из кода мастер-страницы созданный автоматически дескриптор ContentPlaceHolder и через контекстное меню редактора выполните команду Add Content Page (добавить контекстную страницу)
  • Оболочка сгенерирует такую заготовку файла Default.aspx

    <%@ Page Language="C#" MasterPageFile="~/FirstMasterPage.master" 
        AutoEventWireup="true" CodeFile="Default.aspx.cs" 
        Inherits="_Default" Title="Untitled Page" %>
    <%-- Add content controls here --%>

    Получился пустой файл содержимого, где имеется только директива @Page со стандартными атрибутами страницы и атрибутом подключения шаблона.

    Обратите внимание, как можно вставлять комментарии <%--...--%> в дескрипторный код файлов ASP.NET

    Оболочка прописала значение атрибута подключения MasterPageFile с путем относительно корня Web-дерева сайта (там наш шаблон и находится). Если уже подключенный к каким-то страницам шаблон переместить из корня в другую папку Web-дерева, то оболочка автоматически не меняет путь атрибута MasterPageFile во всех ссылающихся на шаблон страницах, это придется делать вручную. Поэтому нужно заранее спланировать, будем ли мы использовать мастер-страницы и где они будут находиться.

  • Создайте в панели Solution Explorer через контекстное меню для корня Web-дерева проекта командой New Folder каталог MasterPages
  • Переместите мышью в папку 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>

    Если сейчас исполнить такую страницу, то мы получим ошибку времени выполнения, потому что содержимое страницы должно находиться в специальных именованных контейнерах

    <asp:Content></asp:Content>

  • Поместите строку текста в контейнер <asp:Content> и исполните страницу
  • <%@ 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>

    Вновь произойдет ошибка выполнения, потому что каждый контейнер <asp:Content></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> идентификатор-ссылку 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, должен получиться такой результат с учетом действия темы MyTheme, подключенной в конфигурационном файле корня Web-дерева
  • Поместите на шаблон еще один резервирующий контейнер 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
  • Оболочка добавит пустой контейнер содержимого и автоматически свяжет его в дескрипторном коде с именованной резервируемой областью.

  • Добавьте в контейнер с содержимым какой-нибудь текст и несколько компонентов из панели Toolbox и выполните страницу
  • Это очень удобная технология. Если планируется создание нескольких страниц на базе мастер-страницы, то разработку лучше начать с мастер-страницы. После того, как мастер-страница будет готова, на ее базе можно создать сколько угодно заготовок страниц с содержимым, выполняя в контектном меню шаблона команду Add Content Page. При этом, если мастер-страница имеет файл отделенного кода *.cs, то и контекстные страницы также будут создаваться с файлами отделенного кода.

    После того, как на вновь созданной странице с содержимым проявятся именованные области шаблона, их можно переопределить и заполнить, создав командой контекстного меню Create Custom Content контейнеры индивидуального содержимого. Удалить контейнер содержимого можно командой Default to Master's Content.

  • На нижней области контекстной страницы FirstMasterPage.aspx вызовите контекстное меню и выполните команду Default to Master's Content, чтобы удалить содержимое
  • Если именованные области шаблона пустые и их не переопределять индивидуальным содержимым для какой-то страницы, или переопределять пустым содержимым, то на этапе выполнения эта область не будет видна.

    Упражнение 14. Применение усложненной мастер-страницы

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

    Создание заготовки мастер-страницы

  • В панели Solution Explore r вызовите контекстное меню для папки MasterPages, которую мы создали ранее, и выполните команду Add New Item
  • В окне мастера создания заготовок выберите шаблон Master Page и создайте эталонную страницу с именем Title.master без файла отделенного кода
  • Оболочка добавит заготовку эталонной страницы в каталог MasterPages, где ей и место.

    Создание баннера

  • Переведите в редакторе эталонную страницу Title.master в режим Design и удалите пока автоматически созданный экземпляр элемента ContentPlaceHolder (просто для тренировки)
  • В режиме Design из вкладки Standard панели Toolbox поместите в самом начале мастер-страницы экземпляр элемента Image как контейнер для размещения баннера. Присвойте ему имя ID =" ImageBanner "
  • Элемент Image из вкладки HTML здесь не подойдет. Он не будет проявляться на контекстной странице, потому что не обрабатывается сервером по механизму мастер-страниц (если только ему не присвоить атрибут runat="server" )

  • Вызовите контекстное меню для папки MasterPages и выполните команду Add Existing Item. Найдите через открывшееся диалоговое окно файл Banner.png, который находится в прилагаемом к данной работе каталоге Source, и скопируйте его в папку MasterPages текущего сайта
  • Выделите экземпляр ImageBanner и присоедините к нему через свойство ImageUrl рисунок Banner.png из папки MasterPages Web-дерева сайта
  • Наш баннер слишком великоват, но для локального компьютера сойдет и такой

  • Разместите после баннера заголовочный дескриптор с внутренним стилем
  • <h1 style="color: Green">
                    Мы приветствуем Вас на нашем сайте!!!
                </h1>

    Добавление к мастер-странице таблицы с шаблонами

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

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

    Мы должны четко представлять, что элементы управления ContentPlaceHolder и Content являются прозрачными контейнерами для своего содержимого, если только мы искусстенно не создадим в них фон другими элементами, например, элементом Panel, Image, Table. Поэтому, если мы на странице содержимого не создадим свой фон в замещающих элементах Content, то будет проявляться в итоговой странице фон элемента-контейнера мастер-страницы.

  • Поместите на мастер-страницу Title.master после заголовочного дескриптора из вкладки HTML панели Toolbox элемент управления Table
  • Используя боковые маркеры таблицы и команды меню Layout оболочки, постройте таблицу из трех строк и двух столбцов, чтобы она соответстворвала приведенному выше рисунку
  • В конечном виде таблица вместе с вутренними стилями должна описываться следующими HTML -кодом

    <table>
                    <tr>
                        <td colspan="2">
                        </td>
                    </tr>
                    <tr>
                        <td style="width: 25%; background-color: #9DFF7E" valign="top">
                        </td>
                        <td style="background-color: #FFB8FF">
                        </td>
                    </tr>
                    <tr style="background-color: #FFFF92">
                        <td colspan="2">
                        </td>
                    </tr>
                </table>

    Внешний вид таблицы будет таким

    Теперь все готово для размещения элементов управления в ячейках созданной таблицы.

  • Поместите из вкладки Standard панели Toolbox в верхнюю строку таблицы компонент Panel, к которому мы должны присоединить рисунок, задающий фон ячейки
  • Задайте для созданного экземпляра элемента управления Panel значения свойств: Width=100% ; Height=100%, чтобы элемент полностью занял верхнюю ячейку таблицы
  • Через проводник Windows найдите в прилагаемом к данной работе каталоге Source файл BackTitleSrc.jpg и в любом графическом редакторе вырежьте из него область с хорошо стыкуемыми при повторении рисунка границами, или возьмите мною вырезанный готовый рисунок BackTitle.jpg
  • Рисунок не должен быть слишком большим, чтобы "не застревал в проводах и не тормозил " по пути к пользователю.

  • В панели Solution Explorer в контекстном меню для папки MasterPages выполните команду Add Existing Item, найдите рисунок BackTitle.jpg и скопируйте его в эту папку
  • Выделите на странице экземпляр Panel1 в верхней строке таблицы и через панель инструментов Properties присоедините в свойстве BackImageUrl рисунок BackTitle.jpg, который многократным повторением займет всю площадь элемента Panel1
  • Теперь можно разместить в ячейках таблицы прозрачные контейнеры-шаблоны элемента управления ContentPlaceHolder.

  • Поместите из вкладки Standard панели Toolbox в верхнюю строку таблицы в объект Panel1 элемент-контейнер ContentPlaceHolder, который будет представлять на странице именованную область ContentPlaceHolder1 для размещения верхнего колонтитула
  • То же самое сделайте для правой-средней и нижней ячеек таблицы
  • В контейнер ContentPlaceHolder1 вставьте и отредактируйте такую строку
  • <div style="font-weight: bold; font-size: xx-large; color: red; text-align: center">
        Сведения об автошколе в целом
    </div>
  • Вставьте в правый-средний контейнер ContentPlaceHolder2 любой текст, например, такой или его часть
  • Вот уже более 15 лет мы занимаемся подготовкой водителей
    категории "B" в г.Красноярске и всегда занимали первые места
    в сводках МРЭО ГИБДД по качеству обучения. Автошкола всегда нацелена
    на успех наших клиентов в сдаче экзаменов ГАИ с первого раза
    и своевременном получении водительского удостоверения.  
    Мы обеспечиваем максимальные удобства своим клиентам. Для
    этого существуют вечерняя и воскресная формы обучения.
    Наши курсанты имеют возможность бесплатно и неограниченно пользоваться
    компьютерным классом. Учебные автомобили прибывают по заявке
    клиента к назначенному им месту. Развивается сеть филиалов автошколы
    в г.Красноярске.  
        Наша компьютерная экзаменационная программа успешно используется
    в ГИБДД г.Красноярска, Железногорска, Назарово, Емельяново, Шарыпово
    для государственной аттестации кандидатов в водители. Мы неоднократно
    выпускали цветные учебно-методические пособия по Правилам дорожного
    движения, которыми постоянно пользуются большинство автошкол
    города и края.  
        Лекционные занятия в автошколе ведут квалифицированные преподаватели
    с учеными степенями и званиями с применением современных компьютерных
    технологий. Комплексный подход к обучению и разнообразие форм
    позволяют максимально сократить усилия наших клиентов и сроки
    усвоения ими учебного материала. Парк учебных автомобилей насчитывает
    более 30 штук, включая иномарки.
  • В нижний контейнер ContentPlaceHolder3, резервирующий место под нижний колонтитул, вставьте примерно такой код
  • <strong>
    Автошкола Диалог-Сервис в целом работает без перерыва 
    на обед и выходных с 8 часов утра до 9 часов вечера!!! 
    </strong>
  • Поместите в левую ячейку таблицы надпись, а из вкладки Standard панели Toolbox три гиперссылки элемента управления HyperLink
  • Дескрипторный код содержимого левой ячейки таблицы, которое размещается внутри тегов <td></td>, будет примерно таким

    <strong style="color: Blue">Наши филиалы:</strong><br />
    <br />
    <asp:hyperlink id="HyperLink1" runat="server">Ленинский район</asp:hyperlink><br />
    <br />
    <asp:hyperlink id="HyperLink2" runat="server">Центральный район</asp:hyperlink><br />
    <br />
    <asp:hyperlink id="HyperLink3" runat="server">Свердловский район</asp:hyperlink><br />
    <br />
    <asp:hyperlink id="HyperLink4" runat="server">Октябрьский район</asp:hyperlink>

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

    Итак, таблицу мы заполнили. Попробуем разместить еще что-нибудь ниже таблицы, например, товарный знак и страшилку от воров интеллектуальной собственности.

  • Отступите ниже таблицы одну-две строчки и добавьте из вкладки HTML панели Toolbox элемент управления Image. Щелкните на нем правой кнопкой мыши и выполните команду Run As Server Control, чтобы содержимое элемента проявилось на будущей контекстной странице как заполнитель
  • В панели Solution Explorer вызовите контекстное меню для папки MasterPages и скопируйте в проект командой Add Existing Item из прилагаемого каталога Source рисунок LIGHT.ICO нашего товарного знака, сворованный ранее из ресурсов Большого Билла
  • Через панель Properties и свойство Src ( Source - источник) присоедините ссылку на файл LIGHT.ICO к нашему компоненту отображения Image
  • Правее товарного знака разместите заявку на интеллектуальную собственность
  • Окончательный дескрипторный код этого раздела будет таким

    <p style="text-align: right; color: Blue; font-family: 'Courier New'; font: bold italic">
        <img id="IMG1" runat="server" src="LIGHT.ICO" />
        nbsp;Copyright copy 2009
    </p>
  • Для наглядности, поместите еще один контейнер в самом низу мастер-страницы и внесите в него какой-нибудь последний писк, например такой
  • <asp:ContentPlaceHolder ID="ContentPlaceHolder4" runat="server">
        <strong><em>Вы уж, пожалуйста, наведывайтесь к нам почаще!</em></strong>
    </asp:ContentPlaceHolder>

    И последний штрих к оформлению мастер-страницы - нужно отцентрировать все ее содержимое.

  • Найдите вверху дескрипторного кода самый внешний дескриптор <div> и определите в нем внутренний стиль CSS такой
  • <body>
        <form id="form1" runat="server">
            <div style="text-align: center">
        ..................................
            </div>
        </form>
    </body>

    Наступил момент истины! Пора тиражировать контекстные страницы на базе созданной мастер-страницы и проверять сделанную работу. При этом не нужно забывать, что страница не должна быть слишком длинной. Наилучшей считается длина 1 - 2 экрана, максимум 3 экрана. Ширина непереносимого контекста страницы должна быть около 770 пикселов, чтобы свободно вписываться в разрешение монитора по ширине от 640 до 1024 px.

    Создание контекстных страниц для мастер-страницы

    В продолжение упражнения создадим 5 контекстных страниц с простыми именами:

  • Satellites.aspx - первая страница, главная, не будет иметь свое содержимое, значит вся мастер страница проявится на выходе. Остальные три страницы будут переопределять все шаблоны мастер-страницы, кроме первого колонтитула, в котором находятся ссылки
  • Satellite1.aspx - будет вызываться по первой ссылке в разделе "Наши филиалы" и переопределять содержимое всех контейнеров, кроме верхнего колонтитула
  • Satellite2.aspx - будет вызываться по второй ссылке в разделе "Наши филиалы" ...
  • Satellite3.aspx - будет вызываться по третьей ссылке в разделе "Наши филиалы" ...
  • Satellite4.aspx - будет вызываться по четвертой ссылке в разделе "Наши филиалы" ...
  • Есть несколько способов создания с помощью оболочки контекстных страниц на базе готовой мастер-страницы. Применим эти способы последовательно к созданию наших страниц Satellite?

    Способ 0. Создание страницы Satellite.aspx

  • В панели Solution Explorer выделите корень Web-дерева и выполните команду меню Website/Add New Item (или команду Add New Item контекстного меню)
  • В появившемся диалоговом окне задайте имя страницы Satellite.aspx и отметьте показанные настройки, включая Select master page
  • Оболочка предложит еще одно окно, в котором выберите мастер-страницу Title.master
  • Созданная страница Satellite.aspx с отделенным кодом будет добавлена в корень Web-дерева и будет содержать контейнеры с подключенным пустым содержимым для всех шаблонов выбранной мастер-страницы

    <%@ Page Language="C#" MasterPageFile="~/MasterPages/Title.master" 
        Title="Untitled Page" %>
    <asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
    </asp:Content>
    <asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder2" Runat="Server">
    </asp:Content>
    <asp:Content ID="Content3" ContentPlaceHolderID="ContentPlaceHolder3" Runat="Server">
    </asp:Content>
    <asp:Content ID="Content4" ContentPlaceHolderID="ContentPlaceHolder4" Runat="Server">
    </asp:Content>

    Способ 1. Создание страницы Satellite1.aspx

  • В панели Solution Explorer выделите нужную мастер-страницу Title.master и выполните команду меню Website/Add Content Page (или команду Add Content Page контекстного меню)
  • Будет создана контекстная страница с дежурным именем, размещенная в том же каталоге, что и выделенная мастер-страница, и не будет иметь файл отделенного кода, потому что мы создали мастер-страницу без отделенного кода. Добавленный атрибут MasterPageFile будет прописан относительно корня Web-дерева как абсолютный адрес в пределах сайта

    MasterPageFile="~/MasterPages/Title.master"

    Это значит, что можно переместить контекстную страницу в любую папку в пределах Web-дерева, и везде адрес мастер-страницы буден известен системе ASP.NET. Но если переместить мастер-страницу, то оболочка автоматически не исправит ее адрес в использующих контекстных страницах, а это нужно делать вручную.

    Поэтому необходимо соблюдать простое правило, при котором не придется долго искать ошибки: перемещать мастер-страницу по Web-дереву беспрепятственно можно до тех пор, пока для нее не созданы контекстные страницы.

  • Переименуйте созданную контекстную страницу Default.aspx на Satellite1.aspx и переместите ее в корневой каталог Web-дерева
  • Способ 2. Создание страницы Satellite2.asp

  • Откройте мастер-страницу Title.master на редактирование и щелкните на ней в любом месте правой кнопкой мыши
  • Появится контекстное меню с уже известной нам командой Add Content Page, выполнение которой создаст файл Default.aspx.

  • Переименуйте созданную контекстную страницу Default.aspx на Satellite2.aspx и переместите ее в корневой каталог Web-дерева
  • Способ 3. Создание страниц Satellite3.aspx и Satellite4.aspx

  • Получите в корневом каталоге сайта 2 копии любого из созданных файлов Satellite?.aspx и назовите их именами Satellite3.aspx и Satellite4.aspx, учитывая, что все страницы ссылаются на одну и туже мастер-страницу Title.master
  • Наполнение содержимым контекстных страниц

    Теперь необходимо скорректировать заготовки контекстных страниц Satellite?. Но прежде всего нужно завершить корректировку мастер-страницы Title.master, определив ссылки в левой ячейке таблицы на только что созданные контекстные страницы, чтобы они были на всех контекстных страницах как содержимое самого шаблона.

  • Выполните команду Window/Close All Documents, чтобы закрыть все редактируемые на текущий момент файлы
  • В панели Solution Explorer щелкните правой кнопкой мыши на файле ~/MasterPages/Title.master и выполните команду View Designer, чтобы открыть мастер-страницу сразу в режиме Design
  • Поочередно выделяя элементы гиперссылок, установите их свойство NavigateUrl на соответствующие контекстные страницы Satellite1.aspx, Satellite2.aspx, Satellite3.aspx, Satellite4.aspx. Сохраните и закройте мастер-страницу Title.master
  • Отредактируем страницы содержимого, начнем с Satellite.aspx.

  • В панели Solution Explorer назначьте страницу Satellite.aspx стартовой и откройте ее на редактирование в режиме Design
  • Что мы видим на данном этапе времени проектирования:

  • Мы видим сквозь контекстную страницу просвечивающуюся мастер-страницу. Причем заполнители (элементы вне контейнеров-шаблонов) мастер-страницы просвечиваются тускло. Это говорит о том, что они находятся на нижнем слое и их редактирование недоступно из контекстной страницы, но тем не менее они полностью отобразятся и будут доступны на этапе выполнения.
  • Места, где на мастер-странице расположены контейнеры-шаблоны, показаны ярко, но закрыты пустыми контейнерами контекста, сгенерированными оболочкой при создании контекстной страницы по готовым шаблонам. Причем закрыто только содержимое шаблонов мастер-страницы, а ее фон полность просвечивается.
  • Удалите в режиме Design командой контекстного меню Default to Master's Content все контейнеры контекста <asp:Content></asp:Content> из страницы Satellite.aspx
  • Эта страница у нас будет стартовой без своего содержимого и будет отображать только содержимое мастер-страницы. Мы вынуждены создать страницу без собственного контекста, только для запуска мастер-страницы, поскольку самостоятельно мастер-страница исполняться не может.

  • Убедитесь, что после удаления закрепленных блоков контекстной страницы проявился нижний слой - содержимое шаблонов мастер-страницы. Это содержимое и будет отображаться при выполнении на своих зарезервированных местах, если контекстная страница не имеет для них прикрепленных контейнеров с содержимым
  • Исполните страницу Satellite.aspx, получится ожидаемый результат
  • Пощелкайте по ссылкам и обратите внимание, как меняется адрес в броузере (ссылки работают). На адресуемых страницах мы пока не убрали и не изменили контейнеры содержимого, поэтому там на месте контейнеров шаблона ничего не будет (пустое содержимое контейнеров место при исполнении не занимает).
  • Теперь нужно наполнить контекстные страницы, которые мы подгружаем по ссылкам, индивидуальным содержимым.

    Приведем окончательный вариант дескрипторного кода для всех страниц данного примера:

    Интерфейсный код мастер-страницы Title.master

    Обратите внимание, что все контейнеры ContentPlaceHolder должны находиться внутри дескриптора <form></form>.

    <%@ Master Language="C#" %>
        
    <script runat="server">
        
    </script>
        
    <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">
                nbsp;<asp:Image ID="ImageBanner" runat="server" ImageUrl="~/MasterPages/Banner.png" /><br />
                nbsp;<h1 style="color: green">
                    Мы приветствуем Вас на нашем сайте!!!
                </h1>
                <table>
                    <tr>
                        <td colspan="2">
                            <asp:Panel ID="Panel1" runat="server" BackImageUrl="~/MasterPages/BackTitle.jpg"
                                Height="100%">
                                <asp:ContentPlaceHolder ID="ContentPlaceHolder1" runat="server">
                                    nbsp;<div style="font-weight: bold; font-size: xx-large; color: red; text-align: center">
                                        Сведения об автошколе в целом</div>
                                </asp:ContentPlaceHolder>
                            </asp:Panel>
                        </td>
                    </tr>
                    <tr>
                        <td style="width: 25%; background-color: #9DFF7E">
                            <strong style="color: Blue">Наши филиалы:</strong><br />
                            <br />
                            <asp:HyperLink ID="HyperLink1" runat="server" NavigateUrl="~/Satellite1.aspx">Ленинский район
                                    </asp:HyperLink><br />
                            <br />
                            <asp:HyperLink ID="HyperLink2" runat="server" NavigateUrl="~/Satellite2.aspx">Центральный район
                                    </asp:HyperLink><br />
                            <br />
                            <asp:HyperLink ID="HyperLink3" runat="server" NavigateUrl="~/Satellite3.aspx">Свердловский район
                                    </asp:HyperLink><br />
                            <br />
                            <asp:HyperLink ID="HyperLink4" runat="server" NavigateUrl="~/Satellite4.aspx">Октябрьский район
                                    </asp:HyperLink>
                        </td>
                        <td style="background-color: #FFB8FF">
                            <asp:ContentPlaceHolder ID="ContentPlaceHolder2" runat="server">
        <p>Вот уже более 15 лет мы занимаемся подготовкой водителей
    категории "B" в г.Красноярске и всегда занимали первые места
    в сводках МРЭО ГИБДД по качеству обучения. Автошкола всегда нацелена
    на успех наших клиентов в сдаче экзаменов ГАИ с первого раза
    и своевременном получении водительского удостоверения.</p>
    <p>Мы обеспечиваем максимальные удобства своим клиентам. Для
    этого существуют вечерняя и воскресная формы обучения.
    Наши курсанты имеют возможность бесплатно и неограниченно пользоваться
    компьютерным классом. Учебные автомобили прибывают по заявке
    клиента к назначенному им месту. Развивается сеть филиалов автошколы
    в г.Красноярске.</p>
       <p> Наша компьютерная экзаменационная программа успешно используется
    в ГИБДД г.Красноярска, Железногорска, Назарово, Емельяново, Шарыпово
    для государственной аттестации кандидатов в водители. Мы неоднократно
    выпускали цветные учебно-методические пособия по Правилам дорожного
    движения, которыми постоянно пользуются большинство автошкол
    города и края. </p>
       <p> Лекционные занятия в автошколе ведут квалифицированные преподаватели
    с учеными степенями и званиями с применением современных компьютерных
    технологий. Комплексный подход к обучению и разнообразие форм
    позволяют максимально сократить усилия наших клиентов и сроки
    усвоения ими учебного материала. Парк учебных автомобилей насчитывает
    более 30 штук, включая иномарки.</p>
                            </asp:ContentPlaceHolder>
                        </td>
                    </tr>
                    <tr style="background-color: #FFFF92">
                        <td colspan="2">
                            <asp:ContentPlaceHolder ID="ContentPlaceHolder3" runat="server">
                                <strong>Автошкола Диалог-Сервис в целом работает без перерыва на обед и выходных с 8
                                    часов утра до 9 часов вечера!!! </strong>
                            </asp:ContentPlaceHolder>
                        </td>
                    </tr>
                </table>
                <p style="text-align: right; color: Blue; font-family: 'Courier New'; font: bold italic">
                    <img id="IMG1" runat="server" src="LIGHT.ICO" />
                    nbsp;Copyright copy 2009
                </p>
                <asp:ContentPlaceHolder ID="ContentPlaceHolder4" runat="server">
                    <strong><em>Вы уж, пожалуйста, наведывайтесь к нам почаще!</em></strong>
                </asp:ContentPlaceHolder>
            </div>
        </form>
    </body>
    </html>

    Запускающая контекстная страница Satellite.aspx

    Все контейнеры <asp:Content> уничтожены, поэтому страница используется только как запускающая и не добавляет к мастер-странице своего содержимого. В ней осталась только строка с директивой @Page и привязкой к мастер-странице

    <%@ Page Language="C#" MasterPageFile="~/MasterPages/Title.master" 
        Title="Untitled Page" %>

    Контекстная страница Satellite1.aspx

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

    <%@ Page Language="C#" MasterPageFile="~/MasterPages/Title.master" Title="Untitled Page" %>
      
    <asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" runat="Server">
        <div style="font-weight: bold; font-size: xx-large; color: red; text-align: center">
            Сведения о филиале в Ленинском районе
        </div>
    </asp:Content>
      
    <asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder2" Runat="Server">
        Филиал в Ленинском районе такой хороший!<br />
        ..........................<br />
        и т.д.
    </asp:Content>
      
    <asp:Content ID="Content3" ContentPlaceHolderID="ContentPlaceHolder3" Runat="Server">
        <strong>Филиал в Ленинском районе работает без перерыва на обед и выходных с 8
            часов утра до 9 часов вечера!!! 
        </strong>
    </asp:Content>
      
    <asp:Content ID="Content4" ContentPlaceHolderID="ContentPlaceHolder4" Runat="Server">
        <strong><em>Вы уж, пожалуйста, наведывайтесь к нам почаще в филиал Ленинского района!</em></strong>
    </asp:Content>

    Контекстная страница Satellite2.aspx

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

    <%@ Page Language="C#" MasterPageFile="~/MasterPages/Title.master" Title="Untitled Page" %>
      
    <asp:Content ID="Content1" runat="Server" ContentPlaceHolderID="ContentPlaceHolder1">
        <div style="font-weight: bold; font-size: xx-large; color: red; text-align: center">
            Сведения о филиале в Центральном районе
        </div>
    </asp:Content>
      
    <asp:Content ID="Content2" runat="Server" ContentPlaceHolderID="ContentPlaceHolder2">
        Филиал в Центральном районе такой хороший!<br />
        ..........................<br />
        и т.д.
    </asp:Content>
      
    <asp:Content ID="Content3" runat="Server" ContentPlaceHolderID="ContentPlaceHolder3">
        <strong>
            Филиал в Центральном районе работает с перерывом на обед и с выходными!!!
        </strong>
    </asp:Content>
      
    <asp:Content ID="Content4" runat="Server" ContentPlaceHolderID="ContentPlaceHolder4">
        <strong><em>Вы уж, пожалуйста, наведывайтесь к нам почаще в филиал Центрального района!</em>
        </strong>
    </asp:Content>

    Контекстная страница Satellite3.aspx

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

    <%@ Page Language="C#" MasterPageFile="~/MasterPages/Title.master" Title="Untitled Page" %>
      
    <asp:Content ID="Content1" runat="Server" ContentPlaceHolderID="ContentPlaceHolder1">
        <div style="font-weight: bold; font-size: xx-large; color: red; text-align: center">
            Сведения о филиале в Свердловком районе
        </div>
    </asp:Content>
      
    <asp:Content ID="Content2" runat="Server" ContentPlaceHolderID="ContentPlaceHolder2">
        Филиал в Свердловском районе такой хороший!<br />
        ..........................<br />
        и т.д.
    </asp:Content>
      
    <asp:Content ID="Content4" runat="Server" ContentPlaceHolderID="ContentPlaceHolder4">
    </asp:Content>

    Контекстная страница Satellite4.aspx

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

    <%@ Page Language="C#" MasterPageFile="~/MasterPages/Title.master" Title="Untitled Page" %>
      
    <asp:Content runat="Server" ContentPlaceHolderID="ContentPlaceHolder1">
        <div style="font-weight: bold; font-size: xx-large; color: red; text-align: center">
            Сведения о филиале в Октябрьском районе
        </div>
    </asp:Content>
      
    <asp:Content runat="Server" ContentPlaceHolderID="ContentPlaceHolder3">
    </asp:Content>
      
    <asp:Content runat="Server" ContentPlaceHolderID="ContentPlaceHolder4">
    </asp:Content>

    Управление элементами мастер-страницы из контекстной страницы

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

  • Вначале ASP.NET создает элементы управления мастер-страницы, а потом дочерние элементы на контекстной странице
  • Затем генерируется событие Page.Init мастер-страницы, вслед за которым возбуждается событие Page.Init контекстной страницы
  • Затем срабатывает событие Page.Load мастер-страницы, а за ним то же событие контекстной страницы
  • Такой порядок означает, что если мы программно меняем одни и те же настройки в мастер-странице и в контекстной странице , то последними останутся изменения, выполненные в коде контекстной страницы.

    В контекстной странице мы можем менять только содержимое контейнеров <asp:Content>, а содержимое заполнителей мастер-страницы и ее заголовок являются недоступными на этапе проектирования. Но именно они участвуют в формировании HTML-вывода. Иногда, не трогая спроектированную мастер-страницу, нужно в ее заполнителях что-то изменить из конкретной контекстной страницы. Это можно сделать программно на этапе выполнения.

    Упражнение 15. Программирование заголовка мастер-страницы

    Вначале рассмотрим вопросы программирования заголовка мастер-страницы, который генерирует содержимое дескрипторов <head> в HTML-выводе. Мастер-страница имеет заголовок <head>...</head> обычной страницы, где можно задавать все составляющие его дескрипторы и это появится на результирующей странице.

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

    Программно можно переопределять не только дескриптор <title>, но и другие дескрипторы заголовка мастер-страницы. И опять, дескриптор <head> мастер-страницы должен обязательно быть серверным. Для программного управления элементами заголовка <head> мастер-страницы из текущей контекстной страницы нужно в контекстной странице получить ссылку на объект Page мастер-страницы. Это делается через свойство Page.Master объекта текущей контекстной страницы, у которого, в свою очередь, свойство Master.Page и определяет ссылку на объект Page мастер-страницы.

    Ниже приведены равноценные коды получения ссылки на объект Page мастер-страницы из контекстной страницы

    Page masterPage = base.Master.Page;
                Page masterPage = this.Master.Page;
                Page masterPage = Master.Page;

    Ключевые слова base или this не являются обязательными. Они напоминают, что Master является свойством объекта текущей страницы.

    Декларативное переопределение <title>

  • Создайте копию страницы Satellite.aspx, назначьте ей имя SatelliteExt.aspx и сделайте стартовой
  • Попробуем на контекстной странице SatelliteExt.aspx переопределить дескриптор <title> мастер-страницы. Исходный код страницы SatelliteExt.aspx такой

    <%@ Page Language="C#" MasterPageFile="~/MasterPages/Title.master" 
        Title="Untitled Page" %>
  • Замените атрибут Title директивы @Page на следующий
  • <%@ Page Language="C#" MasterPageFile="~/MasterPages/Title.master" 
        Title="Декларативный заголовок SatelliteExt.aspx" %>
  • Исполните страницу с новым заголовком окна броузера - результат будет таким
  • Программное переопределение <title>

    Поскольку мы не создавали для страницы SatelliteExt.aspx отдельный файл кода поддержки, то код переопределения заголовка будем писать в скриптах страницы

  • Переведите страницу SatelliteExt.aspx в режим Design и щелкните дважды на притушенной области проступающего заполнителя мастер-страницы
  • Оболочка автоматически создаст блок скрипта и вставит в него заготовку обработчика события Page.Load.

  • Заполните обработчик так
  • <%@ Page Language="C#" MasterPageFile="~/MasterPages/Title.master" 
        Title="Декларативный заголовок SatelliteExt.aspx" %>
      
    <script runat="server">
      
        protected void Page_Load(object sender, EventArgs e)
        {
            // Получаем ссылку на мастер-страницу
            Page masterPage = Master.Page;
            
            // Переопределяем строку заголовка
            masterPage.Header.Title = "Программный заголовок SatelliteExt.aspx";
        }
    </script>
  • Исполните страницу - результат будет таким
  • Несмотря на то, что мы оставили декларативный атрибут Title, обработчик переопределил его в окончательном выводе. Это и понятно: декларативный атрибут инициируется при создании объекта заголовка, а обработчик Page_Load() срабатывает позднее.

    Управление другими элементами мастер-страницы

    Рассмотрим управление элементами заполнителя мастер-страницы из контекстной страницы на примере дескриптора <h1>, определенного в заполнителе так

    <h1 style="color: Green">
        Мы приветствуем Вас на нашем сайте!!!
    </h1>

    Его отображение в броузере будет следующим

  • Дополните дескриптор <h1> мастер-страницы Title.master необходимыми для программного управления атрибутами
  • <h1 style="color: Green" runat="server" id="Header1">
        Мы приветствуем Вас на нашем сайте!!!
    </h1>

    Управлять будем из страницы SatelliteExt.aspx одним из двух способов:

  • Инкапсулируем (упакуем) в мастер-странице дескриптор <h1> в отдельное свойство с именем MyHeader1 и из контекстной страницы будем обращаться к нему через это свойство
  • Не трогая код мастер-страницы, найдем из контекстной страницы дескриптор <h1> по его ID и будем обращаться к нему напрямую
  • Первый способ

  • Двойным щелчком на свободном от элементов управления месте мастер-страницы 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>
  • Дополните контекстную страницу 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>
  • Исполните контекстную страницу 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>

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

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

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

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

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

  • Контекстная страница, динамически меняющая шаблон, не должна иметь контекстных блоков, ссылающихся на несуществующие в новой мастер-странице контейнеры ContentPlaceHolderID
  • Замена шаблона должна выполняться в обработчике события Page.PreInit контекстной страницы. Если это сделать в обработчиках более поздних событий, то будет сгенерировано исключение
  • Все контекстные страницы, на которые имеются ссылки в текущей странице содержимого, должны иметь код подключения выбранного шаблона
  • Для межстраничной передачи информации о выбранном шаблоне ее нужно где-то сохранять. Как и в случае с темами, это могут быть скрытые поля состояния вида (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>
  • Откройте поочередно файлы 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>
  • Исполните начальную страницу 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>
  • Теперь также в корне 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>

    Обратите внимание, что при попытке переключить созданную мастер-страницу в режим 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>
  • Назначьте контекстную страницу TestMasterPageLevel.aspx стартовой и исполните ее
  • Результат будет таким

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

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

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

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

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

    Страницы:

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

    Файлы к лабораторной работе Вы можете скачать здесь.

    Единообразие в оформлении множества отдельных страниц сайта является важной его характеристикой. Visual Studio 2005 имеет три возможности для поддержки стилевого оформления (форматирования) сайта:

  • Каскадные таблицы стилей ( CSS - Cascading Style Sheet) - типовое средство языка HTML для управления стилями тегов
  • Темы (theme) - специфическое средство ASP.NET, позволяющее определить детали оформления отдельных элементов управления и воспроизводить их на множестве страниц, где есть связанные с темой элементы. Можно заранее создать несколько разных тем и переключать их, если необходимо изменить стилевое оформление страниц, использующих темы
  • Эталонные страницы (master page) - представляют собой многократно используемые шаблоны страниц. Их еще называют мастер-страницами. В шаблоне можно определить необходимые детали оформления, такие как заголовки, панели меню, банеры и связать их с обычными страницами. После этого все использующие шаблон страницы будут генерировать одинаковый дизайн
  • Каскадные таблицы стилей

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

  • Внутренняя (inline) - когда отдельный тег HTML имеет свои индивидуальные настройки стиля, областью действия которых является только этот тег
  • Встроенная (embedded) - когда таблица стилей находится на странице внутри блока <head> и имеет описание стилей общее для тех дескрипторов страницы, на которые она должна действовать. Это индивидуальная настройка стилей для страницы и бластью их действия являются теги только одного документа
  • Внешняя (linked) - когда описание стилей находится в отдельном файле и действуют они на те страницы сайта, к которым подключен этот файл
  • По отношению к этим трем типам CSS также соблюдается свойство каскадности, т.е. стиль внутреннего каскада имеет более высокий приоритет и перекрывает действие внешнего стиля.

    Кратко рассмотрим все перечисленные типы CSS и их поддержку оболочкой Visual Studio 2005.

    Упражнение 1. Внутренние стили inline

    Стиль отдельного дескриптора определяется атрибутом style. Синтаксис применения внутреннего стиля на примере дескриптора <h1> будет таким

    Значением стилевого атрибута является строка, представляющая пары ' стиль:значение ', отделенные друг от друга разделителем 'точка с запятой' или пробелом. Определенный в дескрипторе внутренний стиль будет действовать только на свой дескриптор и перекрывать все определения внешних стилей.

  • Командой главного меню оболочки File/New/Website создайте новый пустой проект с именем PageStyles
  • Командой главного меню оболочки Website/Add New Item добавьте в корневой узел проекта конфигурационный файл Web.config, который будет действовать на уровне всего сайта
  • Запустите утилиту WAT (Web Site Administration Tool) командой меню Website/ASP.NET Configuration (то же самое можно сделать в панели Solution Explorer, щелкнув на пиктограмме ASP.NET Configuration )
  • В открывшемся окне броузера на странице WAT перейдите на вкладку Application, щелкните на ссылке Configure debugging and tracing и в появившейся странице включите флажок Enable debugging
  • Выполненное действие установит в конфигурационном файле элемент debug в значение true.

    <configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
        <appSettings/>
        <connectionStrings/>
        <system.web>
            <compilation debug="true" />
            <authentication mode="Windows" />
        </system.web>
    </configuration>
  • Добавьте к проекту пассивную страницу Page1.htm
  • Поместите внутрь дескриптора <body> заголовочный дескриптор с внутренними стилевыми настройками, дескриптор горизонтальной линии и ссылку на следующую страницу
  • <h1 style="color: red; text-align: center">
            Этот заголовочный текст будет центрирован в 
            сгенерированном документе и иметь красный цвет!
        </h1>
        <hr />
        <a href=Page2.htm>Вперед</a>
  • Запустите текущую страницу и убедитесь, что внутренний стиль дескриптора действует
  • Упражнение 2. Встроенные стили embedded

    Определение внутренних стилей отдельных дескрипторов используется редко. Чаще применяются встроенные стили, которые действуют в пределах всей страницы. Встроенные стили не разбросаны по странице, а размещаются внутри дескрипторного блока <head>...</head> в специальном стилевом контейнере <style>...</style>. Синтаксис встроенных стилей следующий

    Атрибут type="text/css" дескриптора стиля указывает броузеру, что ниже перечислены стили, которые нужно применить к тексту при его отображении. Внутри стилевого блока описываются именованные группы пар 'стиль:значение' с разделителем 'точка с запятой', которые называются селекторами.

    Имена селекторов могут совпадать с именами дескрипторов, например, в приведенном выше примере селекторы body и h1 совпадают с именами дескрипторов HTML. В этом случае дополнительных указаний внутри дескрипторов не требуется.

    Если используется список селекторов с определенными идентификаторами, то описанный в списке стиль применяется к элементам страницы с указанными идентификаторами и дополнительных указаний внутри именованных дескрипторов не требуется.

    В качестве универсальных меток стилей могут использоваться селекторы классов " .имя_класса ". Тогда в тех дескрипторах документа, где необходимо применить определенный в классе стиль, должен присутствовать атрибут class=" имя_класса". Классы для конкретного вида тегов " тег.имя_класса " используются только в тегах заданного типа по той же самой схеме class=" имя_класса". Имена классов " .имя_класса " выбираются произвольно.

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

  • В панели Solution Explorer вызовите контекстное меню для корневого узла проекта и командой New Folder добавьте папку с именем Resources
  • В панели Solution Explorer вызовите контекстное меню для узла Resources и командой Add Existing Item скопируйте в папку Resources из прилагаемого к работе каталога Source файлы BackgroundImage.bmp и Web.jpg
  • Добавьте к проекту пассивную HTML-страницу с именем Page2.htm и заполните ее следующим кодом
  • <html xmlns="http://www.w3.org/1999/xhtml" >
    <head>
        <title>Документ со встроенной CSS</title>
        <meta content="text/html; charset=windows-1251" http-equiv="Content-Type">
        <style type="text/css">
            body
            {
              background:url(Resources/Web.jpg);
              font-family:Arial, Helvetica, sans-serif;
            }
      
            h1
            {
              color:#FF0000;
              text-align:center;
            }
      
            #TextBox1
            {
              font-family:"Courier New", Courier, mono;
              font-size:24px;
              font:bold italic;
              background-color:#0000FF;
              color:yellow;
              width:400;
            }
      
            .btnResult
            {
              background-image:url(Resources/BackgroundImage.bmp);
              color:white;
            }
        </style>
    </head>
    <body>
        <h1>
            Заполните анкету!
        </h1>
        <form id="form1" action="Page2.htm">
            <p>
                Введите ФИО:
                <input id="TextBox1" name="TextBox1" type="text">
            </p>
            <p>
                Введите ИНН:
                <input id="TextBox2" name="TextBox2" type="text">
            </p>
            <center>
                <input id="Button1" class="btnResult" name="Button1" 
                       type="button" value="Отослать">
            </center>
        </form>
    </body>
    </html>
  • Запустите страницу Page2.htm и проверьте результат
  • Упражнение 3. Внешние стили linked

    Внешними считаются стили, которые хранятся в отдельном файле текстового формата (чаще, для узнаваемости, - с расширением .css, но будет работать и любое другое расширение). Содержимое стиля в отдельном файле отличается от встроенного стиля:

  • Во внешнем стиле не должно быть дескрипторных скобок <style></style>
  • Подключение стилевого файла к HTML-документу выполняется добавлением в блок <head>...</head> дескриптора <link> или директивы @impot
  • Пусть имеется два файла с внешними стилями и произвольными именами, например, header.css и default.css. Варианты их подключения в HTML-документ Page3.htm ( обязательно в заголовочный блок <head>...</head> ) приведены в таблице:

    Варианты подключения CSS к HTML-документу
    Вариант В блоке <head> файла Page3.htm Замечания
    Подключается одна CSS <link rel="stylesheet" href="header.css">  
    Подключается две CSS
    <link rel="stylesheet" href="header.css">
    <link rel="stylesheet" href="default.css">
     
    Подключается одна CSS
    <style type="text/css">
        @import url(header.css);
    </style>
    Не поддерживается Netscape Navigator 4
    Подключается две CSS
    <style type="text/css">
        @import url(header.css);
        @import url(default.css); 
    </style>
    Не поддерживается Netscape Navigator 4
    Подключается две CSS ( в страницу импортируется цепочка CSS)
    <style type="text/css">
        @import url(header.css);
    </style>

    В самом начале файла header.css имеется голая строка прямого подключения: @import url(default.css)

    Подключается две CSS
    <style type="text/css">
        @import url(header.css);
    </style>
    <link rel="stylesheet" href="default.css">
    Порядок следования не влияет
    Подключается две CSS
    <link rel="stylesheet" href="default.css">
    <style type="text/css">
        @import url(header.css);
    </style>
    Порядок следования не влияет
    Подключается три CSS
    <link href="default.css" rel="stylesheet">
    <style type="text/css">
        
        @import url(header.css);
        
        body
        {
            background:white url(Web.jpg);
            font-family:Arial, Helvetica, sans-serif;
        }
          
        h1
        {
            color:#FF0000;
            text-align:center;
        }
          
        #TextBox1
        {
            font-family:"Courier New", Courier, mono;
            font-size:24px;
            font:bold italic;
            background-color:#0000FF;
            color:yellow;
            width:400;
        }
          
        .btnResult
        {
            background-image:url(BackgroundImage.bmp);
            color:white;
        }
        
    </style>

    Подключается две внешних таблицы в дополнение к встроенной.

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

    Порядок следования не влияет

    Подключается три CSS
    <style type="text/css">
        
        body
        {
            background:white url(Web.jpg);
            font-family:Arial, Helvetica, sans-serif;
        }
          
        h1
        {
            color:#FF0000;
            text-align:center;
        }
          
        #TextBox1
        {
            font-family:"Courier New", Courier, mono;
            font-size:24px;
            font:bold italic;
            background-color:#0000FF;
            color:yellow;
            width:400;
        }
          
        .btnResult
        {
            background-image:url(BackgroundImage.bmp);
            color:white;
        }
        
        @import url(header.css);
        
    </style>
      
    <link href="default.css" rel="stylesheet">

    Подключается две внешних таблицы в дополнение к встроенной.

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

    Порядок следования не влияет

    Применим рассмотренный материал на практике.

  • В панели Solution Explorer найдите страницу Page2.htm и сделайте из нее две копии с именами Page3.htm и ExternalCSS.css
  • Откройте файл Page3.htm и замените в заголовке <head> встроенный стиль на строку подключения внешней каскадной таблицы стилей
  • <link rel="stylesheet" href=" ExternalCSS.css">

    <html xmlns="http://www.w3.org/1999/xhtml" >
    <head>
        <title>Документ со встроенной CSS</title>
        <meta content="text/html; charset=windows-1251" http-equiv="Content-Type">
        <link href="ExternalCSS.css" rel="stylesheet">
    </head>
    <body>
        <h1>
            Заполните анкету!
        </h1>
        <form id="form1" action="Page3.htm">
            <p>
                Введите ФИО:
                <input id="TextBox1" name="TextBox1" type="text">
            </p>
            <p>
                Введите ИНН:
                <input id="TextBox2" name="TextBox2" type="text">
            </p>
            <center>
                <input id="Button1" class="btnResult" name="Button1" 
                       type="button" value="Отослать">
            </center>
        </form>
    </body>
    </html>
  • Откройте файл ExternalCSS.css и оставьте только чистый код селекторов и определений стилей
  • body
            {
              background:url(Resources/Web.jpg);
              font-family:Arial, Helvetica, sans-serif;
            }
      
            h1
            {
              color:#FF0000;
              text-align:center;
            }
      
            #TextBox1
            {
              font-family:"Courier New", Courier, mono;
              font-size:24px;
              font:bold italic;
              background-color:#0000FF;
              color:yellow;
              width:400;
            }
      
            .btnResult
            {
              background-image:url(Resources/BackgroundImage.bmp);
              color:white;
            }
  • Откройте страницу Page3.htm в броузере и убедитесь, что получился тот же результат, что и при использовании встроенных стилей в странице Page2.htm
  • Опробуйте другие варианты подключения внешней CSS, приведенные ранее в таблице
  • Упражнение 4. Управление внешними стилями средствами оболочки

    Итак, внешняя CSS, это обычный текстовый файл, которому традиционно назначают расширение .css для лучшей узнаваемости, хотя может использоваться любое другое расширение. Его можно создавать отдельно в любом текстовом редакторе, а затем использовать в Web-проекте. Но Visual Studio 2005 имеет свои встроенные средства создания CSS. Рассмотрим их.

  • Добавьте в корневой узел сайта командой Website/Add New Item новый файл типа Style Sheet с именем StyleSheet.css (по умолчанию)
  • Оболочка добавит в проект новый файл каскадной таблицы стилей и покажет панель инструментов Style Sheet, помогающую редактировать этот файл. Здесь, правда, возможностей не так много, как в специализированных редакторах стилей, но основные средства все-таки имеются.

    Наша главная задача, как программистов, - понимать и правильно применять CSS, а на чем она подготовлена или кем (пусть у дизайнеров голова болит), не очень важно. Но если приспичит, можно порекомендовать специализированный редактор CSS, который позволяет кодировать вручную или использовать удобный интерфейс. Это редактор StyleMaster, информацию о котором можно найти в Internet по ссылке www.westciv.com/style_master

    Не будем использовать созданную оболочкой заготовку StyleSheet.css, а научимся подключать к странице через оболочку свою уже готовую таблицу стилей ExternalCSS.css.

  • Удалите созданную оболочкой заготовку StyleSheet.css
  • Создайте копию статической страницы Page3.htm и присвойте копии имя Page4.htm
  • Назначьте страницу Page4.htm стартовой (команда Set As Start Page ) и запустите ее на выполнение, чтобы убедиться в ее работоспособности
  • Если страница не содержит серверных элементов с атрибутом runat="server", то система ASP.NET просто отсылает клиенту код этой страницы в неизменном виде. Если посмотреть на исходный HTML-код присланной броузеру страницы (команда View Source контекстного меню окна броузера), то он остался тем же самым, что и в генерирующей странице Page4.htm

    <html xmlns="http://www.w3.org/1999/xhtml" >
    <head>
        <title>Документ со встроенной CSS</title>
        <meta content="text/html; charset=windows-1251" http-equiv="Content-Type">
        <link href="ExternalCSS.css" rel="stylesheet">
    </head>
    <body>
        <h1>
            Заполните анкету!
        </h1>
        <form id="form1" action="Page3.htm">
            <p>
                Введите ФИО:
                <input id="TextBox1" name="TextBox1" type="text">
            </p>
            <p>
                Введите ИНН:
                <input id="TextBox2" name="TextBox2" type="text">
            </p>
            <center>
                <input id="Button1" class="btnResult" name="Button1" 
                       type="button" value="Отослать">
            </center>
        </form>
    </body>
    </html>
    <html xmlns="http://www.w3.org/1999/xhtml" >
    <head>
        <title>Документ со встроенной CSS</title>
        <meta content="text/html; charset=windows-1251" http-equiv="Content-Type">
        <link href="ExternalCSS.css" rel="stylesheet">
    </head>
    <body>
        <h1>
            Заполните анкету!
        </h1>
        <form id="form1" action="Page4.htm">
            <p>
                Введите ФИО:
                <input id="TextBox1" name="TextBox1" type="text">
            </p>
            <p>
                Введите ИНН:
                <input id="TextBox2" name="TextBox2" type="text">
            </p>
            <center>
                <input id="Button1" class="btnResult" name="Button1" 
                       type="button" value="Отослать">
            </center>
        </form>
    </body>
    </html>

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

    Чтобы успокоить редактор, нужно сообщить ему, чтобы он проверял синтакис чистого HTML, а не XHTML.

  • Выполните команду Tools/Customize и отметьте флажок HTML Source Editing
  • В верхней части каркаса оболочки появится панель инструментов настройки HTML-редактора, в которой нужно установить через раскрывающийся список опцию HTML 4.01. Через некоторое время, после очередного сканирования синтаксиса документа HTML-редактор успакаивается. Может остаться подчеркнутым открывающий HTML-дескриптор <form>. Чтобы избавиться и от этого замечания редактора, нужно добавить атрибут action с целевой страницей, которая будет обрабатывать обратную отсылку (постинг) на сервер, в нашем случае

    <form action="Page4.htm">

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

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

  • Вызовите контекстное меню щелчком правой кнопки мыши на свободном месте верхней части каркаса оболочки и сбросьте флажок HTML Source Editing, чтобы закрыть одноименную панель настройки редактора
  • Удалите из заголовочного блока страницы Page4.htm код подключения внешней таблицы стилей , а именно, удалите строку
  • <link rel="stylesheet" href="ExternalCSS.css">

  • Переведите страницу в режим Design и посмотрите, какая она сразу стала некрасивая
  • Захватите мышью в панели Solution Explorer файл ExternalCSS.css и перенесите его на страницу, которая сразу преобразится уже на этапе проектирования
  • Вернитесь в режим Source и убедитесь, что оболочка добавила нужный дескриптор подключения CSS в нужное место
  • Опять удалите дескриптор подключения CSS
  • Перейдите в режим Design, откройте панель Properties и через раскрывающийся список в ее верхней части выберите объект DOCUMENT
  • Найдите свойство StyleSheet и подключите к нему таблицу ExternalCSS.css через диалоговое окно. Оболочка опять добавит нужный дескриптор подключения CSS в нужное место
  • Очистите поле свойства StyleSheet, оболочка вновь удалит дескриптор подключения
  • Таблицы CSS применяются не только к простым дескрипторам HTML, но и к дескрипторам серверных элементов управления. Проверим это, поместив на страницу серверный элемент управления, например, Label.

  • Откройте панель Toolbox
  • Мы видим, что доступной является только вкладка HTML, потому, что наша тестовая страница Page4.htm имеет расширение .htm.

  • Измените в панели Solution Explorer расширение страницы на Page4.aspx и сделайте страницу принимающей, исправив дескриптор <form>
  • <form action="Page4.aspx" runat="server">

    Вкладка Standard в панели Toolbox все равно не появилась, потому что редактор оболочки все еще считает страницу статической.

  • Для изменения мнения редактора о типе страницы закройте редактирование страницы Page4.aspx, затем вновь откройте
  • Поместите на страницу между текстовым полем и кнопкой из вкладки Standard текстовую метку Label и в панели Properties определите
  • CssClass=label
  • Text=Стиль класса label
  • Откройте через Solution Explorer файл ExternalCSS.css и добавьте стилевое правило для класса label
  • .label
            {
                background-color:Green;
                color:White;
                font:bold;
                font-size:x-large;
                font-family:Courier New;
            }    
            
            body
            {
              background:white url(Web.jpg);
              font-family:Arial, Helvetica, sans-serif;
            }
      
            h1
            {
              color:#FF0000;
              text-align:center;
            }
      
            #TextBox1
            {
              font-family:"Courier New", Courier, mono;
              font-size:24px;
              font:bold italic;
              background-color:#0000FF;
              color:yellow;
              width:400;
            }
      
            .btnResult
            {
              background-image:url(BackgroundImage.bmp);
              color:white;
            }
  • Переведите страницу в режим Design, захватите мышью в панели Solution Explorer файл ExternalCSS.css и перенесите его на страницу
  • Выполните страницу, которая в броузере станет такой
  • Краткое знакомство с CSS и их поддержкой в Visual Studio 2005 закончили!

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

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

    Темы

    Как и CSS, темы определяют набор стилей, которые называют оформлениями и которые можно применять на многих страницах ASP.NET. Особенности тем состоят в следующем:

  • Стилевые параметры тем преобразуются элементами управления во внутренние стили HTML-дескрипторов при генерации HTML-кода и не могут быть реализованы напрямую на стороне броузера
  • Темы могут применяться вместе с таблицами стилей, тогда клиенту отсылается комбинированный стиль, определяемый CSS и сгенерированным стилем темы
  • CSS можно использовать как часть темы
  • Темы могут подключаться к множеству страниц через конфигурационные файлы
  • К странице одновременно может применяться только одна тема, темы друг с другом не объединяются. Темы для одной и той же страницы можно менять только программно при каждой новой загрузке страницы
  • Приоритетом внутреннего стиля элемента управления и его стиля, определенного в теме, можно управлять
  • Разработчик должен создавать свои темы с нуля или может загружать готовые темы с других Web-сайтов, например, http:// www.asp.net. Но если тема используется, то для обеспечения гибкости и автономности она должна физически находиться в каталоге приложения.
  • Все темы помещаются в каталог с зарезервированным именем App_Themes, который можно добавить к приложению в панели Solution Explorer, выполнив команду Add ASP.NET Folder/Theme контекстного меню для узла приложения

    Каждая тема должна храниться в своей одноименной папке и может состоять из нескольких файлов текстового формата с произвольным именем и обязательным расширением .skin (skin - кожа). Эти файлы называются файлами оформлений и добавляются в папку App_Themes командой Website/Add New Item через соответствующее диалоговое окно

    Папку App_Themes отдельно можно и не создавать, потому, что при попытке добавления нового файла типа Skin File оболочка предложит автоматически создать папку App_Themes

    Для темы можно все стилевые настройки элементов управления разместить в одном файле оформленийscin, а можно и в нескольких. Иногда просто удобнее группировать оформления по отдельным категориям элементов управления в соответствующих файлах. Для ASP.NET это не имеет никакого значения, поскольку она все равно считывает все файлы оформлений в каталоге указанной темы как неделимое целое определения одной темы.

    Структура папки App_Themes может иметь следующий вид

    Проверим сказанное на практике.

    Упражнение 5. Простое стилевое оформление страницы с помощью темы

  • Добавьте к проекту новую страницу Page5.aspx с отделенным кодом
  • Добавьте на страницу из вкладки Standard следующие элементы управления
  • BulletedList
  • Label
  • CheckBox
  • Button
  • Назначьте свойства Text этих элементов (для BulletedList - коллекция Items ) и скомпонуйте их на странице так, чтобы их внешний вид на этапе проектирования был следующим
  • В панели Solution Explorer создайте копию файла Page5.aspx и присвойте ей имя FirstSkin.aspx. Из него мы в дальнейшем сделаем файл оформлений
  • Удалите файл кодовой поддержки FirstSkin.aspx.cs
  • Настройте декларативно в панели Properties стилевые свойства объектов страницы FirstSkin.aspx
  • Таблица свойств элементов управления страницы FirstSkin.aspx
    Элемент Свойство Значение
    BulletedList ID BulletedList1
      BackColor Yellow
      BorderColor Red
      BorderStyle Ridge
      BorderWidth 10px
      BulletStyle Numbered
      ForeColor Blue
    Label ID Label1
      BackColor Blue
      ForeColor Yellow
      ToolTip Текстовая метка
    CheckBox ID CheckBox1
      BackColor Red
      BorderStyle Groove
      Font/Bold True
      ForeColor White
    Button ID Button1
      BackColor #FFC0C0
      BorderColor Blue
      BorderStyle Solid
      BorderWidth 2px
      Font/Bold True
      ForeColor Blue

    Внешний вид элементов управления после проведенных стилевых настроек должен быть таким

  • Перейдите в режим Source и удалите из файла FirstSkin.aspx весь код, кроме открывающих дескрипторов элементов управления, в которых тоже удалите атрибуты, не относящиеся к настройкам стиля, включая атрибут ID
  • В файле FirstSkin.aspx должно остаться следующее

    <asp:BulletedList runat="server" 
                BackColor="Yellow" BorderColor="Red" 
                BorderStyle="Ridge" BorderWidth="10px" 
                BulletStyle="Numbered" ForeColor="Blue" />
            <asp:Label runat="server" 
                BackColor="Blue" ForeColor="Yellow" 
                ToolTip="Текстовая метка" />
            <asp:CheckBox runat="server" 
                BackColor="Red" BorderStyle="Groove" 
                Font-Bold="True" ForeColor="White" />
            <asp:Button runat="server"
                BackColor="#FFC0C0" BorderColor="Blue" 
                BorderStyle="Solid" BorderWidth="2px" 
                Font-Bold="True" ForeColor="Blue" />
  • Присвойте файлу FirstSkin.aspx новое расширение FirstSkin.skin
  • Создайте в приложении папку с зарезервированным именем App_Themes и определите в ней подпапку с именем MyTheme, в которой будут храниться файлы оформлений этого упражнения (точнее, будет всего один файл FirstSkin.skin )
  • В панели Solution Explorer захватите мышью файл FirstSkin.skin и переместите его в папку MyTheme
  • Тема подключается к странице с помощью атрибута Theme директивы @Page. Так, например, при применении темы с именем каталога MyTheme к странице упражнения Page5.aspx директива будет выглядеть так

    <%@ Page Language="C#" AutoEventWireup="true" 
             CodeFile="Page5.aspx.cs" Inherits="Page5"
             Theme="MyTheme" %>

    Тему можно подключить к странице и в панели Properties через свойство Theme объекта DOCUMENT. В этом случае оболочка автоматически добавит атрибут Theme к директиве @Page с выбранным значением.

  • В раскрывающемся списке панели Properties для страницы Page5.aspx выделите объект DOCUMENT и установите свойство Theme="MyTheme"
  • Назначьте страницу Page5.aspx стартовой и выполните ее
  • Представление рендеринга на стороне клиента, сгенерированного активной страницей Page5.aspx, с учетом присоединенных через тему стилевых настроек элементов управления будет иметь вид

    Естественно, что переопределяются только те стилевые настройки элементов управления страницы, которые указаны в подключенной теме. Остальные настройки не переопределяются.

    Упражнение 6. Приоритеты тем и CSS

    При подключении CSS стили начинают действовать на отображение элементов немедленно, на этапе проектирования и мы можем их подбирать, сразу видя результат. При определении свойства Theme действие стилей мы увидим только на этапе выполнения. Механизм тем реализует стилевые способности генерирующих элементов управления только при выполнении в соответствии с настройками, хранящимися отдельно в одном или нескольких файлах оформлений каталога темы.

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

  • В панели Solution Explorer получите копию страницы Page5.aspx и присвойте ей имя Page6.aspx
  • Установите прямо в настройках страницы Page6.aspx для элемента CheckBox1 атрибуты
  • BackColor="Black"
  • ForeColor="Yellow"
  • Мы хотим, чтобы элемент CheckBox1 имел черный фон и желтый текст, т.е. ждем его таким

  • Выполните страницу и убедитесь, что элемент CheckBox1 попрежнему будет иметь цвет фона красный, а цвет текста белый, определенные в подключенной к странице теме Theme="MyTheme"
  • Объект DOCUMENT страницы имеет еще одно свойство StyleSheetTheme, к которому также можно подключить внешнюю тему. В этом случае приоритет будут иметь индивидуальные настройки элементов управления, которые в них были явно определены. Но те настройки по умолчанию, которые мы не определяли на странице, но которые содержатся в теме, на свойства по умолчанию действуют.

  • Для страницы Page6.aspx в панели Properties выделите объект DOCUMENT, очистите поле его свойства Theme и подключите тему MyTheme к свойству StyleSheetTheme
  • Выполните страницу и убедитесь, что индивидуальная настройка элемента CheckBox1 имеет приоритет
  • Любой вариант влияния темы на элемент управления можно отключить, если установить в этом элементе свойство EnableTheming="False". Влияние настроек темы на всю страницу можно отключить, если в директиве @Page страницы установить атрибут EnableTheming="False" (по умолчанию это свойство страницы имеет значение True ).
  • Для страницы Page6.aspx в панели Properties выделите объект DOCUMENT, очистите поле его свойства StyleSheetTheme и подключите тему MyTheme к свойству Theme
  • Выделите объект CheckBox1 с индивидуальными настройками BackColor="Black" ; ForeColor="Yellow" и отключите для него влияние темы установкой значения свойства
  • EnableTheming="False"
  • Выполните страницу и убедитесь, что настройки темы отключены и действуют только настройки по умолчанию и индивидуальные настройки элемента
  • Выделите объект CheckBox1 и верните для него EnableTheming="True"
  • Упражнение 7. Именованные оформления элементов управления в теме

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

    Если просто определить в теме несколько вариантов настроек для одного и того же элемента управления, то ASP.NET выдаст ошибку об обнаружении неоднозначности. В этом случае в теме нужно применять именованные оформления с помощью атрибута SkinID. Стиль элемента управления в файле оформлений будет иметь метку SkinID, и на странице наряду с общим подключением темы с помощью свойства Theme нужно будет определить дополнительные свойства SkinID для отдельных элементов управления.

    Значения SkinID в определениях темы должно быть уникальным только в группе однотипных элементов. Это значит, что мы можем создать несколько групп настроек разнотипных элементов с одинаковыми значениями атрибута SkinID и подключать из одной темы, по мере необходимости, к каждому элементу управления свои индивидуальные настройки.

  • Скопируйте страницу Page5.aspx в страницу Page7.aspx
  • Добавьте в страницу Page7.aspx элемент Table из вкладки HTML панели Toolbox. Таблицу расположите между кнопкой и предшествующими элементами. Она будет компоновать расположение части элементов управления на странице и мы ее будем называть далее компоновочной
  • Для нашего упражнения таблица должна иметь три столбца и пять строк. Оболочка не обладает развитыми средствами редактирования таблиц, поэтому проще настроить таблицу в режиме Source.

  • Поместите из вкладки Standard панели Toolbox в средний столбец таблицы пять серверных элементов Label, а в правый - пять серверных элементов CheckBox
  • Компоновочная таблица
    SkinID не подключен: действуют общие настройки по умолчанию Label2
    Именованные настройки подключены: SkinID="Variant3" Label3
    Именованные настройки подключены: SkinID="Variant4" Label4
    SkinID="Variant5" подключен, но тема подавлена: EnableTheming="False" Label5
    Подключен несуществующий SkinID="XXX": настройки темы не действуют Label6
  • В панели Solution Explorer щелкните на узле MyTheme и добавьте к проекту новый файл оформлений с именем FirstSkinID.skin
  • Очистите от комментариев файл оформленийFirstSkinID.skin и дополните его индивидуальными именованными настройками стилей, чтобы общий код его стал таким
  • <asp:Label runat="server" BackColor="Blue" ForeColor="Yellow" SkinID="Variant2" />
    <asp:CheckBox runat="server" BackColor="Red" BorderStyle="Groove"
        Font-Bold="True" ForeColor="White" SkinID="Variant2" />
    <asp:Label runat="server" ForeColor="Blue" SkinID="Variant3" />
    <asp:CheckBox runat="server" BorderStyle="Dotted" Font-Bold="True" 
        ForeColor="Red" SkinID="Variant3" />
    <asp:Label runat="server" BackColor="Yellow" ForeColor="Red" SkinID="Variant4" />
    <asp:CheckBox runat="server" BackColor="Red" BorderStyle="Solid"
        ForeColor="Yellow" SkinID="Variant4" />
    <asp:Label runat="server" BackColor="Blue" ForeColor="Yellow" SkinID="Variant5" />
    <asp:CheckBox runat="server" BackColor="Red" BorderStyle="Groove"
        Font-Bold="True" ForeColor="White" SkinID="Variant5" />
    <asp:Label runat="server" BackColor="Blue" ForeColor="Yellow" SkinID="Variant6" />
    <asp:CheckBox runat="server" BackColor="Red" BorderStyle="Groove"
        Font-Bold="True" ForeColor="White" SkinID="Variant6" />
  • Установите в добавленных объектах свойство SkinID в соответствии с первым столбцом компоновочной таблицы, приведенной ранее
  • Выполните страницу Page7.aspx, результат должен быть примерно таким
  • Вспомним, что активизируются все файлы оформлений, соответствующие указанной теме и расположенные в соответствующем одноименном каталоге. И в нашем упражнении часть элементов страницы оформляется с помощью неименованных атрибутов, расположенных в файле App_Themes\MyTheme\ FirstSkin.skin, а часть - с помощью именованных, расположенных в файле App_Themes\MyTheme\ FirstSkinID.skin.

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

    Окончательный код интерфейсной части страницы Page7.aspx будет следующим

    <%@ Page Language="C#" AutoEventWireup="true" 
             CodeFile="Page7.aspx.cs" Inherits="Page7"
             Theme="MyTheme" %>
        
    <html xmlns="http://www.w3.org/1999/xhtml" >
    <head runat="server">
        <title>Untitled Page</title>
    </head>
    <body>
        <form id="form1" runat="server">
            <div>
                <asp:BulletedList ID="BulletedList1" runat="server">
                    <asp:ListItem>Туризм</asp:ListItem>
                    <asp:ListItem>Спорт</asp:ListItem>
                    <asp:ListItem>Музыка</asp:ListItem>
                    <asp:ListItem Value="Танцы"></asp:ListItem>
                </asp:BulletedList>
                <asp:Label ID="Label1" runat="server" Text="Текстовая метка"></asp:Label>
                <asp:CheckBox ID="CheckBox1" runat="server" Text="Флажок" />
                <br />
                <br />
                <table bgcolor="#FFFFFF">
                    <tr>
                        <td>
                            SkinID не подключен: действуют общие настройки по умолчанию
                        </td>
                        <td>
                            <asp:Label ID="Label2" runat="server" Text="Label2"></asp:Label></td>
                        <td style="height: 26px">
                            <asp:CheckBox ID="CheckBox2" runat="server" Text="CheckBox2" /></td>
                    </tr>
                    <tr>
                        <td>
                            Именованные настройки подключены: SkinID="Variant3"</td>
                        <td>
                            <asp:Label ID="Label3" runat="server" SkinID="Variant3" Text="Label3"></asp:Label></td>
                        <td>
                            <asp:CheckBox ID="CheckBox3" runat="server" SkinID="Variant3" Text="CheckBox3" /></td>
                    </tr>
                    <tr>
                        <td>
                            Именованные настройки подключены: SkinID="Variant4"</td>
                        <td>
                            <asp:Label ID="Label4" runat="server" SkinID="Variant4" Text="Label4"></asp:Label></td>
                        <td>
                            <asp:CheckBox ID="CheckBox4" runat="server" SkinID="Variant4" Text="CheckBox4" /></td>
                    </tr>
                    <tr>
                        <td>
                            SkinID="Variant5" подключен, но тема подавлена: EnableTheming="False"</td>
                        <td>
                            <asp:Label ID="Label5" runat="server" EnableTheming="False" SkinID="Variant5" 
                                       Text="Label5"></asp:Label></td>
                        <td>
                            <asp:CheckBox ID="CheckBox5" runat="server" EnableTheming="False" SkinID="Variant5"
                                Text="CheckBox5" /></td>
                    </tr>
                    <tr>
                        <td>
                            Подключен несуществующий SkinID="XXX": настройки темы не действуют</td>
                        <td>
                            <asp:Label ID="Label6" runat="server" SkinID="XXX" Text="Label6"></asp:Label></td>
                        <td>
                            <asp:CheckBox ID="CheckBox6" runat="server" SkinID="XXX" Text="CheckBox6" /></td>
                    </tr>
                </table>
                <br />
                <asp:Button ID="Button1" runat="server" Text="Отправить" />
            </div>
        </form>
    </body>
    </html>

    Упражнение 8. Оформления сложных элементов управления

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

  • Создайте новую страницу со встроенным кодом поддержки и с именем Page8.aspx
  • Назначьте страницу стартовой и поместите на нее из вкладки Standard элемент управления Calendar
  • Определите для объекта Calendar1 следующие стилевые настройки
  • <asp:Calendar ID="Calendar1" runat="server" 
                BackColor="white" ForeColor="black" BorderColor="black"
                BorderStyle="solid" CellSpacing="1" Font-Names="Vernada" 
                Font-Size="9pt" Height="250px" Width="500px" 
                NextPrevFormat="ShortMonth" SelectionMode="day">
                <SelectedDayStyle BackColor="darkorange" ForeColor="white" />
                <DayStyle BackColor="orange" Font-Bold="true" ForeColor="white" />
                <NextPrevStyle Font-Bold="true" Font-Size="8pt" ForeColor="white" />
                <DayHeaderStyle Font-Bold="true" Font-Size="8pt" 
                    ForeColor="#333333" Height="8pt" />
                <TitleStyle BackColor="firebrick" BorderStyle="none" 
                    Font-Bold="true" Font-Size="12pt" 
                    ForeColor="white" Height="12pt" />
                <OtherMonthDayStyle BackColor="navajowhite" 
                    Font-Bold="false" ForeColor="darkgray" />
            </asp:Calendar>
  • В панели Solution Explorer вызовите контекстное меню для папки MyTheme и командой Add New Item создайте новый файл оформлений с именем Calendar.skin
  • Запомните, как пишутся комментарии в интерфейсных файлах ASP.NET и очистите автоматически сгенерированный текст в файле оформленийCalendar.skin
  • Скопируйте из страницы Page8.aspx в файл оформленийCalendar.skin весь дескрипторный код объекта Calendar1 вместе с его стилевыми настройками и удалите в копии атрибут ID="Calendar1", оставив только атрибуты стилевых настроек
  • <asp:Calendar runat="server"
                BackColor="white" ForeColor="black" BorderColor="black"
                BorderStyle="solid" CellSpacing="1" Font-Names="Vernada" 
                Font-Size="9pt" Height="250px" Width="500px" 
                NextPrevFormat="ShortMonth" SelectionMode="day" >
                <SelectedDayStyle BackColor="darkorange" ForeColor="white" />
                <DayStyle BackColor="orange" Font-Bold="true" ForeColor="white" />
                <NextPrevStyle Font-Bold="true" Font-Size="8pt" ForeColor="white" />
                <DayHeaderStyle Font-Bold="true" Font-Size="8pt" ForeColor="#333333" Height="8pt" />
                <TitleStyle BackColor="firebrick" BorderStyle="none" Font-Bold="true" 
                    Font-Size="12pt" ForeColor="white" Height="12pt" />
                <OtherMonthDayStyle BackColor="navajowhite" Font-Bold="false" ForeColor="darkgray" />    
            </asp:Calendar>
  • Удалите в дескрипторном коде страницы Page8.aspx код стилевых настроек объекта Calendar1, чтобы от объекта осталось только простое описание
  • <asp:Calendar ID="Calendar1" runat="server"> 
        </asp:Calendar>
  • Через панель Properties выделите объект DOCUMENT и поключите к странице тему MyTheme в свойстве Theme
  • Выполните страницу и убедитесь, что вынесенный со страницы в тему большой блок стилевых настроек элемента Calendar работает также, как если бы он находился на самой странице
  • Ниже приведены два варианта представления объекта Calendar1: без темы и с подключенной темой

    Тема с настройками не подключена Внешние стилевые настройки работают

    Упражнение 9. Стилевое оформление рисунков

    С помощью внешних файлов оформлений и тем можно подключать стилевые компоненты рисунков. Пусть мы хотим использовать несколько кнопок элемента управления ImageButton с разными рисунками в зависимости от контекста применения. Пусть для кнопок OK будет использован один рисунок, а для кнопок Cancel - другой. Темы позволяют легко решить эту задачу.

  • В панели Solution Explorer через контекстное меню на папке MyTheme командой New Folder создайте подпапку ButtonImages для хранения рисунков кнопок
  • В панели Solution Explorer через контекстное меню на новой папке ButtonImages командой Add Existing Item вызовите диалоговое окно копирования и из прилагаемого каталога Source скопируйте рисунки ButtonCancel.png и ButtonOK.png
  • Создайте из страницы Page8.aspx копию с именем Page9.aspx и назначьте ее стартовой
  • Добавьте на страницу Page9.aspx из вкладки Standard панели Toolbox две кнопки типа ImageButton и настройте через одноименные свойства в панели Properties их дескрипторы так
  • <asp:ImageButton ID="ImageButton1" runat="server" 
        ImageUrl="~/App_Themes/MyTheme/ButtonImages/ButtonOK.png" 
        SkinID="OKButton" />
    <asp:ImageButton ID="ImageButton2" runat="server" 
        ImageUrl="~/App_Themes/MyTheme/ButtonImages/ButtonCancel.png" 
        SkinID="CancelButton" />
  • В контекстном меню на папке MyTheme выполните команду Add New Item, чтобы добавить в тему новый файл оформленияButtons.skin
  • Скопируйте настроенные дескрипторы элементов ImageButton в файл Buttons.skin, предварительно очистив его от автоматически сгенерированных инструкций
  • Удалите из файла оформлений Buttons.skin атрибуты ID, чтобы код стал выглядеть так
  • <asp:ImageButton runat="server" 
        ImageUrl="ButtonImages/ButtonOK.png" 
        SkinID="OKButton" />
    <asp:ImageButton runat="server" 
        ImageUrl="ButtonImages/ButtonCancel.png" 
        SkinID="CancelButton" />

    Обратите внимание, что мы в атрибуте ImageUrl указали путь только относительно каталога темы. Путь до корня виртуального каталога сайта " ~/App_Themes/MyTheme/ " добавит ASP.NET при компиляции этого кода и автоматически преобразует его в абсолютный путь файловой системы сервера.

  • Откорректируйте дескрипторы ImageButton в файле Page9.aspx, чтобы от них осталось только следующее
  • <asp:ImageButton ID="ImageButton1" runat="server" SkinID="OKButton" />
    <asp:ImageButton ID="ImageButton2" runat="server" SkinID="CancelButton" />
  • Исполните страницу Page9.aspx и получите следующий результат с внешними оформлениями в темах сложного календаря и рисуночных кнопок
  • На интерфейсной странице находится дескрипторный код только создания сложных элементов управления, а большой код тонких настроек вынесен в файлы темы. Это очень удобно и для централизованного изменения стилевого оформления элементов управления, и для читабельности самой страницы.

    Упражнение 10. Комбинирование темы и внешних CSS

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

  • Скопировать файл внешней CSS в каталог темы
  • Включить тему в свойстве Theme объекта DOCUMENT страницы
  • Если CSS содержит классы, подключить их в свойстве CssClass элементов управления страницы
  • Вместо дескриптора <link>, используемого при традиционном способе подключения к странице, проследить, чтобы заголовочный дескриптор страницы был объявлен как серверный ( <head runat="server"> ). В этом случае ASP.NET сама при запросах страницы будет генерировать столько дескрипторов <link> в выходном HTML-коде, сколько файлов CSS будет находиться в подключенной к странице теме
  • Проверим сказанное. В панели Solution Explorer оболочки выполните следующие действия:

  • Скопируйте файл Page4.aspx, присвойте копии имя Page10.aspx и назначьте новую страницу стартовой
  • Откройте файл Page5.aspx, скопируйте из него на страницу Page10.aspx (ниже кнопки) интерфейсный код серверных элементов управления BulletedList, Label, CheckBox
  • После этого вид страницы Page10.aspx без стилевого оформления на этапе проектирования должен быть таким

  • Скопируйте таблицу стилей ExternalCSS.css в папку MyTheme проекта. Копирование выполните в панели Solution Explorer так: щелкните правой кнопкой мыши на файле ExternalCSS.css и выполните команду Copy, затем щелкните правой кнопкой мыши на папке MyTheme и выполните команду Paste. Назначьте копии имя ThemeCSS.css
  • Подобным же образом скопируйте из каталога Resources в каталог MyTheme два файла BackgroundImage.bmp и Web.jpg, чтобы они оказались в одном каталоге с файлом ThemeCSS.css
  • Откройте страницу Page10.aspx на редактирование, удалите в ней дескриптор <link> подключения внешней таблицы стилей и добавьте в заголовочный дескриптор <head> атрибут runat="server", чтобы он стал обрабатываться средой ASP.NET
  • Откройте файл ThemeCSS.css и замените пути доступа к рисункам, а именно
  • url(Resources/Web.jpg) ---> url(Web.jpg)
    url(Resources/BackgroundImage.bmp) ---> url(BackgroundImage.bmp)
  • Подключить тему MyTheme к странице Page10.aspx, добавив в самое ее начало директиву
  • <%@ Page Language="C#" Theme="MyTheme" %>

  • Выполните страницу Page10.aspx и убедитесь, что стили из внешней таблицы ThemeCSS.css работают
  • Результат выполнения страницы будет таким

    На данной странице Page10.aspx таблица стилей CSS и файлы оформления темы действуют вместе. Система ASP.NET на стороне сервера трансформирует серверные элементы управления в HTML-дескрипторы с уже готовыми внутренними стилями, согласно оформлениям темы. На заключительном этапе, перед отсылкой сформированной HTML-страницы на броузер, среда подключает с помощью дескриптора <link> ссылку на внешнюю CSS. Броузер, получив ссылку на внешнюю CSS, проверяет в соответствии с правилом приоритета стилей возможность их применения только в тех дескрипторах HTML, в которых нет своих внутренних стилей, сгенерированных серовером на основании темы.

    Иными словами

    Теперь осталось проверить тот факт, что ASP.NET подключает к странице все таблицы стилей, помещенные в тему.

  • В каталоге Theme сделайте еще три копии файла ThemeCSS.css с именем ThemeCSS1.css, ThemeCSS2.css, ThemeCSS3.css
  • Выполните страницу Page10.aspx и в появившемся окне броузера выполните команду контекстного меню View Source для присланной с сервера страницы-отклика
  • Исследуем блок кода заголовочного дескриптора <head>. Он будет содержать такие строки

    <head>
        <title>Документ с внешней CSS</title>
        <meta content="text/html; charset=windows-1251" http-equiv="Content-Type" />
        <link href="App_Themes/MyTheme/ThemeCSS.css" type="text/css" rel="stylesheet" />
        <link href="App_Themes/MyTheme/ThemeCSS1.css" type="text/css" rel="stylesheet" />
        <link href="App_Themes/MyTheme/ThemeCSS2.css" type="text/css" rel="stylesheet" />
        <link href="App_Themes/MyTheme/ThemeCSS3.css" type="text/css" rel="stylesheet" />
    </head>

    Мы видим, что среда исполнения ASP.NET автоматически вставляет в код столько дескрипторов подключения CSS, сколько файлов с таблицами находится в каталоге темы. Обратите внимание, что пути в ссылках прописываются относительно корня виртуального каталога, в котором находится сама страница.

  • Удалите лишние файлы ThemeCSS1.css, ThemeCSS2.css и ThemeCSS3.css
  • Упражнение 11. Применение общей темы в конфигурационном файле

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

    Ранее мы говорили, что при подключении к странице темы (в наших примерах - MyTheme ) через свойство Theme объекта DOCUMENT оболочка автоматически вставляет соответствующий атрибут в директиву @Page этой страницы, тем самым подключая к странице индивидуальную тему.

    Например

    <% @Page Language="C#" Theme="MyTheme" %>

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

  • Откройте страницы наших примеров, использующих темы, а именно: Page5.aspx, Page8.aspx, Page10.aspx, и скопируйте их в файлы с именами TestTheme.aspx, TestCalendar.aspx, ThemeCSS.aspx соответственно
  • Удалите в них из директивы @Page атрибут Theme="MyTheme"
  • Вызывая для каждой страницы контекстное меню и выполняя команду View in Browser, исполните их по очереди, чтобы убедиться, что отключенная тема в них не действует. Пока получилась грустная, унылая картина
  • Откройте на редактирование файл web.config и добавьте в секцию <system.web> дескриптор с указанием общей темы
  • <?xml version="1.0"?>
    <configuration>
      <system.web>
        <compilation debug="true"/>
        <authentication mode="Windows"/>
        <pages theme="MyTheme" />
      </system.web>
    </configuration>
  • Вновь выполняя для каждой страницы команду View in Browser, исполните файлы по очереди, чтобы убедиться, что тема опять заработала так, как будто-бы она была индивидуально подключена к каждой странице
  • Если нужно сделать так, чтобы общая тема действовала по умолчанию на множестве страниц, но не перезаписывала на них индивидуальные стилевые настройки элементов управления, то конфигурационный файл должен быть таким

    <?xml version="1.0"?>
    <configuration>
      <system.web>
        <compilation debug="true"/>
        <authentication mode="Windows"/>
        <pages  styleSheetTheme="MyTheme" />
      </system.web>
    </configuration>

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

  • Установить для страницы индивидуальную тему, как это мы делали раньше, вставив в директиву @Page атрибут Theme=" Другая_тема "
  • Вообще отключить тему, чтобы действовали индивидуальные стиливые свойства элементов управления, назначенные нами или установленные по умолчанию библиотечными значениями. Для этого в директиву @Page страницы нужно вставить атрибут EnableTheming="false"
  • Следует заметить, что если к странице явно подключена CSS дескриптором <link> или в конфигурационном файле каталога, где размещена страница, установлена тема по умолчанию, содержащая хотя-бы одну CSS, то отключение темы по умолчанию атрибутом EnableTheming="false" отключает только skin -файлы, CSS все равно останется подключенной.

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

    В развитом Web-дереве применение общей темы по умолчанию является удобным способом стилизации страниц. В каждом каталоге можно разместить отдельный файл web.config со своей общей темой и все страницы этого каталога будут использовать эту тему по умолчанию.

    Упражнение 12. Переключение тем по выбору пользователя

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

    Все, что нужно сделать, это предусмотреть на странице интерфейс выбора темы, а в коде страницы динамически присвоить свойству Page.Theme или Page.StyleSheetTheme выбранное пользователем значение. При этом важно понимать, что тема настраивается перед инициализацией страницы, поэтому соответствующий код нужно поместить в обработчик Page_PreInit(). Если это сделать позже, в обработчиках других событий, то попытка присвоить новое значение свойству Page.Theme или Page.StyleSheetTheme вызовет ошибку компиляции.

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

  • Прочитать выбор пользователя, когда это станет возможным после полной загрузки страницы
  • Принудительно перезапустить страницу на сервере, чтобы в обработчике Page_PreInit() назначить новую тему
  • При перезапуске страницы она должна быть переадресована на саму себя. Такую операцию можно выполнить либо методом Response.Redirect(), либо методом Server.Transfer(). Первый метод реализует полный цикл с отправкой на броузер и автоматическим возвращением на сервер. Он тратит лишнее время на пересылку. Второй метод перезапускает страницу сразу на сервере без цикла через клиента. Но есть одна существенная особенность: оба метода сбрасывают состояние вида всех элементов управления и запускают страницу как в при первом запросе. Поскольку выбирать не из чего, то мы применим второй метод Server.Transfer().

    Рассмотрим все это на примере. Прежде всего, нужно создать несколько тем, чтобы было из чего пользователю выбирать.

  • В панели Sulution Explorer вызовите контекстное меню для папки MyTheme и выполните команду Copy
  • В панели Sulution Explorer вызовите контекстное меню для папки App_Themes, где должны храниться все темы приложения, и выполните на ней команду Paste два раза, чтобы создать еще две темы
  • Присвойте копиям имена, например, MyTheme1 и MyTheme2
  • Обратите внимание, что новые темы имеют старое содержание, которое было-бы желательно подправить, чтобы наглядно видеть влияние выбранной темы на оформление страницы. Мы это сделаем попозже.

    Учитывая, что протокол HTTP не поддерживает сохранение состояний и что страница существует только на время обработки обратной отсылки, выбранную пользователем тему нужно где-то сохранять. Это могут быть cookie -наборы ( куки -файлы) на компьютере клиента, которые мы туда запишем без его ведома, если он забыл выключить их поддержку. Это могут быть скрытые поля состояния вида (VIEWSTATE), которые мы будем пересылать туда-сюда. Это могут быть глобальные объекты уровня сеанса или приложения, которые существуют на сервере, пока не закончится сеанс с пользователем или не закроется приложение. Выберем объект уровня сеанса Session.

  • Через панель Solution Explorer скопируйте в корневой виртуальный каталог нашего сайта файл TestTheme.aspx и назовите его TestSwitchThemes.aspx
  • Определите новую страницу TestSwitchThemes.aspx стартовой
  • Откройте файл TestSwitchThemes.aspx на редактирование и добавьте в верхнюю часть страницы из вкладки Standard элемент списока DropDownList для выбора темы
  • Переименуйте ID списка в lstSwitchThemes
  • Через панель Properties в режиме Events создайте для списка заготовку обработчика события SelectedIndexChanged, которое поступит на сервер с обратной отсылкой, если пользователь изменит выбор в списке
  • Добавьте на страницу перед списком серверный элемент Label с именем lblFirstLoad
  • Интерфейсная часть страницы TestSwitchThemes.aspx примет следующий вид

    Код интерфейсной части страницы TestSwitchThemes.aspx следующий

    <%@ Page AutoEventWireup="true" CodeFile="TestSwitchThemes.aspx.cs" 
        Inherits="TestSwitchThemes" 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>
            <asp:Label ID="lblFirstLoad" runat="server" Text="Label"></asp:Label>
            <br />
            <asp:DropDownList ID="lstSwitchThemes" runat="server"
                              OnSelectedIndexChanged="lstSwitchThemes_SelectedIndexChanged">
            </asp:DropDownList>
            <br />
            <asp:BulletedList ID="BulletedList1" runat="server">
                <asp:ListItem>Туризм</asp:ListItem>
                <asp:ListItem>Спорт</asp:ListItem>
                <asp:ListItem>Музыка</asp:ListItem>
                <asp:ListItem Value="Танцы"></asp:ListItem>
            </asp:BulletedList>
            <asp:Label ID="Label1" runat="server" Text="Текстовая метка"></asp:Label>
            <asp:CheckBox ID="CheckBox1" runat="server" Text="Флажок" /><br />
            <br />
            <asp:Button ID="Button1" runat="server" Text="Отправить" /></div>
        </form>
    </body>
    </html>
  • Откройте на редактирование файл кода TestSwitchThemes.aspx.cs и заполните его так
  • using System;
        
    public partial class TestSwitchThemes : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!Page.IsPostBack)
            {
                lblFirstLoad.Text = "Первая загрузка страницы";
          
                // Заполняем список именами каталогов тем
                System.IO.DirectoryInfo themeDir =
                    new System.IO.DirectoryInfo(Server.MapPath("App_Themes"));
                lstSwitchThemes.DataTextField = "Name";
                lstSwitchThemes.DataSource = themeDir.GetDirectories();
                lstSwitchThemes.DataBind();
            }
            else
                lblFirstLoad.Text = "Повторная загрузка страницы";
        }
          
        protected void Page_PreInit(object sender, EventArgs e)
        {
            if (Session["Theme"] != null)
            {
                // Был ранее сохраненный выбор, включить его
                Page.Theme = (string)Session["Theme"];
          
                // Восстанавливаем новое состояние списка
                lstSwitchThemes.SelectedIndex = (int)Session["index"];
                // Восстанавливаем текущее состояние "Флажка" 
                CheckBox1.Checked = (bool)Session["Флажок"];
            }
        }
        
        protected void lstSwitchThemes_SelectedIndexChanged(object sender, EventArgs e)
        {
            // Запоминаем новый выбор пользователя
            Session["Theme"] = lstSwitchThemes.SelectedValue;
            // Запоминаем новое состояние списка
            Session["index"] = lstSwitchThemes.SelectedIndex;
            // Сохраняем текущее состояние "Флажка"
            Session["Флажок"] = CheckBox1.Checked;
          
            // Первый вариант:
            // Обновляем страницу с полным циклом,
            // чтобы новая тема вступила в действие
            //Response.Redirect(Request.Url.ToString());
          
            // Второй вариант:
            // Обновляем страницу без полного цикла,
            // чтобы новая тема вступила в действие
            Server.Transfer(Request.FilePath);
        }
    }

    Сделаем некоторые пояснения по кодовой части страницы:

  • В коде используется подключение только одного пространства имен System, потому что:
  • объекты пользовательского интерфейса определены в интерфейсной части страницы и не требуют явного указания пространства имен
  • класс System.IO. DirectoryInfo прописан с полным путем и компилятор его увидит
  • объекты Session, Server, Response, Request являются общедоступными свойствами класса System.Web.UI.Page, который наследуется нашим классом TestSwitchThemes и явно приобретает все эти свойства
  • Оставлено объявление пространства имен System только потому, что в коде имеются ключевые слова string, int, bool, используемые при явном приведении типов. Но и здесь можно было бы использовать полные имена библиотечных типов System. string, System. int, System. bool
  • При принудительной перезагрузке страницы полностью сбрасывается состояние ее элементов управления в начальное, поэтому мы вынуждены его сохранять и восстанавливать в объекте сеанса
  • Язык C# допускает использование в идентификаторах символов кириллицы при условии соблюдения общепринятых правил: должны состоять из букв цифр и знаков подчеркивания, не должны начинаться с цифры, и т.д.
  • При внутренней обработке кода страницы можно (в разумных пределах) использовать весь арсенал библиотеки .NET Framework, а не только специализированные Web-средства
  • Теперь осталось внести какое-либо видимое различие в оформления тем, чтобы мы могли убедиться, что программно была подключена действительно другая тема. Для экономии своих сил внесем минимальные изменения, например, сделаем разными цвета фона страницы.

  • Вызовите на редактирование файл App_Themes/MyTheme1/ThemeCSS.css и поменяйте в селекторе body определение стиля фона background, чтобы фон страницы стал зеленым
  • body
            {
              background:green;
              font-family:Arial, Helvetica, sans-serif;
            }
  • Вызовите на редактирование файл App_Themes/MyTheme2/ThemeCSS.css и поменяйте в селекторе body определение стиля фона background, чтобы фон страницы стал красным
  • body
            {
              background:red;
              font-family:Arial, Helvetica, sans-serif;
            }
  • Исполните страницу и убедитесь в том, что пользователь может сам выбирать варианты стилевого оформления страниц подключением соответствующих тем
  • Подобным образом программно можно менять и другие стилевые свойства страницы и ее элементов, например, именованные оформления элементов управления, определенные атрибутом SkinID. Не обязательно давать пользователю возможность настройки стилевого оформления напрямую. Можно сделать оформление страниц и элементов косвенно зависимым от выбора пользователя.

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

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

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

    Шаблон размечает именованные области визуализации на исполнимой странице, в которых будут размещены либо прикрепленные группы элементов страницы, либо содержимое именованной области самого шаблона. Группы элементов управления страницы, закрепленные за именованными областями шаблона, называются содержимым (content). Страница, к которой подключен шаблон, должна содержать замещающие группы, каждая из которых может ссылаться только на одну именованную область шаблона. Если для какой-то именованной области шаблона прикрепленной группы нет, то на странице появится содержимое именованной области самого шаблона.

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

    Перекомпоновка подключенного шаблона или его содержимого, а также подключение к исполнимой странице нового шаблона декларативно или программно приводит к немедленному изменению ее структуры и содержимого шаблонной части.

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

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

    <asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
    </asp:Content>

    Упражнение 13. Простая мастер-страница

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

  • Добавьте к проекту командой 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. Шаблоны представляют собой обычные страницы, за исключением нескольких отличий:

  • Шаблоны нельзя выполнить напрямую, они могут только поддерживать ссылающиеся на них исполнимые страницы
  • Файлы шаблонов имеют расширение .master, а файлы кода поддержки для них на языке C# - .master.cs
  • В декларации интерфейсного представления шаблона вместо директивы @Page используется директива @Master
  • Класс кода поддержки шаблона наследует базовый класс System.Web.UI.MasterPage, вместо базового класса System.Web.UI.Page для обычной исполнимой страницы
  • Элемент управления ContentPlaceHolder используется для резервирования именованных контекстных областей только в шаблонах. Если раскрыть вкладку Standard контекстно-зависимой панели Toolbox при редактировании обычной страницы, то мы этого элемента просто не увидим, поскольку оболочка его скрывает до случая редактирования шаблона
  • Элементы ContentPlaceHolder ( держатель места для содержимого ) шаблонной страницы можно размещать в ячейках таблицы. При выполнении эти ячейки заполнятся присоединенным содержимым, а остальные ячейки можно занять заполнителем, который будет виден и доступен на любой странице, использующей этот шаблон
  • При загрузке страницы во время выполнения вначале создается объект мастер-страницы, а потом сама страница с содержимым
  • Во время выполнения именованные области мастер-страницы сжимаются до минимального размера фактического содержимого
  • Удалите из кода мастер-страницы созданный автоматически дескриптор ContentPlaceHolder и через контекстное меню редактора выполните команду Add Content Page (добавить контекстную страницу)
  • Оболочка сгенерирует такую заготовку файла Default.aspx

    <%@ Page Language="C#" MasterPageFile="~/FirstMasterPage.master" 
        AutoEventWireup="true" CodeFile="Default.aspx.cs" 
        Inherits="_Default" Title="Untitled Page" %>
    <%-- Add content controls here --%>

    Получился пустой файл содержимого, где имеется только директива @Page со стандартными атрибутами страницы и атрибутом подключения шаблона.

    Обратите внимание, как можно вставлять комментарии <%--...--%> в дескрипторный код файлов ASP.NET

    Оболочка прописала значение атрибута подключения MasterPageFile с путем относительно корня Web-дерева сайта (там наш шаблон и находится). Если уже подключенный к каким-то страницам шаблон переместить из корня в другую папку Web-дерева, то оболочка автоматически не меняет путь атрибута MasterPageFile во всех ссылающихся на шаблон страницах, это придется делать вручную. Поэтому нужно заранее спланировать, будем ли мы использовать мастер-страницы и где они будут находиться.

  • Создайте в панели Solution Explorer через контекстное меню для корня Web-дерева проекта командой New Folder каталог MasterPages
  • Переместите мышью в папку 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>

    Если сейчас исполнить такую страницу, то мы получим ошибку времени выполнения, потому что содержимое страницы должно находиться в специальных именованных контейнерах

    <asp:Content></asp:Content>

  • Поместите строку текста в контейнер <asp:Content> и исполните страницу
  • <%@ 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>

    Вновь произойдет ошибка выполнения, потому что каждый контейнер <asp:Content></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> идентификатор-ссылку 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, должен получиться такой результат с учетом действия темы MyTheme, подключенной в конфигурационном файле корня Web-дерева
  • Поместите на шаблон еще один резервирующий контейнер 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
  • Оболочка добавит пустой контейнер содержимого и автоматически свяжет его в дескрипторном коде с именованной резервируемой областью.

  • Добавьте в контейнер с содержимым какой-нибудь текст и несколько компонентов из панели Toolbox и выполните страницу
  • Это очень удобная технология. Если планируется создание нескольких страниц на базе мастер-страницы, то разработку лучше начать с мастер-страницы. После того, как мастер-страница будет готова, на ее базе можно создать сколько угодно заготовок страниц с содержимым, выполняя в контектном меню шаблона команду Add Content Page. При этом, если мастер-страница имеет файл отделенного кода *.cs, то и контекстные страницы также будут создаваться с файлами отделенного кода.

    После того, как на вновь созданной странице с содержимым проявятся именованные области шаблона, их можно переопределить и заполнить, создав командой контекстного меню Create Custom Content контейнеры индивидуального содержимого. Удалить контейнер содержимого можно командой Default to Master's Content.

  • На нижней области контекстной страницы FirstMasterPage.aspx вызовите контекстное меню и выполните команду Default to Master's Content, чтобы удалить содержимое
  • Если именованные области шаблона пустые и их не переопределять индивидуальным содержимым для какой-то страницы, или переопределять пустым содержимым, то на этапе выполнения эта область не будет видна.

    Упражнение 14. Применение усложненной мастер-страницы

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

    Создание заготовки мастер-страницы

  • В панели Solution Explore r вызовите контекстное меню для папки MasterPages, которую мы создали ранее, и выполните команду Add New Item
  • В окне мастера создания заготовок выберите шаблон Master Page и создайте эталонную страницу с именем Title.master без файла отделенного кода
  • Оболочка добавит заготовку эталонной страницы в каталог MasterPages, где ей и место.

    Создание баннера

  • Переведите в редакторе эталонную страницу Title.master в режим Design и удалите пока автоматически созданный экземпляр элемента ContentPlaceHolder (просто для тренировки)
  • В режиме Design из вкладки Standard панели Toolbox поместите в самом начале мастер-страницы экземпляр элемента Image как контейнер для размещения баннера. Присвойте ему имя ID =" ImageBanner "
  • Элемент Image из вкладки HTML здесь не подойдет. Он не будет проявляться на контекстной странице, потому что не обрабатывается сервером по механизму мастер-страниц (если только ему не присвоить атрибут runat="server" )

  • Вызовите контекстное меню для папки MasterPages и выполните команду Add Existing Item. Найдите через открывшееся диалоговое окно файл Banner.png, который находится в прилагаемом к данной работе каталоге Source, и скопируйте его в папку MasterPages текущего сайта
  • Выделите экземпляр ImageBanner и присоедините к нему через свойство ImageUrl рисунок Banner.png из папки MasterPages Web-дерева сайта
  • Наш баннер слишком великоват, но для локального компьютера сойдет и такой

  • Разместите после баннера заголовочный дескриптор с внутренним стилем
  • <h1 style="color: Green">
                    Мы приветствуем Вас на нашем сайте!!!
                </h1>

    Добавление к мастер-странице таблицы с шаблонами

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

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

    Мы должны четко представлять, что элементы управления ContentPlaceHolder и Content являются прозрачными контейнерами для своего содержимого, если только мы искусстенно не создадим в них фон другими элементами, например, элементом Panel, Image, Table. Поэтому, если мы на странице содержимого не создадим свой фон в замещающих элементах Content, то будет проявляться в итоговой странице фон элемента-контейнера мастер-страницы.

  • Поместите на мастер-страницу Title.master после заголовочного дескриптора из вкладки HTML панели Toolbox элемент управления Table
  • Используя боковые маркеры таблицы и команды меню Layout оболочки, постройте таблицу из трех строк и двух столбцов, чтобы она соответстворвала приведенному выше рисунку
  • В конечном виде таблица вместе с вутренними стилями должна описываться следующими HTML -кодом

    <table>
                    <tr>
                        <td colspan="2">
                        </td>
                    </tr>
                    <tr>
                        <td style="width: 25%; background-color: #9DFF7E" valign="top">
                        </td>
                        <td style="background-color: #FFB8FF">
                        </td>
                    </tr>
                    <tr style="background-color: #FFFF92">
                        <td colspan="2">
                        </td>
                    </tr>
                </table>

    Внешний вид таблицы будет таким

    Теперь все готово для размещения элементов управления в ячейках созданной таблицы.

  • Поместите из вкладки Standard панели Toolbox в верхнюю строку таблицы компонент Panel, к которому мы должны присоединить рисунок, задающий фон ячейки
  • Задайте для созданного экземпляра элемента управления Panel значения свойств: Width=100% ; Height=100%, чтобы элемент полностью занял верхнюю ячейку таблицы
  • Через проводник Windows найдите в прилагаемом к данной работе каталоге Source файл BackTitleSrc.jpg и в любом графическом редакторе вырежьте из него область с хорошо стыкуемыми при повторении рисунка границами, или возьмите мною вырезанный готовый рисунок BackTitle.jpg
  • Рисунок не должен быть слишком большим, чтобы "не застревал в проводах и не тормозил " по пути к пользователю.

  • В панели Solution Explorer в контекстном меню для папки MasterPages выполните команду Add Existing Item, найдите рисунок BackTitle.jpg и скопируйте его в эту папку
  • Выделите на странице экземпляр Panel1 в верхней строке таблицы и через панель инструментов Properties присоедините в свойстве BackImageUrl рисунок BackTitle.jpg, который многократным повторением займет всю площадь элемента Panel1
  • Теперь можно разместить в ячейках таблицы прозрачные контейнеры-шаблоны элемента управления ContentPlaceHolder.

  • Поместите из вкладки Standard панели Toolbox в верхнюю строку таблицы в объект Panel1 элемент-контейнер ContentPlaceHolder, который будет представлять на странице именованную область ContentPlaceHolder1 для размещения верхнего колонтитула
  • То же самое сделайте для правой-средней и нижней ячеек таблицы
  • В контейнер ContentPlaceHolder1 вставьте и отредактируйте такую строку
  • <div style="font-weight: bold; font-size: xx-large; color: red; text-align: center">
        Сведения об автошколе в целом
    </div>
  • Вставьте в правый-средний контейнер ContentPlaceHolder2 любой текст, например, такой или его часть
  • Вот уже более 15 лет мы занимаемся подготовкой водителей
    категории "B" в г.Красноярске и всегда занимали первые места
    в сводках МРЭО ГИБДД по качеству обучения. Автошкола всегда нацелена
    на успех наших клиентов в сдаче экзаменов ГАИ с первого раза
    и своевременном получении водительского удостоверения.  
    Мы обеспечиваем максимальные удобства своим клиентам. Для
    этого существуют вечерняя и воскресная формы обучения.
    Наши курсанты имеют возможность бесплатно и неограниченно пользоваться
    компьютерным классом. Учебные автомобили прибывают по заявке
    клиента к назначенному им месту. Развивается сеть филиалов автошколы
    в г.Красноярске.  
        Наша компьютерная экзаменационная программа успешно используется
    в ГИБДД г.Красноярска, Железногорска, Назарово, Емельяново, Шарыпово
    для государственной аттестации кандидатов в водители. Мы неоднократно
    выпускали цветные учебно-методические пособия по Правилам дорожного
    движения, которыми постоянно пользуются большинство автошкол
    города и края.  
        Лекционные занятия в автошколе ведут квалифицированные преподаватели
    с учеными степенями и званиями с применением современных компьютерных
    технологий. Комплексный подход к обучению и разнообразие форм
    позволяют максимально сократить усилия наших клиентов и сроки
    усвоения ими учебного материала. Парк учебных автомобилей насчитывает
    более 30 штук, включая иномарки.
  • В нижний контейнер ContentPlaceHolder3, резервирующий место под нижний колонтитул, вставьте примерно такой код
  • <strong>
    Автошкола Диалог-Сервис в целом работает без перерыва 
    на обед и выходных с 8 часов утра до 9 часов вечера!!! 
    </strong>
  • Поместите в левую ячейку таблицы надпись, а из вкладки Standard панели Toolbox три гиперссылки элемента управления HyperLink
  • Дескрипторный код содержимого левой ячейки таблицы, которое размещается внутри тегов <td></td>, будет примерно таким

    <strong style="color: Blue">Наши филиалы:</strong><br />
    <br />
    <asp:hyperlink id="HyperLink1" runat="server">Ленинский район</asp:hyperlink><br />
    <br />
    <asp:hyperlink id="HyperLink2" runat="server">Центральный район</asp:hyperlink><br />
    <br />
    <asp:hyperlink id="HyperLink3" runat="server">Свердловский район</asp:hyperlink><br />
    <br />
    <asp:hyperlink id="HyperLink4" runat="server">Октябрьский район</asp:hyperlink>

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

    Итак, таблицу мы заполнили. Попробуем разместить еще что-нибудь ниже таблицы, например, товарный знак и страшилку от воров интеллектуальной собственности.

  • Отступите ниже таблицы одну-две строчки и добавьте из вкладки HTML панели Toolbox элемент управления Image. Щелкните на нем правой кнопкой мыши и выполните команду Run As Server Control, чтобы содержимое элемента проявилось на будущей контекстной странице как заполнитель
  • В панели Solution Explorer вызовите контекстное меню для папки MasterPages и скопируйте в проект командой Add Existing Item из прилагаемого каталога Source рисунок LIGHT.ICO нашего товарного знака, сворованный ранее из ресурсов Большого Билла
  • Через панель Properties и свойство Src ( Source - источник) присоедините ссылку на файл LIGHT.ICO к нашему компоненту отображения Image
  • Правее товарного знака разместите заявку на интеллектуальную собственность
  • Окончательный дескрипторный код этого раздела будет таким

    <p style="text-align: right; color: Blue; font-family: 'Courier New'; font: bold italic">
        <img id="IMG1" runat="server" src="LIGHT.ICO" />
        nbsp;Copyright copy 2009
    </p>
  • Для наглядности, поместите еще один контейнер в самом низу мастер-страницы и внесите в него какой-нибудь последний писк, например такой
  • <asp:ContentPlaceHolder ID="ContentPlaceHolder4" runat="server">
        <strong><em>Вы уж, пожалуйста, наведывайтесь к нам почаще!</em></strong>
    </asp:ContentPlaceHolder>

    И последний штрих к оформлению мастер-страницы - нужно отцентрировать все ее содержимое.

  • Найдите вверху дескрипторного кода самый внешний дескриптор <div> и определите в нем внутренний стиль CSS такой
  • <body>
        <form id="form1" runat="server">
            <div style="text-align: center">
        ..................................
            </div>
        </form>
    </body>

    Наступил момент истины! Пора тиражировать контекстные страницы на базе созданной мастер-страницы и проверять сделанную работу. При этом не нужно забывать, что страница не должна быть слишком длинной. Наилучшей считается длина 1 - 2 экрана, максимум 3 экрана. Ширина непереносимого контекста страницы должна быть около 770 пикселов, чтобы свободно вписываться в разрешение монитора по ширине от 640 до 1024 px.

    Создание контекстных страниц для мастер-страницы

    В продолжение упражнения создадим 5 контекстных страниц с простыми именами:

  • Satellites.aspx - первая страница, главная, не будет иметь свое содержимое, значит вся мастер страница проявится на выходе. Остальные три страницы будут переопределять все шаблоны мастер-страницы, кроме первого колонтитула, в котором находятся ссылки
  • Satellite1.aspx - будет вызываться по первой ссылке в разделе "Наши филиалы" и переопределять содержимое всех контейнеров, кроме верхнего колонтитула
  • Satellite2.aspx - будет вызываться по второй ссылке в разделе "Наши филиалы" ...
  • Satellite3.aspx - будет вызываться по третьей ссылке в разделе "Наши филиалы" ...
  • Satellite4.aspx - будет вызываться по четвертой ссылке в разделе "Наши филиалы" ...
  • Есть несколько способов создания с помощью оболочки контекстных страниц на базе готовой мастер-страницы. Применим эти способы последовательно к созданию наших страниц Satellite?

    Способ 0. Создание страницы Satellite.aspx

  • В панели Solution Explorer выделите корень Web-дерева и выполните команду меню Website/Add New Item (или команду Add New Item контекстного меню)
  • В появившемся диалоговом окне задайте имя страницы Satellite.aspx и отметьте показанные настройки, включая Select master page
  • Оболочка предложит еще одно окно, в котором выберите мастер-страницу Title.master
  • Созданная страница Satellite.aspx с отделенным кодом будет добавлена в корень Web-дерева и будет содержать контейнеры с подключенным пустым содержимым для всех шаблонов выбранной мастер-страницы

    <%@ Page Language="C#" MasterPageFile="~/MasterPages/Title.master" 
        Title="Untitled Page" %>
    <asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
    </asp:Content>
    <asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder2" Runat="Server">
    </asp:Content>
    <asp:Content ID="Content3" ContentPlaceHolderID="ContentPlaceHolder3" Runat="Server">
    </asp:Content>
    <asp:Content ID="Content4" ContentPlaceHolderID="ContentPlaceHolder4" Runat="Server">
    </asp:Content>

    Способ 1. Создание страницы Satellite1.aspx

  • В панели Solution Explorer выделите нужную мастер-страницу Title.master и выполните команду меню Website/Add Content Page (или команду Add Content Page контекстного меню)
  • Будет создана контекстная страница с дежурным именем, размещенная в том же каталоге, что и выделенная мастер-страница, и не будет иметь файл отделенного кода, потому что мы создали мастер-страницу без отделенного кода. Добавленный атрибут MasterPageFile будет прописан относительно корня Web-дерева как абсолютный адрес в пределах сайта

    MasterPageFile="~/MasterPages/Title.master"

    Это значит, что можно переместить контекстную страницу в любую папку в пределах Web-дерева, и везде адрес мастер-страницы буден известен системе ASP.NET. Но если переместить мастер-страницу, то оболочка автоматически не исправит ее адрес в использующих контекстных страницах, а это нужно делать вручную.

    Поэтому необходимо соблюдать простое правило, при котором не придется долго искать ошибки: перемещать мастер-страницу по Web-дереву беспрепятственно можно до тех пор, пока для нее не созданы контекстные страницы.

  • Переименуйте созданную контекстную страницу Default.aspx на Satellite1.aspx и переместите ее в корневой каталог Web-дерева
  • Способ 2. Создание страницы Satellite2.asp

  • Откройте мастер-страницу Title.master на редактирование и щелкните на ней в любом месте правой кнопкой мыши
  • Появится контекстное меню с уже известной нам командой Add Content Page, выполнение которой создаст файл Default.aspx.

  • Переименуйте созданную контекстную страницу Default.aspx на Satellite2.aspx и переместите ее в корневой каталог Web-дерева
  • Способ 3. Создание страниц Satellite3.aspx и Satellite4.aspx

  • Получите в корневом каталоге сайта 2 копии любого из созданных файлов Satellite?.aspx и назовите их именами Satellite3.aspx и Satellite4.aspx, учитывая, что все страницы ссылаются на одну и туже мастер-страницу Title.master
  • Наполнение содержимым контекстных страниц

    Теперь необходимо скорректировать заготовки контекстных страниц Satellite?. Но прежде всего нужно завершить корректировку мастер-страницы Title.master, определив ссылки в левой ячейке таблицы на только что созданные контекстные страницы, чтобы они были на всех контекстных страницах как содержимое самого шаблона.

  • Выполните команду Window/Close All Documents, чтобы закрыть все редактируемые на текущий момент файлы
  • В панели Solution Explorer щелкните правой кнопкой мыши на файле ~/MasterPages/Title.master и выполните команду View Designer, чтобы открыть мастер-страницу сразу в режиме Design
  • Поочередно выделяя элементы гиперссылок, установите их свойство NavigateUrl на соответствующие контекстные страницы Satellite1.aspx, Satellite2.aspx, Satellite3.aspx, Satellite4.aspx. Сохраните и закройте мастер-страницу Title.master
  • Отредактируем страницы содержимого, начнем с Satellite.aspx.

  • В панели Solution Explorer назначьте страницу Satellite.aspx стартовой и откройте ее на редактирование в режиме Design
  • Что мы видим на данном этапе времени проектирования:

  • Мы видим сквозь контекстную страницу просвечивающуюся мастер-страницу. Причем заполнители (элементы вне контейнеров-шаблонов) мастер-страницы просвечиваются тускло. Это говорит о том, что они находятся на нижнем слое и их редактирование недоступно из контекстной страницы, но тем не менее они полностью отобразятся и будут доступны на этапе выполнения.
  • Места, где на мастер-странице расположены контейнеры-шаблоны, показаны ярко, но закрыты пустыми контейнерами контекста, сгенерированными оболочкой при создании контекстной страницы по готовым шаблонам. Причем закрыто только содержимое шаблонов мастер-страницы, а ее фон полность просвечивается.
  • Удалите в режиме Design командой контекстного меню Default to Master's Content все контейнеры контекста <asp:Content></asp:Content> из страницы Satellite.aspx
  • Эта страница у нас будет стартовой без своего содержимого и будет отображать только содержимое мастер-страницы. Мы вынуждены создать страницу без собственного контекста, только для запуска мастер-страницы, поскольку самостоятельно мастер-страница исполняться не может.

  • Убедитесь, что после удаления закрепленных блоков контекстной страницы проявился нижний слой - содержимое шаблонов мастер-страницы. Это содержимое и будет отображаться при выполнении на своих зарезервированных местах, если контекстная страница не имеет для них прикрепленных контейнеров с содержимым
  • Исполните страницу Satellite.aspx, получится ожидаемый результат
  • Пощелкайте по ссылкам и обратите внимание, как меняется адрес в броузере (ссылки работают). На адресуемых страницах мы пока не убрали и не изменили контейнеры содержимого, поэтому там на месте контейнеров шаблона ничего не будет (пустое содержимое контейнеров место при исполнении не занимает).
  • Теперь нужно наполнить контекстные страницы, которые мы подгружаем по ссылкам, индивидуальным содержимым.

    Приведем окончательный вариант дескрипторного кода для всех страниц данного примера:

    Интерфейсный код мастер-страницы Title.master

    Обратите внимание, что все контейнеры ContentPlaceHolder должны находиться внутри дескриптора <form></form>.

    <%@ Master Language="C#" %>
        
    <script runat="server">
        
    </script>
        
    <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">
                nbsp;<asp:Image ID="ImageBanner" runat="server" ImageUrl="~/MasterPages/Banner.png" /><br />
                nbsp;<h1 style="color: green">
                    Мы приветствуем Вас на нашем сайте!!!
                </h1>
                <table>
                    <tr>
                        <td colspan="2">
                            <asp:Panel ID="Panel1" runat="server" BackImageUrl="~/MasterPages/BackTitle.jpg"
                                Height="100%">
                                <asp:ContentPlaceHolder ID="ContentPlaceHolder1" runat="server">
                                    nbsp;<div style="font-weight: bold; font-size: xx-large; color: red; text-align: center">
                                        Сведения об автошколе в целом</div>
                                </asp:ContentPlaceHolder>
                            </asp:Panel>
                        </td>
                    </tr>
                    <tr>
                        <td style="width: 25%; background-color: #9DFF7E">
                            <strong style="color: Blue">Наши филиалы:</strong><br />
                            <br />
                            <asp:HyperLink ID="HyperLink1" runat="server" NavigateUrl="~/Satellite1.aspx">Ленинский район
                                    </asp:HyperLink><br />
                            <br />
                            <asp:HyperLink ID="HyperLink2" runat="server" NavigateUrl="~/Satellite2.aspx">Центральный район
                                    </asp:HyperLink><br />
                            <br />
                            <asp:HyperLink ID="HyperLink3" runat="server" NavigateUrl="~/Satellite3.aspx">Свердловский район
                                    </asp:HyperLink><br />
                            <br />
                            <asp:HyperLink ID="HyperLink4" runat="server" NavigateUrl="~/Satellite4.aspx">Октябрьский район
                                    </asp:HyperLink>
                        </td>
                        <td style="background-color: #FFB8FF">
                            <asp:ContentPlaceHolder ID="ContentPlaceHolder2" runat="server">
        <p>Вот уже более 15 лет мы занимаемся подготовкой водителей
    категории "B" в г.Красноярске и всегда занимали первые места
    в сводках МРЭО ГИБДД по качеству обучения. Автошкола всегда нацелена
    на успех наших клиентов в сдаче экзаменов ГАИ с первого раза
    и своевременном получении водительского удостоверения.</p>
    <p>Мы обеспечиваем максимальные удобства своим клиентам. Для
    этого существуют вечерняя и воскресная формы обучения.
    Наши курсанты имеют возможность бесплатно и неограниченно пользоваться
    компьютерным классом. Учебные автомобили прибывают по заявке
    клиента к назначенному им месту. Развивается сеть филиалов автошколы
    в г.Красноярске.</p>
       <p> Наша компьютерная экзаменационная программа успешно используется
    в ГИБДД г.Красноярска, Железногорска, Назарово, Емельяново, Шарыпово
    для государственной аттестации кандидатов в водители. Мы неоднократно
    выпускали цветные учебно-методические пособия по Правилам дорожного
    движения, которыми постоянно пользуются большинство автошкол
    города и края. </p>
       <p> Лекционные занятия в автошколе ведут квалифицированные преподаватели
    с учеными степенями и званиями с применением современных компьютерных
    технологий. Комплексный подход к обучению и разнообразие форм
    позволяют максимально сократить усилия наших клиентов и сроки
    усвоения ими учебного материала. Парк учебных автомобилей насчитывает
    более 30 штук, включая иномарки.</p>
                            </asp:ContentPlaceHolder>
                        </td>
                    </tr>
                    <tr style="background-color: #FFFF92">
                        <td colspan="2">
                            <asp:ContentPlaceHolder ID="ContentPlaceHolder3" runat="server">
                                <strong>Автошкола Диалог-Сервис в целом работает без перерыва на обед и выходных с 8
                                    часов утра до 9 часов вечера!!! </strong>
                            </asp:ContentPlaceHolder>
                        </td>
                    </tr>
                </table>
                <p style="text-align: right; color: Blue; font-family: 'Courier New'; font: bold italic">
                    <img id="IMG1" runat="server" src="LIGHT.ICO" />
                    nbsp;Copyright copy 2009
                </p>
                <asp:ContentPlaceHolder ID="ContentPlaceHolder4" runat="server">
                    <strong><em>Вы уж, пожалуйста, наведывайтесь к нам почаще!</em></strong>
                </asp:ContentPlaceHolder>
            </div>
        </form>
    </body>
    </html>

    Запускающая контекстная страница Satellite.aspx

    Все контейнеры <asp:Content> уничтожены, поэтому страница используется только как запускающая и не добавляет к мастер-странице своего содержимого. В ней осталась только строка с директивой @Page и привязкой к мастер-странице

    <%@ Page Language="C#" MasterPageFile="~/MasterPages/Title.master" 
        Title="Untitled Page" %>

    Контекстная страница Satellite1.aspx

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

    <%@ Page Language="C#" MasterPageFile="~/MasterPages/Title.master" Title="Untitled Page" %>
      
    <asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" runat="Server">
        <div style="font-weight: bold; font-size: xx-large; color: red; text-align: center">
            Сведения о филиале в Ленинском районе
        </div>
    </asp:Content>
      
    <asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder2" Runat="Server">
        Филиал в Ленинском районе такой хороший!<br />
        ..........................<br />
        и т.д.
    </asp:Content>
      
    <asp:Content ID="Content3" ContentPlaceHolderID="ContentPlaceHolder3" Runat="Server">
        <strong>Филиал в Ленинском районе работает без перерыва на обед и выходных с 8
            часов утра до 9 часов вечера!!! 
        </strong>
    </asp:Content>
      
    <asp:Content ID="Content4" ContentPlaceHolderID="ContentPlaceHolder4" Runat="Server">
        <strong><em>Вы уж, пожалуйста, наведывайтесь к нам почаще в филиал Ленинского района!</em></strong>
    </asp:Content>

    Контекстная страница Satellite2.aspx

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

    <%@ Page Language="C#" MasterPageFile="~/MasterPages/Title.master" Title="Untitled Page" %>
      
    <asp:Content ID="Content1" runat="Server" ContentPlaceHolderID="ContentPlaceHolder1">
        <div style="font-weight: bold; font-size: xx-large; color: red; text-align: center">
            Сведения о филиале в Центральном районе
        </div>
    </asp:Content>
      
    <asp:Content ID="Content2" runat="Server" ContentPlaceHolderID="ContentPlaceHolder2">
        Филиал в Центральном районе такой хороший!<br />
        ..........................<br />
        и т.д.
    </asp:Content>
      
    <asp:Content ID="Content3" runat="Server" ContentPlaceHolderID="ContentPlaceHolder3">
        <strong>
            Филиал в Центральном районе работает с перерывом на обед и с выходными!!!
        </strong>
    </asp:Content>
      
    <asp:Content ID="Content4" runat="Server" ContentPlaceHolderID="ContentPlaceHolder4">
        <strong><em>Вы уж, пожалуйста, наведывайтесь к нам почаще в филиал Центрального района!</em>
        </strong>
    </asp:Content>

    Контекстная страница Satellite3.aspx

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

    <%@ Page Language="C#" MasterPageFile="~/MasterPages/Title.master" Title="Untitled Page" %>
      
    <asp:Content ID="Content1" runat="Server" ContentPlaceHolderID="ContentPlaceHolder1">
        <div style="font-weight: bold; font-size: xx-large; color: red; text-align: center">
            Сведения о филиале в Свердловком районе
        </div>
    </asp:Content>
      
    <asp:Content ID="Content2" runat="Server" ContentPlaceHolderID="ContentPlaceHolder2">
        Филиал в Свердловском районе такой хороший!<br />
        ..........................<br />
        и т.д.
    </asp:Content>
      
    <asp:Content ID="Content4" runat="Server" ContentPlaceHolderID="ContentPlaceHolder4">
    </asp:Content>

    Контекстная страница Satellite4.aspx

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

    <%@ Page Language="C#" MasterPageFile="~/MasterPages/Title.master" Title="Untitled Page" %>
      
    <asp:Content runat="Server" ContentPlaceHolderID="ContentPlaceHolder1">
        <div style="font-weight: bold; font-size: xx-large; color: red; text-align: center">
            Сведения о филиале в Октябрьском районе
        </div>
    </asp:Content>
      
    <asp:Content runat="Server" ContentPlaceHolderID="ContentPlaceHolder3">
    </asp:Content>
      
    <asp:Content runat="Server" ContentPlaceHolderID="ContentPlaceHolder4">
    </asp:Content>

    Управление элементами мастер-страницы из контекстной страницы

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

  • Вначале ASP.NET создает элементы управления мастер-страницы, а потом дочерние элементы на контекстной странице
  • Затем генерируется событие Page.Init мастер-страницы, вслед за которым возбуждается событие Page.Init контекстной страницы
  • Затем срабатывает событие Page.Load мастер-страницы, а за ним то же событие контекстной страницы
  • Такой порядок означает, что если мы программно меняем одни и те же настройки в мастер-странице и в контекстной странице , то последними останутся изменения, выполненные в коде контекстной страницы.

    В контекстной странице мы можем менять только содержимое контейнеров <asp:Content>, а содержимое заполнителей мастер-страницы и ее заголовок являются недоступными на этапе проектирования. Но именно они участвуют в формировании HTML-вывода. Иногда, не трогая спроектированную мастер-страницу, нужно в ее заполнителях что-то изменить из конкретной контекстной страницы. Это можно сделать программно на этапе выполнения.

    Упражнение 15. Программирование заголовка мастер-страницы

    Вначале рассмотрим вопросы программирования заголовка мастер-страницы, который генерирует содержимое дескрипторов <head> в HTML-выводе. Мастер-страница имеет заголовок <head>...</head> обычной страницы, где можно задавать все составляющие его дескрипторы и это появится на результирующей странице.

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

    Программно можно переопределять не только дескриптор <title>, но и другие дескрипторы заголовка мастер-страницы. И опять, дескриптор <head> мастер-страницы должен обязательно быть серверным. Для программного управления элементами заголовка <head> мастер-страницы из текущей контекстной страницы нужно в контекстной странице получить ссылку на объект Page мастер-страницы. Это делается через свойство Page.Master объекта текущей контекстной страницы, у которого, в свою очередь, свойство Master.Page и определяет ссылку на объект Page мастер-страницы.

    Ниже приведены равноценные коды получения ссылки на объект Page мастер-страницы из контекстной страницы

    Page masterPage = base.Master.Page;
                Page masterPage = this.Master.Page;
                Page masterPage = Master.Page;

    Ключевые слова base или this не являются обязательными. Они напоминают, что Master является свойством объекта текущей страницы.

    Декларативное переопределение <title>

  • Создайте копию страницы Satellite.aspx, назначьте ей имя SatelliteExt.aspx и сделайте стартовой
  • Попробуем на контекстной странице SatelliteExt.aspx переопределить дескриптор <title> мастер-страницы. Исходный код страницы SatelliteExt.aspx такой

    <%@ Page Language="C#" MasterPageFile="~/MasterPages/Title.master" 
        Title="Untitled Page" %>
  • Замените атрибут Title директивы @Page на следующий
  • <%@ Page Language="C#" MasterPageFile="~/MasterPages/Title.master" 
        Title="Декларативный заголовок SatelliteExt.aspx" %>
  • Исполните страницу с новым заголовком окна броузера - результат будет таким
  • Программное переопределение <title>

    Поскольку мы не создавали для страницы SatelliteExt.aspx отдельный файл кода поддержки, то код переопределения заголовка будем писать в скриптах страницы

  • Переведите страницу SatelliteExt.aspx в режим Design и щелкните дважды на притушенной области проступающего заполнителя мастер-страницы
  • Оболочка автоматически создаст блок скрипта и вставит в него заготовку обработчика события Page.Load.

  • Заполните обработчик так
  • <%@ Page Language="C#" MasterPageFile="~/MasterPages/Title.master" 
        Title="Декларативный заголовок SatelliteExt.aspx" %>
      
    <script runat="server">
      
        protected void Page_Load(object sender, EventArgs e)
        {
            // Получаем ссылку на мастер-страницу
            Page masterPage = Master.Page;
            
            // Переопределяем строку заголовка
            masterPage.Header.Title = "Программный заголовок SatelliteExt.aspx";
        }
    </script>
  • Исполните страницу - результат будет таким
  • Несмотря на то, что мы оставили декларативный атрибут Title, обработчик переопределил его в окончательном выводе. Это и понятно: декларативный атрибут инициируется при создании объекта заголовка, а обработчик Page_Load() срабатывает позднее.

    Управление другими элементами мастер-страницы

    Рассмотрим управление элементами заполнителя мастер-страницы из контекстной страницы на примере дескриптора <h1>, определенного в заполнителе так

    <h1 style="color: Green">
        Мы приветствуем Вас на нашем сайте!!!
    </h1>

    Его отображение в броузере будет следующим

  • Дополните дескриптор <h1> мастер-страницы Title.master необходимыми для программного управления атрибутами
  • <h1 style="color: Green" runat="server" id="Header1">
        Мы приветствуем Вас на нашем сайте!!!
    </h1>

    Управлять будем из страницы SatelliteExt.aspx одним из двух способов:

  • Инкапсулируем (упакуем) в мастер-странице дескриптор <h1> в отдельное свойство с именем MyHeader1 и из контекстной страницы будем обращаться к нему через это свойство
  • Не трогая код мастер-страницы, найдем из контекстной страницы дескриптор <h1> по его ID и будем обращаться к нему напрямую
  • Первый способ

  • Двойным щелчком на свободном от элементов управления месте мастер-страницы 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>
  • Дополните контекстную страницу 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>
  • Исполните контекстную страницу 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>

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

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

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

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

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

  • Контекстная страница, динамически меняющая шаблон, не должна иметь контекстных блоков, ссылающихся на несуществующие в новой мастер-странице контейнеры ContentPlaceHolderID
  • Замена шаблона должна выполняться в обработчике события Page.PreInit контекстной страницы. Если это сделать в обработчиках более поздних событий, то будет сгенерировано исключение
  • Все контекстные страницы, на которые имеются ссылки в текущей странице содержимого, должны иметь код подключения выбранного шаблона
  • Для межстраничной передачи информации о выбранном шаблоне ее нужно где-то сохранять. Как и в случае с темами, это могут быть скрытые поля состояния вида (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>
  • Откройте поочередно файлы 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>
  • Исполните начальную страницу 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>
  • Теперь также в корне 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>

    Обратите внимание, что при попытке переключить созданную мастер-страницу в режим 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>
  • Назначьте контекстную страницу TestMasterPageLevel.aspx стартовой и исполните ее
  • Результат будет таким

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

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

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

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

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

    Вернуться к учебному плану