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 programaticamente com um controle (via FindControl). Examina esse problema e 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 geralmente são usados no script do lado do cliente para referenciar programaticamente um determinado elemento HTML. Considerando isso, 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. Isso 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 de rótulo com um ID valor de ProductName. Quando o GridView está associado à fonte de dados em runtime, esse Rótulo é repetido uma vez para cada linha gridView. Cada Rótulo 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 do controle de 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 IDProductName 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 controle de servidor ASP.NET específico deve funcionar como um contêiner de nomenclatura. A INamingContainer interface não explica os métodos que o controle do servidor deve implementar; em vez disso, ela é usada como um marcador. Ao gerar a marcação renderizada, se um controle implementar essa interface, o mecanismo de ASP.NET prefixa automaticamente seu ID valor nos valores de atributo renderizados id de seus descendentes. Esse processo é discutido mais detalhadamente 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 referenciar controles em um GridView ou outro contêiner de nomenclatura.

Como você pode ter imaginado, master páginas e ContentPlaceHolders são implementados como contêineres de nomenclatura. Neste tutorial, examinamos como master páginas afetam valores de elemento id HTML e maneiras de referenciar programaticamente controles da Web em uma página de conteúdo usando FindControl.

Etapa 1: Adicionar uma nova página de 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 chamada IDIssues.aspx na pasta raiz, associando-a à Site.master página master.

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

Figura 01: Adicionar a página IDIssues.aspx de conteúdo à pasta raiz

O Visual Studio cria automaticamente um controle conteúdo para cada um dos quatro ContentPlaceHolders da página master. Conforme observado no tutorial Vários ContentPlaceHolders e Conteúdo Padrão, se um controle content não estiver presente, o conteúdo padrão da página master ContentPlaceHolder será emitido. Como os QuickLoginUI ContentPlaceHolders e LeftColumnContent contêm marcação padrão adequada para esta página, vá em frente e remova seus controles de Conteúdo correspondentes de IDIssues.aspx. Neste ponto, a marcação declarativa da página de conteúdo deve ser semelhante à seguinte:

<%@ 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, meta tags 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 Web.sitemap marcação do arquivo 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 de mapa de site em Web.sitemap é refletida imediatamente na seção Lições na coluna à 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 feitas pelo mecanismo de ASP.NET nos valores renderizados id dos controles do servidor, vamos adicionar alguns controles da 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 de botão e um controle Web de rótulo. Defina as propriedades e Columns textBox ID como Age e 3, respectivamente. Defina as propriedades e ID do Text Botão como "Enviar" e SubmitButton. Desmarque a propriedade do Text Rótulo e defina-a ID como 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: uma caixa de texto, um botão e um rótulo

Figura 03: a página inclui três controles da Web: uma Caixa de Texto, 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 a origem HTML. Como mostra a marcação abaixo, os id valores dos elementos HTML para os controles TextBox, Button e Label Web são uma combinação dos ID valores dos controles da 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 master e seus ContentPlaceHolders servem como contêineres de nomenclatura. Consequentemente, ambos contribuem com os valores renderizados ID de seus controles aninhados. Veja o atributo textbox id , 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 master, ctl00. O efeito net é um id valor de atributo que consiste nos ID valores da página master, do controle ContentPlaceHolder e do próprio TextBox.

A Figura 4 ilustra esse comportamento. Para determinar o renderizado id do Age TextBox, comece com o ID valor do controle TextBox, Age. Em seguida, trabalhe até a hierarquia de controle. Em cada contêiner de nomenclatura (esses nós com uma cor de pêssego), prefixe o atual renderizado id com o contêiner de nomenclatura.id

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 master, mas você pode estar se perguntando como esse ID valor surgiu. Não o especificamos em nenhum lugar em nossa página de master ou conteúdo. A maioria dos controles de servidor em uma página ASP.NET são adicionados explicitamente por meio da marcação declarativa da página. O MainContent controle ContentPlaceHolder foi especificado explicitamente na marcação de Site.master; a marcação Age textbox foi definida IDIssues.aspx. Podemos especificar os ID valores para esses tipos de controles por meio do janela Propriedades ou da sintaxe declarativa. Outros controles, como a própria página master, 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 runtime para esses controles cujas IDs não foram definidas explicitamente. Ele usa o padrão ctlXXde nomenclatura , em que XX é um valor inteiro sequencialmente crescente.

Como a página master em si serve como um contêiner de nomenclatura, os controles da Web definidos na página master também têm valores de atributo renderizados id alterados. Por exemplo, o DisplayDate Rótulo que adicionamos à página master no tutorial Criando um layout de Site-Wide 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 master (ctl00) e o ID valor do controle Web Label (DateDisplay).

