관리 메뉴

IT 쟁이

DataGrid컨트롤 -2 -출처 태요.net 본문

ASP.NET/서버컨트롤

DataGrid컨트롤 -2 -출처 태요.net

클라인STR 2008. 1. 16. 18:05

Taeyo's ASP.NET

   강좌 최초 작성일 : 2004년 12월 22일
   강좌 최종 수정일 : 2005년 01월 10일

   강좌 읽음 수 : 24208 회

   작성자 : Taeyo(김 태영)
   편집자 : Taeyo(김 태영)

   강좌 제목 : DataGrid Control (2)

강좌 전 태오의 잡담>

잠시 출장을 핑계로 LA를 다녀왔습니다. 그 바람에 강좌 업데이트가 조금 늦어졌지요~~ ^^
간만에 미국을 갔다오니... 기분이 씽뚱쌩뚱 하네요... 히히


대상 : 기존 DataList 강좌를 진짜로 읽고, 공부하신 분~~
선수지식 : ADO.NET 기본 지식.

DataGrid 컨트롤

이제 실제적인 업데이트 작업 쪽으로 들어가 보도록 할까요? [업데이트] 버튼을 눌러서 행의 변경된 데이터들을 서버에 반영하기 위해서는 해당 행으로부터 현재 행의 키 컬럼(titles 테이블의 경우 title_id) 값을 얻어올 수 있어야 합니다. 물론, 현재의 경우는 DataGrid 컨트롤의 첫 번째 컬럼으로 title_id를 출력하고 있으므로, 그 값을 얻어오는 것이 그리 어렵지 않지만, 일반적인 경우는 DataGrid 컨트롤의 DataKeyField 속성을 설정해 둠으로써 그 값을 얻어오는 것이 추천되는 편입니다. 이전 DataList 예제에서 해 보았듯이 말이죠. 그렇다면, 그러한 부분을 DataGrid 코드에 추가 작성해 보도록 해요. 그렇게 변경된 HTML 코드는 다음과 같을 것이랍니다.

<asp:DataGrid id="DataGrid1" runat="server" DataKeyField="title_id" CellPadding="4"     AutoGenerateColumns="False" BorderColor="#336666" BorderStyle="Double"     BorderWidth="3px" BackColor="White" GridLines="Horizontal">

이제 업데이트를 위한 모든 준비가 되었네요. 이제 남은 일은 실제 [업데이트] 버튼이 클릭될 경우에 발생하는 UpdateCommand 이벤트 처리기를 작성하는 일입니다.

기존과 마찬가지로 웹 폼 디자이너의 [속성 창]에서 번개 모양의 버튼을 클릭하고, 나열되는 이벤트 목록 중에서 UpdateCommand 와 CancelCommand 이벤트를 더블 클릭하도록 해요. 코드 비하인드 페이지에는 자동으로 각각의 이벤트 처리기 함수가 만들어져 있을 것이며, 여러분은 그 처리기에 다음과 같은 코드를 작성하면 될 것입니다.

private void DataGrid1_UpdateCommand(object source,
    System.Web.UI.WebControls.DataGridCommandEventArgs e)
{
    string title = ((TextBox)e.Item.Cells[1].Controls[0]).Text;
    string price = ((TextBox)e.Item.Cells[2].Controls[0]).Text;
    string pubdate = ((TextBox)e.Item.Cells[3].Controls[0]).Text;
    string title_id = DataGrid1.DataKeys[e.Item.ItemIndex].ToString();

    string connectStr = "Server=(local); database=Pubs; user id=sa";
    SqlConnection Con = new SqlConnection(connectStr);

    string strSql = "UPDATE titles SET title=@title, price=@price, pubdate=@pubdate "
            + " WHERE title_id=@title_id";

    SqlCommand Cmd = new SqlCommand(strSql, Con);

    Cmd.Parameters.Add("@title", SqlDbType.VarChar, 80);
    Cmd.Parameters.Add("@price", SqlDbType.Money);
    Cmd.Parameters.Add("@pubdate", SqlDbType.DateTime);
    Cmd.Parameters.Add("@title_id", SqlDbType.VarChar, 6);

    Cmd.Parameters["@title"].Value = title;
    Cmd.Parameters["@price"].Value = price;
    Cmd.Parameters["@pubdate"].Value = pubdate;
    Cmd.Parameters["@title_id"].Value = title_id;

    Con.Open();
    Cmd.ExecuteNonQuery();
    Con.Close();

    DataGrid1.EditItemIndex = -1;
    BindData();
}

private void DataGrid1_CancelCommand(object source,
    System.Web.UI.WebControls.DataGridCommandEventArgs e)
{
    DataGrid1.EditItemIndex = -1;
    BindData();
}

