Programming/.NET
GridView 컨트롤은 데이터 집합을 사용자에게 보여주기 위해 ASP.NET 에서 사용할 수 있는 대표적 컨트롤입니다. GridView는 단순히 데이터를 화면에 표시하는 기능이외에도 데이터를 정렬하거나 게시판에서와 같은 페이징기능도 제공하며 심지어는 표시된 데이터를 임의로 수정할 수도 있습니다.

<코드 1-1>은 AdventureWorks2012 데이터베이스의 Person.Person 테이블에서 데이터를 Select 하는 SqlDataSource 컨트롤을 소스로 하여 GridView 컨트롤을 사용한 예제입니다.
<asp:SqlDataSource ID="SqlDataSource1" runat="server" ConnectionString="<%$ ConnectionStrings:new_connection %>" SelectCommand="SELECT * FROM [Person].[Person] WHERE ([PersonType] = @PersonType)">
    <SelectParameters>
        <asp:Parameter DefaultValue="EM" Name="PersonType" Type="String" />
    </SelectParameters>
</asp:SqlDataSource>

<asp:GridView ID="GridView1" runat="server" DataSourceID="SqlDataSource1"></asp:GridView>
▶ <코드 1-1>

데이터 소스를 GridView에 설정하고 페이지를 실행하면 데이터 소스의 스키마와 일치하는 컬럼을 자동으로 생성하여 화면에 표시하게 됩니다.

▶ <그림 1-1>

필요하다면 전체 컬럼을 그대로 가져오는 것이 아니라 데이터 소스의 쿼리를 수정하거나 아니면 GridView에 직접 특정 컬럼을 지정해 놓고 해당 컬럼만 생성하여 표시하도록 할 수 있습니다.
<asp:GridView ID="GridView1" runat="server" DataSourceID="SqlDataSource1" AutoGenerateColumns="false">
    <Columns>
        <asp:BoundField DataField="BusinessEntityID" HeaderText="번호" />
        <asp:BoundField DataField="FirstName" HeaderText="이름" />
        <asp:BoundField DataField="LastName" HeaderText="성" />
    </Columns>
</asp:GridView>
▶ <코드 1-2>

GridView 의 AutoGenerateColumns 속성을 false 로 설정하면 데이터 소스의 스키마와 일치하는 컬럼을 자동으로 생성하는 것을 막게되고 다시 Columns 객체를 사용하여 GridView에 원하는 컬럼을 생성합니다.

마지막으로 BoundField 에서는 DataField 속성을 통해 데이터 소스의 컬럼과 일치하는 속성이나 컬럼명을 명시하면 비로소 관련 데이터를 화면에 표시하게 됩니다.


이때 데이터소스의 데이터집합이 GridView에 바인딩되는 순간을 잡아내려면 OnDataBounding 이벤트를 사용하면 됩니다.(반면 바인딩이 완료되는 시점에는 OnDataBound 이벤트가 발생합니다.)

참고로 GridView가 데이터를 표시하기 위해 Row를 생성하는 경우 OnRowCreated 이벤트가 발생하는데 이벤트를 활용하면 특정 Row의 스타일등을 Runtime 단에서 조정할 수 있으며
<asp:GridView ID="GridView1" runat="server" DataSourceID="SqlDataSource1" OnRowCreated="GridView1_RowCreated"></asp:GridView>
protected void GridView1_RowCreated(object sender, GridViewRowEventArgs e)
{
    e.Row.BackColor = System.Drawing.Color.SkyBlue; //생성되는 Row의 배경색을 SkyBlue로 지정
}

OnRowDataBound 이벤트는 OnRowCreated 이벤트 생성 후 실제 데이터가 바인딩될때 발생하는 이벤트로서 바인딩데이터를 확인해 보는 것이 가능합니다. 이 특징을 잘 활용하면 표시될 데이터가 특정 값을 가진 경우에 대해 별도의 처리를 진행할 수 있습니다.

<asp:GridView ID="GridView1" runat="server" DataSourceID="SqlDataSource1" OnRowDataBound="GridView1_RowDataBound"></asp:GridView>
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
    if (e.Row.DataItem != null) {
        DataRowView drv = (DataRowView)e.Row.DataItem;

        if (drv["MiddleName"] == DBNull.Value)
            e.Row.BackColor = System.Drawing.Color.Red; //MiddleName 필드의 값이 Null 인 경우 해당 Row의 색을 붉은색으로 표시함
    }
}
 EmptyData