Etapa 3: Referenciar programaticamente controles Web por meio deFindControl

Cada ASP.NET controle de servidor 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 da 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 runtime uma instância do controle é criada para cada linha GridView. Consequentemente, os controles gerados em runtime 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 da Web de dados, consulte Formatação personalizada baseada em dados.) Esse mesmo cenário ocorre ao adicionar dinamicamente controles da Web a um Web Form, 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 referencia programaticamente TextBox Age e Results Label usando o FindControl método e exibe uma mensagem em Results com base na entrada do usuário.

Observação

É claro que não precisamos usar para referenciar FindControl os controles Label e TextBox para este exemplo. Poderíamos referenciá-los diretamente por meio de seus ID valores de propriedade. FindControl Uso aqui para ilustrar o que acontece ao usar FindControl 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 difere ligeiramente 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 classes code-behind ASP.NET 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 esse código, visite a IDIssues.aspx página por meio de um navegador, insira sua idade e clique no botão "Enviar". Ao clicar no botão "Enviar", um NullReferenceException é gerado (consulte a Figura 5).

Uma NullReferenceException é gerada

Figura 05: Um NullReferenceException é Gerado (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 para FindControl retornar Nothing. O NullReferenceException é gerado quando tentamos acessar a Age propriedade do Text TextBox.

O problema é que Control.FindControl pesquisa apenas os descendentes do Controle que estão no mesmo contêiner de nomenclatura. Como a página master constitui um novo contêiner de nomenclatura, uma chamada para Page.FindControl("controlID") nunca permeia o objeto ctl00de página master . (Consulte a Figura 4 para exibir a hierarquia de controle, que mostra o Page objeto como o pai do objeto ctl00de página master .) Portanto, Label Results e Age TextBox não são encontrados e ResultsLabel e AgeTextBox são atribuídos valores de Nothing.

Há duas soluções alternativas para esse desafio: podemos fazer drill down, um contêiner de nomenclatura por vez, para o controle apropriado; ou podemos criar nosso próprio FindControl método que permeia contêineres de nomenclatura. Vamos examinar cada uma dessas opções.

Detalhando o contêiner de nomenclatura apropriado

Para usar para fazer FindControl referência ao Results Rótulo ou Age TextBox, 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 Results ou Age que está dentro do mesmo contêiner de nomenclatura. Em outras palavras, chamar o FindControl método do MainContent controle, conforme mostrado no snippet de código abaixo, retorna corretamente uma referência aos Results controles ou 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 ContentPlaceHolder MainContent da classe code-behind da página de conteúdo usando a sintaxe acima porque ContentPlaceHolder está definido na página master. Em vez disso, precisamos usar FindControl para obter uma referência a MainContent. Substitua o código no SubmitButton_Click 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, insira sua idade e clique no botão "Enviar", um NullReferenceException será gerado. 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 que com os Results controles Label e Age TextBox: FindControl inicia sua pesquisa na parte superior da hierarquia de controle e não penetra em contêineres de nomenclatura, mas ContentPlaceHolder MainContent está dentro da página master, 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 master. Depois que tivermos uma referência à página master, poderemos obter uma referência ao MainContent ContentPlaceHolder por meio FindControl de e, a partir daí, referências ao Results Rótulo e Age TextBox (novamente, usando FindControl). Mas como obter uma referência à página master? Ao inspecionar os id atributos na marcação renderizada, é evidente que o valor da ID página master é ctl00. Portanto, poderíamos usar Page.FindControl("ctl00") para obter uma referência à página master e, em seguida, usar esse objeto para obter uma referência a MainContente 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 o gerado automaticamente ID da página master sempre será ctl00. Nunca é uma boa ideia fazer suposições sobre valores gerados automaticamente.

Felizmente, uma referência à página master é acessível por meio da Page propriedade da Master classe. Portanto, em vez de precisar usar FindControl("ctl00") para obter uma referência da página master para acessar o MainContent ContentPlaceHolder, podemos usar Page.Master.FindControl("MainContent"). Atualize o SubmitButton_Click manipulador de 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 mensagem no Results Rótulo, conforme o 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)

Pesquisa recursiva por meio de contêineres de nomenclatura

