콘텐츠 페이지에서 컨트롤 ID 이름 지정(C#)

작성자 : Scott Mitchell

PDF 다운로드

ContentPlaceHolder 컨트롤이 명명 컨테이너 역할을 하므로 FindControl을 통해 프로그래밍 방식으로 컨트롤 작업을 어렵게 만드는 방법을 보여 줍니다. 이 문제와 해결 방법을 살펴봅니다. 또한 결과 ClientID 값에 프로그래밍 방식으로 액세스하는 방법에 대해서도 설명합니다.

소개

모든 ASP.NET 서버 컨트롤에는 컨트롤을 고유하게 식별하는 속성이 포함 ID 되며 코드 숨김 클래스에서 컨트롤이 프로그래밍 방식으로 액세스되는 수단입니다. 마찬가지로 HTML 문서의 요소에는 요소를 고유하게 식별하는 특성이 포함될 id 수 있습니다. 이러한 id 값은 종종 클라이언트 쪽 스크립트에서 특정 HTML 요소를 프로그래밍 방식으로 참조하는 데 사용됩니다. 이 경우 ASP.NET 서버 컨트롤이 HTML로 렌더링될 때 해당 ID 값이 렌더링된 HTML 요소의 값으로 id 사용된다고 가정할 수 있습니다. 특정 상황에서 단일 ID 값이 있는 단일 컨트롤이 렌더링된 태그에 여러 번 나타날 수 있기 때문에 반드시 그렇지는 않습니다. ProductName 값이 있는 레이블 웹 컨트롤이 있는 TemplateField를 포함하는 GridView 컨트롤을 ID 고려합니다. GridView가 런타임에 데이터 원본에 바인딩되면 이 레이블은 모든 GridView 행에 대해 한 번 반복됩니다. 렌더링된 각 레이블에는 고유한 id 값이 필요합니다.

이러한 시나리오를 처리하기 위해 ASP.NET 특정 컨트롤을 명명 컨테이너로 표시할 수 있습니다. 명명 컨테이너는 새 ID 네임스페이스 역할을 합니다. 명명 컨테이너 내에 표시되는 모든 서버 컨트롤에는 명명 컨테이너 컨트롤의 접두사로 렌더링된 idID 값이 있습니다. 예를 들어 GridViewGridViewRow 클래스는 모두 명명 컨테이너입니다. 따라서 ProductName을 사용하여 GridView TemplateField ID 에 정의된 레이블 컨트롤에 렌더링된 값GridViewID_GridViewRowID_ProductNameid 지정됩니다. GridViewRowID는 각 GridView 행에 대해 고유하므로 결과 id 값은 고유합니다.

참고

인터페이스는 INamingContainer 특정 ASP.NET 서버 컨트롤이 명명 컨테이너로 작동해야 함을 나타내는 데 사용됩니다. 인터페이스는 INamingContainer 서버 컨트롤이 구현해야 하는 메서드를 철자하지 않고 표식으로 사용됩니다. 렌더링된 태그를 생성할 때 컨트롤이 이 인터페이스를 구현하는 경우 ASP.NET 엔진은 해당 값을 하위 항목의 렌더링된 id 특성 값에 자동으로 접두 ID 사로 추가합니다. 이 프로세스는 2단계에서 자세히 설명합니다.

컨테이너 이름을 지정하면 렌더링된 id 특성 값이 변경될 뿐만 아니라 ASP.NET 페이지의 코드 숨김 클래스에서 컨트롤을 프로그래밍 방식으로 참조하는 방법에도 영향을 줍니다. 메서드는 FindControl("controlID") 일반적으로 프로그래밍 방식으로 웹 컨트롤을 참조 하는 데 사용 합니다. 그러나 는 FindControl 명명 컨테이너를 통해 침투하지 않습니다. 따라서 메서드를 Page.FindControl 직접 사용하여 GridView 또는 다른 명명 컨테이너 내의 컨트롤을 참조할 수 없습니다.

추측한 대로 master 페이지와 ContentPlaceHolders는 모두 명명 컨테이너로 구현됩니다. 이 자습서에서는 master 페이지가 HTML 요소 id 값에 미치는 영향과 를 사용하여 FindControl콘텐츠 페이지 내에서 웹 컨트롤을 프로그래밍 방식으로 참조하는 방법을 살펴봅니다.

1단계: 새 ASP.NET 페이지 추가

이 자습서에서 설명하는 개념을 보여 주려면 웹 사이트에 새 ASP.NET 페이지를 추가해 보겠습니다. 루트 폴더에 라는 IDIssues.aspx 새 콘텐츠 페이지를 만들어 master 페이지에 바인딩합니다 Site.master .

루트 폴더에 콘텐츠 페이지 IDIssues.aspx 추가

그림 01: 루트 폴더에 콘텐츠 페이지 IDIssues.aspx 추가

Visual Studio는 master 페이지의 4개 ContentPlaceHolders 각각에 대한 콘텐츠 컨트롤을 자동으로 만듭니다. 다중 ContentPlaceHolders 및 기본 콘텐츠 자습서에서 설명한 대로 콘텐츠 컨트롤이 없는 경우 master 페이지의 기본 ContentPlaceHolder 콘텐츠가 대신 내보내집니다. QuickLoginUILeftColumnContent ContentPlaceHolders에 이 페이지에 적합한 기본 태그가 포함되어 있으므로 계속 진행하여 에서 해당 콘텐츠 컨트롤을 제거합니다IDIssues.aspx. 이 시점에서 콘텐츠 페이지의 선언적 태그는 다음과 같이 표시됩니다.

<%@ 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>

마스터 페이지의 제목, 메타 태그 및 기타 HTML 헤더 지정 자습서에서는 명시적으로 설정되지 않은 경우 페이지의 제목을 자동으로 구성하는 사용자 지정 기본 페이지 클래스(BasePage)를 만들었습니다. IDIssues.aspx 페이지에서 이 기능을 사용하려면 페이지의 코드 숨김 클래스가 대신 클래스System.Web.UI.Page에서 BasePage 파생되어야 합니다. 코드 숨김 클래스의 정의를 다음과 같이 수정합니다.

public partial class IDIssues : BasePage
{
}

마지막으로 이 새 단원에 Web.sitemap 대한 항목을 포함하도록 파일을 업데이트합니다. <siteMapNode> 요소를 추가하고 해당 titleurl 특성을 각각 "컨트롤 ID 명명 문제" 및 ~/IDIssues.aspx로 설정합니다. 이 추가 Web.sitemap 를 수행한 후 파일의 태그는 다음과 유사하게 표시됩니다.

<?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>

그림 2에서 알 수 있듯이 의 새 사이트 맵 항목 Web.sitemap 은 왼쪽 열의 Lessons 섹션에 즉시 반영됩니다.

이제 단원 섹션에

그림 02: 이제 단원 섹션에 "컨트롤 ID 명명 문제"에 대한 링크가 포함되어 있습니다.

2단계: 렌더링된ID변경 내용 검사

ASP.NET 엔진이 서버 컨트롤의 렌더링된 id 값에 대한 수정 사항을 더 잘 이해하려면 페이지에 몇 가지 웹 컨트롤을 IDIssues.aspx 추가한 다음 브라우저로 전송된 렌더링된 태그를 살펴보겠습니다. 특히 텍스트 "age:"를 입력한 다음 TextBox 웹 컨트롤을 입력합니다. 페이지 아래로 단추 웹 컨트롤과 레이블 웹 컨트롤을 추가합니다. TextBox 및 ID 속성을 각각 및 Columns 3으로 Age 설정합니다. Button의 TextID 속성을 "제출" 및 SubmitButton로 설정합니다. Label의 Text 속성을 지우고 를 IDResults설정합니다.

이 시점에서 콘텐츠 컨트롤의 선언적 태그는 다음과 유사하게 표시됩니다.

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

그림 3은 Visual Studio 디자이너를 통해 볼 때의 페이지를 보여 줍니다.

페이지에는 TextBox, 단추 및 레이블의 세 가지 웹 컨트롤이 포함되어 있습니다.

그림 03: 페이지에는 TextBox, Button 및 Label의 세 가지 웹 컨트롤이 포함되어 있습니다(전체 크기 이미지를 보려면 클릭).

브라우저를 통해 페이지를 방문한 다음 HTML 원본을 봅니다. 아래 태그에서 알 수 있듯이 TextBox, id Button 및 Label 웹 컨트롤의 HTML 요소 값은 웹 컨트롤 값과 ID 페이지의 명명 컨테이너 값의 조합 ID 입니다.

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

이 자습서의 앞부분에서 설명한 것처럼 master 페이지와 ContentPlaceHolders는 모두 명명 컨테이너 역할을 합니다. 따라서 둘 다 중첩된 컨트롤의 렌더링된 ID 값을 제공합니다. instance ctl00_MainContent_AgeTextBox의 id 특성을 사용합니다. TextBox 컨트롤의 ID 값은 이었습니다 Age. 이 접두사는 ContentPlaceHolder 컨트롤의 IDMainContent입니다. 또한 이 값에는 master 페이지의 IDctl00이 접두사로 추가됩니다. 순 효과는 id master 페이지, ContentPlaceHolder 컨트롤 및 TextBox 자체의 값으로 구성된 ID 특성 값입니다.

그림 4에서는 이 동작을 보여 줍니다. TextBox의 Age 렌더링을 id 확인하려면 TextBox 컨트롤AgeID 값으로 시작합니다. 다음으로, 컨트롤 계층 구조를 작동합니다. 각 명명 컨테이너(복숭아 색의 노드)에서 명명 컨테이너의 id로 렌더링된 id 현재 접두사입니다.

렌더링된 ID 특성은 명명 컨테이너의 ID 값을 기반으로 합니다.

그림 04: 렌더링된 id 특성은 명명 컨테이너의 값을 기반으로 ID 합니다.

참고

설명한 대로 렌더링된 ctl00id 특성의 부분은 master 페이지의 값을 구성 ID 하지만 이 값이 ID 어떻게 제공되었는지 궁금할 수 있습니다. master 또는 콘텐츠 페이지의 아무 곳에도 지정하지 않았습니다. ASP.NET 페이지의 대부분의 서버 컨트롤은 페이지의 선언적 태그를 통해 명시적으로 추가됩니다. MainContent ContentPlaceHolder 컨트롤은 의 Site.masterAge 태그에 명시적으로 지정되었습니다. TextBox는 '의 태그로 정의IDIssues.aspx되었습니다. 속성 창 또는 선언적 구문을 통해 이러한 유형의 컨트롤에 대한 값을 지정할 ID 수 있습니다. master 페이지 자체와 같은 다른 컨트롤은 선언적 태그에 정의되지 않습니다. 따라서 해당 값은 ID 자동으로 생성되어야 합니다. ASP.NET 엔진은 ID가 ID 명시적으로 설정되지 않은 컨트롤의 값을 런타임에 설정합니다. XX는 순차적으로 증가하는 정수 값인 명명 패턴을 ctlXX사용합니다.

master 페이지 자체는 명명 컨테이너 역할을 하므로 master 페이지에 정의된 웹 컨트롤에도 렌더링된 특성 값이 변경되었습니다id. 예를 들어 Master DisplayDate Pages를 사용하여 Site-Wide 레이아웃 만들기 자습서의 master 페이지에 추가한 레이블에는 다음과 같은 렌더링된 태그가 있습니다.

<span id="ctl00_DateDisplay">current date</span>

특성에는 id master 페이지의 ID 값()과 ID 레이블 웹 컨트롤(ctl00)의 값이DateDisplay 모두 포함됩니다.

3단계: 를 통해 프로그래밍 방식으로 웹 컨트롤 참조FindControl

모든 ASP.NET 서버 컨트롤에는 컨트롤의 하위 항목에서 controlID라는 컨트롤을 검색하는 메서드가 포함되어 FindControl("controlID") 있습니다. 이러한 컨트롤이 발견되면 반환됩니다. 일치하는 컨트롤이 없으면 를 FindControl 반환합니다 null.

FindControl 는 컨트롤에 액세스해야 하지만 컨트롤에 대한 직접 참조가 없는 시나리오에서 유용합니다. 예를 들어 GridView와 같은 데이터 웹 컨트롤을 사용하는 경우 GridView 필드 내의 컨트롤은 선언적 구문에서 한 번 정의되지만 런타임에 각 GridView 행에 대해 컨트롤의 instance 만들어집니다. 따라서 런타임에 생성된 컨트롤이 존재하지만 코드 숨김 클래스에서 사용할 수 있는 직접 참조는 없습니다. 따라서 GridView의 필드 내에서 특정 컨트롤을 프로그래밍 방식으로 작업하는 데 를 사용해야 FindControl 합니다. (를 사용하여 FindControl 데이터 웹 컨트롤의 템플릿 내에서 컨트롤에 액세스하는 방법에 대한 자세한 내용은 데이터 기반 사용자 지정 서식 지정을 참조하세요.) 이 시나리오는 동적 데이터 항목 사용자 인터페이스 만들기에서 설명하는 항목인 웹 양식에 웹 컨트롤을 동적으로 추가할 때 발생합니다.

메서드를 FindControl 사용하여 콘텐츠 페이지 내에서 컨트롤을 검색하는 방법을 보여 주려면 의 Click 이벤트에 대한 SubmitButton이벤트 처리기를 만듭니다. 이벤트 처리기에서 다음 코드를 추가합니다. 이 코드는 메서드를 사용하여 TextBox 및 Results Label을 프로그래밍 방식으로 참조 Age 한 다음 사용자의 입력에 따라 에 메시지를 Results 표시합니다.FindControl

참고

물론 이 예제에서는 Label 및 TextBox 컨트롤을 참조하는 데 를 사용할 FindControl 필요가 없습니다. 속성 값을 통해 직접 참조할 수 있습니다 ID . 여기서 를 사용하여 FindControl 콘텐츠 페이지에서 를 사용할 FindControl 때 발생하는 작업을 설명합니다.

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);
}