작성된 코드는 DataList 강좌에서 작성했었던 것과 아주 유사하죠? 다른 부분이 있다면, 현재 편집한 행에서 각각의 컬럼에 해당하는 TextBox의 변경 값을 가져오는 방법 정도랍니다. DataList 예제에서는 편집 모드에서 사용할 각각의 TextBox를 우리가 직접 템플릿에 작성하고 ID를 부여하여 사용했었지만, 현재의 경우(BoundColumn을 사용하는 경우)는 각각의 TextBox에 ID를 직접 지정할 수 있는 방법이 없으므로 기존과는 다른 방식으로 각각의 TextBox에 접근해야만 해요. 그리고, 이를 위해서는 다음과 같은 코드를 사용할 수 있는 거지요~

string title = ((TextBox)e.Item.Cells[1].Controls[0]).Text;
string price = ((TextBox)e.Item.Cells[2].Controls[0]).Text;
string pubdate = ((TextBox)e.Item.Cells[3].Controls[0]).Text;

이벤트 처리기의 두 번째 인자로 넘어오는 DataGridCommandEventArgs 개체의 Item 속성은 현재 아이템 자체를 나타내고 있는 개체이며, 그 Item 개체가 현재의 행 자체를 나타낸다고 생각할 수 있습니다. Item 속성은 사실 내부적으로 DataGridItem 개체를 의미하며, 이 개체는 TableRow와 비슷한 성격을 띄고 있어요.

Item 속성 개체(DataGridItem 개체)를 통해 노출되는 속성으로는 각각의 셀(TableCell)들의 컬렉션인 Cells가 있는데, 우리는 이를 통해서 현재 행의 각각의 컬럼에 접근할 수가 있습니다. 예를 들면, Cells[0]은 현재 행의 첫 번째 열을 의미하며, Cells[1] 은 현재 행의 두 번째 열을 의미하는 것이죠.

각각의 셀(TableCell)도 하나의 컨트롤이기에 모든 컨트롤이 가지고 있는 속성인 Controls 속성을 가지고 있습니다. 해서, 각각의 셀 안에 들어있는 컨트롤들은 Controls 컬렉션을 통해서 접근할 수가 있게 되는 것이죠. 다음 그림을 통해서 이러한 사실을 정리해 보도록 해요.

위의 그림에서는 책의 ID가 BU1111인 두 번째 행이 편집 모드에 있는 것을 알 수 있습니다. 여러분이 이 중 가격을 입력하는 TextBox에 접근하려면 어떻게 접근을 하면 될까요? 일단, 현재의 행은 e.Item 을 통해서 접근할 수 있음을 알고 있을 것입니다. 현재의 행에 접근한 다음에는 가격을 나타내는 셀로 접근을 시도해야 하겠죠?? 다음과 같이 말입니다.

e.Item.Cells[2]

그리고, 그 셀 안에는 오직 하나의 컨트롤, 즉 TextBox만이 존재하므로, 최종적으로 그 TextBox에 접근하는 코드는 다음과 같을 것입니다.

e.Item.Cells[2].Controls[0]

이와 같은 접근 방법을 통해서 코드에서는 각각의 TextBox의 값을 얻어올 수가 있어요. 그러한 코드를 다시 한번 살펴보도록 하겠습니다.

string title = ((TextBox)e.Item.Cells[1].Controls[0]).Text;

이는 수정된 제목의 값을 얻어오는 코드입니다. 제목 TextBox는 현재의 행에 2번째 셀(배열은 0부터 시작한다) 안에 들어있는 첫번째 TextBox 컨트롤이므로 위와 같이 접근할 수 있어요. 단, 접근한 개체를 사용하기 전에 반드시 TextBox 타입으로 형 변환을 해주어야 합니다. e.Item.Cells[1].Controls[0] 은 추상적인 Control 개체를 나타내기에, 이 들이 TextBox라는 사실을 먼저 확실하게 알려주어야 한다는 것이죠.

그리드 컨트롤 내부에 존재하는 각각의 컨트롤들을 프로그래밍적으로 접근하려면 이와 같이 해야합니다. 각각의 TextBox의 ID가 무엇인지 알 수가 없는 경우라면 말이죠. 만일, 각각의 TextBox에 ID를 부여할 수 있었다면 이렇게 복잡하게 할 필요는 없을 것입니다. DataList 예제에서 해 보았던 것처럼 FindControl 메서드를 사용하면 간단할테니 말이죠.

각 컨트롤의 값은 위와 같은 방법으로 가져올 수 있었습니다. 그렇다면, 레코드를 업데이트 하기 위해서 반드시 필요한 title_id의 값은 어떻게 가져올 수 있을까요? 그렇습니다. 이미 우리는 DataGrid의 DataKeyField로 title_id를 지정해 두었으므로 다음과 같이 쉽게 그 값을 얻어올 수가 있습니다. (이는 DataList 예제의 경우와 동일하지요)

string title_id = DataGrid1.DataKeys[e.Item.ItemIndex].ToString();