GridView 에 제공되는 Data Source 는 경우에 따라 표시할 데이터 내용을 가지고 있지 않는 경우가 있습니다. 날짜에 따라, 또는 검색조건에 따라 기타 여러가지 이유로 일치하는 항목이 없는 경우등을 예로 들 수 있겠습니다. GridView 는 표시할 내용이 아무것도 없는 경우에는 기본적으로 어떠한 내용도 표시하지 않습니다.

그렇다 하여 실제 사용자에게 덩그러니 빈 화면만 보여주게 되면 실제 내용이 없어서 표시가 안되는것인지 아니면 어떤 오류상황에 의해 데이터를 표시하지 않는 것인지 혼동을 줄 수 있으므로 적당한 안내멘트를 대신 뿌려줄 필요가 있습니다. GridView는 이런 경우에 대한 해결책으로 두가지 방법을 사용할 수 있습니다.

그 중 첫번째 방법은 EmptyDataText 속성을 사용하는 것입니다.
<asp:GridView ID="GridView1" runat="server" DataSourceID="SqlDataSource1" EmptyDataText="<font color='red'>표시할 내용 없음</font>"></asp:GridView>
▶ <코드 1-3>

EmptyDataText 는 html 태그를 포함한 간단한 문자열을 값으로 지정할 수 있으며 아무런 내용도 표시할 수 없을때 새로운 DataRow 를 생성하여 EmptyDataText 에 할당한 값을 대신 표시하게 됩니다. 두번째 방법은 GridView안에 EmptyDataTemplate 이라는 컨트롤 템플릿을 사용하는 방법으로서
<asp:GridView ID="GridView1" runat="server" DataSourceID="SqlDataSource1">
    <EmptyDataTemplate>
        <table border="5"><tr><td>표시할 내용 없음</td></tr></table>
    </EmptyDataTemplate>
</asp:GridView>
▶ <코드 1-4>

이때는 다소 복잡한 HTML 내용이나 심지어 ASP.NET 컨트롤 까지도 포함한 내용을 직접 작성할 수 있게 되며 내용이 제법 방대하고 복잡한 경우에 사용할 수 있는 방법에 해당합니다. 이 처럼 EmptyDataTemplate 은 임의로 생성되는 Row를 완벽하게 커스터마이징 할 수 있는 확장성을 제공합니다.

참고로 데이터 전체가 아닌 특정 컬럼의 값에 대해서 개별적으로 NULL 이나 빈값에 대응하는 표시내용을 설정해야 하는 경우가 있는데 이 때는 <코드 1-5>에서 처럼 BoundField 에서 직접 처리하기를 원하는 내용을 지정해 줘야 합니다.
<asp:GridView ID="GridView1" runat="server" DataSourceID="SqlDataSource1" AutoGenerateColumns="false">
    <Columns>
        <asp:BoundField DataField="BusinessEntityID" />
        <asp:BoundField DataField="FirstName"  />
        <asp:BoundField DataField="LastName" />
        <asp:BoundField DataField="Title" NullDisplayText="N/A" />
    </Columns>
</asp:GridView>
▶ <코드 1-5>

<코드 1-5>에서는 Title 열값에 Null 이 존재하는 경우 NullDisplayText 속성을 통해 비어있는 내용대신 'N/A'로 표시할 수 있게 하였습니다.

 데이터 정렬 (SortExpression)

GridView 는 기본적으로 특정 컬럼을 오름차순이나 내림차순으로 정렬할 수 있도록 하는 기능을 제공합니다. 이 기능은 단지 GridView 의 AllowSorting 속성을 true로 설정하기만 하면 됩니다.
<asp:GridView ID="GridView1" runat="server" DataSourceID="SqlDataSource1" AllowSorting="true"></asp:GridView>

이 설정을 거치고 나면 GridView Header 부분에 링크가 걸린것을 확인할 수 있으며 이 링크를 반복해서 클릭하면 해당 컬럼에 대한 오름차순/내림차순 정렬을 순환하여 표시하게 됩니다.


만일 정렬대상 컬럼이 2개 이상이라면 GridView 의 Sorting 이벤트를 통해 직접 정렬대상을 지정할 수 있습니다.
protected void GridView1_Sorting(object sender, GridViewSortEventArgs e)
{
    string oldSE = GridView1.SortExpression;
    string newSE = e.SortExpression;

    if (oldSE.Trim() != string.Empty)
        e.SortExpression = newSE + "," + oldSE;
    else
        e.SortExpression = newSE;
}
▶ <코드 2-1>

<코드 2-1>은 데이터 정렬시 발생하는 Sorting 이벤트에서 이전 정렬식(이전 정렬 컬럼;oldSE)의 내용을 가져와 새로운 정렬식(현재 정렬 컬럼;newSE)을 콤마(,)로 구분하여 결합한 뒤 실제 정렬을 수행하는데 필요한 e.SortExpression 속성에 지정하여 다중컬럼을 대상으로 정렬을 수행할 수 있도록 하였습니다.