메서드를 호출 FindControl 하는 데 사용되는 구문은 의 SubmitButton_Click처음 두 줄에서 약간 다르지만 의미상 동일합니다. 모든 ASP.NET 서버 컨트롤에는 메서드가 포함되어 있습니다 FindControl . 여기에는 모든 ASP.NET 코드 숨김 클래스가 파생되어야 하는 클래스가 포함 Page 됩니다. 따라서 호출 FindControl("controlID") 은 코드 숨김 클래스 또는 사용자 지정 기본 클래스에서 메서드를 FindControl 재정의하지 않은 경우 를 호출Page.FindControl("controlID")하는 것과 같습니다.

이 코드를 입력한 후 브라우저를 IDIssues.aspx 통해 페이지를 방문하여 나이를 입력하고 "제출" 단추를 클릭합니다. "제출" 단추를 클릭하면 가 NullReferenceException 발생합니다(그림 5 참조).

NullReferenceException이 발생함

그림 05: A NullReferenceException 가 발생합니다(전체 크기 이미지를 보려면 클릭).

이벤트 처리기에서 SubmitButton_Click 중단점을 설정하면 두 호출이 모두 값을 반환 null 하는 FindControl 것을 볼 수 있습니다. 는 NullReferenceException TextBox의 Text 속성에 Age 액세스하려고 할 때 발생합니다.

