Controlar nomenclatura de ID em páginas de conteúdo (VB)
por Scott Mitchell
Ilustra como os controles ContentPlaceHolder servem como um contêiner de nomenclatura e, portanto, dificultam o trabalho programático com um controle (por meio de FindControl). Analisa esse problema e as soluções alternativas. Também discute como acessar programaticamente o valor ClientID resultante.
Introdução
Todos os controles de servidor ASP.NET incluem uma ID
propriedade que identifica exclusivamente o controle e é o meio pelo qual o controle é acessado programaticamente na classe code-behind. Da mesma forma, os elementos em um documento HTML podem incluir um id
atributo que identifica exclusivamente o elemento; esses id
valores são frequentemente usados no script do lado do cliente para referenciar programaticamente um elemento HTML específico. Diante disso, você pode supor que, quando um controle de servidor ASP.NET é renderizado em HTML, seu ID
valor é usado como o id
valor do elemento HTML renderizado. Esse não é necessariamente o caso porque, em determinadas circunstâncias, um único controle com um único ID
valor pode aparecer várias vezes na marcação renderizada. Considere um controle GridView que inclui um TemplateField com um controle Web Label com um ID
valor de ProductName
. Quando o GridView é associado à sua fonte de dados em runtime, esse Label é repetido uma vez para cada linha do GridView. Cada Label renderizado precisa de um valor exclusivo id
.
Para lidar com esses cenários, ASP.NET permite que determinados controles sejam indicados como contêineres de nomenclatura. Um contêiner de nomenclatura serve como um novo ID
namespace. Todos os controles de servidor que aparecem no contêiner de nomenclatura têm seu valor renderizado id
prefixado com o ID
controle do contêiner de nomenclatura. Por exemplo, as GridView
classes e GridViewRow
são contêineres de nomenclatura. Consequentemente, um controle Label definido em um GridView TemplateField com ID
ProductName
recebe um valor renderizado id
de GridViewID_GridViewRowID_ProductName
. Como GridViewRowID é exclusivo para cada linha GridView, os valores resultantes id
são exclusivos.
Observação
A INamingContainer
interface é usada para indicar que um determinado controle de servidor ASP.NET deve funcionar como um contêiner de nomenclatura. A INamingContainer
interface não especifica nenhum método que o controle do servidor deva implementar; em vez disso, ela é usada como um marcador. Ao gerar a marcação renderizada, se um controle implementar essa interface, o mecanismo ASP.NET prefixará automaticamente seu ID
valor aos valores de atributo renderizados id
de seus descendentes. Esse processo é discutido com mais detalhes na Etapa 2.
Os contêineres de nomenclatura não apenas alteram o valor do atributo renderizado id
, mas também afetam como o controle pode ser referenciado programaticamente da classe code-behind da página ASP.NET. O FindControl("controlID")
método é comumente usado para referenciar programaticamente um controle Web. No entanto, FindControl
não penetra por meio de contêineres de nomenclatura. Consequentemente, você não pode usar diretamente o Page.FindControl
método para fazer referência a controles em um GridView ou outro contêiner de nomenclatura.
Como você deve ter adivinhado, as páginas mestras e os ContentPlaceHolders são implementados como contêineres de nomenclatura. Neste tutorial, examinamos como as páginas mestras afetam os valores dos elementos id
HTML e as maneiras de referenciar programaticamente os controles da Web em uma página de conteúdo usando FindControl
o .
Etapa 1: adicionar uma nova página ASP.NET
Para demonstrar os conceitos discutidos neste tutorial, vamos adicionar uma nova página ASP.NET ao nosso site. Crie uma nova página de conteúdo nomeada IDIssues.aspx
na pasta raiz, vinculando-a Site.master
à página mestra.
Figura 01: Adicionar a página IDIssues.aspx
de conteúdo à pasta raiz
O Visual Studio cria automaticamente um controle de conteúdo para cada um dos quatro ContentPlaceHolders da página mestra. Conforme observado no tutorial Vários ContentPlaceHolders e Conteúdo Padrão, se um controle Content não estiver presente, o conteúdo ContentPlaceHolder padrão da página mestra será emitido. Como o e LeftColumnContent
ContentPlaceHolders QuickLoginUI
contêm marcação padrão adequada para esta página, vá em frente e remova seus controles de conteúdo correspondentes do IDIssues.aspx
. Neste ponto, a marcação declarativa da página de conteúdo deve ter a seguinte aparência:
<%@ 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>
No tutorial Especificando o título, as metatags e outros cabeçalhos HTML na página mestra, criamos uma classe de página base personalizada (BasePage
) que configura automaticamente o título da página se ela não estiver definida explicitamente. Para que a IDIssues.aspx
página empregue essa funcionalidade, a classe code-behind da página deve derivar da BasePage
classe (em vez de System.Web.UI.Page
). Modifique a definição da classe code-behind para que ela se pareça com o seguinte:
Partial Class IDIssues
Inherits BasePage
End Class
Por fim, atualize o Web.sitemap
arquivo para incluir uma entrada para esta nova lição. Adicione um <siteMapNode>
elemento e defina seus title
atributos e url
como "Problemas de nomenclatura de ID de controle" e ~/IDIssues.aspx
, respectivamente. Depois de fazer essa adição, a marcação do arquivo Web.sitemap
deve ser semelhante à seguinte:
<?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 ilustra a Figura 2, a nova entrada do mapa do site é Web.sitemap
imediatamente refletida na seção Lições na coluna da esquerda.
Figura 02: A seção Lições agora inclui um link para "Problemas de nomenclatura de ID de controle"
Etapa 2: Examinando as alterações renderizadasID
Para entender melhor as modificações que o mecanismo de ASP.NET faz nos valores renderizados id
dos controles de servidor, vamos adicionar alguns controles Web à IDIssues.aspx
página e, em seguida, exibir a marcação renderizada enviada ao navegador. Especificamente, digite o texto "Insira sua idade:" seguido por um controle Web TextBox. Mais abaixo na página, adicione um controle Web Button e um controle Web Label. Defina as propriedades e TextBox ID
Columns
como Age
e 3, respectivamente. Defina as propriedades do Text
botão e ID
como "Enviar" e SubmitButton
. Limpe a propriedade do Text
rótulo e defina como ID
Results
.
Neste ponto, a marcação declarativa do controle de conteúdo deve ser semelhante à seguinte:
<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>
A Figura 3 mostra a página quando exibida por meio do designer do Visual Studio.
Figura 03: A página inclui três controles Web: um TextBox, um botão e um rótulo (clique para exibir a imagem em tamanho real)
Visite a página por meio de um navegador e exiba o código-fonte HTML. Como mostra a marcação abaixo, os id
valores dos elementos HTML para os controles Web TextBox, Button e Label são uma combinação dos ID
valores dos controles Web e dos ID
valores dos contêineres de nomenclatura na 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>
Conforme observado anteriormente neste tutorial, a página mestra e seus ContentPlaceHolders servem como contêineres de nomenclatura. Consequentemente, ambos contribuem com os valores renderizados ID
de seus controles aninhados. Veja o atributo do id
TextBox, por exemplo: ctl00_MainContent_Age
. Lembre-se de que o valor do ID
controle TextBox era Age
. Isso é prefixado com o valor do ID
controle ContentPlaceHolder, MainContent
. Além disso, esse valor é prefixado com o valor da ID
página mestra, ctl00
. O efeito líquido é um id
valor de atributo que consiste nos ID
valores da página mestra, do controle ContentPlaceHolder e do próprio TextBox.
A Figura 4 ilustra esse comportamento. Para determinar a renderização id
do Age
TextBox, comece com o ID
valor do controle TextBox, Age
. Em seguida, suba na hierarquia de controle. Em cada contêiner de nomenclatura (aqueles nós com uma cor de pêssego), prefixe o renderizado id
atual com o contêiner de id
nomenclatura .
Figura 04: Os atributos renderizados id
são baseados nos ID
valores dos contêineres de nomenclatura
Observação
Como discutimos, a ctl00
parte do atributo renderizado id
constitui o ID
valor da página mestra, mas você pode estar se perguntando como esse ID
valor surgiu. Não o especificamos em nenhum lugar em nossa página mestra ou de conteúdo. A maioria dos controles de servidor em uma página ASP.NET é adicionada explicitamente por meio da marcação declarativa da página. O MainContent
controle ContentPlaceHolder foi especificado explicitamente na marcação de ; a Age
marcação de Site.master
TextBox foi definidaIDIssues.aspx
. Podemos especificar os ID
valores para esses tipos de controles por meio da janela Propriedades ou da sintaxe declarativa. Outros controles, como a própria página mestra, não são definidos na marcação declarativa. Consequentemente, seus ID
valores devem ser gerados automaticamente para nós. O mecanismo ASP.NET define os ID
valores em tempo de execução para os controles cujas IDs não foram definidas explicitamente. Ele usa o padrão ctlXX
de nomenclatura , onde XX é um valor inteiro sequencialmente crescente.
Como a própria página mestra serve como um contêiner de nomenclatura, os controles Web definidos na página mestra também alteraram os valores de atributo renderizados id
. Por exemplo, o DisplayDate
rótulo que adicionamos à página mestra no tutorial Criando um layout em todo o site com páginas mestras tem a seguinte marcação renderizada:
<span id="ctl00_DateDisplay">current date</span>
Observe que o id
atributo inclui o valor da ID
página mestra (ctl00
) e o ID
valor do controle Web Label (DateDisplay
).
Etapa 3: Referenciando programaticamente controles Web por meio deFindControl
Cada controle de servidor ASP.NET inclui um FindControl("controlID")
método que pesquisa os descendentes do controle em busca de um controle chamado controlID. Se esse controle for encontrado, ele será retornado; Se nenhum controle correspondente for encontrado, FindControl
retornará Nothing
.
FindControl
é útil em cenários em que você precisa acessar um controle, mas não tem uma referência direta a ele. Ao trabalhar com controles Web de dados como o GridView, por exemplo, os controles dentro dos campos do GridView são definidos uma vez na sintaxe declarativa, mas em tempo de execução uma instância do controle é criada para cada linha GridView. Consequentemente, os controles gerados em tempo de execução existem, mas não temos uma referência direta disponível na classe code-behind. Como resultado, precisamos usar FindControl
para trabalhar programaticamente com um controle específico dentro dos campos do GridView. (Para obter mais informações sobre como usar FindControl
para acessar os controles nos modelos de um controle Web de dados, consulte Formatação personalizada com base em dados.) Esse mesmo cenário ocorre ao adicionar dinamicamente controles da Web a um formulário da Web, um tópico discutido em Criando interfaces de usuário de entrada de dados dinâmicos.
Para ilustrar o uso do FindControl
método para pesquisar controles em uma página de conteúdo, crie um manipulador de eventos para o SubmitButton
evento do Click
. No manipulador de eventos, adicione o código a seguir, que faz referência programaticamente ao TextBox e Results
ao Age
Label usando o FindControl
método e, em seguida, exibe uma mensagem com base na Results
entrada do usuário.
Observação
É claro que não precisamos usar FindControl
para fazer referência aos controles Label e TextBox para este exemplo. Poderíamos referenciá-los diretamente por meio de seus ID
valores de propriedade. Eu uso FindControl
aqui para ilustrar o que acontece ao usar FindControl
a partir de uma página de conteúdo.
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
Embora a sintaxe usada para chamar o FindControl
método seja ligeiramente diferente nas duas primeiras linhas de SubmitButton_Click
, elas são semanticamente equivalentes. Lembre-se de que todos os controles de servidor ASP.NET incluem um FindControl
método. Isso inclui a Page
classe da qual todas as ASP.NET classes code-behind devem derivar. Portanto, chamar FindControl("controlID")
é equivalente a chamar Page.FindControl("controlID")
, supondo que você não tenha substituído o FindControl
método em sua classe code-behind ou em uma classe base personalizada.
Depois de inserir este código, visite a página por meio de IDIssues.aspx
um navegador, insira sua idade e clique no botão "Enviar". Ao clicar no botão "Enviar", um NullReferenceException
é levantado (consulte a Figura 5).
Figura 05: A NullReferenceException
é elevado (clique para exibir a imagem em tamanho real)
Se você definir um ponto de interrupção no SubmitButton_Click
manipulador de eventos, verá que ambas as chamadas retornam FindControl
Nothing
. O NullReferenceException
é gerado quando tentamos acessar a Age
propriedade do Text
TextBox.
O problema é que Control.FindControl
pesquisa apenas os descendentes do Control que estão no mesmo contêiner de nomenclatura. Como a página mestra constitui um novo contêiner de nomenclatura, uma chamada para Page.FindControl("controlID")
nunca permeia o objeto ctl00
de página mestra. (Consulte a Figura 4 para exibir a hierarquia de controle, que mostra o Page
objeto como o pai do objeto ctl00
de página mestra.) Portanto, o Label e o Results
TextBox não são encontrados e AgeTextBox
ResultsLabel
recebem valores de Nothing
.Age
Há duas soluções alternativas para esse desafio: podemos fazer uma busca detalhada, um contêiner de nomenclatura por vez, para o controle apropriado; Ou podemos criar nosso próprio FindControl
método que permeia a nomenclatura de contêineres. Vamos examinar cada uma dessas opções.
Detalhamento do contêiner de nomenclatura apropriado
Para usar FindControl
para referenciar o Label ou Age
TextBoxResults
, precisamos chamar FindControl
de um controle ancestral no mesmo contêiner de nomenclatura. Como a Figura 4 mostrou, o MainContent
controle ContentPlaceHolder é o único ancestral de ou Age
que está dentro do mesmo contêiner de Results
nomenclatura. Em outras palavras, chamar o FindControl
método do controle, conforme mostrado no trecho de MainContent
código abaixo, retorna corretamente uma referência aos Results
controles or Age
.
Dim ResultsLabel As Label = CType(MainContent.FindControl("Results"), Label)
Dim AgeTextBox As TextBox = CType(MainContent.FindControl("Age"), TextBox)
No entanto, não podemos trabalhar com o MainContent
ContentPlaceHolder da classe code-behind da nossa página de conteúdo usando a sintaxe acima porque o ContentPlaceHolder é definido na página mestra. Em vez disso, temos que usar FindControl
para obter uma referência a MainContent
. Substitua o SubmitButton_Click
código no manipulador de eventos pelas seguintes modificações:
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
Se você visitar a página por meio de um navegador, digite sua idade e clique no botão "Enviar", um NullReferenceException
será exibido. Se você definir um ponto de interrupção no SubmitButton_Click
manipulador de eventos, verá que essa exceção ocorre ao tentar chamar o MainContent
método do FindControl
objeto. O MainContent
objeto é igual a Nothing
porque o FindControl
método não pode localizar um objeto chamado "MainContent". O motivo subjacente é o mesmo dos Results
controles Label e Age
TextBox: FindControl
inicia sua pesquisa na parte superior da hierarquia de controle e não penetra nos contêineres de nomenclatura, mas o MainContent
ContentPlaceHolder está dentro da página mestra, que é um contêiner de nomenclatura.
Antes de podermos usar FindControl
para obter uma referência a MainContent
, primeiro precisamos de uma referência ao controle de página mestra. Assim que tivermos uma referência à página mestra, podemos obter uma referência ao MainContent
ContentPlaceHolder por meio FindControl
e, a partir daí, referências ao Label e Age
ao Results
TextBox (novamente, usando FindControl
). Mas como obtemos uma referência à página mestra? Ao inspecionar os id
atributos na marcação renderizada, é evidente que o valor da ID
página mestra é ctl00
. Portanto, poderíamos usar Page.FindControl("ctl00")
para obter uma referência à página mestra e, em seguida, usar esse objeto para obter uma referência a MainContent
, e assim por diante. O snippet a seguir ilustra essa 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)
Embora esse código certamente funcione, ele pressupõe que a página ID
mestra gerada automaticamente sempre será ctl00
. Nunca é uma boa ideia fazer suposições sobre valores gerados automaticamente.
Felizmente, uma referência à página mestra pode ser acessada por meio da Page
propriedade da Master
classe. Portanto, em vez de ter que usar FindControl("ctl00")
para obter uma referência da página mestra para acessar o MainContent
ContentPlaceHolder, podemos usar Page.Master.FindControl("MainContent")
. Atualize o manipulador de SubmitButton_Click
eventos com o seguinte 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
Desta vez, visitar a página por meio de um navegador, inserir sua idade e clicar no botão "Enviar" exibe a Results
mensagem no rótulo, conforme esperado.
Figura 06: A idade do usuário é exibida no rótulo (clique para exibir a imagem em tamanho real)
Pesquisando recursivamente por meio de contêineres de nomenclatura
O motivo pelo qual o exemplo de código anterior fazia referência ao MainContent
controle ContentPlaceHolder da página mestra e, em seguida, aos Results
controles Label e Age
TextBox de MainContent
, é porque o Control.FindControl
método pesquisa apenas no contêiner de nomenclatura do Control. Ter FindControl
permanecido dentro do contêiner de nomenclatura faz sentido na maioria dos cenários porque dois controles em dois contêineres de nomenclatura diferentes podem ter os mesmos ID
valores. Considere o caso de um GridView que define um controle Web Label nomeado ProductName
em um de seus TemplateFields. Quando os dados são associados ao GridView em runtime, um ProductName
Label é criado para cada linha do GridView. Se FindControl
pesquisado em todos os contêineres de nomenclatura e chamarmos Page.FindControl("ProductName")
, qual instância de FindControl
Label deve retornar? O ProductName
Rótulo na primeira linha do GridView? Aquele na última linha?
Portanto, ter Control.FindControl
a pesquisa apenas no contêiner de nomenclatura do Control faz sentido na maioria dos casos. Mas há outros casos, como o que enfrentamos, em que temos um exclusivo ID
em todos os contêineres de nomenclatura e queremos evitar ter que referenciar meticulosamente cada contêiner de nomenclatura na hierarquia de controle para acessar um controle. Ter uma FindControl
variante que pesquisa recursivamente todos os contêineres de nomenclatura também faz sentido. Infelizmente, o .NET Framework não inclui esse método.
A boa notícia é que podemos criar nosso próprio FindControl
método que pesquisa recursivamente todos os contêineres de nomenclatura. Na verdade, usando métodos de extensão, podemos adicionar um FindControlRecursive
método à Control
classe para acompanhar seu método existente FindControl
.
Observação
Os métodos de extensão são um recurso novo do C# 3.0 e do Visual Basic 9, que são as linguagens fornecidas com o .NET Framework versão 3.5 e o Visual Studio 2008. Resumindo, os métodos de extensão permitem que um desenvolvedor crie um novo método para um tipo de classe existente por meio de uma sintaxe especial. Para obter mais informações sobre esse recurso útil, consulte meu artigo, Estendendo a funcionalidade de tipo base com métodos de extensão.
Para criar o método de extensão, adicione um novo arquivo à App_Code
pasta chamada PageExtensionMethods.vb
. Adicione um método de extensão chamado FindControlRecursive
que usa como entrada um String
parâmetro chamado controlID
. Para que os métodos de extensão funcionem corretamente, é vital que a classe seja marcada como a Module
e que os métodos de extensão sejam prefixados com o <Extension()>
atributo. Além disso, todos os métodos de extensão devem aceitar como primeiro parâmetro um objeto do tipo ao qual o método de extensão se aplica.
Adicione o seguinte código ao PageExtensionMethods.vb
arquivo para definir isso Module
e o método de FindControlRecursive
extensão:
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
Com esse código em vigor, retorne à IDIssues.aspx
classe code-behind da página e comente as chamadas de método atuais FindControl
. Substitua-os por chamadas para Page.FindControlRecursive("controlID")
. O que é interessante sobre os métodos de extensão é que eles aparecem diretamente nas listas suspensas do IntelliSense. Como mostra a Figura 7, quando você digita Page
e pressiona o ponto, o FindControlRecursive
método é incluído na lista suspensa do IntelliSense junto com os outros Control
métodos de classe.
Figura 07: Métodos de extensão incluídos nas listas suspensas do IntelliSense (clique para exibir a imagem em tamanho real)
Insira o código a seguir no manipulador de SubmitButton_Click
eventos e teste-o visitando a página, inserindo sua idade e clicando no botão "Enviar". Como mostrado na Figura 6, a saída resultante será a mensagem: "Você tem anos de idade!"
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
Observação
Como os métodos de extensão são novos no C# 3.0 e no Visual Basic 9, se você estiver usando o Visual Studio 2005, não poderá usar métodos de extensão. Em vez disso, você precisará implementar o FindControlRecursive
método em uma classe auxiliar. Rick Strahl tem esse exemplo em sua postagem no blog, ASP.NET Maser Pages e FindControl
.
Etapa 4: Usando o valor de atributo corretoid
no script do lado do cliente
Conforme observado na introdução deste tutorial, o atributo renderizado id
de um controle da Web geralmente é usado no script do lado do cliente para referenciar programaticamente um elemento HTML específico. Por exemplo, o JavaScript a seguir faz referência a um elemento HTML por seu id
e, em seguida, exibe seu valor em uma caixa de mensagem modal:
var elem = document.getElementById("Age");
if (elem != null)
alert("You entered " + elem.value + " into the Age text box.");
Lembre-se de que, em páginas ASP.NET que não incluem um contêiner de nomenclatura, o atributo do id
elemento HTML renderizado é idêntico ao valor da propriedade do ID
controle Web. Por causa disso, é tentador codificar valores de atributo no id
código JavaScript. Ou seja, se você souber que deseja acessar o controle Web TextBox por meio do Age
script do lado do cliente, faça isso por meio de uma chamada para document.getElementById("Age")
.
O problema com essa abordagem é que, ao usar páginas mestras (ou outros controles de contêiner de nomenclatura), o HTML id
renderizado não é sinônimo da propriedade do ID
controle Web. Sua primeira inclinação pode ser visitar a página por meio de um navegador e visualizar a fonte para determinar o atributo real id
. Depois de saber o valor renderizado id
, você pode colá-lo na chamada para getElementById
acessar o elemento HTML com o qual você precisa trabalhar por meio do script do lado do cliente. Essa abordagem não é ideal porque certas alterações na hierarquia de controle da página ou nas propriedades ID
dos controles de nomenclatura alterarão o atributo resultante id
, interrompendo assim o código JavaScript.
A boa notícia é que o valor do id
atributo renderizado pode ser acessado no código do lado do servidor por meio da propriedade do ClientID
controle Web. Você deve usar essa propriedade para determinar o valor do atributo usado no script do lado do id
cliente. Por exemplo, para adicionar uma função JavaScript à página que, quando chamada, exibe o valor do TextBox em uma caixa de mensagem modal, adicione o seguinte código ao Page_Load
manipulador de Age
eventos:
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)
O código acima injeta o valor da Age
propriedade TextBox ClientID
na chamada JavaScript para getElementById
. Se você visitar esta página por meio de um navegador e visualizar o código-fonte HTML, encontrará o seguinte 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 como o valor correto id
do atributo, ctl00_MainContent_Age
, aparece na chamada para getElementById
. Como esse valor é calculado em tempo de execução, ele funciona independentemente de alterações posteriores na hierarquia de controle de página.
Observação
Este exemplo de JavaScript mostra apenas como adicionar uma função JavaScript que faz referência correta ao elemento HTML renderizado por um controle de servidor. Para usar essa função, você precisaria criar JavaScript adicional para chamar a função quando o documento for carregado ou quando alguma ação específica do usuário ocorrer. Para obter mais informações sobre esses e outros tópicos relacionados, leia Trabalhando com script do lado do cliente.
Resumo
Determinados controles de servidor ASP.NET atuam como contêineres de nomenclatura, o que afeta os valores de atributo renderizados id
de seus controles descendentes, bem como o escopo dos controles pesquisados pelo FindControl
método. Com relação às páginas mestras, a própria página mestra e seus controles ContentPlaceHolder são contêineres de nomenclatura. Conseqüentemente, precisamos trabalhar um pouco mais para referenciar programaticamente os controles na página de conteúdo usando FindControl
. Neste tutorial, examinamos duas técnicas: detalhar o controle ContentPlaceHolder e chamar seu FindControl
método; e rolar nossa própria FindControl
implementação que pesquisa recursivamente em todos os contêineres de nomenclatura.
Além dos problemas do lado do servidor que os contêineres de nomenclatura introduzem em relação à referência a controles da Web, também há problemas do lado do cliente. Na ausência de contêineres de nomenclatura, o valor da propriedade do ID
controle da Web e o valor do atributo renderizado id
são um só. Mas com a adição do contêiner de nomenclatura, o atributo renderizado id
inclui os ID
valores do controle Web e os contêineres de nomenclatura na ancestralidade de sua hierarquia de controle. Essas preocupações de nomenclatura não são um problema, desde que você use a propriedade do ClientID
controle Web para determinar o valor do atributo renderizado id
no script do lado do cliente.
Boa programação!
Leitura Adicional
Para obter mais informações sobre os tópicos discutidos neste tutorial, consulte os seguintes recursos:
- ASP.NET Páginas mestras e
FindControl
- Criando interfaces de usuário de entrada de dados dinâmicos
- Como fazer referência ASP.NET conteúdo da página mestra
- Mater Pages: dicas, truques e armadilhas
- Trabalhando com script do lado do cliente
Sobre o autor
Scott Mitchell, autor de vários livros ASP/ASP.NET e fundador da 4GuysFromRolla.com, trabalha com tecnologias da Web da Microsoft desde 1998. Scott trabalha como consultor, instrutor e escritor independente. Seu último livro é Sams Teach Yourself ASP.NET 3.5 em 24 horas. Scott pode ser contatado em mitchell@4GuysFromRolla.com ou através de seu blog em http://ScottOnWriting.NET.
Agradecimentos especiais a
Esta série de tutoriais foi revisada por muitos revisores úteis. Os principais revisores deste tutorial foram Zack Jones e Suchi Barnerjee. Interessado em revisar meus próximos artigos do MSDN? Em caso afirmativo, envie-me uma mensagem para mitchell@4GuysFromRolla.com.