여기서 oldSE 는 현재가 아닌 이전에 정렬된 식을 가져오는 변수로 GridView에서 데이터 출력 후 처음으로 데이터를 정렬하기 위해 GridView의 Header 링크를 클릭한 경우라면 이 변수는 비어있게 됩니다. 예를 들어 처음 FirstName 을 클릭하여 정렬을 시도한 경우라면 oldSE 는 공백, newSE 는 FirstName 값을 갖게 되며 이 후 다시 LastName 을 클릭했을 때 oldSE 는 FirstName 을 newSE 는 LastName 값을 가지게 됩니다.

예제에서 보시는 바와 같이 e.SortExpression 을 잘만 활용하면 단독이나 다수개의 컬럼을 대상으로 정렬을 지정할 수 있고 아예 공백을 대입하여 Sort 자체를 무력화 할 수도 있습니다.

참고로 <코드 2-1>에서 Sorting 처리의 방법을 알아보기 위해 OnSorting 이벤트를 적용하였는데 데이터가 정렬되고 난 직후의 이벤트라면 OnSorted 이벤트를 사용할 수도 있습니다.

 페이징(Paging)

GridView 에 페이징을 구성하려면 AllowPaging 속성을 true 로 직접 설정하거나 컨트롤의 스마트태그에서 Enable Paging 부분을 체크하면 됩니다.
<asp:GridView ID="GridView1" runat="server" DataSourceID="SqlDataSource1" AllowPaging="true"></asp:GridView>
 
GridView 의 페이징기능은 기본적으로 10페이지씩 분할하게 되며 페이지를 이동할 수 있는 숫자는 하단 왼쪽에 위치하게 됩니다.

PageSize 속성을 이용하면 기본 10페이지로 나누는 범위를 조정할 수 있으며 PagerSettings-Mode 를 사용하면 페이지이동 숫자(기본값)이외에 맨처음, 마지막 등의 점프링크를 페이징에 추가할 수도 있거나 숫자를 제거하고 이전, 다음으로만 이동이 가능하도록 제한 할 수도 있습니다.
<asp:GridView ID="GridView1" runat="server" DataSourceID="SqlDataSource1" AllowPaging="true" PageSize="20" PagerSettings-Mode="NumericFirstLast"></asp:GridView>

하단에 표시되는 페이징 부분을 좀더 시각적으로 디자인하고자 한다면 PagerStyle- 로 시작하는 속성을 사용합니다. 글자의 색상이나 크기등 다양한 디자인 요소를 적용할 수 있습니다.
<asp:GridView ID="GridView1" runat="server" DataSourceID="SqlDataSource1" AllowPaging="true" PagerStyle-ForeColor="Red"></asp:GridView>

페이징과 관련된 주요 이벤트로는 2개가 있는데 PageIndexChanging 과 OnPageIndexChanged 이며 페이징 전/후로 이벤트가 발생됩니다. 주로 페이징을 수행할때 이전 페이지번호등을 확인하거나 필요하지 않은 경우 페이징동작을 무력화 하는경우에 사용합니다.

 Style 지정

GridViw 는 GridView 자체에 대한 디자인을 설정할 수 있도록 많은 속성을 제공합니다. 간단하게는 Gird의 제목을 지정할 수 있는 Caption 속성에서 HeaderStyle- 이나 FooterStyle- 으로 Header와 Footer 의 전경색및 글자색, 크기등 Grid의 다양한 스타일을 지정할 수 있습니다. 또한 경우에 따라 ShowHeader 나 ShowFooter 로 제목부분을 보이거나 보이지 않도록 하는 것도 가능합니다.

AlternatingRowStyle- 은 Grid의 짝수행에 대한 개별적인 스타일지정을, EditRowStyle- 은 Grid가 편집모드일때 해당 행에 대한 스타일 지정을 의미합니다. EmptyDataRowStyle- 은 Grid의 특정 Row에 표시할 내용이 없을 경우의 스타일을, SelectedRowStyle 선택상태에 있는 Row에 대한 스타일에 해당하며 PagerStyle- 로 Gird의 페이징 부분을 디자인할 수 있고 RowStyle- 로 Gird Row 에 대한 전체적인 부분을 다룰 수 있습니다.
<asp:GridView ID="GridView1" runat="server" DataSourceID="SqlDataSource1" AllowPaging="True" AlternatingRowStyle-BackColor="AliceBlue" PagerSettings-Mode="NextPreviousFirstLast" PagerSettings-FirstPageText="처음" PagerSettings-LastPageText="마지막"></asp:GridView>

 Column 커스터마이징

