• Microsoft .NET
  • ASP.NET
  • Обмен информацией между страницами с содержимым (Content Pages) и мастер–страницами (Master Pages)

Обмен информацией между страницами с содержимым (Content Pages) и мастер–страницами (Master Pages)

ОГЛАВЛЕНИЕ

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

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

Основы дизайна мастер-страницы и страницы с содержимым

По мере роста веб-сайта, без сомненья, новые страницы с содержимым будут добавлены и связаны с уже существующими мастер-страницами, либо существующие страницы, использовавшие мастер-страницу X, могут быть перенастроены, и теперь можно будет использовать мастер-страницу Y. Следовательно, было бы разумно разрабатывать мастер-страницы таким образом, чтобы они не были зависимы от страниц с содержимым. То есть, мастер-страница не должна "предполагать", что ее страница обладает определенным методом, или же что она имеет определенный набор элементов управления, определенных в декларативной разметке. Такая мастер-страница называется слабосвязанной со своей страницей с содержимым; если мастер-страница требует наличия определенных методов элементов управления на соответствующих страницах, то она называется сильно связанной.

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

Почему предпочтительнее имееть сильную связь между страницей с содержимым и мастер-страницей?
Вам , вероятно, интересно, почему я посоветовал использовать связывание со стороны страницы с содержимым по направлению к мастер-странице, а не наоборот. Когда существует связь от A к B, изменение B обычно требует соответствующей модификации A.  Во-первых, поскольку я предполагаю, что у многих веб-сайтов гораздо меньше мастер-страниц, чем страниц с содержимым, и не так велика  вероятность частого обновления сильно связанного интерфейса мастер–страниц,  то это будет сделано на странице с содержимым (с точки зрения оптимизации больший смысл обретает мысль о том, что необходимо использовать связывание от страниц по направлению к мастер-страницам).  Во-вторых, данное направление связывания "ощущается" лучшим образом. При связывании новой веб-страницы с существующей мастер-страницей как-то не практично останавливаться и думать: "Хорошо, а какой функциональностью должна обладать данная страница, чтобы она могла работать с этой мастер-страницей?" Если приведенная информация сбивает вас с толку, то не задумывайтесь так сильно над ней - после того, как вы исследуете примеры из приложения к данной статье, я думаю, все будет более понятным.


Передача информации от страницы к её мастер-странице (Master Page)

Мастер-страница может напрямую предоставить свои элементы-управления страницам с содержимым. Чтобы передать метод, просто сделайте его публичным (public) в классе code-behind мастер-страницы:

' VB...
Partial Class MasterPageFiles_Main
   Inherits System.Web.UI.MasterPage

   Public Sub DisplayDataFromPage(ByVal message As String)
      DataFromPage.Text = message
   End Sub

End Class

// C#
public partial class MasterPageFiles_MainCS : System.Web.UI.MasterPage
{
   public void DisplayDataFromPage(string message)
   {
      DataFromPage.Text = message;
   }

}

Здесь DataFromPage это элемент управления Label в декларативной разметке мастер-страницы. Метод DisplayDataFromPage является публичным методом, который может быть вызван со страницы, принадлежащей мастер-странице. Строка, переданная в данный метод, присваивается свойству Text элемента управления Label. Используя данный метод, страница с содержимым может настроить отображаемый текст в элементе DataFromPage Label.

Вдобавок, мастер-страница может передать элемент управления из своего декларативного синтаксиса в качестве свойства, доступного только для чтения (read-only):

' VB...
Partial Class MasterPageFiles_Main
   Inherits System.Web.UI.MasterPage

   Public ReadOnly Property DataFromPageLabelControl() As Label
      Get
         Return Me.DataFromPage
      End Get
   End Property

End Class

// C#
public partial class MasterPageFiles_MainCS : System.Web.UI.MasterPage
{
   public Label DataFromPageLabelControl
   {
      get
      {
         return this.DataFromPage;
      }
   }

}

Чтобы получить доступ из страницы с содержимым к методам или свойствам мастер-страницы, свяжите мастер-страницу при помощи свойства Page.Master. Данное свойство возвращает объект типа MasterPage, так что вам придется осуществить его явное преобразование в соответствующий тип до вызова его методов или связывания со свойствами. В качестве альтернативы, вы можете установить директиву @MasterType, которая добавляет свойство в автоматически сгенерированный код класса ASP.NET code-behind, названный Master, который является тесно связанным с указанной мастер-страницей.

Следующая разметка в файле .aspx страницы с содержимым определяет тип мастер-страницы:

