Программирование на ASP.NET

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

Разбить на страницы
Показывать лекцию целиком
Файлы к лекции Вы можете скачать здесь

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

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

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

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

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

    Добавление стилей в HTML-дескриптор

    Хотя CSS обычно не применяется для изменения вида каждого тега в отдельности, но принципиально можно это делать, особенно, когда нужно перекрыть действие внешнего каскада стиля для конкретного дескриптора. Синтаксис применения внутреннего стиля на примере дескриптора <h1> будет таким

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

    Пример

    Пусть отдельный заголовочный дескриптор будет определен в данном документе так

    <h1 style="color:red; text-align:center">Этот заголовочный 
    текст будет центрирован в текущем документе и иметь красный 
    цвет (бледный вид)!</h1>

    Отклик броузера выделен в рамочку и будет таким

    Добавление стилей на Web-страницу

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

    Атрибут type="text/css" дескриптора стиля указывает броузеру, что все последующие стили будут css -стилями. В приведенном примере селекторы body и h1 совпадают с именами дескрипторов HTML. Список селекторов HTML-объектов содержит значения идентификаторов объектов страницы, к которым нужно применить соответствующие правила стилей. Элементы списка селекторов отделяются друг от друга запятыми или пробелами. Если в определение одного свойства подразумевается много значений, то они также разделяются запятыми или пробелами. Если одно значение правила состоит из нескольких частей, разделенных пробелами, то оно обязательно заключается в двойные или одинарные кавычки.

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

    Пример

    Пусть страница имеет код, приведенный ниже (файл embedded.htm в каталоге CSS рассматриваемой темы)

    <html>
    <head>
        <title>Документ со встроенной CSS</title>
        <meta content="text/html; charset=windows-1251" http-equiv="Content-Type">
        <style type="text/css">
            body
            {
            	background:white url(MyWeb.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>
    </head>
    <body>
        <h1>
            Заполните анкету!</h1>
        <form>
            <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>

    Чтобы получить оригинальный результат, щелкните на кнопке, расположенной внизу приведенного примера кода. Если на Вашем броузере включена поддержка JavaScript, то он запустит новую страницу в отдельном окне, где можно посмотреть результат с использованием встроенной CSS. "Стильный" снимок моего результата будет таким

    Внешние стили

    Внешними считаются стили, которые хранятся в отдельном файле текстового формата (чаще с расширением .css, но будет работать любое расширение). Содержимое стиля в отдельном файле отличается от встроенного стиля тем, что во внешнем стиле не должно быть дескрипторных скобок <style></style>. Кроме этого, подключение стилевого файла к HTML-документу выполняется добавлением в блок <head>...</head> дескриптора <link> или директивы @import.

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

    Варианты подключения CSS к HTML-документу
    Вариант В блоке <head> файла MyPage.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 rel="stylesheet" href="default.css">
    <style type="text/css">
    
        @import url(header.css);
    
       body
       {
          	background:white url(MyWeb.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(MyWeb.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 rel="stylesheet" href="default.css">
    Подключается две внешних таблицы в дополнение к встроенной.

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

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

    Чтобы проверить сказанное, выполните следующее (вручную, без Visual Studio 2005!)

  • Найдите в прилагаемом каталоге CSS файл embedded.htm и в этом же каталоге сделайте из него две копии с именами TestCSS.htm и ExternalCSS.css
  • Откройте в текстовом редакторе Блокнот файл TestCSS.htm и замените в заголовке <head> встроенный стиль на строку подключения внешней CSS
    <link rel="stylesheet" href="ExternalCSS.css">
  • Откройте в текстовом редакторе Блокнот файл ExternalCSS.css и оставьте только чистый код селекторов и определений стилей
  • Откройте страницу
  • Опробуйте другие варианты подключения внешней CSS, приведенные ранее (если хотите!)
  • Применение внешних стилей в Visual Studio 2005

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

  • Создайте через Проводник Windows новый каталог с именем WebSite6
  • Откройте этот каталог через оболочку Visual Studio 2005 командой File/Open/Web Site
  • Выполните команду Website/Add New Item, выберите вариант Style Sheet и оставьте имя файла по умолчанию StyleSheet.css
  • Оболочка добавит в проект новый файл каскадной таблицы стилей и покажет панель инструментов Style Sheet, помогающую редактировать этот файл. Здесь, правда, возможностей не так богато, как в специализированных редакторах стилей, но главная линия все-таки соблюдается.

    Наша главная задача, как программистов, - понимать и правильно

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

  • Откройте созданный нами ранее почти пустой проект WebSite6 (если не открыт)
  • Выполните команду Website/Add Existing Item. В появившемся диалоговом окне войдите в каталог CSS (мы в нем уже работали) и отметьте нужные файлы, удерживая при этом клавишу Ctrl
  • После щелчка на кнопке Add оболочка физически скопирует выделенные файлы в файл проекта WebSite6 и они появятся в панели Solution Explorer. Если вы не увидели там этих файлов, то щелкните на кнопке Refresh панели Solution Explorer, чтобы оболочка обновила представляемую после копирования информацию

  • Назначьте статическую страницу TestCSS.htm стартовой (команда Set As Start Page )и запустите ее на выполнение, чтобы убедиться, что и в оболочке она работает точно также, как и ранее в автономном режиме
  • Если страница не содержит исполняемого ASP.NET -кода, то система ASP.NET ничего с ней не делает, а отсылает в неизменном виде клиенту. Если посмотреть на исходный HTML-код присланной броузеру страницы (команда View Source контекстного меню окна броузера), то он остался тем же самым, каким мы его прежде сочинили

    <html>
    <head>
        <title>Документ со встроенной CSS</title>
        <meta content="text/html; charset=windows-1251" http-equiv="Content-Type">
        <link rel="stylesheet" href="ExternalCSS.css">
    </head>
    <body>
        <h1>
            Заполните анкету!</h1>
        <form>
            <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>
  • Откройте двойным щелчком в Solution Explorer файл TestCSS.htm на редактирование
  • Отвлечемся немного от темы стилей и обратим внимание на то, сколько строк нашего кода подчеркнул редактор оболочки красной линией. Это значит, что его не устраивает синтаксис кода, на проверку которого он настроен

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

  • Выполните команду Tools/Customize и отметьте флажок HTML Source Editing
  • В верхней части каркаса оболочки появится панель инструментов настройки HTML-редактора, в которой нужно установить через раскрывающийся список опцию HTML 4.01

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

    <form action=TestCSS.htm>

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

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

  • Вызовите контекстное меню щелчком правой кнопки мыши на свободном месте верхней части каркаса оболочки и сбросьте флажок HTML Source Editing, чтобы закрыть одноименную панель настройки редактора
  • Удалите из заголовочного блока страницы код подключения внешней таблицы стилей TestCSS.htm, а именно, удалите строку
    <link rel="stylesheet" href="ExternalCSS.css">
  • Переведите страницу в режим Design и посмотрите, какая она сразу стала блеклая (тусклая, бледная, невзрачная, некрасивая, ... - студент Леша Зиборов, продолжай! Только ты можешь дочитать до этого места!)
  • Захватите мышью в панели Solution Explorer файл ExternalCSS.css и перенесите его на страницу, которая сразу преобразится уже на этапе проектирования
  • Вернитесь в режим Source и убедитесь, что оболочка добавила нужный дескриптор подключения CSS в нужное место
  • Опять удалите дескриптор подключения CSS
  • Перейдите в режим Design, откройте панель Properties и через раскрывающийся список в ее верхней части выберите объект DOCUMENT
  • Найдите свойство
  • Очистите поле свойства StyleSheet, оболочка вновь удалит дескриптор подключения

    Таблицы CSS применяются не только к простым дескрипторам HTML, но и к дескрипторам серверных элементов управления. Давайте это проверим, поместив на страницу серверный элемент управления, например, Label.

  • Откройте панель Toolbox

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

  • Переименуйте в панели Solution Explorer тестовую страницу в TestCSS.aspx и сделайте ее же принимающей, исправив дескриптор <form>
    <form action=TestCSS.aspx runat="server">
  • Поместите на страницу из вкладки Standard текстовую метку Label и в панели Properties определите
  • CssClass=label
  • Text=Стиль класса label
  • Откройте через Solution Explorer файл ExternalCSS.css и добавьте стилевое правило для класса label
    (рис ) Добавление нового стиля в класс label файла ExternalCSS.cssbody
    {
      background:white url(MyWeb.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;
    }
    	
    .label
    {
      background-color:Green;
      color:White;
      font:bold;
      font-size:x-large;
      font-family:Courier New;
    }
  • Выполните страницу, которая в броузере станет такой
  • Краткое знакомство с 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. Эти файлы называются файлами оформлений и добавляются командой Website/Add New Item через соответствующее диалоговое окно

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

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

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

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

  • Перейдите в режим Source и удалите из файла FirstSkin.aspx весь код, кроме открывающих дескрипторов элементов управления, в которых тоже удалите атрибуты, не относящиеся к настройкам стиля. Должно остаться следующее
    (рис ) Оставшийся код в файле 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
  • Создайте в приложении папку с зарезервированным именем
  • В панели Solution Explorer захватите мышью файл FirstSkin.skin и переместите его в папку MyTheme
  • Тема подключается к странице с помощью атрибута Theme директивы @Page. Так, например, при применении темы с именем каталога MyTheme к нашей тестовой странице TestTheme.aspx директива будет выглядеть так

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

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

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

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

    Приоритеты оформлений и стилевых настроек

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

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

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

    Любой вариант влияния темы на элемент управления можно отключить, если установить в этом элементе свойство EnableTheming="False".

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

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

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

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

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

  • Поместите из вкладки Standard панели Toolbox в средний столбец таблицы пять серверных элементов Label, а в правый - пять серверных элементов CheckBox
  • Установите в добавленных объектах свойства в соответствии с контекстом таблицы
    SkinID не подключен: действуют общие настройки по умолчанию Label2 CheckBox2
    Именованные настройки подключены: SkinID="Variant3" Label3 CheckBox3
    Именованные настройки подключены: SkinID="Variant4" Label4 CheckBox4
    SkinID="Variant5" подключен, но тема подавлена: EnableTheming="False" Label5 CheckBox5
    Подключен несуществующий SkinID="XXX": настройки темы не действуют Label6 CheckBox6
  • Откройте файл оформлений FirstSkin.skin и дополните его индивидуальными настройками стилей, чтобы общий код его стал таким
    (рис ) Код файла оформлений FirstSkin.skin темы MyTheme<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" />
    	
    <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, то эти стилевые настройки никогда не будут применяться.

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

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

  • Создайте новую страницу со встроенным кодом поддержки и с именем TestCalendar.aspx
  • Назначьте страницу стартовой и поместите на нее из вкладки Standard элемент управления Calendar
  • Определите для объекта Calendar1 следующие стилевые настройки
    (рис ) Стилевые настройки объекта Calendar1 страницы TestCalendar.aspx<asp:Calendar ID="Calendar1" runat="server" 
        BackColor="white" ForeColor="black" BorderColor="black"
        BorderStyle="solid" CellSpacing="1" Font-Names="Verdana" 
        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
  • Скопируйте из страницы TestCalendar.aspx в файл оформлений Calendar.skin весь дескрипторный код объекта Calendar1 вместе с его стилевыми настройками и удалите в копии атрибут ID="Calendar1"
  • Удалите в дескрипторном коде страницы TestCalendar.aspx код стилевых настроек объекта Calendar1, выделенный в приведенной выше таблице красным цветом, чтобы от объекта осталось только простое "мокрое место"
    <asp:Calendar ID="Calendar1" runat="server"> 
    </asp:Calendar>
  • Через панель Properties выделите объект DOCUMENT и поключите к странице тему MyTheme в свойстве Theme
  • Выполните страницу и убедитесь, что вынесенный со страницы в тему большой блок стилевых настроек элемента Calendar работает также, как если бы он находился на самой странице
  • Ниже приведены два варианта представления объекта Calendar1

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

  • В панели Solution Explorer через контекстное меню на папке MyTheme командой New Folder создайте подпапку ButtonImages для хранения рисунков кнопок
  • В панели Solution Explorer через контекстное меню на новой папке
  • Добавьте на страницу TestCalendar.aspx две картиночных кнопки ImageButton из вкладки Standard панели Toolbox и настройте через одноименные свойства в панели 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, чтобы добавить в тему новый файл оформления
  • Скопируйте настроенные дескрипторы элементов ImageButton в файл Buttons.skin, предварительно очистив его от автоматически сгенерированных инструкций
  • Откорректируйте оформления в файле Buttons.skin, чтобы они выглядели так
    <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 в файле TestCalendar.aspx, чтобы от них осталось следующее
    <asp:ImageButton ID="ImageButton1" runat="server" SkinID="OKButton" />
    <asp:ImageButton ID="ImageButton2" runat="server" SkinID="CancelButton" />
  • Исполните страницу
  • Посмотрите, на странице находится дескрипторный код только создания сложных элементов управления, а большой код тонких настроек вынесен в файлы темы. Это очень удобно и для централизованного изменения стилевого оформления элементов управления, и для читабельности самой страницы.

    Использование внешних CSS в теме

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

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

  • Скопируйте файл TestCSS.aspx и назначьте копии имя ThemeCSS.aspx и назначьте новую страницу стартовой
  • Скопируйте таблицу стилей ExternalCSS.css и назначьте копии имя ThemeCSS.css
  • Переместите файл ThemeCSS.css в папку MyTheme проекта
  • Удерживая клавишу Ctrl, выделите файлы BackgroundImage.bmp и MyWeb.jpg в корне виртуального каталога и выполните для них команду Copy контекстного меню
  • Щелкните правой кнопкой мыши на узле MyTheme и выполните команду Paste, чтобы рисунки оказались в одном каталоге с таблицей ThemeCSS.css
  • Откройте страницу ThemeCSS.aspx на редактирование, удалите в ней дескриптор <link> подключения внешней таблицы стилей. Удостоверьтесь, что заголовочный дескриптор <head> является серверным
  • Исполните страницу и убедитесь, что стили пока не действуют, потому что не подключены
  • Теперь только осталось подключить тему MyTheme к странице.

  • В панели Properties выделите объект DOCUMENT и установите его свойство Theme в значение MyTheme
  • Выполните страницу ThemeCSS.aspx и убедитесь, что стили из внешней таблицы ThemeCSS.css работают
  • Назначьте страницу TestCSS.aspx, где мы CSS использовали напрямую, стартовой и выполните ее
  • Чтобы сделать некоторые выводы, сравним полученные результаты, приведенные ниже в уменьшенном виде.

    "Найдите десять отличий!" Ха-ха-ха...
    Используется только CSS для несерверных элементов управления и для одного серверного. Для остальных серверных элементов стили неопределены Используется CSS для несерверных элементов управления, а для серверных элементов действуют оформления темы

    Выводы:

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

  • В каталоге Theme сделайте еще три копии файла ThemeCSS.css с именем ThemeCSS1.css, ThemeCSS2.css, ThemeCSS3.css
  • Назначьте страницу ThemeCSS.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, сколько файлов с таблицами находится в каталоге темы. Обратите внимание, что пути в ссылках прописываются относительно корня виртуального каталога, в котором находится сама страница. Ну и последнее, строки подключения CSS в HTML-коде прописываются системой ASP.NET, ну совсем как по теории.

  • Удалите лишние файлы ThemeCSS1.css, ThemeCSS2.css и ThemeCSS3.css, чтобы они дальше не мельтешили у нас в глазах
  • Применение общей темы в конфигурационном файле

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

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

    Например

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

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

  • Откройте страницы наших примеров, использующих темы, а именно: TestTheme.aspx, TestCalendar.aspx, ThemeCSS.aspx, и удалите в них из директивы @Page атрибут Theme="MyTheme"
  • Назначая каждую страницу стартовой, исполните их по очереди, чтобы убедиться, что отключенная тема в них не действует. Пока получилась грустная, унылая картина
  • Откройте на редактирование заготовку файла web.config, автоматически созданную оболочкой
  • Отредактируйте код файла web.config, добавив всего один дескриптор с указанием общей темы <pages theme="MyTheme" />, и сохраните изменения на диске
  • Ниже приведен окончательный код файла web.config с указанием общей темы по умолчанию, в котором убраны громоздкие комментарии и пустые дескрипторы

    <?xml version="1.0"?>
    <configuration>
    	<system.web>
    		<compilation debug="true"/>
    		<authentication mode="Windows"/>
    		<pages theme="MyTheme" />
    	</system.web>
    </configuration>
  • Назначая каждую из перечисленных выше страниц стартовой, вновь исполните их по очереди, чтобы убедиться, что тема опять заработала так, как будто-бы она была индивидуально подключена к каждой странице
  • Если нужно сделать так, чтобы общая тема действовала по умолчанию на множестве страниц, но не перезаписывала на них индивидуальные стилевые настройки элементов управления, то конфигурационный файл должен быть таким

    <?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 со своей общей темой и все страницы этого каталога будут использовать эту тему по умолчанию.

    Применение тем по выбору пользователя

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

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

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

  • Прочитать выбор пользователя, когда это станет возможным после полной загрузки страницы
  • Принудительно перезапустить страницу на сервере, чтобы в обработчике Page_PreInit() назначить новую тему
  • При перезапуске страницы она должна быть переадресована на саму себя. Такую операцию можно выполнить либо методом Response.Redirect(), либо методом Server.Transfer(). Первый метод реализует полный цикл с отправкой на броузер и автоматическим возвращением на сервер. Он тратит лишнее время на пересылку. Второй метод перезапускает страницу сразу на сервере без цикла через клиента. Но есть одна существенная особенность: оба метода сбрасывают состояние вида всех элементов управления и запускают страницу как в при первом запросе. Поскольку выбирать не из чего, то мы выбираем Pepsi и второй метод 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
  • На всякий пожарный случай (чтобы вам - его скопировать, а мне - отчитаться по объему текста перед зав. кафедрой Горенским Б.М.), приведу полный дескрипторный код, который будет иметь страница после проведения последних "оперативно-следственных" мероприятий

    <%@ Page Language="C#" AutoEventWireup="true" 
        CodeFile="TestSwitchThemes.aspx.cs" Inherits="TestTheme" %>
    	
    <html xmlns="http://www.w3.org/1999/xhtml" >
    <head runat="server">
        <title>Тест применения тем</title>
    </head>
    <body>
        <form id="form1" runat="server">
            <div>
                <asp:Label ID="lblFirstLoad" runat="server" Text="Label" />
                <asp:DropDownList ID="lstSwitchThemes" runat="server" 
                    OnSelectedIndexChanged="lstSwitchThemes_SelectedIndexChanged">
                </asp:DropDownList>
                <br />
                <br />
                <asp:BulletedList ID="BulletedList1" runat="server">
                    <asp:ListItem>Туризм</asp:ListItem>
                    <asp:ListItem>Спорт</asp:ListItem>
                    <asp:ListItem>Музыка</asp:ListItem>
                    <asp:ListItem>Танцы</asp:ListItem>
                </asp:BulletedList>
                <asp:Label ID="Label1" runat="server" Text="Текстовая метка" />
                <asp:CheckBox ID="CheckBox1" runat="server" Text="Флажок" 
                    BackColor="Black" ForeColor="Yellow" EnableTheming="True" />
                <br />
                <br />
                <b>Тема подключена к странице через свойство Theme объекта DOCUMENT</b>
                <br />
                <table width="100%" border="1">
                    <tr>
                        <td>
                            SkinID не подключен: действуют общие настройки по умолчанию
                        </td>
                        <td>
                            <asp:Label ID="Label2" runat="server" Text="Label2" />
                        </td>
                        <td>
                            <asp:CheckBox ID="CheckBox2" runat="server" Text="CheckBox2" />
                        </td>
                    </tr>
                    <tr>
                        <td>
                            Именованные настройки подключены: SkinID="Variant3"
                        </td>
                        <td>
                            <asp:Label ID="Label3" runat="server" Text="Label3" 
                                SkinID="Variant3" />
                        </td>
                        <td>
                            <asp:CheckBox ID="CheckBox3" runat="server" 
                                Text="CheckBox3" SkinID="Variant3" />
                        </td>
                    </tr>
                    <tr>
                        <td>
                            Именованные настройки подключены: SkinID="Variant4"
                        </td>
                        <td>
                            <asp:Label ID="Label4" runat="server" Text="Label4" 
                                SkinID="Variant4" />
                        </td>
                        <td>
                            <asp:CheckBox ID="CheckBox4" runat="server" Text="CheckBox4" 
                                SkinID="Variant4" />
                        </td>
                    </tr>
                    <tr>
                        <td>
                            SkinID="Variant5" подключен, но тема подавлена: EnableTheming="False"
                        </td>
                        <td>
                            <asp:Label ID="Label5" runat="server" Text="Label5" 
                                EnableTheming="False" SkinID="Variant5" />
                        </td>
                        <td>
                            <asp:CheckBox ID="CheckBox5" runat="server" Text="CheckBox5" 
                                EnableTheming="False" SkinID="Variant5" />
                        </td>
                    </tr>
                    <tr>
                        <td>
                            Подключен несуществующий SkinID="XXX": настройки темы не действуют
                        </td>
                        <td>
                            <asp:Label ID="Label6" runat="server" Text="Label6" 
                                SkinID="XXX" />
                        </td>
                        <td>
                            <asp:CheckBox ID="CheckBox6" runat="server" Text="CheckBox6" 
                                SkinID="XXX" />
                        </td>
                    </tr>
                </table>
                <br />
                <asp:Button ID="Button1" runat="server" Text="Отправить" />
            </div>
        </form>
    </body>
    </html>
  • Откройте на редактирование файл кода TestSwitchThemes.aspx.cs и заполните его так
  • using System;
        //using System.Data;
        //using System.Configuration;
        //using System.Collections;
        //using System.Web;
        //using System.Web.Security;
        //using System.Web.UI;
        //using System.Web.UI.WebControls;
        //using System.Web.UI.WebControls.WebParts;
        //using System.Web.UI.HtmlControls;
    	
    public partial class TestTheme : 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);
        }
    }

    Прежде, чем выполнить страницу, сделаем некоторые пояснения по приведенному C#-коду

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

  • Вызовите на редактирование файл App_Themes/MyTheme1/ThemeCSS.css и поменяйте в селекторе
  • Вызовите на редактирование файл App_Themes/MyTheme2/ThemeCSS.css и поменяйте в селекторе
  • Наступил момент истины...

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

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

    Вот и закончилась тема про "темы"!

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

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

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

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

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

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

    Шаблоны представляют собой обычные страницы, за исключением нескольких отличий:

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

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

  • Добавьте к проекту командой Website/Add New Item шаблон с именем FirstMasterPage.master с разделяемым кодом
  • Вот какой дескрипторный код создал мастер

    <%@ Master Language="C#" AutoEventWireup="true" 
        CodeFile="FirstMasterPage.master.cs" 
        Inherits="FirstMasterPage" %>
    	
    <html xmlns="http://www.w3.org/1999/xhtml" >
    <head runat="server">
        <title>Untitled Page</title>
    </head>
    <body>
        <form id="form1" runat="server">
        <div>
            <asp:contentplaceholder id="ContentPlaceHolder1" runat="server">
            </asp:contentplaceholder>
        </div>
        </form>
    </body>
    </html>

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

  • Удалите дескриптор ContentPlaceHolder и через контекстное меню редактора выполните команду Add Content Page (добавить страницу содержимого)
  • Оболочка сгенерирует такую заготовку файла Default.aspx

    <%@ Page Language="C#" 
        MasterPageFile="~/FirstMasterPage.master" 
        AutoEventWireup="true" CodeFile="Default.aspx.cs" 
        Inherits="_Default" Title="Тестовая страница содержимого" %>
    <%-- Add content controls here --%>

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

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

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

  • Создайте в панели Solution Explorer через контекстное меню для Web-корня подпапку MasterPages командой New Folder
  • Переместите мышью в папку MasterPages шаблон FirstMasterPage.master
  • Теперь давайте откорректируем дескрипторный код исполнимой страницы.

  • В панели Solution Explorer дайте файлу Default.aspx новое имя FirstMasterPage.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 текстовую строку, чтобы дескрипторный код стал таким
    (рис ) Дескрипторный код шаблона 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">
                <h1>Привет студентам из "именованной области1" мастер-страницы!</h1>
            </asp:ContentPlaceHolder>
        </div>
        </form>
    </body>
    </html>
  • Перейдите в режим
  • Теперь нужно настроить страницу содержимого, использующую шаблон.

  • Откройте на редактирование в режиме Source страницу содержимого FirstMasterPage.aspx, к которой подключен шаблон, и определите в контейнере <asp:Content> атрибуты ID самого дескриптора страницы и ContentPlaceHolderID подключенного шаблона, на которую будет ссылаться дескриптор содержимого. Пользуйтесь услугами подсказчика кода IntelliSense, который вызывается каждый раз при добавлении пробела внутри дескриптора.
  • Идентификатор ID самого контейнера <asp:Content> в странице содержимого определять необязательно, если только мы не планируем управлять им программно. Измененный код будет таким

    <%@ Page Language="C#" 
        MasterPageFile="~/MasterPages/FirstMasterPage.master" 
        AutoEventWireup="true" CodeFile="FirstMasterPage.aspx.cs" 
        Inherits="FirstMasterPage" Title="Тестовая страница содержимого" %>
        
    <%-- Add content controls here --%>
    <asp:Content ContentPlaceHolderID="ContentPlaceHolder1" runat="server">
        <h1>Привет студентам из "страницы содержимого1"!</h1>
    </asp:Content>
  • Исполните страницу
  • Поместите на шаблон еще один контейнер шаблона ContentPlaceHolder и наполните его таким кодом
    (рис ) Дескрипторный код шаблона 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">
                <h1>Привет студентам из "именованной области1" мастер-страницы!</h1>
            </asp:ContentPlaceHolder>
            <br />
            <asp:ContentPlaceHolder ID="ContentPlaceHolder2" runat="server">
                <h1>Привет студентам из "именованной области2" мастер-страницы!</h1>
            </asp:ContentPlaceHolder>
        </div>
        </form>
    </body>
    </html>
  • Перейдите на страницу содержимого
  • Мы видим, что мастер-страница задает схему размещения на итоговой странице. Причем, если на итоговой странице есть контейнер с содержимым, привязанный к именованной области шаблона, то на место именованной области вставляется его содержимое. Если контейнер с содержимым есть, но пустой, то ничего не вставляется. Но если контейнера с содержимым вообще нет, то на итоговой странице появляется содержимое именованной области.

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

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

    Обратите внимание на заголовки именованных областей и их фон. Там, где подставлено содержимое прикрепленного контейнера, фон светлее и в скобках надпись ( Custom ) - заказной. Там, где проявилось содержимое мастер-страницы, фон темнее и надпись ( Master ).

    Давайте добавим контейнер содержимого к нижней именованной области и наполним его.

  • На странице FirstMasterPage.aspx в режиме Design вызовите контекстное меню для нижней именованной области и выполните команду Create Custom Content
  • Оболочка добавит пустой контейнер содержимого и автоматически свяжет его в дескрипторном коде с именованной областью. Отсюда еще одна мораль:

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

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

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

    Компоновка содержимого мастер-страницы

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

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

  • В панели Solution Explorer вызовите контекстное меню для папки 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, который находится в прилагаемом к данной работе каталоге Pictures, и скопируйте его в папку MasterPages текущего сайта
  • Выделите экземпляр ImageBanner и присоедините к нему через свойство ImageUrl рисунок Banner.png из папки MasterPages Web-дерева сайта. Наш баннер слишком великоват, но для локального компьютера сойдет и такой
  • Разместите после баннера заголовочный дескриптор с внутренним стилем
  • <h1 style="color: Green">
        Мы приветствуем Вас на нашем сайте!!!
    </h1>

    Создание таблицы с шаблонами

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

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

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

  • Поместите на мастер-страницу FirstMasterPage.aspx после заголовочного дескриптора из вкладки HTML панели Toolbox элемент управления Table
  • Используя боковые маркеры таблицы и команды меню Layout оболочки, постройте таблицу из трех строк и двух столбцов, чтобы она соответстворвала приведенному выше рисунку
  • Рекламация Большому Биллу: Мы конечно осознаем все революционные возможности Visual Studio 2005 по разработке программ. Но нас-то тоже нужно понять, мы привыкли к развитым HTML-редакторам (ворованным), а в оболочке возможностей редактирования таблиц маловато (хотя она тоже украдена, но мы же учимся).

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

    <table border="0" width="100%">
        <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, к которому мы присоединим рисунок, задающий фон ячейки. ID=Panel1 копии этого элемента изменять не будем, поскольку не планируем в дальнейшем им программно управлять
  • Задайте для созданного экземпляра элемента управления Panel значения свойств: Width=100%; Height=100%, чтобы элемент полностью занял верхнюю ячейку таблицы
  • Через проводник Windows найдите в прилагаемом к данной работе каталоге Pictures файл BackTitleSrc.jpg и в любом графическом редакторе вырежьте из него область с хорошо стыкуемыми при повторении рисунка границами, или возьмите мною вырезанный готовый рисунок BackTitle.jpg
  • Рисунок не должен быть слишком большим, чтобы "не застрял в проводах" по пути к пользователю.

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

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

    <td style="width: 25%; background-color: #9DFF7E" valign="top">
        <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>
    </td>

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

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

  • Отступите ниже таблицы одну-две строчки и добавьте из вкладки HTML панели Toolbox элемент управления Image. Щелкните на нем правой кнопкой мыши и выполните команду Run As Server Control, чтобы содержимое элемента проявилось в будущем на странице содержимого
  • В панели Solution Explorer вызовите контекстное меню для папки MasterPages и скопируйте в проект командой Add Existing Item из прилагаемого каталога Pictures рисунок 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 2007
    </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.

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

    Мы для нашего примера создадим 4 страницы содержимого с простыми именами:

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

    Первый способ

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

    Второй способ

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

    MasterPageFile="~/MasterPages/Title.master" - (как-бы абсолютный адрес в пределах сайта)

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

    Третий способ

  • Откройте мастер-страницу на редактирование и щелкните на ней в любом месте правой кнопкой мыши. Появится контекстное меню с уже известной нам командой Add Content Page
  • Обратите внимание, что двумя последними способами страница создается без отдельного файла поддержки C#. Это легко исправить, добавив к проекту файл .cs с предшествующим именем страницы. Правда, оболочка при этом спросит, создавать ли для этого класса отдельную папку со стандартным именем App_Code, на что нужно ответить Нет. И еще, при таком способе добавления к странице файла поддержки нужно вручную прописать необходимые атрибуты в декларации @Page

    AutoEventWireup="true" CodeFile="XXX.aspx.cs" Inherits="YYY"

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

  • Щелкните правой кнопкой мыши на открытой мастер-странице ~/MasterPages/Title.master в любом месте и командой Add Content Page создайте страницу содержимого Default.aspx
  • Переименуйте страницу Default.aspx в Satellite.aspx и переместите ее в корневую папку Web-дерева
  • Подобным же образом создайте и переместите в корень остальные страницы содержимого: Satellite1.aspx, Satellite2.aspx, Satellite3.aspx. Но можно просто раскопировать в корне в трех экземплярах файл Satellite.aspx, учитывая, что все страницы будут ссылаться на одну и туже мастер-страницу
  • Теперь давайте корректировать заготовки страниц содержимого. Но прежде всего нужно доделать нашу мастер-страницу, определив ссылки в левой ячейке таблицы на только что созданные страницы содержимого.

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

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

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

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

    Мастер-страница Title.master

    Обратите внимание, что все контейнеры ContentPlaceHolder должны находиться внутри дескриптора <form></form>!
    <%@ 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">
       <asp:Image ID="ImageBanner" runat="server" ImageUrl="~/MasterPages/Banner.png" />
       <h1 style="color: Green">
        Мы приветствуем Вас на нашем сайте!!!
       </h1>
       <br />
       <table border="0" width="100%">
        <tr>
         <td colspan="2">
          <asp:Panel ID="Panel1" runat="server" Height="100%" Width="100%" BackImageUrl="~/MasterPages/BackTitle.jpg">
           <asp:ContentPlaceHolder ID="ContentPlaceHolder1" runat="server">
            <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" valign="top">
          <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>
         </td>
         <td style="background-color: #FFB8FF">
          <asp:ContentPlaceHolder ID="ContentPlaceHolder2" runat="server">
           Вот уже более 12 лет мы занимаемся подготовкой водителей категории "B" в г.Красноярске
           и всегда занимали первые места в сводках МРЭО ГИБДД по качеству обучения. Автошкола
           всегда нацелена на успех наших клиентов в сдаче экзаменов ГАИ с первого раза и своевременном
           получении водительского удостоверения.
           <br />
           <br />
           Мы обеспечиваем максимальные удобства своим клиентам. Для этого существуют вечерняя,
           воскресная и заочная формы обучения. Наши курсанты имеют возможность бесплатно и
           неограниченно пользоваться компьютерным классом. Учебные автомобили прибывают по
           заявке клиента к назначенному им месту. Развивается сеть филиалов автошколы в г.Красноярске.
          </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" />
        ampnbsp;Copyright © 2007</p>
       <asp:ContentPlaceHolder ID="ContentPlaceHolder4" runat="server">
        <strong><em>Вы уж, пожалуйста, наведывайтесь к нам почаще!</em></strong>
       </asp:ContentPlaceHolder>
      </div>
     </form>
    </body>
    </html>

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

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

    <%@ 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>

    Программное управление мастер-страницей из страницы содержимого

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

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

    Управление заголовком

    Мастер-страница имеет заголовок <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> мастер-страницы. Текущий код страницы такой

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

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

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

    Оболочка автоматически создаст блок скрипта и вставит в него заготовку обработчика события Page.Load.

  • Заполните обработчик так
    (рис ) Файл SatelliteExt.aspx после добавления обработчика 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 и будем обращаться к нему напрямую
  • Первый способ

  • Двойным щелчком на свободном месте мастер-страницы в режиме Design (или вручную) создайте блок скриптов, который заполните так
    (рис ) Добавление свойства доступа к дескриптору <h1> в файле Title.master<%@ 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 следующим кодом
    (рис ) Дополненный код страницы содержимого 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 и убедитесь, что дескриптор
  • Директива @ MasterType указывает ASP.NET виртуальный путь к соответствующему файлу мастер-страницы.

    Второй способ

  • Измените страницу содержимого SatelliteExt.aspx следующим образом
    (рис ) Измененный код страницы содержимого 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 и убедитесь, что дескриптор
  • Второй способ более предпочтительный, поскольку мы не вмешиваемся в код спроектированной мастер-страницы, которая может быть подключена еще к чему-нибудь!

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

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

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

  • Страница содержимого, динамически меняющая мастер-страницу, не должна иметь ссылок на несуществующие в новой мастер-странице контейнеры ContentPlaceHolderID
  • Замена мастер-страницы должна выполняться в обработчике события Page.PreInit страницы содержимого. Если это сделать в обработчиках более поздних событий, то будет сгенерировано исключение
  • Все страницы содержимого, на которые имеются ссылки в текущей странице содержимого, должны иметь код подключения выбранной мастер-страницы
  • Для межстраничной передачи информации о выбранной мастер-странице ее нужно где-то сохранять. Как и в случае с темами, это могут быть скрытые поля состояния вида ( VIEWSTATE ), глобальные объекты уровня сеанса или приложения
  • Учитывая, что наши страницы Satellite.aspx, Satellite1.aspx, Satellite2.aspx, Satellite3.aspx согласованы с Title.master, сделаем их копии и настроим под динамическую смену мастер-страниц.

  • Откройте мастер-страницу Title.master и добавьте выше объекта ImageBanner (после открывающего дескриптора <div> ) элемент ContentPlaceHolder с именем ContentPlaceHolderList. Сохраните и закройте файл Title.master
  • Сделайте копии соответствующих файлов и присвойте им имена Title1.master, SatelliteDynamic.aspx, SatelliteDynamic1.aspx, SatelliteDynamic2.aspx, SatelliteDynamic3.aspx
  • Определите страницу SatelliteDynamic.aspx стартовой
  • Откройте мастер-страницу Title1.master и выполните в ней следующее:
  • Полностью удалите контейнер скриптов <script runat="server">...</script>
  • Удалите объект ImageBanner и внесите еще какие-нибудь визуально отличимые изменения
  • Добавьте к гиперссылкам HyperLink еще одну с названием "Автошкола в целом"
  • Заполните свойство NavigateUrl гиперссылок значениями новых страниц содержимого
  • Откройте страницу содержимого SatelliteDynamic.aspx и в режиме Design создайте контейнер содержимого для шаблона ContentPlaceHolderList, выполнив на нем команду Create Custom Content контекстного меню
  • Добавьте в контейнер содержимого предваряющий текст "Выберите мастер-страницу:" и элемент DropDownList с именем lstMasterPages
  • Заполните раскрывающийся список Text Value Items[0] Мастер-страница с баннером ~/MasterPages/Title.master Items[1] Мастер-страница без баннера ~/MasterPages/Title1.master
  • Установите для списка lstMasterPages свойство AutoPostBack="True"
  • В режиме Design двойным щелчком на тускло просвечивающемся заполнителе мастер-страницы создайте контейнер для скриптов, обработчик Page_Load() и переименуйте его в Page_Init()
  • Скопируйте заготовку обработчика Page_Init() и переименуйте копию в Page_PreInit(), поскольку все они имеют одинаковую сигнатуру
  • В режиме Design двойным щелчком на объекте lstMasterPages списка создайте обработчик изменения выбора опции lstMasterPages_SelectedIndexChanged()
  • Заполните обработчики так, чтобы окончательный код страницы был таким
    (рис ) Окончательный код страницы SatelliteDynamic.aspx<%@ Page Language="C#" MasterPageFile="~/MasterPages/Title.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 и добавьте в каждый из них после директивы @Page такой блок скриптов
    (рис ) Скрипты остальных страниц содержимого для загрузки выбранной мастер-страницы<script runat="server">
    	
        protected void Page_PreInit(object sender, EventArgs e)
        {
            if (Session["Master"] != null)
            {
                // Был ранее сохраненный выбор, включить его
                Page.MasterPageFile = (string)Session["Master"];
            }
        }
    </script>
  • Исполните начальную страницу SatelliteDynamic.aspx и убедитесь, что мы дали пользователю полнофункциональный механизм выбора мастер-страниц
  • Многослойное подключение мастер-страниц

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

    Но мы, все-таки, рассмотрим эту возможность на примере мастер-страниц двух уровней. Мы создадим две мастер-страницы с последовательным подключением нижнего уровня 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>
  • Теперь создайте мастер-страницу верхнего уровня с именем MasterPageLevel0.master и подключите к ней предыдущую через атрибут MasterPageFile директивы @Master
  • Код страницы должен выглядеть так

    <%@ Master Language="C#" MasterPageFile="~/MasterPageLevel1.master" %>
        
    <asp:Content ID="ContentLevel0" ContentPlaceHolderID="ContentPlaceHolderLevel1" runat="server">
        <table width="100%" 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>
  • Выполните для страницы MasterPageLevel0.master команду Website/Add Content Page, чтобы создать для нее страницу содержимого
  • Измените имя по умолчанию Default.aspx созданной страницы содержимого на TestMasterPageLevel.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 и поместить в нее мастер-страницу и согласованные страницы содержимого

    Наши перспективы

    Ну вот и приплыли, граждане студенты. До сих пор я писал изложение, а вы его читали. Теперь вы будете писать сочинение, а я его буду смотреть в аттестационную и зачетную недели! Отсюда вам мой наказ (читай "Указ!"):

  • На занятия можете не ходить - это ваше личное дело, но знать данный материал вы обязаны (если я ошибаюсь - спросите у декана). Это уже вопрос государственной безопасности! (чтобы перед Большим Биллом не краснеть)
  • Сочинение будете писать на вольную тему в виде взаимосвязанных активных страниц на ASP.NET 2.0
  • Сегодня (беру слепок сверху) 19.03.2009 11:51 и материала для вас уже изложено достаточно, чтобы приступить к работе
  • Ни один не получит зачет (я не боюсь директор магазин, товаровед, завсклад), пока не представит приличный сайт на вольную тему (мне еще темы сочинять не хватало!)
  • Просто статические страницы не пойдут, должен быть код с приличной функциональностью
  • Дизайн и графика большого значения не имеет, хотя настраивать стиль нужно
  • Все, что я знаю и умею по этому средству, должны знать и уметь вы. А то какое-то неравенство и стыд получается!
  • Пора и власть употребить
  • Как говорил наш шеф, куй железо не отходя от кассы
  • Как гласит народная мудрость, время - деньги; не расчитывай на деньги, если нет времени!
  • Страницы:
    Файлы к лекции Вы можете скачать здесь

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

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

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

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

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

    Добавление стилей в HTML-дескриптор

    Хотя CSS обычно не применяется для изменения вида каждого тега в отдельности, но принципиально можно это делать, особенно, когда нужно перекрыть действие внешнего каскада стиля для конкретного дескриптора. Синтаксис применения внутреннего стиля на примере дескриптора <h1> будет таким

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

    Пример

    Пусть отдельный заголовочный дескриптор будет определен в данном документе так

    <h1 style="color:red; text-align:center">Этот заголовочный 
    текст будет центрирован в текущем документе и иметь красный 
    цвет (бледный вид)!</h1>

    Отклик броузера выделен в рамочку и будет таким

    Добавление стилей на Web-страницу

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

    Атрибут type="text/css" дескриптора стиля указывает броузеру, что все последующие стили будут css -стилями. В приведенном примере селекторы body и h1 совпадают с именами дескрипторов HTML. Список селекторов HTML-объектов содержит значения идентификаторов объектов страницы, к которым нужно применить соответствующие правила стилей. Элементы списка селекторов отделяются друг от друга запятыми или пробелами. Если в определение одного свойства подразумевается много значений, то они также разделяются запятыми или пробелами. Если одно значение правила состоит из нескольких частей, разделенных пробелами, то оно обязательно заключается в двойные или одинарные кавычки.

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

    Пример

    Пусть страница имеет код, приведенный ниже (файл embedded.htm в каталоге CSS рассматриваемой темы)

    <html>
    <head>
        <title>Документ со встроенной CSS</title>
        <meta content="text/html; charset=windows-1251" http-equiv="Content-Type">
        <style type="text/css">
            body
            {
            	background:white url(MyWeb.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>
    </head>
    <body>
        <h1>
            Заполните анкету!</h1>
        <form>
            <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>

    Чтобы получить оригинальный результат, щелкните на кнопке, расположенной внизу приведенного примера кода. Если на Вашем броузере включена поддержка JavaScript, то он запустит новую страницу в отдельном окне, где можно посмотреть результат с использованием встроенной CSS. "Стильный" снимок моего результата будет таким

    Внешние стили

    Внешними считаются стили, которые хранятся в отдельном файле текстового формата (чаще с расширением .css, но будет работать любое расширение). Содержимое стиля в отдельном файле отличается от встроенного стиля тем, что во внешнем стиле не должно быть дескрипторных скобок <style></style>. Кроме этого, подключение стилевого файла к HTML-документу выполняется добавлением в блок <head>...</head> дескриптора <link> или директивы @import.

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

    Варианты подключения CSS к HTML-документу
    Вариант В блоке <head> файла MyPage.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 rel="stylesheet" href="default.css">
    <style type="text/css">
    
        @import url(header.css);
    
       body
       {
          	background:white url(MyWeb.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(MyWeb.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 rel="stylesheet" href="default.css">
    Подключается две внешних таблицы в дополнение к встроенной.

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

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

    Чтобы проверить сказанное, выполните следующее (вручную, без Visual Studio 2005!)

  • Найдите в прилагаемом каталоге CSS файл embedded.htm и в этом же каталоге сделайте из него две копии с именами TestCSS.htm и ExternalCSS.css
  • Откройте в текстовом редакторе Блокнот файл TestCSS.htm и замените в заголовке <head> встроенный стиль на строку подключения внешней CSS
    <link rel="stylesheet" href="ExternalCSS.css">
  • Откройте в текстовом редакторе Блокнот файл ExternalCSS.css и оставьте только чистый код селекторов и определений стилей
  • Откройте страницу
  • Опробуйте другие варианты подключения внешней CSS, приведенные ранее (если хотите!)
  • Применение внешних стилей в Visual Studio 2005

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

  • Создайте через Проводник Windows новый каталог с именем WebSite6
  • Откройте этот каталог через оболочку Visual Studio 2005 командой File/Open/Web Site
  • Выполните команду Website/Add New Item, выберите вариант Style Sheet и оставьте имя файла по умолчанию StyleSheet.css
  • Оболочка добавит в проект новый файл каскадной таблицы стилей и покажет панель инструментов Style Sheet, помогающую редактировать этот файл. Здесь, правда, возможностей не так богато, как в специализированных редакторах стилей, но главная линия все-таки соблюдается.

    Наша главная задача, как программистов, - понимать и правильно

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

  • Откройте созданный нами ранее почти пустой проект WebSite6 (если не открыт)
  • Выполните команду Website/Add Existing Item. В появившемся диалоговом окне войдите в каталог CSS (мы в нем уже работали) и отметьте нужные файлы, удерживая при этом клавишу Ctrl
  • После щелчка на кнопке Add оболочка физически скопирует выделенные файлы в файл проекта WebSite6 и они появятся в панели Solution Explorer. Если вы не увидели там этих файлов, то щелкните на кнопке Refresh панели Solution Explorer, чтобы оболочка обновила представляемую после копирования информацию

  • Назначьте статическую страницу TestCSS.htm стартовой (команда Set As Start Page )и запустите ее на выполнение, чтобы убедиться, что и в оболочке она работает точно также, как и ранее в автономном режиме
  • Если страница не содержит исполняемого ASP.NET -кода, то система ASP.NET ничего с ней не делает, а отсылает в неизменном виде клиенту. Если посмотреть на исходный HTML-код присланной броузеру страницы (команда View Source контекстного меню окна броузера), то он остался тем же самым, каким мы его прежде сочинили

    <html>
    <head>
        <title>Документ со встроенной CSS</title>
        <meta content="text/html; charset=windows-1251" http-equiv="Content-Type">
        <link rel="stylesheet" href="ExternalCSS.css">
    </head>
    <body>
        <h1>
            Заполните анкету!</h1>
        <form>
            <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>
  • Откройте двойным щелчком в Solution Explorer файл TestCSS.htm на редактирование
  • Отвлечемся немного от темы стилей и обратим внимание на то, сколько строк нашего кода подчеркнул редактор оболочки красной линией. Это значит, что его не устраивает синтаксис кода, на проверку которого он настроен

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

  • Выполните команду Tools/Customize и отметьте флажок HTML Source Editing
  • В верхней части каркаса оболочки появится панель инструментов настройки HTML-редактора, в которой нужно установить через раскрывающийся список опцию HTML 4.01

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

    <form action=TestCSS.htm>

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

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

  • Вызовите контекстное меню щелчком правой кнопки мыши на свободном месте верхней части каркаса оболочки и сбросьте флажок HTML Source Editing, чтобы закрыть одноименную панель настройки редактора
  • Удалите из заголовочного блока страницы код подключения внешней таблицы стилей TestCSS.htm, а именно, удалите строку
    <link rel="stylesheet" href="ExternalCSS.css">
  • Переведите страницу в режим Design и посмотрите, какая она сразу стала блеклая (тусклая, бледная, невзрачная, некрасивая, ... - студент Леша Зиборов, продолжай! Только ты можешь дочитать до этого места!)
  • Захватите мышью в панели Solution Explorer файл ExternalCSS.css и перенесите его на страницу, которая сразу преобразится уже на этапе проектирования
  • Вернитесь в режим Source и убедитесь, что оболочка добавила нужный дескриптор подключения CSS в нужное место
  • Опять удалите дескриптор подключения CSS
  • Перейдите в режим Design, откройте панель Properties и через раскрывающийся список в ее верхней части выберите объект DOCUMENT
  • Найдите свойство
  • Очистите поле свойства StyleSheet, оболочка вновь удалит дескриптор подключения

    Таблицы CSS применяются не только к простым дескрипторам HTML, но и к дескрипторам серверных элементов управления. Давайте это проверим, поместив на страницу серверный элемент управления, например, Label.

  • Откройте панель Toolbox

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

  • Переименуйте в панели Solution Explorer тестовую страницу в TestCSS.aspx и сделайте ее же принимающей, исправив дескриптор <form>
    <form action=TestCSS.aspx runat="server">
  • Поместите на страницу из вкладки Standard текстовую метку Label и в панели Properties определите
  • CssClass=label
  • Text=Стиль класса label
  • Откройте через Solution Explorer файл ExternalCSS.css и добавьте стилевое правило для класса label
    (рис ) Добавление нового стиля в класс label файла ExternalCSS.cssbody
    {
      background:white url(MyWeb.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;
    }
    	
    .label
    {
      background-color:Green;
      color:White;
      font:bold;
      font-size:x-large;
      font-family:Courier New;
    }
  • Выполните страницу, которая в броузере станет такой
  • Краткое знакомство с 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. Эти файлы называются файлами оформлений и добавляются командой Website/Add New Item через соответствующее диалоговое окно

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

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

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

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

  • Перейдите в режим Source и удалите из файла FirstSkin.aspx весь код, кроме открывающих дескрипторов элементов управления, в которых тоже удалите атрибуты, не относящиеся к настройкам стиля. Должно остаться следующее
    (рис ) Оставшийся код в файле 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
  • Создайте в приложении папку с зарезервированным именем
  • В панели Solution Explorer захватите мышью файл FirstSkin.skin и переместите его в папку MyTheme
  • Тема подключается к странице с помощью атрибута Theme директивы @Page. Так, например, при применении темы с именем каталога MyTheme к нашей тестовой странице TestTheme.aspx директива будет выглядеть так

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

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

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

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

    Приоритеты оформлений и стилевых настроек

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

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

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

    Любой вариант влияния темы на элемент управления можно отключить, если установить в этом элементе свойство EnableTheming="False".

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

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

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

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

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

  • Поместите из вкладки Standard панели Toolbox в средний столбец таблицы пять серверных элементов Label, а в правый - пять серверных элементов CheckBox
  • Установите в добавленных объектах свойства в соответствии с контекстом таблицы
    SkinID не подключен: действуют общие настройки по умолчанию Label2 CheckBox2
    Именованные настройки подключены: SkinID="Variant3" Label3 CheckBox3
    Именованные настройки подключены: SkinID="Variant4" Label4 CheckBox4
    SkinID="Variant5" подключен, но тема подавлена: EnableTheming="False" Label5 CheckBox5
    Подключен несуществующий SkinID="XXX": настройки темы не действуют Label6 CheckBox6
  • Откройте файл оформлений FirstSkin.skin и дополните его индивидуальными настройками стилей, чтобы общий код его стал таким
    (рис ) Код файла оформлений FirstSkin.skin темы MyTheme<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" />
    	
    <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, то эти стилевые настройки никогда не будут применяться.

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

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

  • Создайте новую страницу со встроенным кодом поддержки и с именем TestCalendar.aspx
  • Назначьте страницу стартовой и поместите на нее из вкладки Standard элемент управления Calendar
  • Определите для объекта Calendar1 следующие стилевые настройки
    (рис ) Стилевые настройки объекта Calendar1 страницы TestCalendar.aspx<asp:Calendar ID="Calendar1" runat="server" 
        BackColor="white" ForeColor="black" BorderColor="black"
        BorderStyle="solid" CellSpacing="1" Font-Names="Verdana" 
        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
  • Скопируйте из страницы TestCalendar.aspx в файл оформлений Calendar.skin весь дескрипторный код объекта Calendar1 вместе с его стилевыми настройками и удалите в копии атрибут ID="Calendar1"
  • Удалите в дескрипторном коде страницы TestCalendar.aspx код стилевых настроек объекта Calendar1, выделенный в приведенной выше таблице красным цветом, чтобы от объекта осталось только простое "мокрое место"
    <asp:Calendar ID="Calendar1" runat="server"> 
    </asp:Calendar>
  • Через панель Properties выделите объект DOCUMENT и поключите к странице тему MyTheme в свойстве Theme
  • Выполните страницу и убедитесь, что вынесенный со страницы в тему большой блок стилевых настроек элемента Calendar работает также, как если бы он находился на самой странице
  • Ниже приведены два варианта представления объекта Calendar1

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

  • В панели Solution Explorer через контекстное меню на папке MyTheme командой New Folder создайте подпапку ButtonImages для хранения рисунков кнопок
  • В панели Solution Explorer через контекстное меню на новой папке
  • Добавьте на страницу TestCalendar.aspx две картиночных кнопки ImageButton из вкладки Standard панели Toolbox и настройте через одноименные свойства в панели 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, чтобы добавить в тему новый файл оформления
  • Скопируйте настроенные дескрипторы элементов ImageButton в файл Buttons.skin, предварительно очистив его от автоматически сгенерированных инструкций
  • Откорректируйте оформления в файле Buttons.skin, чтобы они выглядели так
    <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 в файле TestCalendar.aspx, чтобы от них осталось следующее
    <asp:ImageButton ID="ImageButton1" runat="server" SkinID="OKButton" />
    <asp:ImageButton ID="ImageButton2" runat="server" SkinID="CancelButton" />
  • Исполните страницу
  • Посмотрите, на странице находится дескрипторный код только создания сложных элементов управления, а большой код тонких настроек вынесен в файлы темы. Это очень удобно и для централизованного изменения стилевого оформления элементов управления, и для читабельности самой страницы.

    Использование внешних CSS в теме

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

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

  • Скопируйте файл TestCSS.aspx и назначьте копии имя ThemeCSS.aspx и назначьте новую страницу стартовой
  • Скопируйте таблицу стилей ExternalCSS.css и назначьте копии имя ThemeCSS.css
  • Переместите файл ThemeCSS.css в папку MyTheme проекта
  • Удерживая клавишу Ctrl, выделите файлы BackgroundImage.bmp и MyWeb.jpg в корне виртуального каталога и выполните для них команду Copy контекстного меню
  • Щелкните правой кнопкой мыши на узле MyTheme и выполните команду Paste, чтобы рисунки оказались в одном каталоге с таблицей ThemeCSS.css
  • Откройте страницу ThemeCSS.aspx на редактирование, удалите в ней дескриптор <link> подключения внешней таблицы стилей. Удостоверьтесь, что заголовочный дескриптор <head> является серверным
  • Исполните страницу и убедитесь, что стили пока не действуют, потому что не подключены
  • Теперь только осталось подключить тему MyTheme к странице.

  • В панели Properties выделите объект DOCUMENT и установите его свойство Theme в значение MyTheme
  • Выполните страницу ThemeCSS.aspx и убедитесь, что стили из внешней таблицы ThemeCSS.css работают
  • Назначьте страницу TestCSS.aspx, где мы CSS использовали напрямую, стартовой и выполните ее
  • Чтобы сделать некоторые выводы, сравним полученные результаты, приведенные ниже в уменьшенном виде.

    "Найдите десять отличий!" Ха-ха-ха...
    Используется только CSS для несерверных элементов управления и для одного серверного. Для остальных серверных элементов стили неопределены Используется CSS для несерверных элементов управления, а для серверных элементов действуют оформления темы

    Выводы:

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

  • В каталоге Theme сделайте еще три копии файла ThemeCSS.css с именем ThemeCSS1.css, ThemeCSS2.css, ThemeCSS3.css
  • Назначьте страницу ThemeCSS.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, сколько файлов с таблицами находится в каталоге темы. Обратите внимание, что пути в ссылках прописываются относительно корня виртуального каталога, в котором находится сама страница. Ну и последнее, строки подключения CSS в HTML-коде прописываются системой ASP.NET, ну совсем как по теории.

  • Удалите лишние файлы ThemeCSS1.css, ThemeCSS2.css и ThemeCSS3.css, чтобы они дальше не мельтешили у нас в глазах
  • Применение общей темы в конфигурационном файле

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

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

    Например

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

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

  • Откройте страницы наших примеров, использующих темы, а именно: TestTheme.aspx, TestCalendar.aspx, ThemeCSS.aspx, и удалите в них из директивы @Page атрибут Theme="MyTheme"
  • Назначая каждую страницу стартовой, исполните их по очереди, чтобы убедиться, что отключенная тема в них не действует. Пока получилась грустная, унылая картина
  • Откройте на редактирование заготовку файла web.config, автоматически созданную оболочкой
  • Отредактируйте код файла web.config, добавив всего один дескриптор с указанием общей темы <pages theme="MyTheme" />, и сохраните изменения на диске
  • Ниже приведен окончательный код файла web.config с указанием общей темы по умолчанию, в котором убраны громоздкие комментарии и пустые дескрипторы

    <?xml version="1.0"?>
    <configuration>
    	<system.web>
    		<compilation debug="true"/>
    		<authentication mode="Windows"/>
    		<pages theme="MyTheme" />
    	</system.web>
    </configuration>
  • Назначая каждую из перечисленных выше страниц стартовой, вновь исполните их по очереди, чтобы убедиться, что тема опять заработала так, как будто-бы она была индивидуально подключена к каждой странице
  • Если нужно сделать так, чтобы общая тема действовала по умолчанию на множестве страниц, но не перезаписывала на них индивидуальные стилевые настройки элементов управления, то конфигурационный файл должен быть таким

    <?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 со своей общей темой и все страницы этого каталога будут использовать эту тему по умолчанию.

    Применение тем по выбору пользователя

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

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

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

  • Прочитать выбор пользователя, когда это станет возможным после полной загрузки страницы
  • Принудительно перезапустить страницу на сервере, чтобы в обработчике Page_PreInit() назначить новую тему
  • При перезапуске страницы она должна быть переадресована на саму себя. Такую операцию можно выполнить либо методом Response.Redirect(), либо методом Server.Transfer(). Первый метод реализует полный цикл с отправкой на броузер и автоматическим возвращением на сервер. Он тратит лишнее время на пересылку. Второй метод перезапускает страницу сразу на сервере без цикла через клиента. Но есть одна существенная особенность: оба метода сбрасывают состояние вида всех элементов управления и запускают страницу как в при первом запросе. Поскольку выбирать не из чего, то мы выбираем Pepsi и второй метод 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
  • На всякий пожарный случай (чтобы вам - его скопировать, а мне - отчитаться по объему текста перед зав. кафедрой Горенским Б.М.), приведу полный дескрипторный код, который будет иметь страница после проведения последних "оперативно-следственных" мероприятий

    <%@ Page Language="C#" AutoEventWireup="true" 
        CodeFile="TestSwitchThemes.aspx.cs" Inherits="TestTheme" %>
    	
    <html xmlns="http://www.w3.org/1999/xhtml" >
    <head runat="server">
        <title>Тест применения тем</title>
    </head>
    <body>
        <form id="form1" runat="server">
            <div>
                <asp:Label ID="lblFirstLoad" runat="server" Text="Label" />
                <asp:DropDownList ID="lstSwitchThemes" runat="server" 
                    OnSelectedIndexChanged="lstSwitchThemes_SelectedIndexChanged">
                </asp:DropDownList>
                <br />
                <br />
                <asp:BulletedList ID="BulletedList1" runat="server">
                    <asp:ListItem>Туризм</asp:ListItem>
                    <asp:ListItem>Спорт</asp:ListItem>
                    <asp:ListItem>Музыка</asp:ListItem>
                    <asp:ListItem>Танцы</asp:ListItem>
                </asp:BulletedList>
                <asp:Label ID="Label1" runat="server" Text="Текстовая метка" />
                <asp:CheckBox ID="CheckBox1" runat="server" Text="Флажок" 
                    BackColor="Black" ForeColor="Yellow" EnableTheming="True" />
                <br />
                <br />
                <b>Тема подключена к странице через свойство Theme объекта DOCUMENT</b>
                <br />
                <table width="100%" border="1">
                    <tr>
                        <td>
                            SkinID не подключен: действуют общие настройки по умолчанию
                        </td>
                        <td>
                            <asp:Label ID="Label2" runat="server" Text="Label2" />
                        </td>
                        <td>
                            <asp:CheckBox ID="CheckBox2" runat="server" Text="CheckBox2" />
                        </td>
                    </tr>
                    <tr>
                        <td>
                            Именованные настройки подключены: SkinID="Variant3"
                        </td>
                        <td>
                            <asp:Label ID="Label3" runat="server" Text="Label3" 
                                SkinID="Variant3" />
                        </td>
                        <td>
                            <asp:CheckBox ID="CheckBox3" runat="server" 
                                Text="CheckBox3" SkinID="Variant3" />
                        </td>
                    </tr>
                    <tr>
                        <td>
                            Именованные настройки подключены: SkinID="Variant4"
                        </td>
                        <td>
                            <asp:Label ID="Label4" runat="server" Text="Label4" 
                                SkinID="Variant4" />
                        </td>
                        <td>
                            <asp:CheckBox ID="CheckBox4" runat="server" Text="CheckBox4" 
                                SkinID="Variant4" />
                        </td>
                    </tr>
                    <tr>
                        <td>
                            SkinID="Variant5" подключен, но тема подавлена: EnableTheming="False"
                        </td>
                        <td>
                            <asp:Label ID="Label5" runat="server" Text="Label5" 
                                EnableTheming="False" SkinID="Variant5" />
                        </td>
                        <td>
                            <asp:CheckBox ID="CheckBox5" runat="server" Text="CheckBox5" 
                                EnableTheming="False" SkinID="Variant5" />
                        </td>
                    </tr>
                    <tr>
                        <td>
                            Подключен несуществующий SkinID="XXX": настройки темы не действуют
                        </td>
                        <td>
                            <asp:Label ID="Label6" runat="server" Text="Label6" 
                                SkinID="XXX" />
                        </td>
                        <td>
                            <asp:CheckBox ID="CheckBox6" runat="server" Text="CheckBox6" 
                                SkinID="XXX" />
                        </td>
                    </tr>
                </table>
                <br />
                <asp:Button ID="Button1" runat="server" Text="Отправить" />
            </div>
        </form>
    </body>
    </html>
  • Откройте на редактирование файл кода TestSwitchThemes.aspx.cs и заполните его так
  • using System;
        //using System.Data;
        //using System.Configuration;
        //using System.Collections;
        //using System.Web;
        //using System.Web.Security;
        //using System.Web.UI;
        //using System.Web.UI.WebControls;
        //using System.Web.UI.WebControls.WebParts;
        //using System.Web.UI.HtmlControls;
    	
    public partial class TestTheme : 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);
        }
    }

    Прежде, чем выполнить страницу, сделаем некоторые пояснения по приведенному C#-коду

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

  • Вызовите на редактирование файл App_Themes/MyTheme1/ThemeCSS.css и поменяйте в селекторе
  • Вызовите на редактирование файл App_Themes/MyTheme2/ThemeCSS.css и поменяйте в селекторе
  • Наступил момент истины...

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

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

    Вот и закончилась тема про "темы"!

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

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

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

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

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

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

    Шаблоны представляют собой обычные страницы, за исключением нескольких отличий:

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

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

  • Добавьте к проекту командой Website/Add New Item шаблон с именем FirstMasterPage.master с разделяемым кодом
  • Вот какой дескрипторный код создал мастер

    <%@ Master Language="C#" AutoEventWireup="true" 
        CodeFile="FirstMasterPage.master.cs" 
        Inherits="FirstMasterPage" %>
    	
    <html xmlns="http://www.w3.org/1999/xhtml" >
    <head runat="server">
        <title>Untitled Page</title>
    </head>
    <body>
        <form id="form1" runat="server">
        <div>
            <asp:contentplaceholder id="ContentPlaceHolder1" runat="server">
            </asp:contentplaceholder>
        </div>
        </form>
    </body>
    </html>

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

  • Удалите дескриптор ContentPlaceHolder и через контекстное меню редактора выполните команду Add Content Page (добавить страницу содержимого)
  • Оболочка сгенерирует такую заготовку файла Default.aspx

    <%@ Page Language="C#" 
        MasterPageFile="~/FirstMasterPage.master" 
        AutoEventWireup="true" CodeFile="Default.aspx.cs" 
        Inherits="_Default" Title="Тестовая страница содержимого" %>
    <%-- Add content controls here --%>

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

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

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

  • Создайте в панели Solution Explorer через контекстное меню для Web-корня подпапку MasterPages командой New Folder
  • Переместите мышью в папку MasterPages шаблон FirstMasterPage.master
  • Теперь давайте откорректируем дескрипторный код исполнимой страницы.

  • В панели Solution Explorer дайте файлу Default.aspx новое имя FirstMasterPage.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 текстовую строку, чтобы дескрипторный код стал таким
    (рис ) Дескрипторный код шаблона 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">
                <h1>Привет студентам из "именованной области1" мастер-страницы!</h1>
            </asp:ContentPlaceHolder>
        </div>
        </form>
    </body>
    </html>
  • Перейдите в режим
  • Теперь нужно настроить страницу содержимого, использующую шаблон.

  • Откройте на редактирование в режиме Source страницу содержимого FirstMasterPage.aspx, к которой подключен шаблон, и определите в контейнере <asp:Content> атрибуты ID самого дескриптора страницы и ContentPlaceHolderID подключенного шаблона, на которую будет ссылаться дескриптор содержимого. Пользуйтесь услугами подсказчика кода IntelliSense, который вызывается каждый раз при добавлении пробела внутри дескриптора.
  • Идентификатор ID самого контейнера <asp:Content> в странице содержимого определять необязательно, если только мы не планируем управлять им программно. Измененный код будет таким

    <%@ Page Language="C#" 
        MasterPageFile="~/MasterPages/FirstMasterPage.master" 
        AutoEventWireup="true" CodeFile="FirstMasterPage.aspx.cs" 
        Inherits="FirstMasterPage" Title="Тестовая страница содержимого" %>
        
    <%-- Add content controls here --%>
    <asp:Content ContentPlaceHolderID="ContentPlaceHolder1" runat="server">
        <h1>Привет студентам из "страницы содержимого1"!</h1>
    </asp:Content>
  • Исполните страницу
  • Поместите на шаблон еще один контейнер шаблона ContentPlaceHolder и наполните его таким кодом
    (рис ) Дескрипторный код шаблона 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">
                <h1>Привет студентам из "именованной области1" мастер-страницы!</h1>
            </asp:ContentPlaceHolder>
            <br />
            <asp:ContentPlaceHolder ID="ContentPlaceHolder2" runat="server">
                <h1>Привет студентам из "именованной области2" мастер-страницы!</h1>
            </asp:ContentPlaceHolder>
        </div>
        </form>
    </body>
    </html>
  • Перейдите на страницу содержимого
  • Мы видим, что мастер-страница задает схему размещения на итоговой странице. Причем, если на итоговой странице есть контейнер с содержимым, привязанный к именованной области шаблона, то на место именованной области вставляется его содержимое. Если контейнер с содержимым есть, но пустой, то ничего не вставляется. Но если контейнера с содержимым вообще нет, то на итоговой странице появляется содержимое именованной области.

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

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

    Обратите внимание на заголовки именованных областей и их фон. Там, где подставлено содержимое прикрепленного контейнера, фон светлее и в скобках надпись ( Custom ) - заказной. Там, где проявилось содержимое мастер-страницы, фон темнее и надпись ( Master ).

    Давайте добавим контейнер содержимого к нижней именованной области и наполним его.

  • На странице FirstMasterPage.aspx в режиме Design вызовите контекстное меню для нижней именованной области и выполните команду Create Custom Content
  • Оболочка добавит пустой контейнер содержимого и автоматически свяжет его в дескрипторном коде с именованной областью. Отсюда еще одна мораль:

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

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

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

    Компоновка содержимого мастер-страницы

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

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

  • В панели Solution Explorer вызовите контекстное меню для папки 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, который находится в прилагаемом к данной работе каталоге Pictures, и скопируйте его в папку MasterPages текущего сайта
  • Выделите экземпляр ImageBanner и присоедините к нему через свойство ImageUrl рисунок Banner.png из папки MasterPages Web-дерева сайта. Наш баннер слишком великоват, но для локального компьютера сойдет и такой
  • Разместите после баннера заголовочный дескриптор с внутренним стилем
  • <h1 style="color: Green">
        Мы приветствуем Вас на нашем сайте!!!
    </h1>

    Создание таблицы с шаблонами

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

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

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

  • Поместите на мастер-страницу FirstMasterPage.aspx после заголовочного дескриптора из вкладки HTML панели Toolbox элемент управления Table
  • Используя боковые маркеры таблицы и команды меню Layout оболочки, постройте таблицу из трех строк и двух столбцов, чтобы она соответстворвала приведенному выше рисунку
  • Рекламация Большому Биллу: Мы конечно осознаем все революционные возможности Visual Studio 2005 по разработке программ. Но нас-то тоже нужно понять, мы привыкли к развитым HTML-редакторам (ворованным), а в оболочке возможностей редактирования таблиц маловато (хотя она тоже украдена, но мы же учимся).

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

    <table border="0" width="100%">
        <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, к которому мы присоединим рисунок, задающий фон ячейки. ID=Panel1 копии этого элемента изменять не будем, поскольку не планируем в дальнейшем им программно управлять
  • Задайте для созданного экземпляра элемента управления Panel значения свойств: Width=100%; Height=100%, чтобы элемент полностью занял верхнюю ячейку таблицы
  • Через проводник Windows найдите в прилагаемом к данной работе каталоге Pictures файл BackTitleSrc.jpg и в любом графическом редакторе вырежьте из него область с хорошо стыкуемыми при повторении рисунка границами, или возьмите мною вырезанный готовый рисунок BackTitle.jpg
  • Рисунок не должен быть слишком большим, чтобы "не застрял в проводах" по пути к пользователю.

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

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

    <td style="width: 25%; background-color: #9DFF7E" valign="top">
        <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>
    </td>

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

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

  • Отступите ниже таблицы одну-две строчки и добавьте из вкладки HTML панели Toolbox элемент управления Image. Щелкните на нем правой кнопкой мыши и выполните команду Run As Server Control, чтобы содержимое элемента проявилось в будущем на странице содержимого
  • В панели Solution Explorer вызовите контекстное меню для папки MasterPages и скопируйте в проект командой Add Existing Item из прилагаемого каталога Pictures рисунок 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 2007
    </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.

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

    Мы для нашего примера создадим 4 страницы содержимого с простыми именами:

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

    Первый способ

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

    Второй способ

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

    MasterPageFile="~/MasterPages/Title.master" - (как-бы абсолютный адрес в пределах сайта)

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

    Третий способ

  • Откройте мастер-страницу на редактирование и щелкните на ней в любом месте правой кнопкой мыши. Появится контекстное меню с уже известной нам командой Add Content Page
  • Обратите внимание, что двумя последними способами страница создается без отдельного файла поддержки C#. Это легко исправить, добавив к проекту файл .cs с предшествующим именем страницы. Правда, оболочка при этом спросит, создавать ли для этого класса отдельную папку со стандартным именем App_Code, на что нужно ответить Нет. И еще, при таком способе добавления к странице файла поддержки нужно вручную прописать необходимые атрибуты в декларации @Page

    AutoEventWireup="true" CodeFile="XXX.aspx.cs" Inherits="YYY"

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

  • Щелкните правой кнопкой мыши на открытой мастер-странице ~/MasterPages/Title.master в любом месте и командой Add Content Page создайте страницу содержимого Default.aspx
  • Переименуйте страницу Default.aspx в Satellite.aspx и переместите ее в корневую папку Web-дерева
  • Подобным же образом создайте и переместите в корень остальные страницы содержимого: Satellite1.aspx, Satellite2.aspx, Satellite3.aspx. Но можно просто раскопировать в корне в трех экземплярах файл Satellite.aspx, учитывая, что все страницы будут ссылаться на одну и туже мастер-страницу
  • Теперь давайте корректировать заготовки страниц содержимого. Но прежде всего нужно доделать нашу мастер-страницу, определив ссылки в левой ячейке таблицы на только что созданные страницы содержимого.

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

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

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

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

    Мастер-страница Title.master

    Обратите внимание, что все контейнеры ContentPlaceHolder должны находиться внутри дескриптора <form></form>!
    <%@ 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">
       <asp:Image ID="ImageBanner" runat="server" ImageUrl="~/MasterPages/Banner.png" />
       <h1 style="color: Green">
        Мы приветствуем Вас на нашем сайте!!!
       </h1>
       <br />
       <table border="0" width="100%">
        <tr>
         <td colspan="2">
          <asp:Panel ID="Panel1" runat="server" Height="100%" Width="100%" BackImageUrl="~/MasterPages/BackTitle.jpg">
           <asp:ContentPlaceHolder ID="ContentPlaceHolder1" runat="server">
            <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" valign="top">
          <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>
         </td>
         <td style="background-color: #FFB8FF">
          <asp:ContentPlaceHolder ID="ContentPlaceHolder2" runat="server">
           Вот уже более 12 лет мы занимаемся подготовкой водителей категории "B" в г.Красноярске
           и всегда занимали первые места в сводках МРЭО ГИБДД по качеству обучения. Автошкола
           всегда нацелена на успех наших клиентов в сдаче экзаменов ГАИ с первого раза и своевременном
           получении водительского удостоверения.
           <br />
           <br />
           Мы обеспечиваем максимальные удобства своим клиентам. Для этого существуют вечерняя,
           воскресная и заочная формы обучения. Наши курсанты имеют возможность бесплатно и
           неограниченно пользоваться компьютерным классом. Учебные автомобили прибывают по
           заявке клиента к назначенному им месту. Развивается сеть филиалов автошколы в г.Красноярске.
          </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" />
        ampnbsp;Copyright © 2007</p>
       <asp:ContentPlaceHolder ID="ContentPlaceHolder4" runat="server">
        <strong><em>Вы уж, пожалуйста, наведывайтесь к нам почаще!</em></strong>
       </asp:ContentPlaceHolder>
      </div>
     </form>
    </body>
    </html>

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

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

    <%@ 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>

    Программное управление мастер-страницей из страницы содержимого

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

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

    Управление заголовком

    Мастер-страница имеет заголовок <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> мастер-страницы. Текущий код страницы такой

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

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

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

    Оболочка автоматически создаст блок скрипта и вставит в него заготовку обработчика события Page.Load.

  • Заполните обработчик так
    (рис ) Файл SatelliteExt.aspx после добавления обработчика 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 и будем обращаться к нему напрямую
  • Первый способ

  • Двойным щелчком на свободном месте мастер-страницы в режиме Design (или вручную) создайте блок скриптов, который заполните так
    (рис ) Добавление свойства доступа к дескриптору <h1> в файле Title.master<%@ 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 следующим кодом
    (рис ) Дополненный код страницы содержимого 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 и убедитесь, что дескриптор
  • Директива @ MasterType указывает ASP.NET виртуальный путь к соответствующему файлу мастер-страницы.

    Второй способ

  • Измените страницу содержимого SatelliteExt.aspx следующим образом
    (рис ) Измененный код страницы содержимого 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 и убедитесь, что дескриптор
  • Второй способ более предпочтительный, поскольку мы не вмешиваемся в код спроектированной мастер-страницы, которая может быть подключена еще к чему-нибудь!

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

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

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

  • Страница содержимого, динамически меняющая мастер-страницу, не должна иметь ссылок на несуществующие в новой мастер-странице контейнеры ContentPlaceHolderID
  • Замена мастер-страницы должна выполняться в обработчике события Page.PreInit страницы содержимого. Если это сделать в обработчиках более поздних событий, то будет сгенерировано исключение
  • Все страницы содержимого, на которые имеются ссылки в текущей странице содержимого, должны иметь код подключения выбранной мастер-страницы
  • Для межстраничной передачи информации о выбранной мастер-странице ее нужно где-то сохранять. Как и в случае с темами, это могут быть скрытые поля состояния вида ( VIEWSTATE ), глобальные объекты уровня сеанса или приложения
  • Учитывая, что наши страницы Satellite.aspx, Satellite1.aspx, Satellite2.aspx, Satellite3.aspx согласованы с Title.master, сделаем их копии и настроим под динамическую смену мастер-страниц.

  • Откройте мастер-страницу Title.master и добавьте выше объекта ImageBanner (после открывающего дескриптора <div> ) элемент ContentPlaceHolder с именем ContentPlaceHolderList. Сохраните и закройте файл Title.master
  • Сделайте копии соответствующих файлов и присвойте им имена Title1.master, SatelliteDynamic.aspx, SatelliteDynamic1.aspx, SatelliteDynamic2.aspx, SatelliteDynamic3.aspx
  • Определите страницу SatelliteDynamic.aspx стартовой
  • Откройте мастер-страницу Title1.master и выполните в ней следующее:
  • Полностью удалите контейнер скриптов <script runat="server">...</script>
  • Удалите объект ImageBanner и внесите еще какие-нибудь визуально отличимые изменения
  • Добавьте к гиперссылкам HyperLink еще одну с названием "Автошкола в целом"
  • Заполните свойство NavigateUrl гиперссылок значениями новых страниц содержимого
  • Откройте страницу содержимого SatelliteDynamic.aspx и в режиме Design создайте контейнер содержимого для шаблона ContentPlaceHolderList, выполнив на нем команду Create Custom Content контекстного меню
  • Добавьте в контейнер содержимого предваряющий текст "Выберите мастер-страницу:" и элемент DropDownList с именем lstMasterPages
  • Заполните раскрывающийся список Text Value Items[0] Мастер-страница с баннером ~/MasterPages/Title.master Items[1] Мастер-страница без баннера ~/MasterPages/Title1.master
  • Установите для списка lstMasterPages свойство AutoPostBack="True"
  • В режиме Design двойным щелчком на тускло просвечивающемся заполнителе мастер-страницы создайте контейнер для скриптов, обработчик Page_Load() и переименуйте его в Page_Init()
  • Скопируйте заготовку обработчика Page_Init() и переименуйте копию в Page_PreInit(), поскольку все они имеют одинаковую сигнатуру
  • В режиме Design двойным щелчком на объекте lstMasterPages списка создайте обработчик изменения выбора опции lstMasterPages_SelectedIndexChanged()
  • Заполните обработчики так, чтобы окончательный код страницы был таким
    (рис ) Окончательный код страницы SatelliteDynamic.aspx<%@ Page Language="C#" MasterPageFile="~/MasterPages/Title.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 и добавьте в каждый из них после директивы @Page такой блок скриптов
    (рис ) Скрипты остальных страниц содержимого для загрузки выбранной мастер-страницы<script runat="server">
    	
        protected void Page_PreInit(object sender, EventArgs e)
        {
            if (Session["Master"] != null)
            {
                // Был ранее сохраненный выбор, включить его
                Page.MasterPageFile = (string)Session["Master"];
            }
        }
    </script>
  • Исполните начальную страницу SatelliteDynamic.aspx и убедитесь, что мы дали пользователю полнофункциональный механизм выбора мастер-страниц
  • Многослойное подключение мастер-страниц

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

    Но мы, все-таки, рассмотрим эту возможность на примере мастер-страниц двух уровней. Мы создадим две мастер-страницы с последовательным подключением нижнего уровня 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>
  • Теперь создайте мастер-страницу верхнего уровня с именем MasterPageLevel0.master и подключите к ней предыдущую через атрибут MasterPageFile директивы @Master
  • Код страницы должен выглядеть так

    <%@ Master Language="C#" MasterPageFile="~/MasterPageLevel1.master" %>
        
    <asp:Content ID="ContentLevel0" ContentPlaceHolderID="ContentPlaceHolderLevel1" runat="server">
        <table width="100%" 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>
  • Выполните для страницы MasterPageLevel0.master команду Website/Add Content Page, чтобы создать для нее страницу содержимого
  • Измените имя по умолчанию Default.aspx созданной страницы содержимого на TestMasterPageLevel.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 и поместить в нее мастер-страницу и согласованные страницы содержимого

    Наши перспективы

    Ну вот и приплыли, граждане студенты. До сих пор я писал изложение, а вы его читали. Теперь вы будете писать сочинение, а я его буду смотреть в аттестационную и зачетную недели! Отсюда вам мой наказ (читай "Указ!"):

  • На занятия можете не ходить - это ваше личное дело, но знать данный материал вы обязаны (если я ошибаюсь - спросите у декана). Это уже вопрос государственной безопасности! (чтобы перед Большим Биллом не краснеть)
  • Сочинение будете писать на вольную тему в виде взаимосвязанных активных страниц на ASP.NET 2.0
  • Сегодня (беру слепок сверху) 19.03.2009 11:51 и материала для вас уже изложено достаточно, чтобы приступить к работе
  • Ни один не получит зачет (я не боюсь директор магазин, товаровед, завсклад), пока не представит приличный сайт на вольную тему (мне еще темы сочинять не хватало!)
  • Просто статические страницы не пойдут, должен быть код с приличной функциональностью
  • Дизайн и графика большого значения не имеет, хотя настраивать стиль нужно
  • Все, что я знаю и умею по этому средству, должны знать и уметь вы. А то какое-то неравенство и стыд получается!
  • Пора и власть употребить
  • Как говорил наш шеф, куй железо не отходя от кассы
  • Как гласит народная мудрость, время - деньги; не расчитывай на деньги, если нет времени!
  • Вернуться к учебному плану