GridView 가 데이터 소스(Data Source)의 데이터를 표시할때 컬럼(Column)이 자동으로 생성되는 경우라면 기본적으로 BoundField 를 사용하게 됩니다. BoundField 는 데이터 소스의 내용을 문자열로서 취급하는데 다만 데이터가 bool 타입인 경우라면 CheckBoxField 로 대신합니다.

문제는 단순히 데이터소스내용을 그대로 문자열로서 표시하는 경우가 그렇게 흔하지 않다는 것입니다. 더군다나 데이터 소스(select query 에 기반한)에 드러난 모든 컬럼을 그대로 생성하여 표시하는 경우 대부분은 특정 컬럼을 가공하여 출력하는 경우가 많기 때문에 컬럼 자동생성이 그렇게 유용하게 쓰이는 경우는 거의 없습니다.

따라서 GridView 사용시 Column 에 대한 커스터마이징을 해야 하는 경우가 많으며 이때 문자열대신 경우에 따라 연결가능한 하이퍼링크나 버튼, 체크박스형태로 출력하는 것도 가능합니다.

▶ <그림 3-1>

<그림 3-1>은 GirdView 의 Column을 편집하기 위한 화면을 보여주고 있습니다. 이 화면은 GridView 의 스마트태그를 통해 'Edit Columns' 항목을 선택한 경우이며 Add New Column 항목을 클릭하면 새로운 필드 추가를 위한 옵션을 따로 설정하고 즉시 필드를 추가할 수 있습니다.

수정하거나 추가가능한 필드 중 대부분은 이름만 봐도 어떤 필드를 의미하는지 대충 잠작할 수 있으나 몇가지 필드만 확인해보고자 합니다. 우선 BoundField 는 데이터 소스의 데이터값을 그대로 표시하는 필드이며 필드를 자동으로 생성하는 경우 GridView에 만들어지는 기본필드에 해당합니다. CommandField 는 GridView 의 데이터를 수정, 삭제하는등의 동작에 필요한 명령필드를 의미하며 TemplateField 는 사용자가 임의로 필드의 형식을 재정의 하는데 사용됩니다.

필드 추가 방법을 알아보기 위해 <그림 3-2>와 같이 HyperLinkField 를 임의로 생성해 보겠습니다. 이 필드의 목적은 사용자가 추가되는 컬럼에 표시된 링크를 클릭하는 경우 특정 ID값에 관한 인물의 상세정보를 볼 수 있다고 가정한 것입니다.

▶ <그림 3-2>

새로 추가되는 필드의 Header 제목은 'Detail Person'으로 하고 보여질 내용은 '상세정보 - ' 로 설정하였습니다. 뒤에 {0} 부분은 'LastName' 이라는 데이터 소스의 컬럼을 바인딩하여 '상세정보 - Duffy' 와 같은 형태로 보여질 것입니다.

사용자가 HyperLinkField 를 클릭했을때 연결될 사이트로는 'http://localhost/person_detail.aspx' 이며 id 스트링값으로 BusinessEntityID 컬럼의 데이터를 바인딩하여 전달하도록 하였습니다.


물론 스마트태그를 통한 설정대신 아래와 같이 직접 태그를 작성할 수도 있습니다.
<asp:GridView ID="GridView1" runat="server" DataSourceID="SqlDataSource1" AllowPaging="True">
    <Columns>
        <asp:HyperLinkField DataNavigateUrlFields="BusinessEntityID" DataNavigateUrlFormatString="http://localhost/person_detail.aspx?id={0}" DataTextField="LastName" DataTextFormatString="상세정보 - {0}" HeaderText="Detail Person" />
    </Columns>
</asp:GridView>
참고로 DataNavigateUrlFields 에는 2개 이상의 열을 지정할 수 있는데 이 경우 각 열은 콤마(,)로 구분하면 됩니다. DataNavigateUrlFormatString 에서는 {0}...{1} 과 같은 형식으로 지정하면 DataNavigateUrlFields에 구분된 각 열이 순서대로 바인딩될 것입니다.

다만 예제에서 사용된 DataTextField 의 경우에는 다중 열지정이 불가능합니다. 꼭 필요하다면 Select FirstName + ' ' + LastName As PersonName 과 같이 쿼리에서 2개이상의 열값을 가져와 하나의 열로 표시하는 등의 기타 다른 방법을 동원해야 합니다.

 TemplateField 사용