또한, 그리드 컨트롤은 현재 title_id를 첫번째 열에 출력하고 있으므로, 셀에 직접 접근하여 값을 가져오는 기존의 접근 방식으로도 값을 가져올 수 있습니다. 다음과 같은 코드를 통해서 말이졈.

string title_id = e.Item.Cells[0].Text

title_id는 현재 행(Item)의 첫 번째 셀(Cells[0])에 들어있는 텍스트(Text)이므로, 쉽게 위와 같은 코드를 유추해낼 수 있을 것입니다.

재미있지 않나요? 복잡하게 느껴질 수도 있지만 익숙해지면 이 사실은 대단히 재미있습니다. 그리고, 여러분이 조금만 노력해 준다면 DataGrid 컨트롤은 참 설계가 잘 되어져 있다는 느낌도 받을 수 있을 것이구요. 누가 만들었는지는 모르겠지만 참으로 잘 만들었다는 느낌이 들지 않나요??

이제, 페이지를 컴파일하고 실행하여 우리가 작성한 대로 페이지가 올바르게 동작하는지 테스트 해보도록 하겠습니다. 태오는 첫 번째 행의 데이터를 일부 변경해 보았는데요. 책의 제목 뒤에는 "version 2"라는 글자를 덧붙여 보았고, 가격은 29.99로 바꾸어 보았습니다. 또한, 출간 일의 년도를 2002년으로 변경해 보았어요. 다음은 그렇게 변경하고 [업데이트] 버튼을 눌러 변경 내용이 적용된 결과 화면입니다.

[취소] 버튼도 올바르게 동작할 것이고, 모든 것이 제대로 동작하고 있을 것입니다. (물론, 잘못된 형식으로 데이터를 수정한다거나, 가격 값에 문자 데이터를 넣는다거나 하면 예외가 발생할 것이기는 합니다만.. ^^)

이와 같이 BoundColumn과 ButtonColumn, EditCommandColumn을 적절히 혼합해서 사용하면, 데이터 바인딩, 선택, 변경, 업데이트, 취소의 기능을 쉽게 구현할 수가 있습니다. DataList의 경우는 모든 것을 하드 코딩으로 작성해야 했지만, DataGrid의 경우는 자주 사용되는 기능의 경우는 이처럼 특별한 컬럼 형식을 제공함으로써 간편하게 작업할 수 있도록 돕고 있는 것이죠.


HyperLinkColumn 사용하기

자. 이번에는 DataGrid의 또 다른 컬럼 형식인 HyperLinkColumn에 대해서 알아보도록 하겠습니다. 이는 하이퍼링크를 갖는 열을 쉽게 만들 수 있도록 도움을 주는 매우 유용한 컬럼 형식입니다.

먼저, 이 컬럼 형식이 제공하는 속성에 대해서 알아보도록 할께요.

속성 설명
DataNavigateUrlField 하이퍼링크 URL에 사용될 데이터 원본(DataSource)의 필드
DataNavigateUrlFormatString DataNavigateUrlField를 사용하는 하이퍼링크의 문자열 포맷
DataTextField 출력할 텍스트를 나타내는 데이터 원본(DataSource)의 필드
DataTextFormatString DataTextField에 의해 출력될 텍스트의 문자열 포맷
NavigateUrl 하이퍼링크 URL. DataNavigateUrlField를 지정하면 이 속성은 무시된다.
Target 하이퍼링크에 의해 이동할 페이지를 표시하는 대상 윈도우.
_blank는 새 창을 띄우고, 이동하게 된다.
Text 하이퍼링크의 텍스트

이러한 기능을 갖는 HyperLinkColumn 컬럼 형식을 사용하면 여러분은 특정 열을 하이퍼링크를 갖는 열로 손쉽게 만들 수 있습니다. 예제를 통해서 구체적으로 알아보도록 할까요? 저는 기존의 컬럼 중 책의 제목을 출력하는 열을 하이퍼링크를 갖는 열로 대체해볼까 합니다. 그것이 실무에서 자주 사용하는 형태이니 말입니다. 해서, 사용자가 제목을 클릭할 경우에, 특정 페이지로 이동하여 책의 상세 정보를 확인할 수 있도록 해 볼까 해요. 하지만, 그러한 상세 정보 페이지는 만들지 않을 거예요. 그렇게 실제 사용하는 예는 차후에 시간이 허락할 때 다루어보도록 하구요. 여기서는 하이퍼링크를 만들어서 출력하는 부분까지만 이야기해 볼까 합니다.

다음과 같이 기존의 예제의 <Columns> 구역을 편집해 보도록 해요