문제는 동일한 명명 컨테이너에 있는 Control.FindControlControl의 하위 항목만 검색한다는 것입니다. master 페이지는 새 명명 컨테이너를 구성하기 Page.FindControl("controlID") 때문에 에 대한 호출은 master 페이지 개체ctl00에 침투하지 않습니다. (개체를 master 페이지 개체ctl00의 부모로 표시하는 Page 컨트롤 계층 구조를 보려면 그림 4를 다시 참조하세요.) 따라서 Results Label 및 Age TextBox를 찾을 수 AgeTextBoxResultsLabel 없으며 의 null값이 할당됩니다.

이 문제에 대한 두 가지 해결 방법이 있습니다. 한 번에 하나의 명명 컨테이너를 적절한 컨트롤로 드릴다운할 수 있습니다. 또는 명명 컨테이너에 스며드는 고유한 FindControl 메서드를 만들 수 있습니다. 이러한 각 옵션을 살펴보겠습니다.

적절한 명명 컨테이너로 드릴 인

를 사용하여 FindControl Label 또는 Age TextBox를 Results 참조하려면 동일한 명명 컨테이너의 상위 컨트롤에서 를 호출 FindControl 해야 합니다. 그림 4에서 볼 수 MainContent 있듯이 ContentPlaceHolder 컨트롤은 의 유일한 상위 Results 항목이거나 Age 동일한 명명 컨테이너 내에 있습니다. 즉, 아래 코드 조각과 같이 컨트롤에서 MainContent 메서드를 호출 FindControl 하면 또는 Age 컨트롤에 대한 참조가 Results 올바르게 반환됩니다.

