Especificar la página maestra mediante programación (VB)
por Scott Mitchell
Examina el establecimiento de la página maestra de la página de contenido mediante programación a través del controlador de eventos PreInit.
Introducción
Desde el ejemplo inicial en Crear un diseño de todo el sitio mediante páginas maestras, todas las páginas de contenido han hecho referencia a su página maestra de forma declarativa a través del atributo MasterPageFile
de la directiva @Page
. Por ejemplo, la siguiente directiva @Page
vincula la página de contenido a la página maestra Site.master
:
<%@ Page Language="C#" MasterPageFile="~/Site.master"... %>
La clase Page
del espacio de nombres System.Web.UI
incluye una propiedad MasterPageFile
que devuelve la ruta de acceso a la página maestra de la página de contenido; es esta propiedad la que establece la directiva @Page
. Esta propiedad también se puede usar para especificar mediante programación la página maestra de la página de contenido. Este enfoque es útil si desea asignar dinámicamente la página maestra en función de factores externos, como el usuario que visita la página.
En este tutorial, agregamos una segunda página maestra a nuestro sitio web y decidimos dinámicamente qué página maestra usar en tiempo de ejecución.
Paso 1: Un vistazo al ciclo de vida de la página
Cada vez que llega una solicitud al servidor web para una página de ASP.NET que es una página de contenido, el motor de ASP.NET debe fusionar los controles de contenido de la página en los controles ContentPlaceHolder correspondientes de la página maestra. Esta fusión crea una única jerarquía de controles que puede seguir el ciclo de vida típico de las páginas.
En la ilustración 1 se ilustra esta fusión. En el paso 1 de la ilustración 1 se muestra el contenido inicial y las jerarquías de control de la página maestra. Al final de la fase PreInit, los controles de contenido de la página se agregan a los ContentPlaceHolders correspondientes en la página maestra (paso 2). Después de esta fusión, la página maestra actúa como la raíz de la jerarquía de control fusionada. A continuación, esta jerarquía de control fusionada se agrega a la página para generar la jerarquía de control finalizada (paso 3). El resultado neto es que la jerarquía de controles de la página incluye la jerarquía de control fusionada.
Ilustración 01: Las jerarquías de control de la página maestra y la página de contenido se fusionan durante la fase de PreInit (haga clic para ver la imagen de tamaño completo)
Paso 2: Establecer laMasterPageFile
propiedad desde el código
Qué parte de la página maestra participa en esta fusión depende del valor de la propiedad MasterPageFile
del objeto Page
. Establecer el atributo MasterPageFile
en la directiva @Page
tiene el efecto neto de asignar la propiedad MasterPageFile
de Page
durante la fase de inicialización, que es la primera fase del ciclo de vida de la página. También podemos establecer esta propiedad mediante programación. Sin embargo, es imperativo establecer esta propiedad antes de que se produzca la fusión en la figura 1.
Al principio de la fase PreInit, el objeto Page
genera su evento PreInit
y llama a su método OnPreInit
. Para establecer la página maestra mediante programación, podemos crear un controlador de eventos para el evento PreInit
o reemplazar el método OnPreInit
. Veamos ambos métodos.
Comience abriendo Default.aspx.vb
, el archivo de clase de código subyacente para la página principal de nuestro sitio. Cree un controlador de eventos para el evento PreInit
de la página escribiendo el siguiente código:
Protected Sub Page_PreInit(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.PreInit
End Sub
Desde aquí podemos establecer la propiedad MasterPageFile
. Actualice el código para que asigne el valor "~/Site.master" a la propiedad MasterPageFile
.
Protected Sub Page_PreInit(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.PreInit
Me.MasterPageFile = "~/Site.master"
End Sub
Si establece un punto de interrupción y comienza con la depuración, verá que cada vez que se visita la página Default.aspx
o cada vez que hay un postback a esta página, se ejecuta el controlador de eventos Page_PreInit
y la propiedad MasterPageFile
se asigna a "~/Site.master".
Como alternativa, puede invalidar el método OnPreInit
de la clase Page
y establecer la propiedad MasterPageFile
allí. En este ejemplo, no estableceremos la página maestra en una página determinada, sino desde BasePage
. Recuerde que hemos creado una clase de página base personalizada (BasePage
) en el tutorial Especificar el título, las etiquetas meta y otros encabezados HTML en el tutorial de página maestra. Actualmente BasePage
invalida el método OnLoadComplete
de la clase Page
, donde establece la propiedad Title
de la página en función de los datos del mapa del sitio. Vamos a actualizar BasePage
para invalidar también el método OnPreInit
para especificar mediante programación la página maestra.
Protected Overrides Sub OnPreInit(ByVal e As System.EventArgs)
Me.MasterPageFile = "~/Site.master"
MyBase.OnPreInit(e)
End Sub
Dado que todas nuestras páginas de contenido derivan de BasePage
, todas ellas ahora tienen asignada su página maestra mediante programación. En este momento, el controlador de eventos PreInit
de Default.aspx.vb
es superfluo; no dude en quitarlo.
¿Qué ocurre con la directiva @Page
?
Lo que puede resultar un poco confuso es que las propiedades MasterPageFile
de las páginas de contenido ahora se especifican en dos lugares: mediante programación en el método OnPreInit
de la clase BasePage
, así como a través del atributo MasterPageFile
en la directiva @Page
de cada página de contenido.
La primera fase del ciclo de vida de la página es la fase de inicialización. Durante esta fase, a la propiedad MasterPageFile
del objeto Page
se le asigna el valor del atributo MasterPageFile
en la directiva @Page
(si se proporciona). La fase PreInit sigue la fase de inicialización y es aquí donde establecemos mediante programación la propiedad MasterPageFile
del objeto Page
, lo que sobrescribe el valor asignado de la directiva @Page
. Dado que estamos estableciendo la propiedad MasterPageFile
del objeto Page
mediante programación, podríamos quitar el atributo MasterPageFile
de la directiva @Page
sin afectar a la experiencia del usuario final. Para convencerse de esto, continúe y quite el atributo MasterPageFile
de la directiva @Page
en Default.aspx
y, a continuación, visite la página a través de un explorador. Como cabría esperar, la salida es la misma que antes de quitar el atributo.
Si la propiedad MasterPageFile
se establece a través de la directiva @Page
o mediante programación no afecta a la experiencia del usuario final. Sin embargo, Visual Studio usa el atributo MasterPageFile
de la directiva @Page
durante el diseño para generar la vista WYSIWYG en el Diseñador. Si vuelve a Default.aspx
en Visual Studio y navega al Diseñador, verá el mensaje "Master Page error: The page has controls that require a Master Page reference, but none is specified" (Error de página maestra: la página tiene controles que requieren una referencia de página maestra, pero no se especifica ninguno) (vea la ilustración 2).
En resumen, es necesario dejar el atributo MasterPageFile
en la directiva @Page
para disfrutar de una rica experiencia en tiempo de diseño en Visual Studio.
Atributo MasterPageFile de la directiva @Page para representar la vista de diseño" />
Ilustración 02: Visual Studio usa el atributo MasterPageFile
de la directiva @Page
para representar la vista diseño (haga clic para ver la imagen de tamaño completo)
Paso 3: Crear una página maestra alternativa
Dado que la página maestra de una página de contenido se puede establecer mediante programación en tiempo de ejecución, es posible cargar dinámicamente una página maestra determinada basada en algunos criterios externos. Esta funcionalidad puede ser útil en situaciones en las que el diseño del sitio debe variar en función del usuario. Por ejemplo, una aplicación web del motor de blog puede permitir a sus usuarios elegir un diseño para su blog, donde cada diseño está asociado a una página maestra diferente. En tiempo de ejecución, cuando un visitante está viendo el blog de un usuario, la aplicación web tendría que determinar el diseño del blog y asociar dinámicamente la página maestra correspondiente a la página de contenido.
Vamos a ver cómo se carga dinámicamente una página maestra en tiempo de ejecución en función de algunos criterios externos. Nuestro sitio web contiene actualmente solo una página maestra (Site.master
). Necesitamos otra página maestra para ilustrar la elección de una página maestra en tiempo de ejecución. Este paso se centra en la creación y configuración de la nueva página maestra. En el paso 4 se examina la determinación de la página maestra que se va a usar en tiempo de ejecución.
Cree una nueva página maestra en la carpeta raíz denominada Alternate.master
. Agregue también una nueva hoja de estilos al sitio web denominado AlternateStyles.css
.
Ilustración 03: Agregar otra página maestra y archivo CSS al sitio web (haga clic para ver la imagen de tamaño completo)
He diseñado la página maestra Alternate.master
para que el título se muestre en la parte superior de la página, centrado y sobre un fondo azul marino. He dispensado la columna izquierda y he movido ese contenido debajo del control ContentPlaceHolder MainContent
, que ahora abarca todo el ancho de la página. Además, rechacé la lista de lecciones no ordenadas y la reemplacé por una lista horizontal encima de MainContent
. También actualicé las fuentes y colores utilizados por la página maestra (y, por extensión, sus páginas de contenido). En la figura 4 se muestra Default.aspx
cuando se utiliza la página maestra Alternate.master
.
Nota:
ASP.NET incluye la capacidad de definir temas. Un tema es una colección de imágenes, archivos CSS y configuraciones de propiedades de control web relacionadas con el estilo que se pueden aplicar a una página en tiempo de ejecución. Los temas son el camino a seguir si los diseños del sitio solo difieren en las imágenes mostradas y por sus reglas CSS. Si los diseños difieren de forma más sustancial, como por ejemplo si se utilizan diferentes controles web o tienen un diseño radicalmente diferente, deberá usar páginas maestras independientes. Para obtener más información sobre los temas, consulte la sección Información adicional al final de este tutorial.
Ilustración 04: Nuestras páginas de contenido ahora pueden usar una nueva apariencia (hacer clic para ver la imagen de tamaño completo)
Cuando se fusiona el marcado de las páginas maestras y de contenido, la clase MasterPage
comprueba que cada control de contenido de la página de contenido haga referencia a un ContentPlaceHolder en la página maestra. Se produce una excepción si se encuentra un control de contenido que hace referencia a un ContentPlaceHolder no existente. En otras palabras, es imperativo que la página maestra que se asigna a la página de contenido tenga un ContentPlaceHolder para cada control de contenido de la página de contenido.
La página maestra Site.master
incluye cuatro controles ContentPlaceHolder:
head
MainContent
QuickLoginUI
LeftColumnContent
Algunas de las páginas de contenido de nuestro sitio web incluyen solo uno o dos controles de contenido; otras incluyen un control de contenido para cada uno de los ContentPlaceHolders disponibles. Si nuestra nueva página maestra (Alternate.master
) se puede asignar alguna vez a esas páginas de contenido que tienen controles de contenido para todos los ContentPlaceHolders en Site.master
, es fundamental que Alternate.master
también incluya los mismos controles ContentPlaceHolder que Site.master
.
Para que la página maestra Alternate.master
tenga un aspecto similar a la mía (vea la figura 4), empiece por definir los estilos de la página maestra en la hoja de estilos AlternateStyles.css
. Añada las siguientes reglas a 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;
}
A continuación, agregue el siguiente marcado declarativo a Alternate.master
. Como puede ver, Alternate.master
contiene cuatro controles ContentPlaceHolder con los mismos valores ID
que los controles ContentPlaceHolder en Site.master
. Además, incluye un control ScriptManager, que es necesario para aquellas páginas de nuestro sitio web que utilicen el marco ASP.NET AJAX.
<!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>
Prueba de la nueva página maestra
Para probar esta nueva página maestra, actualice el método OnPreInit
de la clase BasePage
para que se asigne el valor "~/Alternate.maser"
a la propiedad MasterPageFile
y, a continuación, visite el sitio web. Todas las páginas deben funcionar sin errores excepto dos: ~/Admin/AddProduct.aspx
y ~/Admin/Products.aspx
. Agregar un producto a DetailsView en ~/Admin/AddProduct.aspx
da como resultado una NullReferenceException
desde la línea de código que intenta establecer la propiedad GridMessageText
de la página maestra. Al visitar ~/Admin/Products.aspx
, se lanza una InvalidCastException
en la carga de página con el mensaje: "Unable to cast object of type 'ASP.alternate_master' to type 'ASP.site_master'" (No se puede convertir el objeto de tipo 'ASP.alternate_master' al tipo 'ASP.site_master').
Estos errores se producen porque la clase de código subyacente Site.master
incluye eventos públicos, propiedades y métodos que no están definidos en Alternate.master
. La parte de marcado de estas dos páginas tiene una directiva @MasterType
que hace referencia a la página maestra Site.master
.
<%@ MasterType VirtualPath="~/Site.master" %>
Además, el controlador de eventos ItemInserted
de DetailsView de ~/Admin/AddProduct.aspx
incluye código que convierte la propiedad Page.Master
de tipo flexible en un objeto de tipo Site
. La directiva @MasterType
(se usa de esta manera) y la conversión en el controlador de eventos ItemInserted
acopla estrechamente las páginas ~/Admin/AddProduct.aspx
y ~/Admin/Products.aspx
a la página maestra Site.master
.
Para romper este estrecho acoplamiento podemos hacer que Site.master
y Alternate.master
deriven de una clase base común que contenga definiciones para los miembros públicos. Después, podemos actualizar la directiva @MasterType
para hacer referencia a este tipo base común.
Crear una clase de página maestra base personalizada
Agregue un nuevo archivo de clase a la carpeta App_Code
denominada BaseMasterPage.vb
y haga que se derive de System.Web.UI.MasterPage
. Es necesario definir el método RefreshRecentProductsGrid
y la propiedad GridMessageText
en BaseMasterPage
, pero no podemos simplemente moverlos allí desde Site.master
porque estos miembros trabajan con controles web específicos de la página maestra Site.master
(el GridView RecentProducts
y la etiqueta GridMessage
).
Lo que debemos hacer es configurar BaseMasterPage
de tal manera que estos miembros se definan allí, pero en realidad se implementan mediante las clases derivadas de BaseMasterPage
(Site.master
y Alternate.master
). Este tipo de herencia es posible marcando la clase como MustInherit
y sus miembros como MustOverride
. En resumen, agregar estas palabras clave a la clase y a sus dos miembros anuncia que BaseMasterPage
no ha implementado RefreshRecentProductsGrid
y GridMessageText
, pero que sus clases derivadas lo harán.
También es necesario definir el evento PricesDoubled
en BaseMasterPage
y proporcionar un medio por las clases derivadas para generar el evento. El patrón usado en .NET Framework para facilitar este comportamiento es crear un evento público en la clase base y agregar un método protegido y reemplazable denominado OnEventName
. A continuación, las clases derivadas pueden llamar a este método para generar el evento o invalidarlo para ejecutar código inmediatamente antes o después de que se genere el evento.
Actualice la clase BaseMasterPage
para que contenga el código siguiente:
Public MustInherit Class BaseMasterPage
Inherits System.Web.UI.MasterPage
Public Event PricesDoubled As EventHandler
Protected Overridable Sub OnPricesDoubled(ByVal e As EventArgs)
RaiseEvent PricesDoubled(Me, e)
End Sub
Public MustOverride Sub RefreshRecentProductsGrid()
Public MustOverride Property GridMessageText() As String
End Class
A continuación, vaya a la clase de código subyacente Site.master
y haga que se derive de BaseMasterPage
. Dado que BaseMasterPage
contiene miembros marcados MustOverride
, necesitamos invalidar esos miembros aquí en Site.master
. Agregue la palabra clave Overrides
a las definiciones de método y propiedad. Actualice también el código que genera el evento PricesDoubled
en el controlador de eventos DoublePrice
de Button Click
con una llamada al método OnPricesDoubled
de la clase base.
Después de estas modificaciones, la clase de código subyacente Site.master
debe contener el código siguiente:
Partial Class Site
Inherits BaseMasterPage
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
DateDisplay.Text = DateTime.Now.ToString("dddd, MMMM dd")
End Sub
Public Overrides Sub RefreshRecentProductsGrid()
RecentProducts.DataBind()
End Sub
Public Overrides Property GridMessageText() As String
Get
Return GridMessage.Text
End Get
Set(ByVal Value As String)
GridMessage.Text = Value
End Set
End Property
Protected Sub DoublePrice_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles DoublePrice.Click
' Double the prices
DoublePricesDataSource.Update()
' Refresh RecentProducts
RecentProducts.DataBind()
' Raise the PricesDoubled event
MyBase.OnPricesDoubled(EventArgs.Empty)
End Sub
End Class
También es necesario actualizar la clase de código subyacente de Alternate.master
para que se derive de BaseMasterPage
e invalidar los dos miembros MustOverride
. Pero dado que Alternate.master
no contiene un control GridView que enumera los productos más recientes ni una etiqueta que muestra un mensaje después de agregar un nuevo producto a la base de datos, estos métodos no necesitan hacer nada.
Partial Class Alternate
Inherits BaseMasterPage
Public Overrides Property GridMessageText() As String
Get
Return String.Empty
End Get
Set(ByVal value As String)
' Do nothing
End Set
End Property
Public Overrides Sub RefreshRecentProductsGrid()
' Do nothing
End Sub
End Class
Hacer referencia a la clase de página maestra base
Ahora que hemos completado la clase BaseMasterPage
y tenemos nuestras dos páginas maestras que la extienden, nuestro paso final es actualizar las páginas ~/Admin/AddProduct.aspx
y ~/Admin/Products.aspx
para hacer referencia a este tipo común. Comience cambiando la directiva @MasterType
en ambas páginas desde:
<%@ MasterType VirtualPath="~/Site.master" %>
A:
<%@ MasterType TypeName="BaseMasterPage" %>
En lugar de hacer referencia a una ruta de acceso de archivo, la propiedad @MasterType
ahora hace referencia al tipo base (BaseMasterPage
). Por lo tanto, la propiedad Master
fuertemente tipada usada en las clases de código subyacente de ambas páginas es ahora de tipo BaseMasterPage
(en lugar de tipo Site
). Con este cambio, vuelva a visitar ~/Admin/Products.aspx
. Anteriormente, esto produjo un error de conversión porque la página está configurada para usar la página maestra Alternate.master
, pero la directiva @MasterType
hizo referencia al archivo Site.master
. Pero ahora la página se representa sin errores. Esto se debe a que la página maestra Alternate.master
se puede convertir a un objeto de tipo BaseMasterPage
(ya que lo extiende).
Hay un pequeño cambio que debe realizarse en ~/Admin/AddProduct.aspx
. El controlador de eventos ItemInserted
del control DetailsView usa tanto la propiedad Master
fuertemente tipada como la propiedad Page.Master
de tipo flexible. Hemos corregido la referencia fuertemente tipada al actualizar la directiva @MasterType
, pero todavía es necesario actualizar la referencia de tipo flexible. Agregue la línea de código siguiente:
Dim myMasterPage As Site = CType(Page.Master, Site)
Con lo siguiente, que convierte Page.Master
al tipo base:
Dim myMasterPage As BaseMasterPage = CType(Page.Master, BaseMasterPage)
Paso 4: Determinar qué página maestra enlazar a las páginas de contenido
Nuestra clase BasePage
establece actualmente todas las propiedades MasterPageFile
de las páginas de contenido en un valor codificado de forma rígida en la fase PreInit del ciclo de vida de la página. Podemos actualizar este código para basar la página maestra en algún factor externo. Quizás la página maestra que se va a cargar depende de las preferencias del usuario que ha iniciado sesión actualmente. En ese caso, tendríamos que escribir código en el método OnPreInit
en BasePage
que busca las preferencias de la página maestra del usuario que visita actualmente.
Vamos a crear una página web que permita al usuario elegir la página maestra que se va a usar (Site.master
o Alternate.master
) y guardar esta opción en una variable Session. Empiece por crear una nueva página web en el directorio raíz denominado ChooseMasterPage.aspx
. Al crear esta página (o cualquier otra página de contenido a partir de ahora), no es necesario enlazarla a una página maestra porque la página maestra se establece mediante programación en BasePage
. Sin embargo, si no enlaza la nueva página a una página maestra, el marcado declarativo predeterminado de la nueva página contiene un formulario web y otro contenido proporcionado por la página maestra. Deberá reemplazar manualmente este marcado por los controles de contenido adecuados. Por ese motivo, me resulta más fácil enlazar la nueva página de ASP.NET a una página maestra.
Nota:
Dado que Site.master
y Alternate.master
tienen el mismo conjunto de controles ContentPlaceHolder, no importa la página maestra que elija al crear la nueva página de contenido. Por coherencia, sugeriría usar Site.master
.
Ilustración 05: adición de una nueva página de contenido al sitio web (Haga clic para ver la imagen de tamaño completo)
Actualice el archivo Web.sitemap
para incluir una entrada para esta lección. Agregue el siguiente marcado debajo de l<siteMapNode>
de la lección Páginas maestras y ASP.NET AJAX:
<siteMapNode url="~/ChooseMasterPage.aspx" title="Choose a Master Page" />
Antes de agregar contenido a la página ChooseMasterPage.aspx
, dedique un momento a actualizar la clase de código subyacente de la página para que derive de BasePage
(en lugar de System.Web.UI.Page
). A continuación, agregue un control DropDownList a la página, establezca su propiedad ID
en MasterPageChoice
y agregue dos ListItems con los valores Text
de "~/Site.master" y "~/Alternate.master".
Agregue un control Button Web a la página y establezca sus propiedades ID
y Text
en SaveLayout
y "Save Layout Choice" (Guardar selección de diseño), respectivamente. En este momento, el marcado declarativo de la página debe tener un aspecto similar al siguiente:
<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>
Cuando se visita la página por primera vez, es necesario mostrar la opción de página maestra seleccionada actualmente por el usuario. Cree un controlador de eventos Page_Load
y agréguele el siguiente código:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not Page.IsPostBack Then
If Session("MyMasterPage") IsNot Nothing Then
Dim li As ListItem = MasterPageChoice.Items.FindByText(Session("MyMasterPage").ToString())
If li IsNot Nothing Then
li.Selected = True
End If
End If
End If
End Sub
El código anterior solo se ejecuta en la primera visita de página (y no en postbacks posteriores). Primero comprueba si existe la variable Session MyMasterPage
. Si es así, intenta encontrar el ListItem coincidente en el DropDownList MasterPageChoice
. Si se encuentra un ListItem coincidente, su propiedad Selected
se establece en True
.
También necesitamos código que guarde la opción del usuario en la variable Session MyMasterPage
. Cree un controlador de eventos para el evento Click
de SaveLayout
Button y agregue el código siguiente:
Protected Sub SaveLayout_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles SaveLayout.Click
Session("MyMasterPage") = MasterPageChoice.SelectedValue
Response.Redirect("ChooseMasterPage.aspx")
End Sub
Nota:
Cuando el controlador de eventos Click
se ejecuta en postback, ya se ha seleccionado la página maestra. Por lo tanto, la selección de lista desplegable del usuario no entrará en vigor hasta que visite la página siguiente. Response.Redirect
obliga al explorador a volver a solicitar ChooseMasterPage.aspx
.
Una vez completada la página ChooseMasterPage.aspx
, nuestra tarea final es que BasePage
asigne la propiedad MasterPageFile
en función del valor de la variable Session MyMasterPage
. Si la variable Session no está establecida, haga que BasePage
sea por defecto Site.master
.
Protected Overrides Sub OnPreInit(ByVal e As System.EventArgs)
SetMasterPageFile()
MyBase.OnPreInit(e)
End Sub
Protected Overridable Sub SetMasterPageFile()
Me.MasterPageFile = GetMasterPageFileFromSession()
End Sub
Protected Function GetMasterPageFileFromSession() As String
If Session("MyMasterPage") Is Nothing Then
Return "~/Site.master"
Else
Return Session("MyMasterPage").ToString()
End If
End Function
Nota:
He movido el código que asigna la propiedad MasterPageFile
del objeto Page
fuera del controlador de eventos OnPreInit
y en dos métodos independientes. Este primer método, SetMasterPageFile
, asigna la propiedad MasterPageFile
al valor devuelto por el segundo método, GetMasterPageFileFromSession
. He marcado el método SetMasterPageFile
como Overridable
para que las clases futuras que extiendan BasePage
puedan anularlo opcionalmente para implementar una lógica personalizada, si es necesario. Veremos un ejemplo de anulación de la propiedad BasePage
de SetMasterPageFile
en el siguiente tutorial.
Con este código, visite la página ChooseMasterPage.aspx
. Inicialmente, se selecciona la página maestra Site.master
(vea la figura 6), pero el usuario puede elegir una página maestra diferente de la lista desplegable.
Ilustración 06: Las páginas de contenido se muestran utilizando la página maestra Site.master
(haga clic para ver la imagen de tamaño completo)
Ilustración 07: Ahora las páginas de contenido se muestran utilizando la página maestra Alternate.master
(haga clic para ver la imagen de tamaño completo)
Resumen
Cuando se visita una página de contenido, sus controles de contenido se fusionan con los controles ContentPlaceHolder de su página maestra. La página maestra de la página de contenido se indica mediante la propiedad MasterPageFile
de la clase Page
, que se asigna al atributo MasterPageFile
de la directiva @Page
durante la fase de inicialización. Como se muestra en este tutorial, podemos asignar un valor a la propiedad MasterPageFile
siempre que lo hagamos antes del final de la fase PreInit. La posibilidad de especificar mediante programación la página maestra abre la puerta a escenarios más avanzados, como la vinculación dinámica de una página de contenido a una página maestra en función de factores externos.
¡Feliz programación!
Lecturas adicionales
Para obtener más información sobre los temas tratados en este tutorial, consulte los siguientes recursos:
- Información general sobre el ciclo de vida de una página ASP.NET
- Información general sobre temas y máscaras de ASP.NET
- Páginas maestras: Recomendaciones, trucos y trampas
- Temas de ASP.NET
Acerca del autor
Scott Mitchell, autor de varios libros de ASP/ASP.NET y fundador de 4GuysFromRolla.com, ha estado trabajando con tecnologías web de Microsoft desde 1998. Scott trabaja como consultor independiente, entrenador y escritor. Su último libro es Sams Teach Yourself ASP.NET 3.5 in 24 Hours. Se puede contactar con Scott en mitchell@4GuysFromRolla.com o a través de su blog en http://ScottOnWriting.NET.
Agradecimientos especiales a
Esta serie de tutoriales fue revisada por muchos revisores que fueron de gran ayuda. El revisor principal de este tutorial fue Suchi Banerjee. ¿Le interesaría revisar mis próximos artículos de MSDN? Si es así, escríbame a mitchell@4GuysFromRolla.com