<Columns>
    <asp:BoundColumn DataField="title_id" HeaderText="ID"></asp:BoundColumn>
    <asp:HyperLinkColumn HeaderText="제목" DataNavigateUrlField="title_id"
        DataNavigateUrlFormatString="Details.aspx?id={0}" DataTextField="title">
        </asp:HyperLinkColumn>
    <asp:BoundColumn DataField="price" HeaderText="가격" DataFormatString="{0:N2}">
        </asp:BoundColumn>
    <asp:BoundColumn DataField="pubdate" HeaderText="출간일"
        DataFormatString="{0:yyyy-MM-dd hh:mm}"></asp:BoundColumn>
    <asp:ButtonColumn HeaderText="선택" ButtonType="PushButton" CommandName="Sel"
        Text=" S "></asp:ButtonColumn>
    <asp:EditCommandColumn ButtonType="PushButton" EditText="편집" CancelText="취소"
        UpdateText="업데이트"></asp:EditCommandColumn>
</Columns>

코드 중에 바뀐 부분인 "제목"을 출력하는 컬럼 부분을 조목조목 살펴보도록 하겠습니다. 우선 HyperLinkColumn의 속성 중 DataNavigateUrlField와 DataNavigateUrlFormatString를 사용하고 있는데, 이 중 DataNavigateUrlFormatString은 실제로 이동할 하이퍼링크의 경로와 전달할 인자의 포맷을 지정하는 속성이며, DataNavigateUrlField은 DataNavigateUrlFormatString에 지정된 하이퍼링크 포맷 문자열에서의 {0}이라는 부분을 대체하여 바인드 될 데이터 원본의 컬럼을 의미한답니다. 예제의 경우는 이 값으로 Details.aspx?id={0} 을 주고 있는데, 이렇게 되면 실제 하이퍼링크가 이와 같은 형태로 만들어 지면서, {0}이라는 부분은 DataNavigateUrlField 속성에 지정된 각각의 컬럼의 값으로 대체 되게 되지요.

이 두 개의 속성으로 실제적인 하이퍼링크가 만들어 진 것입니다. 하지만, 하이퍼링크는 그 자체만으로는 사용자의 눈에 나타나지 않아요. 그러므로, 사용자의 눈에 보여질 텍스트 부분을 작성해야 하는데요. 그를 위해서는 DataTextField 속성을 사용합니다. 예제에서는 이 값을 "title" 컬럼을 지정하고 있어요. 고로, 각각의 행은 하이퍼링크의 텍스트로 책의 제목을 각각 바인드하여 출력하게 될 겁니다.

하이퍼링크를 데이터 원본과의 바인딩을 통해서 구성할 경우에는 이처럼 Data라는 접두어로 시작하는 속성들을 사용하면 됩니다. 그리고, 이것이 가장 보편적인 방법이기도 하죠. 하이퍼링크의 경로와 인자가 고정적이거나, 하이퍼링크의 텍스트가 고정적인 문자열일 경우는 드물 테니 말이예요.

이제 페이지를 컴파일하고 실행해 보도록 해요. 그럼 다음과 같은 결과를 볼 수 있을 겁니다.

호오. 멋지죠? 각각의 하이퍼링크 위에 마우스를 올려놓았을 경우, 브라우저의 상태바에 출력되는 경로를 확인해 보아요. 모두 Details.aspx를 가리키고 있으며, id 인자 부분의 값이 각각의 링크마다 서로 다른 것을 확인할 수 있을 것입니다. 그리고, 그 값은 각각의 책의 title_id 일 것이구요. 우리가 코드에서 지정했듯이 말입니다.

주의할 부분은 HyperLinkColumn으로 지정된 열은 [편집] 모드에서도 그대로 하이퍼링크로 존재한다는 점입니다. 즉, 이는 [편집] 모드로 자동 전환되는 BoundColumn과는 다르다는 것이죠. 다음 그림을 통해 이를 확인해 보아요.

만일, 일반 출력시에는 하이퍼링크로 보이다가 편집 모드에서는 텍스트를 편집할 수 있는 TextBox로 전환되도록 만들고 싶다면, 이는 HyperLinkColumn를 사용해서는 불가능합니다. 하지만, 다음 섹션에서 배울 TemplateColumn을 사용한다면 구현할 수 있지요. 템플릿을 이용하면 불가능한 것은 거의 없답니다.

HyperLinkColumn을 사용하여 하이퍼링크가 출력된 현재의 상태에서는 [편집] 모드에서 제목 컬럼의 변경이 불가능하며, TextBox도 나타나지 않습니다. 그러므로, [업데이트] 버튼을 클릭할 경우 예외가 발생할 거예요. TextBox 컨트롤이 존재하지 않으므로 UpdateCommand 이벤트 내의 다음 코드 부분에서 문제가 생길 것이니 말입니다.

string title = ((TextBox)e.Item.Cells[1].Controls[0]).Text;

고로, 현재의 상태에서 업데이트가 제대로 동작하려면 UpdateCommand 이벤트 처리기의 코드를 수정해야 합니다. 제목은 업데이트 처리를 하지 않도록 말이졈. 하지만, 지금 여러분이 그러한 처리를 할 필요는 없어요. 왜냐하면, 이 예제를 다음 섹션에서 또 다시 변경할 것이 때문에... 굳이 지금 바꿀 필요는 없다는 것이죠. 나중에 한번에 해도 되니까요 ^^; 물론, 원한다면 한번 시도해 보아도 좋아요.

