Nomenclatura de los identificadores de control en las páginas de contenido (VB)
por Scott Mitchell
Muestra cómo los controles ContentPlaceHolder actúan como un contenedor de nomenclatura y, por tanto, hacen que el trabajo mediante programación con un control sea difícil (a través de FindControl). Examina este problema y soluciones alternativas. También se describe cómo acceder mediante programación al valor ClientID resultante.
Introducción
Todos los controles de servidor ASP.NET incluyen una propiedad ID
que identifica de forma única el control y es el medio por el que se accede al control mediante programación en la clase de código subyacente. Del mismo modo, los elementos de un documento HTML pueden incluir un atributo id
que identifica de forma única el elemento; estos valores id
se suelen usar en el script del lado cliente para hacer referencia mediante programación a un elemento HTML determinado. Dado esto, puede suponer que cuando se representa un control de servidor ASP.NET en HTML, su valor ID
se usa como valor id
del elemento HTML representado. Esto no es necesariamente el caso porque, en determinadas circunstancias, un solo control con un solo valor ID
puede aparecer varias veces en el marcado representado. Considere un control GridView que incluya un TemplateField con un control Web de etiqueta con un valor ID
de ProductName
. Cuando GridView está enlazado a su origen de datos en tiempo de ejecución, esta etiqueta se repite una vez por cada fila de GridView. Cada etiqueta representada necesita un valor único id
.
Para controlar estos escenarios, ASP.NET permite que determinados controles se denoten como contenedores de nomenclatura. Un contenedor de nomenclatura actúa como un nuevo espacio de nombres ID
. Los controles de servidor que aparecen dentro del contenedor de nomenclatura tienen su valor id
representado prefijado con el ID
del control de contenedor de nomenclatura. Por ejemplo, las clases GridView
y GridViewRow
son contenedores de nomenclatura. Por lo tanto, un control de etiqueta definido en un TemplateField de GridView con ID
ProductName
recibe un valor id
representado de GridViewID_GridViewRowID_ProductName
. Dado que GridViewRowID es único para cada fila de GridView, los valores resultantes id
son únicos.
Nota:
La INamingContainer
interfaz se usa para indicar que un control de servidor de ASP.NET determinado debe funcionar como un contenedor de nomenclatura. La INamingContainer
interfaz no detalla ningún método que el control de servidor debe implementar; en su lugar, se usa como marcador. Al generar el marcado representado, si un control implementa esta interfaz, el motor de ASP.NET prefija automáticamente su valor ID
a los valores de atributo id
representados de sus descendientes. Este proceso se describe con más detalle en el paso 2.
Los contenedores de nomenclatura no solo cambian el valor del atributo id
representado, sino que también afectan a cómo se puede hacer referencia al control mediante programación desde la clase de código subyacente de la página de ASP.NET. El método FindControl("controlID")
se usa normalmente para hacer referencia mediante programación a un control web. Sin embargo, FindControl
no penetra en los contenedores de nomenclatura. Por lo tanto, no puede usar directamente el método Page.FindControl
para hacer referencia a controles dentro de GridView u otro contenedor de nomenclatura.
Como ya habrá supuesto, las páginas maestras y ContentPlaceHolders se implementan como contenedores de nomenclatura. En este tutorial se examina cómo afectan las páginas maestras a los valores id
de elementos HTML y formas de hacer referencia mediante programación a controles web dentro de una página de contenido mediante FindControl
.
Paso 1: Agregar nuevas páginas ASP.NET
Para demostrar los conceptos descritos en este tutorial, vamos a agregar una nueva página de ASP.NET a nuestro sitio web. Cree una nueva página de contenido denominada IDIssues.aspx
en la carpeta raíz y enlazarla a la página maestra Site.master
.
Ilustración 01: Agregar la página de contenido IDIssues.aspx
a la carpeta raíz
Visual Studio crea automáticamente un control Content para cada uno de los cuatro ContentPlaceHolders de la página maestra. Como se indicó en el tutorial ContentPlaceHolders múltiples y contenido de predeterminado, si un control de contenido no está presente, se emite el contenido ContentPlaceHolder predeterminado de la página maestra en su lugar. Dado que QuickLoginUI
y LeftColumnContent
ContentPlaceHolders contienen marcado predeterminado adecuado para esta página, continúe y quite sus controles de contenido correspondientes de IDIssues.aspx
. En este momento, el marcado declarativo de la página de contenido debe tener un aspecto similar al siguiente:
<%@ Page Language="VB" MasterPageFile="~/Site.master" AutoEventWireup="false" CodeFile="IDIssues.aspx.vb" Inherits="IDIssues" Title="Untitled Page" %>
<asp:Content ID="Content1" ContentPlaceHolderID="head" Runat="Server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" Runat="Server">
</asp:Content>
Recuerde que, en el tutorial Especificar el título, las etiquetas meta y otros encabezados HTML en la página maestra, creamos una clase de página base personalizada (BasePage
) que genera el título de la página si no se establece de manera explícita. Para que la página IDIssues.aspx
utilice esta funcionalidad, la clase de código subyacente de la página debe derivar de la clase BasePage
(en lugar de System.Web.UI.Page
). Modifique la definición de la clase de código subyacente para que tenga el siguiente aspecto:
Partial Class IDIssues
Inherits BasePage
End Class
Por último, actualice el archivo Web.sitemap
para incluir una entrada para esta lección. Agregue un elemento <siteMapNode>
y establezca sus atributos title
y url
en "Problemas de nomenclatura del identificador de control" y ~/IDIssues.aspx
, respectivamente. Después de realizar esta adición, el marcado del archivo Web.sitemap
debe ser similar al siguiente:
<?xml version="1.0" encoding="utf-8" ?>
<siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0" >
<siteMapNode url="~/Default.aspx" title="Home">
<siteMapNode url="~/About.aspx" title="About the Author" />
<siteMapNode url="~/MultipleContentPlaceHolders.aspx" title="Using Multiple ContentPlaceHolder Controls" />
<siteMapNode url="~/Admin/Default.aspx" title="Rebasing URLs" />
<siteMapNode url="~/IDIssues.aspx" title="Control ID Naming Issues" />
</siteMapNode>
</siteMap>
Como se muestra en la ilustración 2, la nueva entrada de mapa de sitio de Web.sitemap
se refleja inmediatamente en la sección Lecciones de la columna izquierda.
Ilustración 02: la sección Lecciones ahora incluye un vínculo a "Problemas de nomenclatura del identificador de control"
Paso 2: Examinar los cambios representadosID
Para comprender mejor las modificaciones que realiza el motor de ASP.NET en los valores id
representados de los controles de servidor, vamos a agregar algunos controles web a la página IDIssues.aspx
y, a continuación, veremos el marcado representado enviado al explorador. En concreto, escriba el texto "Escriba su edad:" seguido de un control Web TextBox. Más abajo en la página, agregue un control Web Button y un control Web Label. Establezca las propiedades ID
y Columns
de TextBox en Age
y 3, respectivamente. Establezca las propiedades Text
y ID
del botón en “Enviar” y SubmitButton
. Desactive la propiedad Text
de etiqueta y establezca su ID
en Results
.
En este momento, el marcado declarativo del control debe tener un aspecto similar al siguiente:
<p>
Please enter your age:
<asp:TextBox ID="Age" Columns="3" runat="server"></asp:TextBox>
</p>
<p>
<asp:Button ID="SubmitButton" runat="server" Text="Submit" />
</p>
<p>
<asp:Label ID="Results" runat="server"></asp:Label>
</p>
En la ilustración 3 se muestra la página cuando se ve a través del diseñador de Visual Studio.
Ilustración 03: la página incluye tres controles web: un TextBox, un botón y una etiqueta (Haga clic para ver la imagen de tamaño completo)
Visite la página a través de un explorador y, a continuación, vea el origen HTML. Como se muestra en el marcado siguiente, los valores id
de los elementos HTML de los controles Web TextBox, botón y etiqueta son una combinación de los valores ID
de los controles Web y los valores ID
de los contenedores de nomenclatura de la página.
<p>
Please enter your age:
<input name="ctl00$MainContent$Age" type="text" size="3" id="ctl00_MainContent_Age" />
</p>
<p>
<input type="submit" name="ctl00$MainContent$SubmitButton" value="Submit" id="ctl00_MainContent_SubmitButton" />
</p>
<p>
<span id="ctl00_MainContent_Results"></span>
</p>
Como se indicó anteriormente en este tutorial, tanto la página maestra como sus ContentPlaceHolders actúan como contenedores de nomenclatura. Por consiguiente, ambos contribuyen a los valores representados ID
de sus controles anidados. Tome el atributo id
de TextBox, por ejemplo: ctl00_MainContent_Age
. Recuerde que el valor ID
del control de TextBox era Age
. Lleva como prefijo el valor del ID
control ContentPlaceHolder, MainContent
. Además, este valor tiene como prefijo el valor ID
de la página maestra, ctl00
. El efecto neto es un valor de atributo id
que consta de los valores ID
de la página maestra, el control ContentPlaceHolder y el propio TextBox.
En la ilustración 4 se muestra este comportamiento. Para determinar la representación id
de Age
TextBox, comience con el valor ID
del control TextBox, Age
. A continuación, trabaje hacia arriba en la jerarquía de controles. En cada contenedor de nomenclatura (esos nodos de color melocotón), prefije el actual representado id
con el contenedor de nomenclatura id
.
Ilustración 04: los atributos id
representados se basan en los valores ID
de los contenedores de nomenclatura
Nota:
Como hemos explicado, la parte ctl00
del atributo id
representado constituye el valor ID
de la página maestra, pero es posible que se pregunte cómo se produjo este valor ID
. No se ha especificado en ninguna parte de nuestra página maestra o de contenido. La mayoría de los controles de servidor de una página de ASP.NET se agregan explícitamente a través del marcado declarativo de la página. El control MainContent
ContentPlaceHolder se especificó explícitamente en el marcado de Site.master
; el Age
TextBox se definió como marcado de IDIssues.aspx
. Podemos especificar los valores ID
de estos tipos de controles a través del ventana Propiedades o de la sintaxis declarativa. Otros controles, como la propia página maestra, no se definen en el marcado declarativo. Por lo tanto, sus valores ID
deben generarse automáticamente para nosotros. El motor de ASP.NET establece los valores ID
en tiempo de ejecución para esos controles cuyos identificadores no se han establecido explícitamente. Usa el patrón ctlXX
de nomenclatura, donde XX es un valor entero que aumenta secuencialmente.
Dado que la propia página maestra actúa como contenedor de nomenclatura, los controles web definidos en la página maestra también tienen valores de atributo id
representados modificados. Por ejemplo, la etiqueta DisplayDate
que agregamos a la página maestra en el tutorial Crear de un diseño para todo el sitio con páginas maestras tiene el siguiente marcado representado:
<span id="ctl00_DateDisplay">current date</span>
Tenga en cuenta que el atributo id
incluye el valor ID
de la página maestra (ctl00
) y el valor ID
del control Web de etiqueta (DateDisplay
).
Paso 3: hacer referencia mediante programación a controles web a través deFindControl
Cada control de servidor ASP.NET incluye un método FindControl("controlID")
que busca los descendientes del control para un control denominado controlID. Si se encuentra este control, se devuelve; si no se encuentra ningún control coincidente, FindControl
devuelve Nothing
.
FindControl
es útil en escenarios en los que necesita acceder a un control, pero no tiene una referencia directa a él. Cuando se trabaja con controles web de datos como GridView, por ejemplo, los controles dentro de los campos de GridView se definen una vez en la sintaxis declarativa, pero en tiempo de ejecución se crea una instancia del control para cada fila de GridView. Por lo tanto, existen los controles generados en tiempo de ejecución, pero no tenemos una referencia directa disponible en la clase de código subyacente. Como resultado, es necesario usar FindControl
para trabajar mediante programación con un control específico dentro de los campos de GridView. (Para obtener más información sobre el uso de FindControl
para acceder a los controles dentro de las plantillas de un control web de datos, consulte Formato personalizado basado en datos). Este mismo escenario se produce cuando se agregan controles web dinámicamente a un formulario web, un tema descrito en Crear interfaces de usuario de entrada de datos dinámicos.
Para ilustrar el uso del método FindControl
para buscar controles dentro de una página de contenido, cree un controlador de eventos para el evento Click
de SubmitButton
. En el controlador de eventos, agregue el siguiente código, que hace referencia mediante programación a TextBox Age
y Etiqueta Results
mediante el método FindControl
y, a continuación, muestra un mensaje en Results
en función de la entrada del usuario.
Nota:
Por supuesto, no es necesario usar FindControl
para hacer referencia a los controles de etiqueta y TextBox para este ejemplo. Podríamos hacer referencia a ellos directamente a través de sus valores de propiedad ID
. Uso FindControl
aquí para ilustrar lo que sucede cuando se usa FindControl
desde una página de contenido.
Protected Sub SubmitButton_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles SubmitButton.Click
Dim ResultsLabel As Label = CType(FindControl("Results"), Label)
Dim AgeTextBox As TextBox = CType(Page.FindControl("Age"), TextBox)
ResultsLabel.Text = String.Format("You are {0} years old!", AgeTextBox.Text)
End Sub
Aunque la sintaxis usada para llamar al método FindControl
difiere ligeramente en las dos primeras líneas de SubmitButton_Click
, son semánticamente equivalentes. Recuerde que todos los controles de servidor ASP.NET incluyen un método FindControl
. Esto incluye la clase Page
, de la que todas las clases de código subyacente ASP.NET deben derivarse. Por lo tanto, llamar FindControl("controlID")
a es equivalente a llamar a Page.FindControl("controlID")
, suponiendo que no haya reemplazado el método FindControl
en la clase de código subyacente o en una clase base personalizada.
Después de escribir este código, visite la página IDIssues.aspx
a través de un explorador, escriba su edad y haga clic en el botón "Enviar". Al hacer clic en el botón "Enviar" se genera un elemento NullReferenceException
(vea la ilustración 5).
Figura 05: se genera un NullReferenceException
(haga clic para ver la imagen a tamaño completo)
Si establece un punto de interrupción en el controlador de eventos SubmitButton_Click
, verá que ambas llamadas a FindControl
devuelven Nothing
. NullReferenceException
se genera cuando intentamos acceder a la propiedad TextBox Age
de Text
.
El problema es que Control.FindControl
solo busca los descendientes de Control que están en el mismo contenedor de nomenclatura. Dado que la página maestra constituye un nuevo contenedor de nomenclatura, una llamada a Page.FindControl("controlID")
nunca impregna el objeto ctl00
de la página maestra. (Consulte la ilustración 4 para ver la jerarquía de controles, que muestra el objeto Page
como elemento primario del objeto ctl00
de la página maestra). Por lo tanto, no se encuentran el Label Results
y TextBox Age
y ResultsLabel
y AgeTextBox
se asignan valores de Nothing
.
Hay dos soluciones alternativas a este desafío: podemos explorar en profundidad, un contenedor de nomenclatura a la vez, para el control adecuado; o podemos crear nuestro propio método FindControl
que permee los contenedores de nomenclatura. Vamos a examinar cada una de estas opciones.
Exploración en profundidad del contenedor de nomenclatura adecuado
Para usar FindControl
para hacer referencia a etiqueta Results
o TextBox Age
, es necesario llamar a FindControl
desde un control anterior en el mismo contenedor de nomenclatura. Como se muestra en la ilustración 4, el control ContentPlaceHolder MainContent
es el único antecesor de Results
o Age
que está dentro del mismo contenedor de nomenclatura. En otras palabras, llamar al método FindControl
desde el control MainContent
, como se muestra en el fragmento de código siguiente, devuelve correctamente una referencia a los controles Results
o Age
.
Dim ResultsLabel As Label = CType(MainContent.FindControl("Results"), Label)
Dim AgeTextBox As TextBox = CType(MainContent.FindControl("Age"), TextBox)
Sin embargo, no podemos trabajar con ContentPlaceHolder MainContent
desde la clase de código subyacente de la página de contenido con la sintaxis anterior porque ContentPlaceHolder se define en la página maestra. En su lugar, tenemos que usar FindControl
para obtener una referencia a MainContent
. Reemplace el código del controlador de eventos SubmitButton_Click
por las siguientes modificaciones:
Protected Sub SubmitButton_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles SubmitButton.Click
Dim MainContent As ContentPlaceHolder = CType(FindControl("MainContent"), ContentPlaceHolder)
Dim ResultsLabel As Label = CType(MainContent.FindControl("Results"), Label)
Dim AgeTextBox As TextBox = CType(MainContent.FindControl("Age"), TextBox)
ResultsLabel.Text = String.Format("You are {0} years old!", AgeTextBox.Text)
End Sub
Si visita la página a través de un explorador, escriba su edad y haga clic en el botón "Enviar", se genera un NullReferenceException
. Si establece un punto de interrupción en el controlador de eventos SubmitButton_Click
, verá que esta excepción se produce al intentar llamar al métodoFindControl
objeto MainContent
. El objeto MainContent
es igual a Nothing
porque el método FindControl
no puede encontrar un objeto denominado "MainContent". La razón subyacente es la misma que con los controles de etiqueta Results
y TextBox Age
: FindControl
inicia su búsqueda desde la parte superior de la jerarquía de controles y no penetra en los contenedores de nomenclatura, pero ContentPlaceHolder MainContent
está dentro de la página maestra, que es un contenedor de nomenclatura.
Antes de poder usar FindControl
para obtener una referencia a MainContent
, primero necesitamos una referencia al control de página maestra. Una vez que tengamos una referencia a la página maestra, podemos obtener una referencia a MainContent
ContentPlaceHolder a través FindControl
de y, desde allí, referencias a Results
Label y Age
TextBox (de nuevo, mediante FindControl
). ¿Pero cómo obtenemos una referencia a la página maestra? Al inspeccionar los id
atributos del marcado representado, es evidente que el valor de ID
la página maestra es ctl00
. Por lo tanto, podríamos usar Page.FindControl("ctl00")
para obtener una referencia a la página maestra y, a continuación, usar ese objeto para obtener una referencia a MainContent
, etc. En el fragmento de código siguiente se muestra esta lógica:
'Get a reference to the master page
Dim ctl00 As MasterPage = CType(FindControl("ctl00"), MasterPage)
'Get a reference to the ContentPlaceHolder
Dim MainContent As ContentPlaceHolder = CType(ctl00.FindControl("MainContent"), ContentPlaceHolder)
'Reference the Label and TextBox controls
Dim ResultsLabel As Label = CType(MainContent.FindControl("Results"), Label)
Dim AgeTextBox As TextBox = CType(MainContent.FindControl("Age"), TextBox)
Aunque este código funcionará sin duda, supone que la página maestra generada ID
automáticamente siempre será ctl00
. Nunca es buena idea realizar suposiciones sobre los valores generados automáticamente.
Afortunadamente, se puede acceder a una referencia a la página maestra a través de la propiedad Page
de la clase Master
. Por lo tanto, en lugar de tener que usar FindControl("ctl00")
para obtener una referencia de la página maestra para tener acceso a ContentPlaceHolder MainContent
, podemos usar Page.Master.FindControl("MainContent")
en su lugar. Actualice el control de eventos SubmitButton_Click
con el siguiente código:
Protected Sub SubmitButton_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles SubmitButton.Click
'Get a reference to the ContentPlaceHolder
Dim MainContent As ContentPlaceHolder = CType(Page.Master.FindControl("MainContent"), ContentPlaceHolder)
'Reference the Label and TextBox controls
Dim ResultsLabel As Label = CType(MainContent.FindControl("Results"), Label)
Dim AgeTextBox As TextBox = CType(MainContent.FindControl("Age"), TextBox)
ResultsLabel.Text = String.Format("You are {0} years old!", AgeTextBox.Text)
End Sub
Esta vez, visitar la página a través de un explorador, escribir su edad y hacer clic en el botón "Enviar" muestra el mensaje en la etiqueta Results
, según lo previsto.
Ilustración 06: la edad del usuario se muestra en la etiqueta (haga clic para ver la imagen a tamaño completo)
Búsqueda recursiva mediante contenedores de nomenclatura
La razón por la que el ejemplo de código anterior hace referencia al control ContentPlaceHolder MainContent
desde la página maestra y, a continuación, los controles de etiqueta Results
y TextBox Age
de MainContent
, se debe a que el método Control.FindControl
solo busca dentro del contenedor de nomenclatura de Control. Que FindControl
se mantenga dentro del contenedor de nomenclatura tiene sentido en la mayoría de los escenarios, ya que dos controles de dos contenedores de nomenclatura diferentes pueden tener los mismos valores ID
. Tenga en cuenta el caso de un control GridView que define un control Web de etiqueta denominado ProductName
dentro de uno de sus TemplateFields. Cuando los datos se enlazan a GridView en tiempo de ejecución, se crea una etiqueta ProductName
para cada fila de GridView. Si FindControl
se busca en todos los contenedores de nomenclatura y se llama a Page.FindControl("ProductName")
, ¿qué instancia de etiqueta debe devolver FindControl
? ¿La etiqueta ProductName
de la primera fila de GridView? ¿La de la última fila?
Por lo tanto, que Control.FindControl
busque solo el contenedor de nomenclatura de Control tiene sentido en la mayoría de los casos. Pero hay otros casos, como el que nos atañe, donde tenemos un único ID
en todos los contenedores de nomenclatura y queremos evitar tener que hacer referencia minuciosamente a cada contenedor de nomenclatura de la jerarquía de control para acceder a un control. Tener una variante FindControl
que busca recursivamente todos los contenedores de nomenclatura también tiene sentido. Desafortunadamente, .NET Framework no incluye este método.
La buena noticia es que podemos crear nuestro propio método FindControl
que busca recursivamente todos los contenedores de nomenclatura. De hecho, el uso de métodos de extensión se puede poner en un método FindControlRecursive
a la clase Control
para acompañar su método FindControl
existente.
Nota:
Los métodos de extensión son una característica nueva de C# 3.0 y Visual Basic 9, que son los lenguajes que se incluyen con .NET Framework versión 3.5 y Visual Studio 2008. En resumen, los métodos de extensión permiten a un desarrollador crear un nuevo método para un tipo de clase existente a través de una sintaxis especial. Para obtener más información sobre esta característica útil, consulte mi artículo Extending Base Type Functionality with Extension Methods (Extender la funcionalidad de tipo base con métodos de extensión).
Para crear el método de extensión, agregue un nuevo archivo a la carpeta App_Code
denominada PageExtensionMethods.vb
. Agregue un método de extensión denominado FindControlRecursive
que toma como entrada un parámetro String
denominado controlID
. Para que los métodos de extensión funcionen correctamente, es fundamental que la clase se marque como Module
y que los métodos de extensión estén prefijos con el atributo <Extension()>
. Además, todos los métodos de extensión deben aceptar como primer parámetro un objeto del tipo al que se aplica el método de extensión.
Agregue el siguiente código al archivo PageExtensionMethods.vb
para definir esto Module
y el método de extensión FindControlRecursive
:
Imports System.Runtime.CompilerServices
Public Module PageExtensionMethods
<Extension()> _
Public Function FindControlRecursive(ByVal ctrl As Control, ByVal controlID As String) As Control
If String.Compare(ctrl.ID, controlID, True) = 0 Then
' We found the control!
Return ctrl
Else
' Recurse through ctrl's Controls collections
For Each child As Control In ctrl.Controls
Dim lookFor As Control = FindControlRecursive(child, controlID)
If lookFor IsNot Nothing Then
Return lookFor ' We found the control
End If
Next
' If we reach here, control was not found
Return Nothing
End If
End Function
End Module
Con este código en su lugar, vuelva a la clase de código subyacente de la página IDIssues.aspx
y convierta en comentario las llamadas al método FindControl
actual. Reemplácelas por llamadas a Page.FindControlRecursive("controlID")
. Lo bueno de los métodos de extensión es que aparecen directamente en las listas desplegables de IntelliSense. Como se muestra en la figura 7, al escribir Page
y, a continuación, alcanzar el punto, el método FindControlRecursive
se incluye en la lista desplegable IntelliSense junto con los otros métodos de clase Control
.
Ilustración 07: los métodos de extensión se incluyen en las listas desplegables de IntelliSense (haga clic para ver la imagen a tamaño completo)
Escriba el código siguiente en el controlador de eventos SubmitButton_Click
y, a continuación, pruébelo visitando la página, escribiendo su edad y haciendo clic en el botón "Enviar". Como se muestra en la ilustración 6, la salida resultante será el mensaje "Tiene años"
Protected Sub SubmitButton_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles SubmitButton.Click
Dim ResultsLabel As Label = CType(Page.FindControlRecursive("Results"), Label)
Dim AgeTextBox As TextBox = CType(Page.FindControlRecursive("Age"), TextBox)
ResultsLabel.Text = String.Format("You are {0} years old!", AgeTextBox.Text)
End Sub
Nota:
Dado que los métodos de extensión son nuevos en C# 3.0 y Visual Basic 9, si usa Visual Studio 2005, no podrá usar métodos de extensión. En su lugar, deberá implementar el FindControlRecursive
método en una clase auxiliar. Rick Strahl tiene un ejemplo de este tipo en su entrada de blog, Páginas maestras ASP.NET y FindControl
.
Paso 4: Usar el valor de atributo correctoid
en el script del lado cliente
Como se indicó en la introducción de este tutorial, el atributo representado id
de un control web suele usarse en el script del lado cliente para hacer referencia mediante programación a un elemento HTML determinado. Por ejemplo, el siguiente JavaScript hace referencia a un elemento HTML por su id
y, a continuación, muestra su valor en un cuadro de mensaje modal:
var elem = document.getElementById("Age");
if (elem != null)
alert("You entered " + elem.value + " into the Age text box.");
Recuerde que en ASP.NET páginas que no incluyen un contenedor de nomenclatura, el atributo del id
elemento HTML representado es idéntico al valor de propiedad del ID
control Web. Por este motivo, es tentador codificar de forma rígida los valores de atributo en id
código JavaScript. Es decir, si sabe que desea acceder al Age
control web TextBox a través del script del lado cliente, hágalo a través de una llamada a document.getElementById("Age")
.
El problema con este enfoque es que al usar páginas maestras (u otros controles de contenedor de nomenclatura), el HTML id
representado no es sinónimo de la propiedad del ID
control Web. Su primera inclinación puede ser visitar la página a través de un explorador y ver el origen para determinar el atributo real id
. Una vez que conozca el valor representado id
, puede pegarlo en la llamada a para getElementById
acceder al elemento HTML con el que debe trabajar a través del script del lado cliente. Este enfoque es menor que ideal porque ciertos cambios en la jerarquía de control de la página o los cambios en las ID
propiedades de los controles de nomenclatura modificarán el atributo resultante id
, lo que interrumpirá el código de JavaScript.
La bueno es que el valor de atributo id
que se representa es accesible en el código del lado servidor a través de la propiedad ClientID
del control Web. Debe usar esta propiedad para determinar el valor de id
atributo usado en el script del lado cliente. Por ejemplo, para agregar una función de JavaScript a la página que, cuando se llama, muestra el valor de TextBox Age
en un cuadro de mensaje modal, agregue el código siguiente al controlador de eventos Page_Load
:
ClientScript.RegisterClientScriptBlock(Me.GetType(), "ShowAgeTextBoxScript", _
"function ShowAge() " & vbCrLf & _
"{" & vbCrLf & _
" var elem = document.getElementById('" & AgeTextBox.ClientID & "');" & vbCrLf & _
" if (elem != null)" & vbCrLf & _
" alert('You entered ' + elem.value + ' into the Age text box.');" & vbCrLf & _
"}", True)
El código anterior inserta el valor de la propiedad TextBox Age
ClientID
en la llamada de JavaScript a getElementById
. Si visita esta página a través de un explorador y ve el origen HTML, encontrará el siguiente código JavaScript:
<script type="text/javascript">
//<![CDATA[
function ShowAge()
{
var elem = document.getElementById('ctl00_MainContent_Age');
if (elem != null)
alert('You entered ' + elem.value + ' into the Age text box.');
}//]]>
</script>
Observe cómo aparece el valor de atributo id
correcto, ctl00_MainContent_Age
, dentro de la llamada a getElementById
. Dado que este valor se calcula en tiempo de ejecución, funciona independientemente de los cambios posteriores en la jerarquía de controles de página.
Nota:
Este ejemplo de JavaScript simplemente muestra cómo agregar una función de JavaScript que hace referencia correctamente al elemento HTML representado por un control de servidor. Para usar esta función, tendría que crear JavaScript adicional para llamar a la función cuando se cargue el documento o cuando transcurra alguna acción de usuario específica. Para obtener más información sobre estos temas y relacionados, lea Trabajar con script del lado cliente.
Resumen
Algunos controles de servidor ASP.NET actúan como contenedores de nomenclatura, lo que afecta a los valores de atributo representados id
de sus controles descendientes, así como al ámbito de los controles en lienzo por el FindControl
método. En lo que respecta a las páginas maestras, tanto la propia página maestra como sus controles ContentPlaceHolder son contenedores de nomenclatura. Por lo tanto, es necesario poner un poco más de trabajo para hacer referencia mediante programación a los controles dentro de la página de contenido mediante FindControl
. En este tutorial hemos examinado dos técnicas: profundizar en el control ContentPlaceHolder y llamar a su FindControl
método; y rodar nuestra propia FindControl
implementación que busca recursivamente en todos los contenedores de nomenclatura.
Además de los problemas del lado servidor, los contenedores de nomenclatura presentan con respecto a las referencias a controles web, también hay problemas del lado cliente. En ausencia de contenedores de nomenclatura, el valor de propiedad ID
del control web y el valor de atributo id
representado son uno en el mismo. Sin embargo, con la adición de un contenedor de nombres, el atributo id
representado incluye los valores ID
del control web y los contenedores de nomenclatura en su jerarquía de controles. Estos problemas de nomenclatura son un problema siempre y cuando use la propiedad ClientID
del control web para determinar el valor de atributo id
representado en el script del lado cliente.
¡Feliz programación!
Lecturas adicionales
Para obtener más información sobre los temas tratados en este tutorial, consulte los siguientes recursos:
- Páginas maestras de ASP.NET y
FindControl
- Creación de interfaces de usuario de entrada de datos dinámicos
- Procedimiento: Hacer referencia al contenido de la página maestra de ASP.NET
- Páginas maestras: recomendaciones, trucos y trampas
- Trabajar con script del lado cliente
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. Los revisores principales de este tutorial fueron Zack Jones y Suchi Barnerjee. ¿Le interesaría revisar mis próximos artículos de MSDN? Si es así, escríbame mitchell@4GuysFromRolla.com.