GridView 의 컬럼을 완벽히 재정의 하려면 TemplateField 를 사용합니다. TemplateField 는 필드의 커스터마이징을 위해 대략 아래 6개 템플릿 필드를 제공합니다.
 
 ItemTemplate  기본적으로 DataSource 의 아이템을 표시하기 위한 템플릿입니다.
 AlternatingItemTemplate  교차 아이템을 표시하기 위한 템플릿입니다. 여기서 말하는 교차는 예를 들어 처음행에는 내용이 있다가 다음행에는 없고 다시 다음행에는 내용이 들어가는 식의 동작을 의미합니다.
 EditItemTemplate  GridView가 편집상태에 들어갔을때 내용을 표시하기 위한 템플릿입니다.
 InsertItemTemplate  GridView가 데이터 추가상태에 들어갔을때 내용을 표시하기 위한 템플릿입니다.
 HeaderTemplate  템플릿필드의 헤더부분을 표시하는 템플릿입니다.
 FooterTemplate  템플릿필드의 푸터부분을 표시하는 템플릿입니다.
▶ <표 1-1>


TemplateField 를 사용해보기 위해 이전에 같은 방법으로 TemplateField 을 GridView에 추가합니다. TemplateField 은 <표 1-1>의 템플릿을 포함하는 컨테이너로서 제공됩니다. 추가된 해당 템플릿을 편집하기 위해 GirdView에서 마우스 오른쪽 버튼을 눌러 Edit Template 하위의 Column[0] - Custom(방금전 추가한 TemplateField) 을 선택하거나


GridView 의 스마트태그에서 'Edit Template' 를 선택합니다.


그리고 위에서 언급한 템플릿 항목중 원하는 항목을 선택하여 TemplateField 에 추가합니다.


아래는 ItemTemplate 을 추가하여 새로운 내용을 표시하도록한 예제입니다.
<asp:GridView ID="GridView1" runat="server" DataSourceID="SqlDataSource1" AllowPaging="True">
    <Columns>
        <asp:TemplateField HeaderText="Custom">
            <ItemTemplate>
                <table>
                    <tr>
                        <td><asp:Label ID="Label1" runat="server" Text='<%# String.Format("{0} - {1}", Eval("FirstName"), Eval("LastName")) %>'></asp:Label></td>
                        <td><asp:Button ID="Button1" runat="server" Text="확인" /></td>
                    </tr>
                </table>
            </ItemTemplate>
        </asp:TemplateField>
    </Columns>
</asp:GridView>
▶ <코드 4-1>

TemplateField 에 Table 태그를 추가하고 해당 Table 의 각 Cell 에 Label 과 Button 컨트롤을 배치하였습니다. 이 예제는 내용상 큰 의미가 없으나 TemplateField 에 단순한 태그뿐만 아니라 컨트롤까지도 포함하여 다양한 처리를 구현할 수 있음을 보여주고 있습니다.


참고적으로 <코드 4-1>에 사용된 Eval 은 바인딩되는 데이터에 접근하기 위한 수식입니다.

 GridView 수정

GridView 컨트롤을 사용하면 사용자에게 데이터를 직접 수정할 수 있도록 하는 기능을 손쉽게 제공할 수 있습니다.

GridView가 SqlDataSource 컨트롤과 같이 사용되는 경우 데이터 조작작업이 가능하게 하기 위해서는 이 두개의 컨트롤 모두 필요한 설정 작업이 선행되어야 합니다. 오해하지 말아야 할것은 GridView를 사용한다고 해서 반드시 SqlDataSource 를 사용해야 하는 것은 아니며 이 두개의 컨트롤이 반드시 연결되어야만 특정 데이터에 대한 추가/수정 작업이 가능한것은 아닙니다.

우선 GridView의 데이터 수정 작업 처리를 위해서 SqlDataSource 컨트롤에 대해 <코드 5-1> 과 같은 설정이 필요합니다. 이 작업은 SqlDataSource 설정마법사나 아니면 직접 마크업을 작성하는 방법으로 처리할 수 있습니다.
<asp:SqlDataSource ID="SqlDataSource1" runat="server" ConnectionString="<%$ ConnectionStrings:new_connection %>" SelectCommand="SELECT [AddressID], [AddressLine1], [City] FROM [Person].[Address] ORDER BY [AddressID]" UpdateCommand="UPDATE [Person].[Address] SET [AddressLine1] = @AddressLine1, [City] = @City WHERE [AddressID] = @AddressID">
</asp:SqlDataSource>
▶ <코드 5-1>

참고로 <코드 5-1> 에서 사용된 데이터 소스는 AdventureWorks2012 DB의 Person.Adress 이며 AddressID와 AddressLine1, City 를 불러오고 이들에 대해 Update(수정) 명령을 처리하도록 Command 속성으로 Update Query 를 할당하였습니다.

