Partilhar via


Controlar nomenclatura de ID em páginas de conteúdo (VB)

por Scott Mitchell

Baixar PDF

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 FindControlo .

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.

Adicionar o IDIssues.aspx da página de conteúdo à pasta raiz

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.

A seção Lições agora inclui um link para

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.

A página inclui três controles da Web: um TextBox, um botão e um rótulo

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 idnomenclatura .

Os atributos de id renderizados são baseados nos valores de ID dos contêineres de 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.masterTextBox 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 ctlXXde 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 SubmitButtonevento 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).

Um NullReferenceException é gerado

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 ctl00de página mestra. (Consulte a Figura 4 para exibir a hierarquia de controle, que mostra o Page objeto como o pai do objeto ctl00de 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.

A idade do usuário é exibida no rótulo

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.

Os métodos de extensão estão incluídos nos menus suspensos do IntelliSense

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 corretoidno 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:

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.