DataGrid는 이처럼 기본적으로 표(<table>)에 필요한 기능들을 사용하기 쉬운 여러 형태의 컬럼 형식으로 제공해 주고 있어요. 이벤트 처리등의 기본적인 처리 기법은 DataList와 매우 유사하지만, 사용하기는 훨씬 편하고, 코딩 양도 상대적으로 적고, 기능적이며 직관적이라는 사실도 느낄 수 있을 것입니다. 그렇습니다. 현재까지의 DataGrid는 제가 서두에서 이야기한 것과는 달리 상당히 사용하기 간편하고, 다루기도 용이합니다. 게다가 기능면에서도 뛰어나다는 느낌까지 주고 있죠.

하지만, 이렇게 영리하고 멋진 DataGrid에게서 왠지 그 멋진 기능들에 비해 뭔가 너무 정형화 되어져 있다는 느낌도 조금은 느껴지지 않나요? 예를 들면, [편집] 모드에서 나타나는 TextBox들의 너비나 테두리가 고정적이어서 보기에 좋지 못하다던지, 하나의 열 안에 여러 개의 데이터를 바인드할 수 없다던지 하는 부분이 말이예요. 게다가, 현재 상태로는 그리드 컨트롤에 체크박스나, 드롭다운 리스트등도 올릴 수가 없어요. 오직, 하이퍼링크와 버튼만을 그리드 컨트롤 안에 넣을 수 있을 뿐이죠. HyperLinkColumn과 ButtonCloumn, EditCommandColumn 을 사용해서 말입니다.

그렇다면, 그러한 제약적인 부분을 풀 수 있는 방법은 없을까요? 없을리가 없겠죠? DataGrid가 제공하는 위의 편리한 기능들로 뭔가 부족함을 느낄 경우 사용할 수 있는 특별한 컬럼 형식이 있는데요. 그것이 바로 이 섹션 중간 중간 누누이 강조했듯이 템플릿을 사용하여 하나부터 열까지 우리가 직접 모든 것을 작성하는 방법입니다. 이제 그 방법에 대해서 알아볼 차례네요. 템플릿을 사용하는 방법을 알게되면, 여러분은 표를 여러분이 원하는 어떤 형태로든지 출력할 수 있게 될 것입니다.


템플릿을 사용하여 서버 컨트롤을 그리드에 추가하기

DataGrid가 제공하는 마지막 컬럼 형식은 바로 TemplateColumn 입니다. 이를 사용하면 여러분이 원하는 대로 컬럼의 형태를 꾸밀 수 있지요. 하지만, 이 형식을 사용할 경우는 데이터가 자동으로 바인딩 되지 않기 때문에 여러분이 직접 바인딩 표현식을 사용하여 ItemTemplate를 작성해야 하며, 각각의 행을 편집할 경우를 위해서는 EditItemTemplate도 작성해야 하기에 코딩의 양이 상당히 늘어나게 됩니다. 다른 컬럼 형식들이 제공했던 편리함을 여러분이 직접 손수 구현해야 한다는 것이죠.

하지만, TemplateColumn을 사용하면 각각의 항목(행)을 마음대로 구성할 수 있다는 장점이 있습니다. DataGrid 위에 버튼 컨트롤이나, 체크박스, 드롭다운 리스트 컨트롤등등의 서버 컨트롤을 추가하고 싶다면, 이 또한 템플릿을 이용하여 가능합니다. 즉, 출력 결과를 처음부터 끝까지 여러분의 구미대로 작성하고 싶다면 템플릿을 이용하는 것이 가장 탁월한 선택이라는 것이지요. 물론, 그 만큼 직접 코딩해야 하는 부분이 늘어나긴 하지만 말입니다.

템플릿을 작성하는 방법은 DataList에서의 사용방법과 동일합니다. 단지, 차이가 있다면 DataList의 템플릿들의 경우는 행 전체를 대상으로 했던 반면, DataGrid의 TemplateColumn은 행의 특정 열(컬럼)을 대상으로 한다는 것이 다릅니다. 즉, DataGrid에서는 열(컬럼) 단위로 템플릿을 작성해야 한다는 것이죠.

다음은 TemplateColumn이 제공하는 템플릿들의 목록입니다.

템플릿 설명
HeaderTemplate 컬럼의 머리글을 정의하는 템플릿
ItemTemplate 컬럼의 출력을 정의하는 템플릿
EditItemTemplate 컬럼이 편집될 경우를 위해 정의하는 템플릿
FooterTemplate 컬럼의 바닥글을 정의하는 템플릿