Query 에 보면 @AddressLine1, @City 와 같은 것을 볼 수 있는데 GridView 에서 데이터가 처리될때 어떤 데이터가 올 수 있는지를 나타내고 있습니다. 물론 ControlParameter 나 혹은 QueryStringParameter 등 별도의 Paramter 구성을 통해서 직접 Paramter의 인수값을 지정하는 것도 가능합니다.

이것으로 SqlDataSource 에 필요한 작업은 마무리되었습니다. 생각보다 굉장히 간단하는 것을 알 수 있습니다. 이제 GridView 컨트롤을 수정하여 데이터의 Update 기능을 부여하도록 하겠습니다.
<asp:GridView ID="GridView1" runat="server" DataSourceID="SqlDataSource1" AllowPaging="True" AutoGenerateEditButton="true"></asp:GridView>
▶ <코드 5-2>

GridView는 SqlDataSource 컨트롤을 설정하는 것보다 더 간단합니다. AutoGenerateEditButton 속성에 true 값을 주기만 하면 GridView에 자동적으로 Update에 필요한 기능이 추가됩니다.


자동으로 생성된 Edit 링크를 클릭하면 해당 Row에 대한 편집상태로 들어가게 되고


임의의 값을 입력하고 Update 를 클릭하면 수정한 데이터가 반영됩니다. 그런데 좀 이상하지 않습니까? Person.Address 테이블의 AddressID는 키값입니다. 사용자가 AddressID 1 번인 Row를 수정하려고 할때 만약 AddressID 입력칸에 다른 값을 집어넣으면 어떻게 될까요? 의도한 경우든 아니든 입력한 값에 해당하는 AddressID 값의 데이터가 변경되어버립니다.

물론 사용자가 데이터 변경시 정확한 규칙을 알고 적절히 GridView를 사용할 수 있지만 그렇다 하더라도 중요한 정보인 Key값은 바꿀 수 없도록 해야 합니다. 이를 위해 GridView안에 DataKeyNames="AddressID" 속성을 추가합니다. 그러면 더이상 지정된 열에 해당하는 값은 바꿀 수 없게 됩니다.
<asp:GridView ID="GridView1" runat="server" DataSourceID="SqlDataSource1" AllowPaging="True" DataKeyNames="AddressID">
참고로 Key로 설정할 열이 2개 이상이면 각 컬럼을 콤마(,)로 구분하여 지정하면 되고 혹시 GridView 의 열이 자동생성이 아닌 직접 BoundField 요소를 사용해 출력되는 경우라면 다음과 같이 해당 BoundField 에 ReadOnly 속성을 설정하면 됩니다.
<asp:BoundField DataField="AddressID" ReadOnly="True" />

GridView 의 데이터 편집기능 추가를 위해 사용한 AutoGenerateEditButton 이외에 GridView 안에 직접 CommandField 요소를 추가하여 같은 기능을 부여할 수도 있습니다.
<asp:GridView ID="GridView1" runat="server" DataSourceID="SqlDataSource1" AllowPaging="True">
    <Columns>
        <asp:CommandField ShowHeader="true" HeaderText="수정" ShowEditButton="true" />
    </Columns>
</asp:GridView>
CommandField 를 사용하면 단순한 Link 이외에 버튼이나 이미지등 다양한 방법으로 사용자에게 데이터 편집기능을 제공할 수 있으며 Edit 뿐만 아니라 Cancel, Delete, Insert, Select 등 다른 기능을 수행할 수 있는 방법을 사용자에게 제공할 수 있습니다.

이전에 TemplateField 를 설명할때 EditItemTemplate 을 거론한적이 있습니다. EditItemTemplate 은 GridView 가 Edit 상태로 접어들때 표시될 내용을 담는 템플릿인데 GridView 의 Update 처리과정을 설명드리면서 EditItemTemplate 의 구체적인 역활에 대한것도 같이 확인해 보도록 하겠습니다.