Label ResultsLabel = MainContent.FindControl("Results") as Label;
TextBox AgeTextBox = MainContent.FindControl("Age") as TextBox;

그러나 ContentPlaceHolder는 master 페이지에 정의되어 있으므로 위의 구문을 사용하여 콘텐츠 페이지의 코드 숨김 클래스에서 ContentPlaceHolder를 사용할 MainContent 수 없습니다. 대신 를 사용하여 FindControl 에 대한 참조를 가져와야 합니다 MainContent. 이벤트 처리기의 코드를 SubmitButton_Click 다음 수정 사항으로 바꿉니다.

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);
}

브라우저를 통해 페이지를 방문하는 경우 나이를 입력하고 "제출" 단추를 클릭하면 가 NullReferenceException 발생합니다. 이벤트 처리기에서 SubmitButton_Click 중단점을 설정하면 개체의 FindControl 메서드를 호출하려고 할 때 이 예외가 MainContent 발생합니다. 개체는 MainContent 메서드가 FindControl "MainContent"라는 개체를 찾을 수 없기 때문입니다null. 기본 이유는 Label 및 TextBox 컨트롤 FindControlResultsAge 동일합니다. 컨트롤 계층 구조의 맨 위에서 검색을 시작하고 명명 컨테이너에 침투하지 않지만 MainContent ContentPlaceHolder는 명명 컨테이너인 master 페이지 내에 있습니다.