각각의 템플릿에 대한 사용법은 기존에 Repeater나 DataList를 통해서 학습한 것과 동일하기에 큰 어려움은 없을 것입니다. 그렇다면, TemplateColumn을 사용하는 예제를 한번 만들어 보도록 할까요?

기존 예제의 모든 컬럼을 TemplateColumn으로 바꾸는 것도 나쁘지는 않겠지만, 급격한 변화는 머리에 무리를 주므로, 기존의 컬럼 형식 중 title_id에 해당하는 컬럼만을 TemplateColumn으로 바꾸어 보도록 하겠습니다. 다음과 같이 기존 DataGrid 코드를 변경해 봐요. 언뜻 보기에는 상당히 복잡한 것처럼 보이지만 알고 보면 그렇지도 않습니다. (저는 추가적으로 DataGrid의 [자동 서식]을 '전문가 2'로 바꾸어 지정하였습니다. 그 편이 보기에 더 나아보여서요)


<Columns>
    <asp:TemplateColumn>
        <HeaderStyle HorizontalAlign="Center" Width="70px"></HeaderStyle>
        <HeaderTemplate>ID</HeaderTemplate>
        <ItemStyle HorizontalAlign="Center"></ItemStyle>
        <ItemTemplate>
            <IMG src='http://localhost/quickstart/ASPPlus/images/Title-
                <%# DataBinder.Eval(Container.DataItem, "title_id") %>.gif'>
        </ItemTemplate>
        <EditItemTemplate>
            <font color="red"><%# DataBinder.Eval(Container.DataItem, "title_id") %></font>
        </EditItemTemplate>
    </asp:TemplateColumn>
    <asp:HyperLinkColumn HeaderText="제목" DataNavigateUrlField="title_id"
        DataNavigateUrlFormatString="Details.aspx?id={0}" DataTextField="title">
    </asp:HyperLinkColumn>
    <asp:BoundColumn DataField="price" HeaderText="가격" DataFormatString="{0:N2}">
        </asp:BoundColumn>
    <asp:BoundColumn DataField="pubdate" HeaderText="출간일"
        DataFormatString="{0:yyyy-MM-dd hh:mm}"></asp:BoundColumn>
    <asp:ButtonColumn HeaderText="선택" ButtonType="PushButton" CommandName="Sel"
        Text=" S "></asp:ButtonColumn>
    <asp:EditCommandColumn ButtonType="PushButton" EditText="편집" CancelText="취소"
        UpdateText="업데이트"></asp:EditCommandColumn>
</Columns>

TemplateColumn 구역으로 바뀐 부분은 기존에는 BoundColumn으로 작성되었던 부분입니다. 예제에서는 HeaderTemplate, ItemTemplate, EditItemTemplate등을 사용하여 각각의 출력 형태를 직접 수동으로 작성해 보았는데요. 각각의 템플릿의 사용방법은 DataList을 공부할 때 구체적으로 다루었었기에 반복해서 설명할 필요는 없을 것 같네요(기억이 안나시는 분들은 복습요망입니다). 위와 같이 변경한 다음 페이지를 컴파일하고, 실행하면 어떤 결과가 나타날 것 같나요? 그것은!!!!!! 직접 확인해 보아요 ^^;

일반 출력시에는 title_id를 출력하는 구역에 해당 이미지가 출력되는 것을 볼 수 있습니다. 그리고, [편집] 모드에서는 이미지 대신 title_id를 빨간 색의 텍스트로 보여주고 있어요. 우리가 템플릿에 작성한대로 말입지요. 다시 한번 강조하지만, DataGrid의 TemplateColumn은 각각의 컬럼 단위로 동작한답니다.

TemplateColumn을 사용하면 이렇듯 각각의 컬럼의 출력 형태를 우리가 원하는대로 제어할 수가 있어요. 뭔가 흥겨워지는 이 분위기를 살려서 조금 더 템플릿을 사용해 보도록 해요. 이번에 템플릿으로 바꿀 부분은 제목으로 출력되는 하이퍼링크 부분인데여. 저는 이 부분을 템플릿을 사용하여, 일반 출력일 경우는 하이퍼링크가 출력되고 [편집] 모드에서는 편집이 가능한 TextBox를 나타나게 해 볼까 합니다. 한번 해볼까요?? 여러분이 코드에서 바꾸어할 부분은 다음과 같습니다