<%@ MasterType VirtualPath="pathToMasterPage" %>  

Добавив данную директиву (и сохранив файл .aspx), вы можете ссылаться на публичные методы и свойства мастер-страницы программным способом в классе code-behind страницы с содержимым, используя Master следующим образом:

' VB...
Partial Class Demos_PassInfoToMasterPage
   Inherits System.Web.UI.Page

   Protected Sub ShowText_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles ShowText.Click
      'Вызов метода DisplayDataFromPage мастер-страницы Main.master
      Master.DisplayDataFromPage(TextToShowInMasterPage.Text)

      ' Опционально: можно сослаться на элемент управления напрямую при помощи свойства DataFromPageLabelControl мастер-страницы
      ' Master.DataFromPageLabelControl.Text = TextToShowInMasterPage.Text
   End Sub
End Class

// C#
public partial class Demos_PassInfoToMasterPageCS : System.Web.UI.Page
{
   protected void ShowText_Click(object sender, EventArgs e)
   {
      // Вызов метода DisplayDataFromPage мастер-страницы Main.master
      Master.DisplayDataFromPage(TextToShowInMasterPage.Text);

      // Опционально: можно сослаться на элемент управления напрямую при помощи свойства DataFromPageLabelControl мастер-страницы
      // Master.DataFromPageLabelControl.Text = TextToShowInMasterPage.Text;
   }
}

Код, рассмотренный здесь, а также доступный в конце данной статьи, был скопирован из страницы, включающей в себя элемент TextBox, названный TextToShowInMasterPage, и элемент Button, названный ShowText. Когда нажимают кнопку ShowText - вызывается метод DisplayDataFromPage, передается значение свойства Text элемента TextBox. (В качестве альтернативы, свойство DataFromPageLabelControl мастер-страницы может быть использовано как ссылка на элемент Label в мастер-странице, где может быть установлено свойство Text.) В конечном счете, ввод текста в TextBox на странице и нажатие кнопки Button в результате обновит вид мастер-страницы, отобразив текст, введенный пользователем.

 

Page.Master против Master
Если вы используете директиву @MasterType для создания ссылки мастер-страницы, строго контролируемой по типу в классе code-behind страницы, то вы должны использовать Master для получения сильно типизированной ссылки. Если ваш код использует Page.Master, то вы получите слабо типизированную ссылку (т.е. тип которой – MasterPage), и тем самым потребуется приведение типов до того, как определенные элементы мастер-страницы будут доступны.



Передача информации от мастер-страницы (Master Page) к странице с содержимым

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

Как уже говорилось, я настоятельно рекомендую не использовать сильное связывание между мастер-страницей и страницей с содержимым. Такого сильного связывания можно избежать путем разумного использования событий. Вкратце: мы можем создать мастер-страницу, которая вызывает событие в момент, когда происходит какое-либо действие на стороне мастер-страницы. Данное действие может передать дополнительную информацию, и затем страницы, которым необходима данная информация, могут "подписаться" на данное событие мастер-страницы.  (Другим подходом к данной ситуации, который не рассматривается в данной статье, является назначение страницей представителя свойства мастер-страницы. Несмотря на то что статья рассматривает пользовательские элементы управления и страницы ASP.NET, данные принципы применимы к мастер-страницам (пользовательский элемент управления) и к страницам с содержимым (страница ASP.NET).)

Чтобы продемонстрировать это, представьте, что у нас есть элемент DropDownList в мастер-странице. Когда в нем изменяется индекс, мы хотим оповестить страницу с содержимым об изменении для того, чтобы она могла соответственно обновить экран. Начните с создания элемента DropDownList с именем Moods на мастер-странице и затем создайте обработчик для события SelectedIndexChanged данного элемента. Далее, нам необходимо определить событие для мастер-страницы, указывающее подпись обработчика события. Обработчик события указывает, какие входные параметры были переданы обработчику события. Например, давайте передадим значения свойств Text и Value выбранной опции списка DropDownList. Тем самым мы можем использовать CommandEventHandler, передающий объект CommandEventArgs в обработчик события, включающий в себя свойства CommandName и CommandArgument, которые мы можем использовать для хранения выбранных значений свойств Text и Value из ListItem.

Чтобы определить событие, названное MoodChanged для мастер-страницы, использующей CommandEventHandler, примените следующий синтаксис:

' VB...
Partial Class MasterPageFiles_Main
   Inherits System.Web.UI.MasterPage

   Public Event MoodChanged As CommandEventHandler