O motivo pelo qual o exemplo de código anterior referenciou o MainContent controle ContentPlaceHolder da página master e, em seguida, os Results controles Label e Age TextBox de MainContent, é porque o Control.FindControl método pesquisa apenas dentro do contêiner de nomenclatura do Control. Manter-se FindControl 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 chamado ProductName em um de seus TemplateFields. Quando os dados são associados ao GridView em runtime, um ProductName Rótulo é criado para cada linha GridView. Se FindControl pesquisado em todos os contêineres de nomenclatura e chamamos Page.FindControl("ProductName"), qual instância de Rótulo deve FindControl retornar? O ProductName Rótulo na primeira linha gridView? O da última linha?

Portanto, ter Control.FindControl pesquisa apenas no contêiner de nomenclatura do Control faz sentido na maioria dos casos. Mas há outros casos, como aquele voltado para nós, 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 usar um FindControlRecursive método para a Control classe para acompanhar seu método existente FindControl .

Observação

Os métodos de extensão são um recurso novo no C# 3.0 e no Visual Basic 9, que são as linguagens fornecidas com o .NET Framework versão 3.5 e o Visual Studio 2008. Em resumo, 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 um 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 seu 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 este 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 nos métodos de extensão é que eles aparecem diretamente nas listas suspensas do IntelliSense. Como mostra a Figura 7, quando você digita Page e atinge o período, o FindControlRecursive método é incluído na lista suspensa IntelliSense junto com os outros Control métodos de classe.

Os métodos de extensão são incluídos nas listas suspensas do IntelliSense

Figura 07: Os métodos de extensão estão incluídos no Drop-Downs do IntelliSense (clique para exibir a imagem em tamanho real)

Insira o código a SubmitButton_Click seguir no manipulador de eventos e teste-o visitando a página, inserindo sua idade e clicando no botão "Enviar". Conforme 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 um exemplo em sua postagem no blog, ASP.NET Maser Pages e FindControl.

Etapa 4: Usando o valor de atributo corretoidno script Client-Side

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 ASP.NET páginas que não incluem um contêiner de nomenclatura, o atributo do id elemento HTML renderizado é idêntico ao valor da propriedade do controle da ID Web. Por isso, é tentador codificar em código em id valores de atributo no código JavaScript. Ou seja, se você souber que deseja acessar o Age controle Web TextBox por meio do 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 master páginas (ou outros controles de contêiner de nomenclatura), o HTML id renderizado não é sinônimo da propriedade do controle da ID Web. Sua primeira inclinação pode ser visitar a página por meio de um navegador e exibir a origem para determinar o atributo real id . Depois de saber o valor renderizado id , você pode colá-lo na chamada para getElementById para acessar o elemento HTML com o qual você precisa trabalhar por meio do script do lado do cliente. Essa abordagem não é ideal porque determinadas alterações na hierarquia de controle da página ou alterações nas ID propriedades dos controles de nomenclatura alterarão o atributo resultante, interrompendo id assim o código JavaScript.

A boa notícia é que o valor do id atributo renderizado é acessível no código do lado do servidor por meio da propriedade do controle da ClientIDWeb. Você deve usar essa propriedade para determinar o valor do id atributo usado no script do lado do cliente. Por exemplo, para adicionar uma função JavaScript à página que, quando chamada, exibe o valor da Age TextBox em uma caixa de mensagem modal, adicione o seguinte código ao Page_Load manipulador de 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 essa página por meio de um navegador e exibir a 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 de atributo correto id , ctl00_MainContent_Age, aparece dentro da chamada para getElementById. Como esse valor é calculado em runtime, ele funciona independentemente das alterações posteriores na hierarquia de controle de página.

Observação

Este exemplo de JavaScript apenas mostra como adicionar uma função JavaScript que referencia corretamente o 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 tópicos relacionados, leia Trabalhando com Client-Side Script.

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 exibidos pelo FindControl método . No que diz respeito a master páginas, a própria página master e seus controles ContentPlaceHolder são contêineres de nomenclatura. Consequentemente, precisamos apresentar um pouco mais de trabalho para referenciar programaticamente os controles na página de conteúdo usando FindControl. Neste tutorial, examinamos duas técnicas: analisar o controle ContentPlaceHolder e chamar seu FindControl método; e implantar 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 apresentam no que diz respeito à 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 controle Web ID e o valor do atributo renderizado id são um no mesmo. Mas com a adição do contêiner de nomenclatura, o atributo renderizado id inclui os ID valores do controle da 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 controle da ClientID Web para determinar o valor do atributo renderizado id no script do lado do cliente.

Programação feliz!

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 do ASP/ASP.NET e fundador da 4GuysFromRolla.com, trabalha com tecnologias da Microsoft Web desde 1998. Scott trabalha como consultor independente, treinador e escritor. 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

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? Nesse caso, deixe-me uma linha em mitchell@4GuysFromRolla.com.