를 사용하여 FindControl 에 대한 참조를 MainContent가져오기 전에 먼저 master 페이지 컨트롤에 대한 참조가 필요합니다. master 페이지에 대한 참조가 있으면 를 통해 ContentPlaceHolder에 MainContent 대한 참조를 가져올 수 있으며, 여기에서 Label 및 Age TextBox에 Results 대한 참조를 얻을 수 있습니다(다시 를 사용하여 FindControl).FindControl 그러나 master 페이지에 대한 참조를 얻으려면 어떻게 해야 할까요? 렌더링된 태그의 id 특성을 검사하면 master 페이지의 ID 값이 임을 알 수 있습니다ctl00. 따라서 를 사용하여 Page.FindControl("ctl00") master 페이지에 대한 참조를 가져와서 해당 개체를 사용하여 에 대한 참조MainContent를 가져오는 등의 작업을 할 수 있습니다. 다음 코드 조각은 이 논리를 보여 줍니다.

// 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;

이 코드는 확실히 작동하지만 master 페이지의 자동 생성 ID 은 항상 이 될 것이라고 ctl00가정합니다. 자동 생성된 값에 대해 가정하는 것은 결코 좋은 생각이 아닙니다.

다행히 클래스 Master 의 속성을 통해 master 페이지에 대한 참조에 Page 액세스할 수 있습니다. 따라서 ContentPlaceHolder에 액세스 MainContent 하기 위해 를 사용하여 FindControl("ctl00") master 페이지의 참조를 가져오는 대신 를 사용할 Page.Master.FindControl("MainContent")수 있습니다. SubmitButton_Click 다음 코드로 이벤트 처리기를 업데이트합니다.

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);
}