<Columns>
    <asp:TemplateColumn>
        <HeaderStyle HorizontalAlign="Center" Width="70px"></HeaderStyle>
        <HeaderTemplate>ID</HeaderTemplate>
        <ItemStyle HorizontalAlign="Center"></ItemStyle>
        <ItemTemplate>
            <IMG src='http://localhost/quickstart/ASPPlus/images/Title-
                <%# DataBinder.Eval(Container.DataItem, "title_id") %>.gif'>
        </ItemTemplate>
        <EditItemTemplate>
            <font color="red"><%# DataBinder.Eval(Container.DataItem, "title_id") %>
            </font>
        </EditItemTemplate>
    </asp:TemplateColumn>
    <asp:TemplateColumn>
        <HeaderStyle HorizontalAlign="Center" Width="350px"></HeaderStyle>
        <HeaderTemplate>제목</HeaderTemplate>
        <ItemTemplate>
            <a href='Details.aspx?id=<%# DataBinder.Eval(Container.DataItem,
                "title_id") %>'><%# DataBinder.Eval(Container.DataItem, "title") %></a>
        </ItemTemplate>
        <EditItemTemplate>
            <asp:TextBox Runat="server" id="title" width="100%" BorderStyle="Groove"
                Text='<%# DataBinder.Eval(Container.DataItem, "title") %>'>
            </asp:TextBox>
        </EditItemTemplate>
    </asp:TemplateColumn>
    <asp:HyperLinkColumn HeaderText="제목" DataNavigateUrlField="title_id"
        DataNavigateUrlFormatString="Details.aspx?id={0}" DataTextField="title">
    </asp:HyperLinkColumn>
    <asp:BoundColumn DataField="price" HeaderText="가격" DataFormatString="{0:N2}">
    </asp:BoundColumn>
    <asp:BoundColumn DataField="pubdate" HeaderText="출간일"
        DataFormatString="{0:yyyy-MM-dd hh:mm}"></asp:BoundColumn>
    <asp:ButtonColumn HeaderText="선택" ButtonType="PushButton" CommandName="Sel"
        Text=" S "></asp:ButtonColumn>
    <asp:EditCommandColumn ButtonType="PushButton" EditText="편집" CancelText="취소"
        UpdateText="업데이트"></asp:EditCommandColumn>
</Columns>

변경된 부분은 제목이 출력되는 기존 HyperLinkColumn 구역입니다. 그 부분은 이제 TemplateColumn으로 변경되었어요. TemplateColumn 내의 ItemTemplate 구역에는 하이퍼링크의 생성 코드를 작성하였으며, EditItemTemplate 구역에는 title이라는 ID를 갖는 TextBox 서버 컨트롤을 하나 만들고, 출력 값을 바인딩 해 보았습니다. [편집] 모드 시에 사용자가 그 내용을 변경할 수 있도록 말입니다. 이미 템플릿에 익숙한 여러분이라면 이렇게 바꾼 결과가 어떨지를 쉽게 예상할 수 있을 것입니다. 일반 모드에서의 출력 결과는 HyperLinkColumn를 사용한 기존 결과와 크게 다르지 않죠? 사실 결과적으로만 본다면 정확하게 동일합니다. 하지만, [편집] 모드는 사정이 조금 다르죠. 기존과는 달리 여러분이 템플릿에 작성한 대로 값을 변경할 수 있는 TextBox가 나타나게 됩니다.

템플릿을 사용하면 BoundColumn을 사용했을 경우에는 설정이 어려웠던 TextBox의 너비 설정이나, 스타일 설정이 가능한 것도 확인할 수 있을 거예요. 결과 화면을 마주하게 되면, 여러분은 데이터들을 적절히 변경한 다음 [업데이트] 버튼을 누르고픈 마음이 들 것 같습니다요. 하지만, 현재의 상태로는 그러한 경우 다음과 같은 예외가 발생해요~~~

예외 정보: System.InvalidCastException: 지정한 캐스트가 잘못되었습니다.

