Программное указание эталонной страницы (C#)Specifying the Master Page Programmatically (C#)

по Скотт Митчеллby Scott Mitchell

Скачать код или скачать PDFDownload Code or Download PDF

Просматривает настройку главной страницы страницы содержимого программным путем с помощью обработчика событий инициализации.Looks at setting the content page's master page programmatically via the PreInit event handler.

ВведениеIntroduction

Так как пример инаугурационный в создании макета на уровне сайта с использованием главных страниц, все страницы содержимого ссылаются на главную страницу декларативно с помощью атрибута MasterPageFile в директиве @Page.Since the inaugural example in Creating a Site-Wide Layout Using Master Pages, all content pages have referenced their master page declaratively via the MasterPageFile attribute in the @Page directive. Например, следующая директива @Page связывает страницу содержимого с главной страницей Site.master:For example, the following @Page directive links the content page to the master page Site.master:

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

КлассPage в пространстве имен System.Web.UI содержит свойствоMasterPageFile , которое возвращает путь к главной странице страницы содержимого. Это свойство задается директивой @Page.The Page class in the System.Web.UI namespace includes a MasterPageFile property that returns the path to the content page's master page; it is this property that is set by the @Page directive. Это свойство также можно использовать для программного указания главной страницы страницы содержимого.This property can also be used to programmatically specify the content page's master page. Этот подход удобен, если необходимо динамически назначить главную страницу на основе внешних факторов, например, если пользователь посещает страницу.This approach is useful if you want to dynamically assign the master page based on external factors, such as the user visiting the page.

В этом учебнике мы добавим на наш веб-сайт вторую главную страницу и динамически решили, какую главную страницу использовать во время выполнения.In this tutorial we add a second master page to our website and dynamically decide which master page to use at runtime.

Шаг 1. Обзор жизненного цикла страницыStep 1: A Look at the Page Lifecycle

Каждый раз при поступлении запроса на веб-сервер для страницы ASP.NET, которая является страницей содержимого, подсистема ASP.NET должна запредохранительить элементы управления содержимым страницы на соответствующие элементы управления ContentPlaceHolder главной страницы.Whenever a request arrives at the web server for an ASP.NET page that is a content page, the ASP.NET engine must fuse the page's Content controls into the master page's corresponding ContentPlaceHolder controls. Это слияние создает одну иерархию элементов управления, которая затем может выполнять стандартный жизненный цикл страницы.This fusion creates a single control hierarchy that can then proceed through the typical page lifecycle.

На рис. 1 показана эта Fusion.Figure 1 illustrates this fusion. На шаге 1 на рис. 1 показано начальное содержимое и иерархии элементов управления главной страницы.Step 1 in Figure 1 shows the initial content and master page control hierarchies. В конце этапа предварительной инициализации элементы управления содержимым на странице добавляются к соответствующему элементов управления ContentPlaceHolder на главной странице (шаг 2).At the tail end of the PreInit stage the Content controls in the page are added to the corresponding ContentPlaceHolders in the master page (Step 2). После этого Fusion Главная страница выступает в качестве корня иерархии элемента управления "плавкий предохранитель".After this fusion, the master page serves as the root of the fused control hierarchy. Затем эта иерархия управления плавким предохранителем добавляется на страницу для создания окончательной иерархии элементов управления (шаг 3).This fused control hierarchy is then added to the page to produce the finalized control hierarchy (Step 3). В итоге иерархия элементов управления страницы включает иерархию элементов управления "плавкий предохранитель".The net result is that the page's control hierarchy includes the fused control hierarchy.

иерархии элементов управления главной страницы и страницы содержимого на этапе предварительной инициализации подключаются друг к другуThe Master Page and Content Page's Control Hierarchies are Fused Together during the PreInit Stage

Рис. 01. Иерархия элементов управления главной страницы и страницы содержимого на этапе предварительной инициализации переключается (щелкните, чтобы просмотреть изображение с полным размером)Figure 01: The Master Page and Content Page's Control Hierarchies are Fused Together during the PreInit Stage (Click to view full-size image)

Шаг 2. Установка свойстваMasterPageFileиз кодаStep 2: Setting theMasterPageFileProperty from Code

Какая Главная страница, партакес в этом Fusion, зависит от значения свойства MasterPageFile объекта Page.What master page partakes in this fusion depends on the value of the Page object's MasterPageFile property. Установка атрибута MasterPageFile в директиве @Page имеет чистый результат назначения свойства MasterPageFile Pageна этапе инициализации, что является самым первым этапом жизненного цикла страницы.Setting the MasterPageFile attribute in the @Page directive has the net effect of assigning the Page's MasterPageFile property during the Initialization stage, which is the very first stage of the page's lifecycle. Можно также задать это свойство программным способом.We can alternatively set this property programmatically. Однако крайне важно, чтобы это свойство было задано до того, как Fusion будет показано на рис. 1.However, it is imperative that this property be set before the fusion in Figure 1 takes place.

В начале этапа предварительной инициализации объект Page вызывает событиеPreInit и вызывает его методOnPreInit.At the start of the PreInit stage the Page object raises its PreInit event and calls its OnPreInit method. Чтобы задать главную страницу программно, можно либо создать обработчик событий для PreInit события, либо переопределить метод OnPreInit.To set the master page programmatically, then, we can either create an event handler for the PreInit event or override the OnPreInit method. Давайте рассмотрим оба подхода.Let's look at both approaches.

Начните с открытия Default.aspx.cs, файла класса программной части для домашней страницы сайта.Start by opening Default.aspx.cs, the code-behind class file for our site's homepage. Добавьте обработчик событий для события PreInit страницы, введя следующий код:Add an event handler for the page's PreInit event by typing in the following code:

protected void Page_PreInit(object sender, EventArgs e) 
{ 
}

Здесь можно задать свойство MasterPageFile.From here we can set the MasterPageFile property. Обновите код таким образом, чтобы он назначил значение "~/Сите.Мастер" свойству MasterPageFile.Update the code so that it assigns the value "~/Site.master" to the MasterPageFile property.

protected void Page_PreInit(object sender, EventArgs e) 
{
    this.MasterPageFile = "~/Site.master"; 
}

Если задать точку останова и начать отладку, вы увидите, что при посещении страницы Default.aspx или при выполнении обратной передачи на эту страницу выполняется обработчик событий Page_PreInit, а свойство MasterPageFile присваивается «~/Сите.мастер».If you set a breakpoint and start with debugging you'll see that whenever the Default.aspx page is visited or whenever there's a postback to this page, the Page_PreInit event handler executes and the MasterPageFile property is assigned to "~/Site.master".

Кроме того, можно переопределить метод OnPreInit класса Page и установить в нем свойство MasterPageFile.Alternatively, you can override the Page class's OnPreInit method and set the MasterPageFile property there. В этом примере не следует задавать главную страницу на конкретной странице, а не BasePage.For this example, let's not set the master page in a particular page, but rather from BasePage. Вспомним, что мы создали пользовательский класс базовой страницы (BasePage) в разделе Указание заголовка, Meta-тегов и других заголовков HTML в руководстве по главной странице .Recall that we created a custom base page class (BasePage) back in the Specifying the Title, Meta Tags, and Other HTML Headers in the Master Page tutorial. В настоящее время BasePage переопределяет метод OnLoadComplete класса Page, где он задает свойство Title страницы на основе данных схемы узла.Currently BasePage overrides the Page class's OnLoadComplete method, where it sets the page's Title property based on the site map data. Теперь обновите BasePage, чтобы переопределить метод OnPreInit для программного указания главной страницы.Let's update BasePage to also override the OnPreInit method to programmatically specify the master page.

protected override void OnPreInit(EventArgs e) 
{ 
    this.MasterPageFile = "~/Site.master"; 
    base.OnPreInit(e); 
}

Так как все страницы содержимого являются производными от BasePage, все они теперь имеют программно назначенную главную страницу.Because all our content pages derive from BasePage, all of them now have their master page programmatically assigned. На этом этапе обработчик событий PreInit в Default.aspx.cs является избыточным; Вы можете удалить его.At this point the PreInit event handler in Default.aspx.cs is superfluous; feel free to remove it.

Что насчет директивы@Page?What About the@PageDirective?

Что может показаться немного запутанным, так как свойства страниц содержимого MasterPageFile теперь указываются в двух местах: программно в методе OnPreInit класса BasePage, а также с помощью атрибута MasterPageFile в директиве @Page каждой страницы содержимого.What may be a little confusing is that the content pages' MasterPageFile properties are now being specified in two places: programmatically in the BasePage class's OnPreInit method as well as through the MasterPageFile attribute in each content page's @Page directive.

Первым этапом жизненного цикла страницы является стадия инициализации.The first stage in the page lifecycle is the Initialization stage. На этом этапе свойству MasterPageFile объекта Page присваивается значение атрибута MasterPageFile в директиве @Page (если оно указано).During this stage the Page object's MasterPageFile property is assigned the value of the MasterPageFile attribute in the @Page directive (if it is provided). Этап предварительной инициализации соответствует этапу начальной настройки, и здесь мы программно устанавливаем свойство MasterPageFile объекта Page, тем самым переписывая значение, присвоенное из директивы @Page.The PreInit stage follows the Initialization stage, and it is here where we programmatically set the Page object's MasterPageFile property, thereby overwriting the value assigned from the @Page directive. Так как мы настраиваете свойство MasterPageFile объекта Page программным способом, можно удалить атрибут MasterPageFile из директивы @Page, не влияя на взаимодействие с конечным пользователем.Because we are setting the Page object's MasterPageFile property programmatically, we could remove the MasterPageFile attribute from the @Page directive without affecting the end user's experience. Чтобы убедить этого, удалите атрибут MasterPageFile из директивы @Page в Default.aspx, а затем перейдите на страницу в браузере.To convince yourself of this, go ahead and remove the MasterPageFile attribute from the @Page directive in Default.aspx and then visit the page through a browser. Как и ожидалось, выходные данные будут такими же, как и до удаления атрибута.As you would expect, the output is the same as before the attribute was removed.

Задано ли свойство MasterPageFile с помощью директивы @Page или программным способом, не применяется к опыту конечного пользователя.Whether the MasterPageFile property is set via the @Page directive or programmatically is inconsequential to the end user's experience. Однако во время разработки для создания представления WYSIWYG в конструкторе Visual Studio использует атрибут MasterPageFile в директиве @Page.However, the MasterPageFile attribute in the @Page directive is used by Visual Studio during design-time to produce the WYSIWYG view in the Designer. Если вы вернетесь в Default.aspx в Visual Studio и перейдите к конструктору, вы увидите сообщение "ошибка главной страницы: страница содержит элементы управления, для которых требуется ссылка на главную страницу, но ничего не указано" (см. рис. 2).If you return to Default.aspx in Visual Studio and navigate to the Designer you'll see the message, "Master Page error: The page has controls that require a Master Page reference, but none is specified" (see Figure 2).

Вкратце, необходимо оставить атрибут MasterPageFile в директиве @Page, чтобы работать с богатыми возможностями во время разработки в Visual Studio.In short, you need to leave the MasterPageFile attribute in the @Page directive to enjoy a rich design-time experience in Visual Studio.

Visual Studio использует атрибут MasterPageFile директивы @Page для визуализации представления конструктора.Visual Studio Uses the @Page Directive's MasterPageFile Attribute to Render the Design View

Рис. 02. Visual Studio использует атрибут MasterPageFile директивы @Page для отрисовки представления конструктора (щелкните, чтобы просмотреть изображение с полным размером)Figure 02: Visual Studio Uses the @Page Directive's MasterPageFile Attribute to Render the Design View (Click to view full-size image)

Шаг 3. создание альтернативной главной страницыStep 3: Creating an Alternative Master Page

Так как эталонная страница страницы содержимого может быть задана программно во время выполнения, можно динамически загружать определенную главную страницу на основе некоторых внешних критериев.Because a content page's master page can be set programmatically at runtime it's possible to dynamically load a particular master page based on some external criteria. Эта функция может оказаться полезной в ситуациях, когда макет сайта должен меняться в зависимости от пользователя.This functionality can be useful in situations where the site's layout needs to vary based on the user. Например, веб-приложение подсистемы блогов может позволить пользователям выбрать макет для своего блога, где каждый макет связан с другой главной страницей.For instance, a blog engine web application may allow its users to choose a layout for their blog, where each layout is associated with a different master page. Во время выполнения, когда посетитель просматривает блог пользователя, веб-приложению потребуется определить макет блога и динамически связать соответствующую главную страницу со страницей содержимого.At runtime, when a visitor is viewing a user's blog, the web application would need to determine the blog's layout and dynamically associate the corresponding master page with the content page.

Давайте рассмотрим, как динамически загружать главную страницу во время выполнения на основе некоторых внешних критериев.Let's examine how to dynamically load a master page at runtime based on some external criteria. Сейчас наш веб-сайт содержит только одну главную страницу (Site.master).Our website currently contains just one master page (Site.master). Для иллюстрации выбора главной страницы во время выполнения требуется другая эталонная страница.We need another master page to illustrate choosing a master page at runtime. Этот шаг посвящен созданию и настройке новой главной страницы.This step focuses on creating and configuring the new master page. На шаге 4 рассматривается определение главной страницы для использования во время выполнения.Step 4 looks at determining what master page to use at runtime.

Создайте новую главную страницу в корневой папке с именем Alternate.master.Create a new master page in the root folder named Alternate.master. Также добавьте новую таблицу стилей на веб-сайт с именем AlternateStyles.css.Also add a new style sheet to the website named AlternateStyles.css.

добавить на веб-сайт еще одну главную страницу и файл CSSAdd Another Master Page and CSS File to the Website

Рис. 03. Добавление на веб-сайт еще одной главной страницы и CSS-файла (щелкните, чтобы просмотреть изображение с полным размером)Figure 03: Add Another Master Page and CSS File to the Website (Click to view full-size image)

Я разработал главную страницу Alternate.master, чтобы заголовок отображался в верхней части страницы, по центру и ВМФ фону.I've designed the Alternate.master master page to have the title displayed at the top of the page, centered and on a navy background. Я рассмотрел левый столбец и переместил это содержимое под элементом управления MainContent ContentPlaceHolder, который теперь занимает всю ширину страницы.I've dispensed of the left column and moved that content beneath the MainContent ContentPlaceHolder control, which now spans the entire width of the page. Кроме того, я никсед список неупорядоченных занятий и заменил его горизонтальным списком, приведенным выше MainContent.Furthermore, I nixed the unordered Lessons list and replaced it with a horizontal list above MainContent. Я также обновил шрифты и цвета, используемые главной страницей (и, по расширению, страницам содержимого).I also updated the fonts and colors used by the master page (and, by extension, its content pages). На рис. 4 показан Default.aspx при использовании главной страницы Alternate.master.Figure 4 shows Default.aspx when using the Alternate.master master page.

Note

ASP.NET включает возможность определения тем.ASP.NET includes the ability to define Themes. Тема — это коллекция изображений, файлов CSS и параметров свойств веб-элементов управления, связанных со стилями, которые могут быть применены к странице во время выполнения.A Theme is a collection of images, CSS files, and style-related Web control property settings that can be applied to a page at runtime. Темы можно выполнять, если макеты веб-узла отличаются только в отображаемых изображениях и их правилах CSS.Themes are the way to go if your site's layouts differ only in the images displayed and by their CSS rules. Если макеты отличаются более значительно, например, при использовании различных веб-элементов управления или при наличии совершенно отличающегося макета, необходимо использовать отдельные главные страницы.If the layouts differ more substantially, such as using different Web controls or having a radically different layout, then you will need to use separate master pages. Дополнительные сведения о темах см. в разделе, посвященном более подробному ознакомлению в конце этого руководства.Consult the Further Reading section at the end of this tutorial for more information on Themes.

наши страницы содержимого теперь могут использовать новый внешний вид и поведениеOur Content Pages Can Now Use a New Look and Feel

Рис. 04. Теперь страницы содержимого могут использовать новый внешний вид и поведение (щелкните, чтобы просмотреть изображение с полным размером)Figure 04: Our Content Pages Can Now Use a New Look and Feel (Click to view full-size image)

Когда разметка для главной страницы и страницы содержимого имеет плавкий предохранитель, класс MasterPage проверяет, что каждый элемент управления содержимым на странице содержимого ссылается на ContentPlaceHolder на главной странице.When the master and content pages' markup are fused, the MasterPage class checks to ensure that every Content control in the content page references a ContentPlaceHolder in the master page. Исключение возникает при обнаружении элемента управления содержимым, ссылающегося на несуществующий элемент ContentPlaceHolder.An exception is thrown if a Content control that references a non-existent ContentPlaceHolder is found. Иными словами, крайне важно, чтобы Главная страница, назначенная странице содержимого, соимела ContentPlaceHolder для каждого элемента управления содержимым на странице содержимого.In other words, it is imperative that the master page being assigned to the content page have a ContentPlaceHolder for each Content control in the content page.

Главная страница Site.master содержит четыре элемента управления ContentPlaceHolder:The Site.master master page includes four ContentPlaceHolder controls:

  • head
  • MainContent
  • QuickLoginUI
  • LeftColumnContent

Некоторые страницы содержимого на веб-сайте включают только один или два элемента управления содержимым. Другие включают элемент управления содержимым для каждого из доступных элементов управления ContentPlaceHolder.Some of the content pages in our website include just one or two Content controls; others include a Content control for each of the available ContentPlaceHolders. Если новая Главная страница (Alternate.master) может быть назначена этим страницам содержимого, имеющим элементы управления содержимым для всех элементов управления ContentPlaceHolder в Site.master, то важно, чтобы Alternate.master также включал те же элементы управления ContentPlaceHolder, что и Site.master.If our new master page (Alternate.master) may ever be assigned to those content pages that have Content controls for all of the ContentPlaceHolders in Site.master then it is essential that Alternate.master also include the same ContentPlaceHolder controls as Site.master.

Чтобы страница Alternate.master была похожа на «мой» (см. рис. 4), сначала определите стили главной страницы в таблице стилей AlternateStyles.css.To get your Alternate.master master page to look similar to mine (see Figure 4), start by defining the master page's styles in the AlternateStyles.css style sheet. Добавьте следующие правила в AlternateStyles.css:Add the following rules into AlternateStyles.css:

body 
{ 
 font-family: Comic Sans MS, Arial; 
 font-size: medium; 
 margin: 0px; 
} 
#topContent 
{ 
 text-align: center; 
 background-color: Navy; 
 color: White; 
 font-size: x-large;
 text-decoration: none; 
 font-weight: bold; 
 padding: 10px; 
 height: 50px;
} 
#topContent a 
{ 
 text-decoration: none; 
 color: White; 
} 
#navContent 
{ 
 font-size: small; 
 text-align: center; 
} 
#footerContent 
{ 
 padding: 10px; 
 font-size: 90%; 
 text-align: center; 
 border-top: solid 1px black; 
} 
#mainContent 
{ 
 text-align: left; 
 padding: 10px; 
}

Затем добавьте следующую декларативную разметку в Alternate.master.Next, add the following declarative markup to Alternate.master. Как видите, Alternate.master содержит четыре элемента управления ContentPlaceHolder с теми же ID значениями, что и элементы управления ContentPlaceHolder в Site.master.As you can see, Alternate.master contains four ContentPlaceHolder controls with the same ID values as the ContentPlaceHolder controls in Site.master. Более того, он включает элемент управления ScriptManager, который необходим для страниц на нашем веб-сайте, использующих платформу ASP.NET AJAX.Moreover, it includes a ScriptManager control, which is necessary for those pages in our website that use the ASP.NET AJAX framework.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml"> 
<head id="Head1" runat="server"> 
 <title>Untitled Page</title>
 <asp:ContentPlaceHolder id="head" runat="server">
 </asp:ContentPlaceHolder> 
 <link href="AlternateStyles.css" rel="stylesheet" type="text/css" /> 
</head> 
<body> 
 <form id="form1" runat="server"> 
 <asp:ScriptManager ID="MyManager" runat="server"> 
 </asp:ScriptManager>
 <div id="topContent">
 <asp:HyperLink ID="lnkHome" runat="server" NavigateUrl="~/Default.aspx" 
 Text="Master Pages Tutorials" /> 
 </div>
 <div id="navContent">
 <asp:ListView ID="LessonsList" runat="server" 
 DataSourceID="LessonsDataSource">
 <LayoutTemplate>
 <asp:PlaceHolder runat="server" ID="itemPlaceholder" /> 
 </LayoutTemplate>
 <ItemTemplate>
 <asp:HyperLink runat="server" ID="lnkLesson" 
 NavigateUrl='<%# Eval("Url") %>' 
 Text='<%# Eval("Title") %>' /> 
 </ItemTemplate>
 <ItemSeparatorTemplate> | </ItemSeparatorTemplate> 
 </asp:ListView>
 <asp:SiteMapDataSource ID="LessonsDataSource" runat="server" 
 ShowStartingNode="false" /> 
 </div>
 <div id="mainContent">
 <asp:ContentPlaceHolder id="MainContent" runat="server"> 
 </asp:ContentPlaceHolder>
 </div> 
 <div id="footerContent">
 <p> 
 <asp:Label ID="DateDisplay" runat="server"></asp:Label> 
 </p>
 <asp:ContentPlaceHolder ID="QuickLoginUI" runat="server"> 
 </asp:ContentPlaceHolder>
 <asp:ContentPlaceHolder ID="LeftColumnContent" runat="server"> 
 </asp:ContentPlaceHolder>
 </div> 
 </form>
</body> 
</html>

Тестирование новой главной страницыTesting the New Master Page

Чтобы протестировать эту новую главную страницу, обновите метод OnPreInit класса BasePage так, чтобы свойству MasterPageFile было присвоено значение "~/Алтернате.Мастер", а затем посетите веб-сайт.To test this new master page update the BasePage class's OnPreInit method so that the MasterPageFile property is assigned the value "~/Alternate.master" and then visit the website. Каждая страница должна работать без ошибок, за исключением двух: ~/Admin/AddProduct.aspx и ~/Admin/Products.aspx.Every page should function without error except for two: ~/Admin/AddProduct.aspx and ~/Admin/Products.aspx. Добавление продукта к элементу DetailsView в ~/Admin/AddProduct.aspx приводит к NullReferenceException из строки кода, которая пытается задать свойство GridMessageText главной страницы.Adding a product to the DetailsView in ~/Admin/AddProduct.aspx results in a NullReferenceException from the line of code that attempts to set the master page's GridMessageText property. При посещении ~/Admin/Products.aspx создается InvalidCastException при загрузке страницы с сообщением: "не удалось привести объект типа" ASP. Alternate_Master "к типу" ASP. site_Master ".When visiting ~/Admin/Products.aspx an InvalidCastException is thrown on page load with the message: "Unable to cast object of type 'ASP.alternate_master' to type 'ASP.site_master'."

Эти ошибки возникают из-за того, что Site.master класс кода программной части включает открытые события, свойства и методы, не определенные в Alternate.master.These errors occur because the Site.master code-behind class includes public events, properties, and methods that are not defined in Alternate.master. В разметке этих двух страниц имеется директива @MasterType, которая ссылается на главную страницу Site.master.The markup portion of these two pages have a @MasterType directive that references the Site.master master page.

<%@ MasterType VirtualPath="~/Site.master" %>

Кроме того, обработчик событий ItemInserted DetailsView в ~/Admin/AddProduct.aspx содержит код, который приводит слабо типизированное свойство Page.Master к объекту типа Site.Also, the DetailsView's ItemInserted event handler in ~/Admin/AddProduct.aspx includes code that casts the loosely-typed Page.Master property to an object of type Site. Директива @MasterType (используется таким образом) и приведение в обработчике событий ItemInserted тесно связывает ~/Admin/AddProduct.aspx и ~/Admin/Products.aspx страниц с главной страницей Site.master.The @MasterType directive (used this way) and the cast in the ItemInserted event handler tightly couples the ~/Admin/AddProduct.aspx and ~/Admin/Products.aspx pages to the Site.master master page.

Чтобы разорвать эту тесную связь, можно использовать Site.master и Alternate.master быть производными от общего базового класса, который содержит определения для открытых членов.To break this tight coupling we can have Site.master and Alternate.master derive from a common base class that contains definitions for the public members. После этого можно обновить директиву @MasterType, чтобы она ссылалась на этот общий базовый тип.Following that, we can update the @MasterType directive to reference this common base type.

Создание пользовательского базового класса главной страницыCreating a Custom Base Master Page Class

Добавьте новый файл класса в папку App_Code с именем BaseMasterPage.cs и получите его от System.Web.UI.MasterPage.Add a new class file to the App_Code folder named BaseMasterPage.cs and have it derive from System.Web.UI.MasterPage. Необходимо определить метод RefreshRecentProductsGrid и свойство GridMessageText в BaseMasterPage, но мы не можем просто переместить их из Site.master поскольку эти члены работают с веб-элементами управления, характерными для главной страницы Site.master (RecentProducts метка GridView и GridMessage).We need to define the RefreshRecentProductsGrid method and the GridMessageText property in BaseMasterPage, but we can't simply move them there from Site.master because these members work with Web controls that are specific to the Site.master master page (the RecentProducts GridView and GridMessage Label).

Необходимо настроить BaseMasterPage таким образом, чтобы эти члены были определены там, но фактически реализуются производными классами BaseMasterPage(Site.master и Alternate.master).What we need to do is configure BaseMasterPage in such a way that these members are defined there, but are actually implemented by BaseMasterPage's derived classes (Site.master and Alternate.master). Этот тип наследования можно реализовать, пометив класс и его члены как abstract.This type of inheritance is possible by marking the class and its members as abstract. Вкратце, Добавление ключевого слова abstract к этим двум членам объявляет, что BaseMasterPage не реализовал RefreshRecentProductsGrid и GridMessageText, но его производные классы будут.In short, adding the abstract keyword to these two members announces that BaseMasterPage hasn't implemented RefreshRecentProductsGrid and GridMessageText, but that its derived classes will.

Также необходимо определить событие PricesDoubled в BaseMasterPage и предоставить средства производными классами для вызова события.We also need to define the PricesDoubled event in BaseMasterPage and provide a means by the derived classes to raise the event. Шаблон, используемый в .NET Framework для упрощения этого поведения, заключается в создании общедоступного события в базовом классе и добавлении защищенного метода virtual с именем OnEventName.The pattern used in the .NET Framework to facilitate this behavior is to create a public event in the base class and add a protected, virtual method named OnEventName. Производные классы могут затем вызвать этот метод, чтобы вызвать событие, или переопределить его для выполнения кода непосредственно до или после вызова события.Derived classes can then call this method to raise the event or can override it to execute code immediately before or after the event is raised.

Обновите класс BaseMasterPage, чтобы он содержал следующий код:Update your BaseMasterPage class so that it contains the following code:

using System; public abstract class BaseMasterPage : System.Web.UI.MasterPage
{ 
    public event EventHandler PricesDoubled; 
    protected virtual void OnPricesDoubled(EventArgs e) 
    { 
        if (PricesDoubled != null) 
        PricesDoubled(this, e); 
    } 
    public abstract void RefreshRecentProductsGrid();
    public abstract string GridMessageText 
    { 
        get; 
        set; 
    } 
}

Затем перейдите к Site.master классу кода программной части и порождаете его из BaseMasterPage.Next, go to the Site.master code-behind class and have it derive from BaseMasterPage. Так как BaseMasterPage является abstract необходимо переопределить эти элементы abstract в Site.master.Because BaseMasterPage is abstract we need to override those abstract members here in Site.master. Добавьте ключевое слово override в определения метода и свойства.Add the override keyword to the method and property definitions. Также обновите код, вызывающий событие PricesDoubled в обработчике событий Click кнопки DoublePrice с вызовом метода OnPricesDoubled базового класса.Also update the code that raises the PricesDoubled event in the DoublePrice Button's Click event handler with a call to the base class's OnPricesDoubled method.

После этих изменений Site.master класс кода программной части должен содержать следующий код:After these modifications the Site.master code-behind class should contain the following code:

public partial class Site : BaseMasterPage { 
    protected void Page_Load(object sender, EventArgs e) 
    { 
        DateDisplay.Text = DateTime.Now.ToString("dddd, MMMM dd"); 
    } 
    public override void RefreshRecentProductsGrid()
    { 
        RecentProducts.DataBind();
    } 
    public override string GridMessageText
    { 
        get 
        {
            return GridMessage.Text;
        } 
        set
        {
            GridMessage.Text = value; 
        } 
    }
    protected void DoublePrice_Click(object sender, EventArgs e) 
    { 
        // Double the prices 
        DoublePricesDataSource.Update();
        // Refresh RecentProducts 
        RecentProducts.DataBind();
        // Raise the PricesDoubled event
        base.OnPricesDoubled(EventArgs.Empty);
    } 
}

Также необходимо обновить класс кода программной части Alternate.master, чтобы он был производным от BaseMasterPage и переопределить два члена abstract.We also need to update Alternate.master's code-behind class to derive from BaseMasterPage and override the two abstract members. Но поскольку Alternate.master не содержит GridView, в котором перечислены самые последние продукты, или метка, которая отображает сообщение после добавления нового продукта в базу данных, этим методам не нужно ничего делать.But because Alternate.master does not contain a GridView that lists the most recent products nor a Label that displays a message after a new product is added to the database, these methods do not need to do anything.

public partial class Alternate : BaseMasterPage 
{ 
    public override void RefreshRecentProductsGrid() 
    { 
        // Do nothing 
    } 
    public override string GridMessageText 
    { 
        get
        { 
            return string.Empty;
        } 
        set
        {
            // Do nothing 
        } 
    }
}

Ссылка на базовый класс главной страницыReferencing the Base Master Page Class

Теперь, когда мы завершили работу с классом BaseMasterPage и создали две главные страницы, наш последний шаг — это обновить ~/Admin/AddProduct.aspx и ~/Admin/Products.aspx страницы, чтобы они ссылались на этот общий тип.Now that we have completed the BaseMasterPage class and have our two master pages extending it, our final step is to update the ~/Admin/AddProduct.aspx and ~/Admin/Products.aspx pages to refer to this common type. Начните с изменения директивы @MasterType на обеих страницах из:Start by changing the @MasterType directive in both pages from:

<%@ MasterType VirtualPath="~/Site.master" %>

на:To:

<%@ MasterType TypeName="BaseMasterPage" %>

Вместо ссылки на путь к файлу свойство @MasterType теперь ссылается на базовый тип (BaseMasterPage).Rather than referencing a file path, the @MasterType property now references the base type (BaseMasterPage). Следовательно, строго типизированное Master свойство, используемое в обеих страницах, теперь имеет тип BaseMasterPage (вместо типа Site).Consequently, the strongly-typed Master property used in both pages' code-behind classes is now of type BaseMasterPage (instead of type Site). После этого изменения вернитесь ~/Admin/Products.aspx.With this change in place revisit ~/Admin/Products.aspx. Ранее это привело к ошибке приведения, так как страница настроена на использование главной страницы Alternate.master, но директива @MasterType ссылалась на файл Site.master.Previously, this resulted in a casting error because the page is configured to use the Alternate.master master page, but the @MasterType directive referenced the Site.master file. Но теперь страница отображается без ошибок.But now the page renders without error. Это обусловлено тем, что Главная страница Alternate.master может быть приведена к объекту типа BaseMasterPage (поскольку он расширяет ее).This is because the Alternate.master master page can be cast to an object of type BaseMasterPage (since it extends it).

В ~/Admin/AddProduct.aspxнеобходимо внести одно небольшое изменение.There's one small change that needs to be made in ~/Admin/AddProduct.aspx. Обработчик событий ItemInserted элемента управления DetailsView использует как строго типизированное свойство Master, так и слабо типизированное свойство Page.Master.The DetailsView control's ItemInserted event handler uses both the strongly-typed Master property and the loosely-typed Page.Master property. Мы исправили строго типизированную ссылку, когда мы обновили директиву @MasterType, но нам по-прежнему нужно обновить слабо типизированную ссылку.We fixed the strongly-typed reference when we updated the @MasterType directive, but we still need to update the loosely-typed reference. Замените следующую строку кода:Replace the following line of code:

Site myMasterPage = Page.Master as Site;

Со следующим параметром, который приводит Page.Master к базовому типу:With the following, which casts Page.Master to the base type:

BaseMasterPage myMasterPage = Page.Master as BaseMasterPage;

Шаг 4. определение главной страницы для привязки к страницам содержимогоStep 4: Determining What Master Page to Bind to the Content Pages

В настоящее время наш класс BasePage задает для всех страниц содержимого свойства MasterPageFile жестко запрограммированным значением на этапе предварительной инициализации жизненного цикла страницы.Our BasePage class currently sets all content pages' MasterPageFile properties to a hard-coded value in the PreInit stage of the page lifecycle. Мы можем обновить этот код для создания главной страницы на основе какого-либо внешнего фактора.We can update this code to base the master page on some external factor. Возможно, эталонная страница загружается в зависимости от настроек пользователя, вошедшего в систему.Perhaps the master page to load depends on the preferences of the currently logged on user. В этом случае нам пришлось бы написать код в методе OnPreInit в BasePage, который ищет параметры главной страницы текущего пользователя.In that case, we'd need to write code in the OnPreInit method in BasePage that looks up the currently visiting user's master page preferences.

Давайте создадим веб-страницу, позволяющую пользователю выбрать, какую эталонную страницу следует использовать — Site.master или Alternate.master-и сохранить этот выбор в переменной сеанса.Let's create a web page that allows the user to choose which master page to use - Site.master or Alternate.master - and save this choice in a Session variable. Начните с создания новой веб-страницы в корневом каталоге с именем ChooseMasterPage.aspx.Start by creating a new web page in the root directory named ChooseMasterPage.aspx. При создании этой страницы (или любой другой страницы содержимого, исходя этого) вам не нужно привязывать ее к главной странице, так как Главная страница задается программно в BasePage.When creating this page (or any other content pages henceforth) you don't need to bind it to a master page because the master page is set programmatically in BasePage. Однако если вы не привязываете новую страницу к главной странице, декларативная разметка по умолчанию для новой страницы содержит веб-форму и другое содержимое, предоставленное главной страницей.However, if you do not bind the new page to a master page then the new page's default declarative markup contains a Web Form and other content supplied by the master page. Вам потребуется вручную заменить эту разметку соответствующими элементами управления содержимым.You'll need to manually replace this markup with the appropriate Content controls. По этой причине я могу легко привязать новую страницу ASP.NET к главной странице.For that reason, I find it easier to bind the new ASP.NET page to a master page.

Note

Поскольку Site.master и Alternate.master имеют одинаковый набор элементов управления ContentPlaceHolder, не имеет значения, какая Главная страница выбрана при создании страницы содержимого.Because Site.master and Alternate.master have the same set of ContentPlaceHolder controls it doesn't matter what master page you choose when creating the new content page. Для обеспечения согласованности я предлагаю использовать Site.master.For consistency, I'd suggest using Site.master.

добавить новую страницу содержимого на веб-сайтAdd a New Content Page to the Website

Рис. 05. Добавление новой страницы содержимого на веб-сайт (щелкните, чтобы просмотреть изображение с полным размером)Figure 05: Add a New Content Page to the Website (Click to view full-size image)

Обновите файл Web.sitemap, включив в него запись для этого занятия.Update the Web.sitemap file to include an entry for this lesson. Добавьте следующую разметку под <siteMapNode> для занятия Master Pages и ASP.NET AJAX:Add the following markup beneath the <siteMapNode> for the Master Pages and ASP.NET AJAX lesson:

<siteMapNode url="~/ChooseMasterPage.aspx" title="Choose a Master Page" />

Прежде чем добавлять содержимое на ChooseMasterPage.aspx страницу, обновите класс кода программной части страницы, чтобы он наследовался от BasePage (а не System.Web.UI.Page).Before adding any content to the ChooseMasterPage.aspx page take a moment to update the page's code-behind class so that it derives from BasePage (rather than System.Web.UI.Page). Затем добавьте на страницу элемент управления DropDownList, задайте для его свойства ID значение MasterPageChoiceи добавьте два элемента ListItem с Text значениями "~/Сите.Мастер" и "~/Алтернате.Мастер".Next, add a DropDownList control to the page, set its ID property to MasterPageChoice, and add two ListItems with the Text values of "~/Site.master" and "~/Alternate.master".

Добавьте на страницу веб-элемент управления Button и задайте для его свойств ID и Text значение SaveLayout и "сохранить вариант макета" соответственно.Add a Button Web control to the page and set its ID and Text properties to SaveLayout and "Save Layout Choice", respectively. На этом этапе декларативная разметка страницы должна выглядеть следующим образом:At this point your page's declarative markup should look similar to the following:

<p> 
 Your layout choice: 
 <asp:DropDownList ID="MasterPageChoice" runat="server"> 
 <asp:ListItem>~/Site.master</asp:ListItem>
 <asp:ListItem>~/Alternate.master</asp:ListItem>
 </asp:DropDownList> 
</p> 
<p> 
 <asp:Button ID="SaveLayout" runat="server" Text="Save Layout Choice" /> 
</p>

При первом посещении страницы необходимо отобразить выбранный в данный момент выбор главной страницы пользователя.When the page is first visited we need to display the user's currently selected master page choice. Создайте обработчик событий Page_Load и добавьте следующий код:Create a Page_Load event handler and add the following code:

protected void Page_Load(object sender, EventArgs e) 
{ 
    if (!Page.IsPostBack) 
    { 
        if (Session["MyMasterPage"] != null)
        {
            ListItem li = MasterPageChoice.Items.FindByText(Session["MyMasterPage"].ToString());
            if (li != null) 
                li.Selected = true; 
        } 
    }
}

Приведенный выше код выполняется только при первом посещении страницы (а не при последующих обратных передачах).The above code executes only on the first page visit (and not on subsequent postbacks). Сначала он проверяет, существует ли переменная сеанса MyMasterPage.It first checks to see if the Session variable MyMasterPage exists. В этом случае он пытается найти соответствующий элемент ListItem в элементе DropDownList MasterPageChoice.If it does, it attempts to find the matching ListItem in the MasterPageChoice DropDownList. Если найден соответствующий элемент ListItem, его свойство Selected имеет значение true.If a matching ListItem is found, its Selected property is set to true.

Нам также нужен код, сохраняющий выбранный пользователем вариант в MyMasterPage переменной сеанса.We also need code that saves the user's choice into the MyMasterPage Session variable. Создайте обработчик событий для Click события кнопки SaveLayout и добавьте следующий код:Create an event handler for the SaveLayout Button's Click event and add the following code:

protected void SaveLayout_Click(object sender, EventArgs e)
{
    Session["MyMasterPage"] = MasterPageChoice.SelectedValue;
    Response.Redirect("ChooseMasterPage.aspx"); 
}

Note

К моменту, когда обработчик событий Click выполняется при обратной передаче, Главная страница уже выбрана.By the time the Click event handler executes on postback, the master page has already been selected. Таким образом, выбор раскрывающегося списка пользователя не вступит в силу до перехода на следующую страницу.Therefore, the user's drop-down list selection won't be in effect until the next page visit. Response.Redirect заставляет браузер повторно запрашивать ChooseMasterPage.aspx.The Response.Redirect forces the browser to re-request ChooseMasterPage.aspx.

После завершения ChooseMasterPage.aspxной страницы наша последняя задача заключается в том, чтобы BasePage назначить свойство MasterPageFile на основе значения переменной сеанса MyMasterPage.With the ChooseMasterPage.aspx page complete, our final task is to have BasePage assign the MasterPageFile property based on the value of the MyMasterPage Session variable. Если для переменной сеанса не задано значение по умолчанию BasePage Site.master.If the Session variable is not set have BasePage default to Site.master.

protected override void OnPreInit(EventArgs e) 
{ 
    SetMasterPageFile();
    base.OnPreInit(e); 
} 
protected virtual void SetMasterPageFile()
{ 
    this.MasterPageFile = GetMasterPageFileFromSession();
} 
protected string GetMasterPageFileFromSession() 
{ 
    if (Session["MyMasterPage"] == null) 
        return "~/Site.master";
    else
        return Session["MyMasterPage"].ToString(); 
}

Note

Я переместил код, который назначает свойство MasterPageFile объекта Page из обработчика событий OnPreInit и в два отдельных метода.I moved the code that assigns the Page object's MasterPageFile property out of the OnPreInit event handler and into two separate methods. Первый метод, SetMasterPageFile, присваивает свойству MasterPageFile значение, возвращаемое вторым методом, GetMasterPageFileFromSession.This first method, SetMasterPageFile, assigns the MasterPageFile property to the value returned by the second method, GetMasterPageFileFromSession. Я сделал SetMasterPageFile метод virtual, чтобы будущие классы, расширяющие BasePage, могли при необходимости переопределить его для реализации пользовательской логики.I made the SetMasterPageFile method virtual so that future classes that extend BasePage can optionally override it to implement custom logic, if needed. Ниже приведен пример переопределения свойства BasePage``SetMasterPageFile в следующем руководстве.We'll see an example of overriding BasePage's SetMasterPageFile property in the next tutorial.

После этого кода посетите страницу ChooseMasterPage.aspx.With this code in place, visit the ChooseMasterPage.aspx page. Изначально выбирается Главная страница Site.master (см. рис. 6), но пользователь может выбрать другую главную страницу из раскрывающегося списка.Initially, the Site.master master page is selected (see Figure 6), but the user can pick a different master page from the drop-down list.

страницы содержимого отображаются с помощью главной страницы Site. masterContent Pages are Displayed Using the Site.master Master Page

Рис. 6. страницы содержимого отображаются с помощью главной страницы Site.master (щелкните, чтобы просмотреть изображение с полным размером)Figure 06: Content Pages are Displayed Using the Site.master Master Page (Click to view full-size image)

страницы содержимого теперь отображаются с помощью главной страницы альтернативного мастера.Content Pages are Now Displayed Using the Alternate.master Master Page

Рис. 07. страницы содержимого теперь отображаются с помощью главной страницы Alternate.master (щелкните, чтобы просмотреть изображение с полным размером)Figure 07: Content Pages are Now Displayed Using the Alternate.master Master Page (Click to view full-size image)

СводкаSummary

При посещении страницы содержимого ее элементы управления содержимым имеют плавкие предохранители с элементами управления ContentPlaceHolder главной страницы.When a content page is visited, its Content controls are fused with its master page's ContentPlaceHolder controls. Эталонная страница страницы содержимого обозначается свойством MasterPageFile Page класса, которое назначается атрибуту MasterPageFile директивы @Page на этапе инициализации.The content page's master page is denoted by the Page class's MasterPageFile property, which is assigned to the @Page directive's MasterPageFile attribute during the Initialization stage. Как было показано в этом руководстве, можно присвоить значение свойству MasterPageFile, пока оно выполняется до окончания этапа предварительной инициализации.As this tutorial showed, we can assign a value to the MasterPageFile property as long as we do so before the end of the PreInit stage. Возможность программно указывать главную страницу открывает дверцу для более сложных сценариев, таких как динамическая привязка страницы содержимого к главной странице на основе внешних факторов.Being able to programmatically specify the master page opens the door for more advanced scenarios, such as dynamically binding a content page to a master page based on external factors.

Поздравляем с программированием!Happy Programming!

Дополнительные материалыFurther Reading

Дополнительные сведения о разделах, обсуждаемых в этом руководстве, см. в следующих ресурсах:For more information on the topics discussed in this tutorial, refer to the following resources:

Об автореAbout the Author

Скотт Митчелл, автор нескольких книг по ASP/ASP. NET и основатель 4GuysFromRolla.com, работал с веб-технологиями майкрософт с 1998.Scott Mitchell, author of multiple ASP/ASP.NET books and founder of 4GuysFromRolla.com, has been working with Microsoft Web technologies since 1998. Скотт работает как независимый консультант, преподаватель и модуль записи.Scott works as an independent consultant, trainer, and writer. Его последняя книга — Sams обучать себя ASP.NET 3,5 за 24 часа.His latest book is Sams Teach Yourself ASP.NET 3.5 in 24 Hours. Скотт можно получить по адресу mitchell@4GuysFromRolla.com или через свой блог по адресу http://ScottOnWriting.NET.Scott can be reached at mitchell@4GuysFromRolla.com or via his blog at http://ScottOnWriting.NET.

Специальная благодарностьSpecial Thanks To

Эта серия руководств была рассмотрена многими полезными рецензентами.This tutorial series was reviewed by many helpful reviewers. Специалист по интересу для этого руководства был Сучи Банержи.Lead reviewer for this tutorial was Suchi Banerjee. Хотите ознакомиться с моими будущими статьями MSDN?Interested in reviewing my upcoming MSDN articles? Если да, расположите строку в mitchell@4GuysFromRolla.comIf so, drop me a line at mitchell@4GuysFromRolla.com