이번에는 브라우저를 통해 페이지를 방문하여 나이를 입력하고 "제출" 단추를 클릭하면 예상대로 레이블에 Results 메시지가 표시됩니다.

사용자 연령이 레이블에 표시됩니다.

그림 06: 사용자 연령이 레이블에 표시됩니다(전체 크기 이미지를 보려면 클릭).

명명 컨테이너를 재귀적으로 검색

이전 코드 예제에서 master 페이지에서 ContentPlaceHolder 컨트롤을 참조한 다음 Results 의 Label 및 Age TextBox 컨트롤MainContent을 참조 MainContent 한 이유는 메서드가 Control의 명명 컨테이너 내에서만 검색하기 때문 Control.FindControl 입니다. FindControl 두 개의 서로 다른 명명 컨테이너에 있는 두 컨트롤의 값이 같 ID 을 수 있으므로 대부분의 시나리오에서 명명 컨테이너 내에 유지하는 것이 좋습니다. TemplateFields 중 하나에 명명 ProductName 된 레이블 웹 컨트롤을 정의하는 GridView의 경우를 고려합니다. 런타임 ProductName 에 데이터가 GridView에 바인딩되면 각 GridView 행에 대해 레이블이 만들어집니다. 모든 명명 컨테이너를 검색하고 를 호출Page.FindControl("ProductName")한 경우 FindControl 레이블 instance 반환해야 하나 FindControl 요? ProductName 첫 번째 GridView 행의 레이블인가요? 마지막 행의 하나?

Control.FindControl 따라서 대부분의 경우 Control의 명명 컨테이너만 검색하는 것이 좋습니다. 그러나 모든 명명 컨테이너에서 고유 ID 하며 컨트롤에 액세스하기 위해 컨트롤 계층의 각 명명 컨테이너를 꼼꼼하게 참조하지 않으려는 경우와 같은 다른 경우가 있습니다. 모든 명명 컨테이너를 FindControl 재귀적으로 검색하는 변형도 의미가 있습니다. 아쉽게도 .NET Framework 이러한 메서드를 포함하지 않습니다.

좋은 소식은 모든 명명 컨테이너를 재귀적으로 검색하는 자체 FindControl 메서드를 만들 수 있다는 것입니다. 실제로 확장 메서드를 사용하면 기존 메서드와 함께 FindControl 클래스에 Control 대한 메서드를 압정 FindControlRecursive 할 수 있습니다.

참고

확장 메서드는 C# 3.0 및 Visual Basic 9의 새로운 기능으로, .NET Framework 버전 3.5 및 Visual Studio 2008과 함께 제공되는 언어입니다. 즉, 확장 메서드를 사용하면 개발자가 특수 구문을 통해 기존 클래스 형식에 대한 새 메서드를 만들 수 있습니다. 이 유용한 기능에 대한 자세한 내용은 확장 메서드를 사용하여 기본 형식 기능 확장 문서를 참조하세요.

확장 메서드를 만들려면 라는 PageExtensionMethods.cs폴더에 App_Code 새 파일을 추가합니다. 라는 매개 변수controlID를 입력으로 사용하는 라는 FindControlRecursive 확장 메서드를 string 추가합니다. 확장 메서드가 제대로 작동하려면 클래스 자체와 확장 메서드를 로 표시하는 static것이 중요합니다. 또한 모든 확장 메서드는 확장 메서드가 적용되는 형식의 개체를 첫 번째 매개 변수로 수락해야 하며 이 입력 매개 변수 앞에 키워드(keyword) this와 함께 와야 합니다.