End Class

// C#
public partial class MasterPageFiles_MainCS : System.Web.UI.MasterPage
{
   public event CommandEventHandler MoodChanged;
}

Когда сработает событие SelectedIndexChanged элемента DropDownList нам нужно будет вызвать событие MoodChanged. Для этого добавьте следующий код в обработчик события SelectedIndexChanged элемента DropDownList :

' VB...
Partial Class MasterPageFiles_Main
   Inherits System.Web.UI.MasterPage

   Public Event MoodChanged As CommandEventHandler

   Protected Sub Moods_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles Moods.SelectedIndexChanged
      If Moods.SelectedIndex <> 0 Then
         RaiseEvent MoodChanged(Me, New CommandEventArgs(Moods.SelectedItem.Text, Moods.SelectedValue))
      End If
   End Sub

End Class

// C#
public partial class MasterPageFiles_MainCS : System.Web.UI.MasterPage
{
   public event CommandEventHandler MoodChanged;

   protected void Moods_SelectedIndexChanged(object sender, EventArgs e)
   {
      if (Moods.SelectedIndex != 0 && MoodChanged != null)
         MoodChanged(this, new CommandEventArgs(Moods.SelectedItem.Text, Moods.SelectedValue));
   }

}

Примечание: в C#(,) сначала нам необходимо удостовериться в том, что событие не равно null до того, как вызывать его (заметьте проверку "&& MoodChanged != null" в условии if). Ссылка события равняется null, если никто не "подписан" на данное событие... ...

Следующим шагом будет "подписывание" на событие в страницах, которые зависят от изменений в выпадающем списке DropDownList. Для того чтобы подписаться на событие, нам следует  программным путем указать в странице, что  в том  случае, когда срабатывает событие MoodChanged мастер-страницы, необходимо запустить определенный обработчик события. Следующий код демонстрирует способ установления данной связи между событием и обработчиком в C# и VB:

' VB...
Partial Class Demos_PassInfoToPage
   Inherits System.Web.UI.Page

   Protected Sub Page_Init(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Init
      'Связываем событие (MoodChanged) с обработчиком (MoodChangedFromMasterPage)
      AddHandler Master.MoodChanged, AddressOf MoodChangedFromMasterPage
   End Sub

   Private Sub MoodChangedFromMasterPage(ByVal sender As Object, ByVal e As CommandEventArgs)
      Dim moodText As String = e.CommandName
      Dim moodValue As String = e.CommandArgument.ToString

      MoodChangedLabel.Text = String.Format("You have selected mood {0}, which has a value of {1}...", moodText, moodValue)
   End Sub

End Class

// C#
public partial class Demos_PassInfoToPageCS : System.Web.UI.Page
{
   protected void Page_Init(object sender, EventArgs e)
   {
      // Связываем событие (MoodChanged) с обработчиком (MoodChangedFromMasterPage)
      Master.MoodChanged += new CommandEventHandler(MoodChangedFromMasterPage);
   }

   private void MoodChangedFromMasterPage(object sender, CommandEventArgs e)
   {
      string moodText = e.CommandName;
      string moodValue = e.CommandArgument.ToString();

      MoodChangedLabel.Text = String.Format("You have selected mood {0}, which has a value of {1}...", moodText, moodValue);
}
}

Обработчик события Page_Init связывает событие MoodChanged с обработчиком события MoodChangedFromMasterPage (и должен переустановить данную связь при каждом постбэке). Заметьте, что свойство Master предоставляет строгий контроль типов. Это происходит потому, что я использовал директиву @MasterType в файле.aspx для данной страницы.



Вывод

В идеале мастер-страницы (Master Pages) и их страницы с содержимым будут полностью независимыми сущностями и не будут требовать обмена информации. Тем не менее, бывают случаи, когда это не может быть воплощено в жизнь без сложностей. Как уже обсуждалось в данной статье, для некоторых случаев существует несколько подходящих техник. Передавая информацию от страницы с содержимым к  мастер-странице, страница может получить сильно типизированную ссылку на мастер-страницу, используя директиву @MasterType. Это позволяет вызывать публичные методы и свойства мастер-страницы из страницы с содержимым с преимуществом проверки типа во время компиляции. При передаче информации с мастер-страницы на ее страницы с содержимым я нашел наилучший способ, при котором мастер-страница вызывает событие и передает информацию. Те страницы, которые необходимо оповестить, могут быть "подписаны" на данное событие.

Веселого программирования!

Scott Mitchell

Исходный код примеров