EditItemTemplate 이 주로 사용되는 목적은 사용자가 GridView의 데이터 내용을 업데이트 할때 입력가능한 데이터에 맞는 입력방식을 제공하기 위함입니다. 예를 들어 Person.Address 테이블의 City 컬럼에 들어갈 수 있는 값이 Bothell, Portland, Seattle 세가지 뿐이라고 가정한다면 GridView 가 편집상태(업데이트)에 들어갔을때 City 입력방식으로 TextBox 형태를 제공하는건 맞지 않을 수 있습니다. TextBox 로는 Bothell, Portland, Seattle 이외에 다른 값의 입력을 막기가 까다로워질 수 있기 때문인데 그래서 이런 경우는 TextBox 형태보다는 DropDownList 와 같은 요소를 제공하여 Bothell, Portland, Seattle 이 세개 항목중 하나를 선택하도록 하는 것이 훨씬 쉬울 수 있습니다.
<asp:GridView ID="GridView1" runat="server" DataSourceID="SqlDataSource1" AllowPaging="True">
    <Columns>
        <asp:CommandField ShowHeader="true" HeaderText="수정" ShowEditButton="true" />
        <asp:TemplateField HeaderText="City">
            <EditItemTemplate>
                <asp:DropDownList ID="DropDownList1" runat="server">
                    <asp:ListItem Value="Bothell">Bothell</asp:ListItem>
                    <asp:ListItem Value="Portland">Portland</asp:ListItem>
                    <asp:ListItem Value="Seattle">Seattle</asp:ListItem>
                </asp:DropDownList>
            </EditItemTemplate>
        </asp:TemplateField>
    </Columns>
</asp:GridView>
▶ <코드 5-3>

<코드 5-3> 은 TemplateField 요소를 추가하고 하위에 EditItemTemplate 요소를 포함하고 있는 예제를 보여주고 있습니다. 여기에 EditItemTemplate 은 다시 DropDownList 컨트롤을 포함하고 있으므로 GridView 가 Edit 모드로 들어가면 해당 DropDownList 컨트롤이 표시될 것입니다.

▶ <그림 5-1>

<그림 5-1> 을 보면 City 컬럼이 2개가 생성되어 있음을 볼 수 있는데 사실 이 상태로도 Update 동작을 수행시킬 수는 있으나 같은 내용의 컬럼이 불필요하게 보여지고 있으므로 GridView 의 컬럼내용을 재정의 하도록 하겠습니다.
<asp:GridView ID="GridView1" runat="server" DataSourceID="SqlDataSource1" AutoGenerateColumns="false" AllowPaging="True">
    <Columns>
        <asp:CommandField ShowHeader="true" HeaderText="수정" ShowEditButton="true" />
        <asp:BoundField HeaderText="AddressID" DataField="AddressID" />
        <asp:BoundField HeaderText="AddressLine1" DataField="AddressLine1" />
        <asp:TemplateField HeaderText="City">
            <ItemTemplate><%# Eval("City") %></ItemTemplate>
            <EditItemTemplate>
                <asp:DropDownList ID="DropDownList1" runat="server">
                    <asp:ListItem Value="Bothell">Bothell</asp:ListItem>
                    <asp:ListItem Value="Portland">Portland</asp:ListItem>
                    <asp:ListItem Value="Seattle">Seattle</asp:ListItem>
                </asp:DropDownList>
            </EditItemTemplate>
        </asp:TemplateField>
    </Columns>
</asp:GridView>
▶ <코드 5-4>

GridView의 자동컬럼생성을 취소하고 BoundField 를 추가하여 다소 수동적이지만 원하는 컬럼만을 표시하도록 하였습니다.

▶ <그림 5-2>

여기서 한가지 더 팁을 설명드리자면 City 열의 DropDownList 로 항목이 표시될때 무조건 첫번째 항목부터 표시되는 것이 아니라 현재 Row 에 바인딩된 City 값이 우선적으로 선택된 상태에 있게 하려면 아래와 같이 OnRowDataBound 이벤트를 정의하여 해당 이벤트 안에 원하는 기능을 구현해 넣도록 합니다.
<asp:GridView ID="GridView1" runat="server" DataSourceID="SqlDataSource1" AutoGenerateColumns="false" AllowPaging="True" OnRowDataBound="GridView1_RowDataBound">
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
    if (e.Row.RowState == DataControlRowState.Edit){
        System.Data.DataRowView drv = (System.Data.DataRowView)e.Row.DataItem;
        DropDownList ddl = (DropDownList)e.Row.Cells[3].FindControl("DropDownList1");
        ListItem li = ddl.Items.FindByValue(drv["city"].ToString());
        li.Selected = true;
    }
}
RowDataBound 이벤트에서는 현재 Row의 상태가 Edit 모드인지 확인(참고로 RowState 는 동시에 2가지 이상의 상태로 있을 수 있는데 이 경우를 정확히 확인하려면 e.Row.RowState == (DataControlRowState.Alternate | DataControlRowState.Edit) 처럼 조건절을 수정해야 합니다.) 하고 해당 Row 에 DropDownList 컨트롤 요소를 가져와 바인딩된 city 값에 해당하는 ListItem 을 추출합니다. 그리고 추출된 ListItem 을 선택상태로 설정함으로서 사용자가 Edit 를 눌러 편집상태로 들어가면 현재 Row의 City 값이 DropDownList 에 바로 선택상태로 나타나게 됩니다.