클래스 파일에 다음 코드를 PageExtensionMethods.cs 추가하여 이 클래스와 확장 메서드를 FindControlRecursive 정의합니다.

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;
        }
    }
}

이 코드를 사용하면 페이지의 코드 숨김 클래스로 돌아가 IDIssues.aspx 현재 FindControl 메서드 호출을 주석 처리합니다. 를 에 대한 호출 Page.FindControlRecursive("controlID")로 대체합니다. 확장 메서드는 IntelliSense 드롭다운 목록 내에 직접 표시됩니다. 그림 7에서 볼 수 있듯이 Page를 입력한 다음 마침표 FindControlRecursive 에 도달하면 메서드가 IntelliSense 드롭다운에 다른 Control 클래스 메서드와 함께 포함됩니다.

확장 메서드는 IntelliSense 드롭다운에 포함됩니다.

그림 07: 확장 메서드가 IntelliSense Drop-Downs 포함되어 있습니다(전체 크기 이미지를 보려면 클릭).

이벤트 처리기에 다음 코드를 SubmitButton_Click 입력한 다음 페이지를 방문하여 나이를 입력하고 "제출" 단추를 클릭하여 테스트합니다. 그림 6에 표시된 것처럼 결과 출력은 "당신은 나이입니다!" 메시지가 됩니다.

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);
}

참고

확장 메서드는 C# 3.0 및 Visual Basic 9에 새로 추가되므로 Visual Studio 2005를 사용하는 경우 확장 메서드를 사용할 수 없습니다. 대신 도우미 클래스에서 메서드를 FindControlRecursive 구현해야 합니다. Rick Strahl 은 그의 블로그 게시물, ASP.NET Maser Pages 및 에 이러한 예제를 가지고 있습니다 FindControl.

4단계: Client-Side 스크립트에서 올바른id특성 값 사용

이 자습서의 소개에서 설명한 것처럼 웹 컨트롤의 렌더링된 id 특성은 특정 HTML 요소를 프로그래밍 방식으로 참조하기 위해 클라이언트 쪽 스크립트에서 자주 사용됩니다. 예를 들어 다음 JavaScript는 HTML 요소를 로 id 참조한 다음 모달 메시지 상자에 해당 값을 표시합니다.

var elem = document.getElementById("Age");
if (elem != null)
    alert("You entered " + elem.value + " into the Age text box.");

명명 컨테이너를 포함하지 않는 ASP.NET 페이지에서 렌더링된 HTML 요소의 id 특성은 웹 컨트롤의 ID 속성 값과 동일합니다. 이 때문에 특성 값의 하드 코드를 JavaScript 코드로 하드 코드 id 하려고 합니다. 즉, 클라이언트 쪽 스크립트를 통해 TextBox 웹 컨트롤에 Age 액세스하려는 경우 에 대한 호출 document.getElementById("Age")을 통해 액세스합니다.

이 방법의 문제는 master 페이지(또는 다른 명명 컨테이너 컨트롤)를 사용할 때 렌더링된 HTML id 이 웹 컨트롤의 ID 속성과 동의어가 아니라는 것입니다. 첫 번째 성향은 브라우저를 통해 페이지를 방문하여 원본을 확인하여 실제 id 특성을 확인하는 것입니다. 렌더링된 id 값을 알고 나면 호출 getElementById 에 붙여넣어 클라이언트 쪽 스크립트를 통해 작업해야 하는 HTML 요소에 액세스할 수 있습니다. 이 방법은 페이지의 컨트롤 계층 구조에 대한 특정 변경 또는 명명 컨트롤의 속성 변경 ID 으로 인해 결과 id 특성이 변경되어 JavaScript 코드가 손상되므로 이상적이지 않습니다.

좋은 소식은 렌더링되는 특성 값이 웹 컨트롤의 ClientID 속성을 통해 서버 쪽 코드에서 액세스할 수 있다는 id 것입니다. 클라이언트 쪽 스크립트에 사용되는 특성 값을 확인 id 하려면 이 속성을 사용해야 합니다. 예를 들어 호출할 때 모달 메시지 상자에 TextBox 값을 Age 표시하는 JavaScript 함수를 페이지에 추가하려면 이벤트 처리기에 다음 코드를 Page_Load 추가합니다.

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);

