Controlar nomenclatura de ID em páginas de conteúdo (C#)
por Scott Mitchell
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 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 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.
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="C#" MasterPageFile="~/Site.master" AutoEventWireup="true" CodeFile="IDIssues.aspx.cs" 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:
public partial class IDIssues : BasePage
{
}
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.
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.
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
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 ctlXX
de 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á null
.
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 SubmitButton
evento 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 void SubmitButton_Click(object sender, EventArgs e)
{
Label ResultsLabel = FindControl("Results") as Label;
TextBox AgeTextBox = Page.FindControl("Age") as TextBox;
ResultsLabel.Text = string.Format("You are {0} years old!", AgeTextBox.Text);
}
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).
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 retornam FindControl
um null
valor. 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 ctl00
de página master . (Consulte a Figura 4 para exibir a hierarquia de controle, que mostra o Page
objeto como o pai do objeto ctl00
de página master .) Portanto, Label Results
e Age
TextBox não são encontrados e ResultsLabel
e AgeTextBox
são atribuídos valores de null
.
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
.
Label ResultsLabel = MainContent.FindControl("Results") as Label;
TextBox AgeTextBox = MainContent.FindControl("Age") as 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 void SubmitButton_Click(object sender, EventArgs e)
{
ContentPlaceHolder MainContent = FindControl("MainContent") as ContentPlaceHolder;
Label ResultsLabel = MainContent.FindControl("Results") as Label;
TextBox AgeTextBox = MainContent.FindControl("Age") as TextBox;
ResultsLabel.Text = string.Format("You are {0} years old!", AgeTextBox.Text);
}
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 ocorre null
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 MainContent
e assim por diante. O snippet a seguir ilustra essa lógica:
// Get a reference to the master page
MasterPage ctl00 = FindControl("ctl00") as MasterPage;
// Get a reference to the ContentPlaceHolder
ContentPlaceHolder MainContent = ctl00.FindControl("MainContent") as ContentPlaceHolder;
// Reference the Label and TextBox controls
Label ResultsLabel = MainContent.FindControl("Results") as Label;
TextBox AgeTextBox = MainContent.FindControl("Age") as 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 void SubmitButton_Click(object sender, EventArgs e)
{
ContentPlaceHolder MainContent = Page.Master.FindControl("MainContent") as ContentPlaceHolder;
Label ResultsLabel = MainContent.FindControl("Results") as Label;
TextBox AgeTextBox = MainContent.FindControl("Age") as TextBox;
ResultsLabel.Text = string.Format("You are {0} years old!", AgeTextBox.Text);
}
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.
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.cs
. 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 própria classe e seus métodos de extensão sejam marcados como static
. 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 e esse parâmetro de entrada deve ser precedido pelo palavra-chave this
.
Adicione o seguinte código ao PageExtensionMethods.cs
arquivo de classe para definir essa classe e o método de FindControlRecursive
extensão:
using System;
using System.Web;
using System.Web.UI;
public static class PageExtensionMethods
{
public static Control FindControlRecursive(this Control ctrl, string controlID)
{
if (string.Compare(ctrl.ID, controlID, true) == 0)
{
// We found the control!
return ctrl;
}
else
{
// Recurse through ctrl's Controls collections
foreach (Control child in ctrl.Controls)
{
Control lookFor = FindControlRecursive(child, controlID);
if (lookFor != null)
return lookFor; // We found the control
}
// If we reach here, control was not found
return null;
}
}
}
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, em seguida, atinge o período, o FindControlRecursive
método é incluído na lista suspensa intelliSense junto com os outros Control
métodos de classe.
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 void SubmitButton_Click(object sender, EventArgs e)
{
Label ResultsLabel = Page.FindControlRecursive("Results") as Label;
TextBox AgeTextBox = Page.FindControlRecursive("Age") as TextBox;
ResultsLabel.Text = string.Format("You are {0} years old!", AgeTextBox.Text);
}
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 corretoid
no 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 ClientID
Web. 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(this.GetType(), "ShowAgeTextBoxScript",
string.Format(@"function ShowAge()
{{
var elem = document.getElementById('{0}');
if (elem != null)
alert('You entered ' + elem.value + ' into the Age text box.');
}}", AgeTextBox.ClientID), true);
O código acima injeta o valor da Age
propriedade ClientID do TextBox 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:
- ASP.NET Páginas Mestras e
FindControl
- Criando interfaces de usuário de entrada de dados dinâmicos
- Como referenciar ASP.NET conteúdo da página mestra
- Páginas mater: dicas, truques e armadilhas
- Trabalhando com Client-Side Script
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.