여기까지 DropDownList 에 값을 사용자가 직접 선택하여 입력할 수 있게끔 하고 나면 실제 DropDwonList 선택값이 DB 에 반영되도록 하는 기능을 부여해야 하며 이 동작은 GridView 의 OnRowUpdating 이벤트를 통해 이루어 집니다.
<asp:GridView ID="GridView1" runat="server" DataSourceID="SqlDataSource1" AutoGenerateColumns="false" AllowPaging="True" OnRowUpdating="GridView1_RowUpdating">
protected void GridView1_RowUpdating(object sender, GridViewUpdateEventArgs e)
{
    GridViewRow gvr = this.GridView1.Rows[this.GridView1.EditIndex];
    DropDownList ddl = (DropDownList)gvr.Cells[3].FindControl("DropDownList1");
    e.NewValues["city"] = ddl.SelectedValue;
}
RowUpdating 이벤트에서는 현재 편집중인 Row에 있는 DropDownList 컨트롤의 선택값을 가져와 GridView 의 NewValues Collection (수정 후 실제 DB에 업데이트하는 값들) 중 필요한 열(여기서는 city)에 해당하는 값을 대입함으로서 실질적인 Update가 이루어 지도록 하였습니다.

여기까지 GridView 를 통한 Update 처리과정을 알아보았는데 GridView 에서 데이터의 업데이트 동작을 수행하는 것만큼 중요한 것이 하나 남았습니다. 업데이트 도중 발생할 수 있는 오류를 감지하고 적절하게 대응하는 것이 그것입니다. GirdView 에서 업데이트 도중 발생할 수 있는 오류는 OnRowUpdated 이벤트를 통해 처리할 수 있습니다.
<asp:GridView ID="GridView1" runat="server" DataSourceID="SqlDataSource1" AllowPaging="True" OnRowUpdated="GridView1_RowUpdated">
    <Columns>
        <asp:CommandField ShowHeader="true" HeaderText="수정" ShowEditButton="true" />
    </Columns>
</asp:GridView>
▶ OnRowUpdated 설정
protected void GridView1_RowUpdated(object sender, GridViewUpdatedEventArgs e)
{
    if (e.Exception != null) {
        e.ExceptionHandled = true;
        Response.Write(e.Exception.Message);
    }
}
Update 도중 오류가 생기면 Exception 예외가 발생하고 이 값이 Null 인지를 확인함으로서 오류 발생여부를 판단합니다. 오류가 발생하면 Message 속성을 통해 오류내용을 표시하도록 했는데 중요한 것은 e.ExceptionHandled = true; 부분입니다. 이것은 오류를 직접 제어하겠다는 의미를 가지고 있는데 이러한 과정을 거치지 않으면 .NET Framework 는 HTTP 500 등의 오류페이지를 직접 띄우기 때문에 Response.Write(e.Exception.Message); 처럼 작성하여도 해당 코드의 처리결과를 확인하기가 어렵게 됩니다.

 GridView 삭제

GridView 의 데이터를 삭제하는 작업은 위에서 설명드린 수정작업보다 훨씬 간단합니다. 우선 데이터를 변경할때와 같은 방법으로 GridView에 AutoGenerateDeleteButton 속성을 설정하고
<asp:GridView ID="GridView1" runat="server" DataSourceID="SqlDataSource1" AllowPaging="True" AutoGenerateDeleteButton="True" DataKeyNames="BusinessEntityID"></asp:GridView>
SqlDataSource 컨트롤에도 삭제관련 DeleteCommand 속성을 설정하기만 하면 됩니다.
<asp:SqlDataSource ID="SqlDataSource1" runat="server" ConnectionString="<%$ ConnectionStrings:new_connection %>" SelectCommand="SELECT * FROM [Person].[PersonPhone]" DeleteCommand="DELETE FROM [Person].[PersonPhone] WHERE [BusinessEntityID] = @BusinessEntityID"></asp:SqlDataSource>

'Programming > .NET' 카테고리의 다른 글

[ASP.NET] DetailsView  (1) 2014.06.20
[ASP.NET] LinqDataSource - 데이터 동시성 (Data Concurrency)  (2) 2014.06.12
[ASP.NET] GridView  (3) 2014.06.11
[ASP.NET] FormView  (0) 2014.06.10
[ASP.NET] web.config 에 저장된 연결정보 다루기  (0) 2014.06.09
[ASP.NET] SiteMapDataSource  (0) 2014.05.28
3 0