코드에 어떤 이상이 있는 것일까요? 예외가 발생하는 부분은 제목 TextBox로부터 값을 가져오는 다음 부분입니다.

    private void DataGrid1_UpdateCommand(object source,
        System.Web.UI.WebControls.DataGridCommandEventArgs e)
    {
        string title = ((TextBox)e.Item.Cells[1].Controls[0]).Text; //이 부분에서 예외 발생
        string price = ((TextBox)e.Item.Cells[2].Controls[0]).Text;
        …

출력 결과를 직접 보면서 TextBox의 위치를 확인해 보세요. 분명 제목을 변경하는 TextBox는 두 번째 Cell의 첫 번째 컨트롤로 자리하고 있지요? 그렇다면, 위의 코드에서 틀린 부분은 없을텐데, 왜 이러한 예외가 발생한 것일까여? 사실은 템플릿을 사용할 경우 눈에 보이지 않는 LiteralControl이 TextBox 앞에 내부적으로 생성되어지기 때문이랍니다. 게다가 이 LiteralControl 컨트롤에는 값 마저도 존재하지 않아서 결과 화면만으로는 그러한 컨트롤이 TextBox 앞에 존재하는지 알 수가 없죠 ㅠㅠ. 해서, 템플릿을 사용할 경우에는 위와 같이 컨트롤의 위치 값을 통해서 찾아들어가는 방식은 그다지 효과적이지 못하게 됩니다. 굳이, 그러한 방식을 고집한다면 제목 TextBox의 값을 가져오는 문제의 코드를 다음과 같이 바꾸어야 하는 것입지요.

string title = ((TextBox)e.Item.Cells[1].Controls[1]).Text;

하지만, 템플릿을 사용한다면 각각의 TextBox에 ID를 부여할 수 있으므로, 그 아이디를 사용하여 각각의 컨트롤을 찾아가는 방법을 사용하는 것이 낫습니다. 즉, 기존 강좌 중 DataList 예제에서 다루어 보았듯이 FindControl 메서드를 사용하여 원하는 컨트롤에 접근하는 방법이 낫다는 것이지요. 다음과 같이 말입니다.

private void DataGrid1_UpdateCommand(object source,
    System.Web.UI.WebControls.DataGridCommandEventArgs e)
{
    string title = ((TextBox)e.Item.FindControl("title")).Text;
    string price = ((TextBox)e.Item.Cells[2].Controls[0]).Text;
    …

이제, 페이지를 컴파일하고 실행해 보도록 하세요. 모든 기능이 문제없이 동작하는 것을 확인할 수 있을 것입니다.

다시 한번 강조하지만, 템플릿을 사용한다면 각각의 열을 우리가 원하는 대로 제어할 수 있습니다. 예를 들면, 제목을 반드시 입력하도록 유효성 검사 컨트롤도 사용할 수 있다는 이야기이며, 다음과 같이 제목용 TemplateColumn에 RequiredFieldValidator를 사용하여 제목을 변경할 경우 반드시 제목을 기입하게 하도록 강제할 수 있다는 이야기입니다.


    <asp:TemplateColumn>
        <HeaderStyle HorizontalAlign="Center" Width="350px"></HeaderStyle>
        <HeaderTemplate>제목</HeaderTemplate>
        <ItemTemplate>
            <a href='Details.aspx?id=<%# DataBinder.Eval(Container.DataItem,
                "title_id") %>'><%# DataBinder.Eval(Container.DataItem, "title") %></a>
        </ItemTemplate>
        <EditItemTemplate>
            <asp:TextBox Runat="server" id="title" width="100%" BorderStyle="Groove"
                Text='<%# DataBinder.Eval(Container.DataItem, "title") %>'>
            </asp:TextBox>
            <asp:RequiredFieldValidator id="RFV1" runat="server" ControlToValidate="title"
                ErrorMessage="* 책의 제목을 입력해 주세요"
                Display="Dynamic"></asp:RequiredFieldValidator>
        </EditItemTemplate>
    </asp:TemplateColumn>

위와 같이 소스를 변경한 뒤, 다시금 페이지를 실행해 보도록 하세요. [편집] 모드에서 TextBox에 값을 모두 지운 뒤에, 업데이트를 시도하면 그림과 같이 경고 메시지가 나오는 것을 확인할 수 있을 것입니다.

템플릿을 사용하게 되면, 여러분이 해야할 추가적인 코딩이 늘어나지만, 그 만큼 기능적으로 만들 수가 있어요. 얼마나 멋진 DataGrid 인지... 하핫~~

DataGrid 가 멋진 컨트롤이기는 하지만, 이도 현업에서 요구하는 모든 기능을 제공해 주지는 못합니다. 현업에서 가장 자주 요구하는 기능 중에 하나가 바로 한번에 여러 행의 데이터를 편집할 수 있도록 하는 기능인데, DataGrid는 지금껏 보아왔듯이, 행 단위의 편집만을 제공하기 때문이지요. 동시에 여러 행을 편집 모드로 전환할 수 있는 기능은 기본적으로 제공하지 않는다는 것이지요. 해서, 만일 그러한 컨트롤을 원한다면 상용으로 제공되는 여러 ASP.NET용 그리드 컴포넌트를 인터넷에서 검색하여 구매해야 하기도 합니다. 여기서 특정 업체명을 이야기하면 오해의 소지가 있을 수 있기에 참도록 하겠습니다. ^^;;; 참고로, 어떤 상용 그리드 컨트롤은 손쉽게 데이터를 계층적으로 출력할 수 있는 기능도 제공하기도 하더군요. 물론, 그러한 컨트롤을 구입해서 사용하려 한다면 별도로 그 컨트롤에 대한 사용법을 또한 학습해야만 할 것이긴 하지만 말입니다.

중요 팁!!!

사실, 편법을 사용하면 DataGrid도 여러 행을 편집할 수 있습니다. 많은 분들이 이 이야기를 궁금해 하실텐데요. 그에 대한 힌트는 다음 링크에서 찾아볼 수 있으니 링크를 알려드리도록 하겠습니다. 단, 이 방법은 성능면에서는 그다지 효과적인 방법이 아니므로, 신중한 고려를 거쳐서 채택해야 할 것입니다.

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dv_vstechart/html/vbtchtopquestionsaboutaspnetdatagridservercontrol.asp

Comments