위의 코드는 TextBox의 Age ClientID 속성 값을 에 대한 JavaScript 호출에 삽입합니다 getElementById. 브라우저를 통해 이 페이지를 방문하여 HTML 원본을 보는 경우 다음 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>

에 대한 호출 내에서 올바른 id 특성 값 가 ctl00_MainContent_Age어떻게 표시되는지 확인 getElementById합니다. 이 값은 런타임에 계산되므로 페이지 컨트롤 계층 구조에 대한 이후 변경 내용에 관계없이 작동합니다.

참고

이 JavaScript 예제에서는 서버 컨트롤에서 렌더링된 HTML 요소를 올바르게 참조하는 JavaScript 함수를 추가하는 방법만 보여 줍니다. 이 함수를 사용하려면 문서가 로드되거나 특정 사용자 작업이 발생할 때 함수를 호출하기 위해 추가 JavaScript를 작성해야 합니다. 이러한 및 관련 topics 대한 자세한 내용은 Client-Side 스크립트 작업을 참조하세요.

요약

특정 ASP.NET 서버 컨트롤은 하위 컨트롤의 렌더링된 id 특성 값과 메서드가 캔버스 FindControl 로 표시하는 컨트롤의 scope 영향을 미치는 명명 컨테이너 역할을 합니다. master 페이지와 관련하여 master 페이지 자체와 ContentPlaceHolder 컨트롤은 모두 컨테이너의 이름을 지정합니다. 따라서 를 사용하여 FindControl콘텐츠 페이지 내의 컨트롤을 프로그래밍 방식으로 참조하기 위해 좀 더 많은 작업을 수행해야 합니다. 이 자습서에서는 ContentPlaceHolder 컨트롤을 드릴 인하고 메서드 FindControl 를 호출하는 두 가지 기술을 살펴보었으며, 모든 명명 컨테이너를 재귀적으로 검색하는 자체 FindControl 구현을 롤링했습니다.

웹 컨트롤 참조와 관련하여 컨테이너 이름을 지정하는 서버 쪽 문제 외에도 클라이언트 쪽 문제도 있습니다. 명명 컨테이너가 없는 경우 웹 컨트롤의 ID 속성 값과 렌더링된 id 특성 값은 동일합니다. 그러나 명명 컨테이너가 추가되면 렌더링된 id 특성에는 웹 컨트롤의 값과 컨트롤 계층 구조의 조상에 명명 컨테이너가 모두 ID 포함됩니다. 이러한 명명 문제는 웹 컨트롤의 ClientID 속성을 사용하여 클라이언트 쪽 스크립트에서 렌더링된 id 특성 값을 결정하는 한 문제가 되지 않습니다.

행복한 프로그래밍!

추가 정보

이 자습서에서 설명하는 topics 대한 자세한 내용은 다음 리소스를 참조하세요.

저자 정보

여러 ASP/ASP.NET 책의 저자이자 4GuysFromRolla.com 설립자인 Scott Mitchell은 1998년부터 Microsoft 웹 기술을 사용하고 있습니다. Scott은 독립 컨설턴트, 트레이너 및 작가로 일합니다. 그의 최신 책은 샘스 자신을 가르친다 ASP.NET 3.5 24 시간. Scott은 에서 mitchell@4GuysFromRolla.com 또는 에서 자신의 블로그 http://ScottOnWriting.NET를 통해 연락할 수 있습니다.

특별 감사

이 자습서 시리즈는 많은 유용한 검토자가 검토했습니다. 이 자습서의 수석 검토자는 잭 존스와 수시 바너지였습니다. 예정된 MSDN 문서를 검토하시겠습니까? 그렇다면 에 줄을 놓습니다 mitchell@4GuysFromRolla.com.