본문 바로가기

Programming/.NET

[ASP.NET] ListView

ListView 는 .NET Framework 3.5 이상부터 추가된 새로운 템플릿 컨트롤중 하나로서 레이아웃이나 기타 작업을 위한 꽤 다양한 옵션을 제공합니다. 해당 옵션은 마법사를 통해 쉽게 적용이 가능합니다.

GridView 는 데이터를 다루는데 필요한 대부분의 기능을 가진 강력한 컨트롤이지만 이 컨트롤이 HTML 로 렌더링되어 표시될때는 꽤 많은 양의 마크업코드와 디자인및 레이아웃을 잡기 위한 추가적인 CSS 설정은 페이지 무겁게 하는 요인이 되었습니다.

과거에는 이를 대체하기 위한 수단으로 DataList 나 Repeater 컨트롤을 사용했는데 GridView 에 비해 가벼워서 좋았지만 이들은 페이징이나 정렬은 물론이고 데이터를 편집하기 위한 기능이 빠져있었기에 개발자가 임의로 만들어야 하는 수고가 필요했습니다.

여기서 ListView 컨트롤은 GridView와 다른 데이터 컨트롤의 사이를 메우기 위한 훌륭한 대체수단이 될 수 있습니다. ListView 는 안에서는 데이터를 추가/수정/편집하기 위한 기능을 제공하면서도 겉으로는 데이터를 표시하기 위해 자체적으로 생성하는 요소를 가지고 있지 않습니다. 이러한 기능은 ListView 내부에 정의하는 템플릿에 의존하며 이 템플릿을 구현하는 종류에 따라 생성되는 HTML 코드가 달라지게 되는 것입니다.

 ListView

ListView 컨트롤을 디자인배치하고 DataSource 를 설정하고 나면 다른 데이터 컨트롤처럼 화면상에 나타나는 레이아웃 형태가 즉시 Design-Time 에 반영되지 않습니다. 왜냐하면 ListView 는 기본적으로 정의된 레이아웃 형태가 존재하지 않으며 LayoutTemplate 과 ItemTemplate 요소 등으로 레이아웃 자체를 정의해야 하기 때문입니다.

ListView 의 레이아웃을 정의하기 위한 가장 쉬운 방법은 컨트롤의 smart tag 를 통해 'Configure ListView' 를 선택하여 레이아웃을 구성하는 것입니다.


Configure ListView Dialog 화면에서 Select a Layout 은 전체적인 Layout 화면을 Select a Style 은 각 Row에 대한 개별적인 디자인설정을 의미합니다. 물론 이 화면에서 특정 Layout 과 Style 을 선택했다고 해서 그 상태로 고정되어야 하는 것은 아니고 세부적인 코드의 편집을 통해 원하는 방향으로 변경할 수 있습니다.

또한 필요하다면 Editing, Inserting, Deleting, Paging 등의 설정을 통하여 어떤 형태의 레이아웃을 선택하든지 해당 기능을 부여하는 것이 가능합니다.

원하는 레이아웃과 스타일을 선택하고 나면 <그림 1-1>과 같은 화면을 볼 수 있습니다.

▶ <그림 1-1>

 Templates

ListView 에서 생성할 수 있는 Template 으로는 ItemTemplate, AlternatingItemTemplate, SelectedItemTemplate, InsertItemTemplate, EditItemTemplate,
EmptyDataTemplate, and LayoutTemplate 등이 있으며 각 컨트롤 템플릿의 용도는 <표 1-1>에 정리하였습니다.

 ItemTemplate  각 데이터 아이템항목에 대한 사용자 인터페이스를 지정합니다.
 ItemSeparatorTemplate  각 Row Item 에 대한 개별적인 UI를 지정합니다.
 AlternatingItemTemplate  짝수 Row에 대한 개별적인 UI를 지정합니다.
 SelectedItemTemplate  현재 선택된 Row에 대한 개별적인 UI를 지정합니다.
 InsertItemTemplate  Row Item 에 대한 데이터 추가시 보여질 UI를 지정합니다.
 EditItemTemplate  Row Item 에 대한 데이터 변경시 보여질 UI를 지정합니다.
 EmptyItemTemplate  현재 페이지의 Row에서 더이상 표시할 Item이 없는 경우 보여질 UI를 지정합니다.
 EmptyDataTemplate  보여줄 Data가 없는 경우 대신 표시할 UI를 지정합니다.
 LayoutTemplate  전체적인 레이아웃에 대한 UI를 지정힙니다.
 GroupTemplate  각 Group 에 대한 UI를 지정합니다.
 GroupSeparatorTemplate  각 Group 사이의 UI를 지정합니다.

특히 LayoutTemplate 과 ItemTemplate 이 중요한데 이 둘은 ListView 컨트롤이 레이아웃을 나타내기 위한 최소한의 구성요소입니다. 여기서 LayoutTemplate 은 최상위의 템플릿이며 여기서 데이터를 표시하기 위한 전체 레이아웃이 정의됩니다.
<LayoutTemplate>
    <table runat="server">
        <tr runat="server">
            <td runat="server">
                <table id="itemPlaceholderContainer" runat="server" border="1" style="background-color: #FFFFFF;border-collapse: collapse;border-color: #999999;border-style:none;border-width:1px;font-family: Verdana, Arial, Helvetica, sans-serif;">
                    <tr runat="server" style="background-color:#DCDCDC;color: #000000;">
                        <th runat="server">BusinessEntityID</th>
                        <th runat="server">PhoneNumber</th>
                        <th runat="server">PhoneNumberTypeID</th>
                        <th runat="server">ModifiedDate</th>
                    </tr>
                    <tr id="itemPlaceholder" runat="server">
                    </tr>
                </table>
            </td>
        </tr>
        <tr runat="server">
            <td runat="server" style="text-align: center;background-color: #CCCCCC;font-family: Verdana, Arial, Helvetica, sans-serif;color: #000000;"></td>
        </tr>
    </table>
</LayoutTemplate>
▶ <코드 2-1>

<코드 2-1>은 LayoutTemplate 의 전체적인 마크업코드입니다. 안에는 Table 요소가 있으며 tr 로 Header 부분이 정의되어 있고 몇몇 CSS 구성도 볼 수 있습니다.

LayoutTemplate 이 전체적인 레이아웃을 담당하고 있다면 ItemTemplate 은 실제 Item 항목을 나타내는 개별적인 Row 부분을 담당합니다.
<ItemTemplate>
    <tr style="background-color:#DCDCDC;color: #000000;">
        <td>
            <asp:Label ID="BusinessEntityIDLabel" runat="server" Text='<%# Eval("BusinessEntityID") %>' />
        </td>
        <td>
            <asp:Label ID="PhoneNumberLabel" runat="server" Text='<%# Eval("PhoneNumber") %>' />
        </td>
        <td>
            <asp:Label ID="PhoneNumberTypeIDLabel" runat="server" Text='<%# Eval("PhoneNumberTypeID") %>' />
        </td>
        <td>
            <asp:Label ID="ModifiedDateLabel" runat="server" Text='<%# Eval("ModifiedDate") %>' />
        </td>
    </tr>
</ItemTemplate>
▶ <코드 2-3>

<코드 2-3>을 보면 알 수 있듯이 LayoutTemplate 의 Table 에 들어갈 각각의 tr 이 정의되어 있으며 tr 안에 표시될 데이터 Item 항목이 Eval 을 통해 지정되어 있습니다.

ListView 는 런타임에서 스스로 레이아웃을 위한 어떠한 HTML 마크업도 생성하지 않으며 GridView 에서처럼 자동적인 필드의 생성로직도 포함하지 않습니다. 따라서 <코드 2-3>과 같은 정의된 레이아웃에서는 각 필드의 Item 값을 표시하기 위한 방법으로 기본적인 ASP.NET 의 inline 데이터 바인딩 구문을 사용하며 이 같은 방법은 ListView 의 모든 템플릿에도 적용됩니다.



ListView 컨트롤은 실행과정에서 스스로 렌더링될때 LayoutTemplate 안에 Item Container 가 정의되어 있음을 기대하고 'itemPlaceholderContainer' 와 같은 형식의 id 값을 갖는 요소를 찾게 됩니다. 그리고 하위의 'itemPlaceholder' 요소 안에 ItemTemplate 의 요소를 삽입하여 최종적인 화면을 구성합니다.(ItemTemplate 뿐만 아니라 AlternatingItemTemplate, EditItemTemplate, EmptyDataTemplate, InsertItemTemplate, SelectedItemTemplate 등도 마찬가지)

 Grouping

ListView 는 각각의 Item 이 렌더링될때 Group 별로 Item 을 표시할 수 있는 GroupTemplate 을 지원합니다.

ListView 의 Item 을 그룹화 하려면 우선 각 Item 을 몇개의 Group 으로 나눌것인지를 지정해야 하며 이것은 ListView 의 GroupItemCount 속성을 통해 설정합니다.
<asp:ListView ID="ListView1" runat="server" DataSourceID="SqlDataSource1" DataKeyNames="BusinessEntityID,PhoneNumber,PhoneNumberTypeID" GroupItemCount="2">
그 다음으로 GroupTemplate 을 추가해 Grouping 될때 보여질 레이아웃을 정의합니다. GroupTemplate 은 LayoutTemplate 안에서 정의된 itemPlaceholderContainer ID 의 table 요소를 GroupItemCount 에서 지정된 수만큼 나누어 담고 화면에 렌더링하는 역활을 수행합니다.
<GroupTemplate>
    <tr runat="server">
        <td runat="server" id="itemContainer" />
    </tr>
</GroupTemplate>
위에서 정의한 GroupTemplate 안에 itemContainer ID 를 갖는 td 가 실제 Item 요소인 ItemTemplate 이 들어갈 부분이며 이것은 ListVIew 컨트롤의 ItemPlaceholderID 를 통해 설정됩니다.

또한 실제 나누어질 각 Group 의 기준은 LayoutTemplate 의 itemPlaceholderContainer ID 를 갖는 table 에 해당하므로 ListView 컨트롤에 이러한 사실을 알리기 위하여 GroupPlaceholderID 속성값을 itemPlaceholderContainer 값으로 지정하면 됩니다.
<asp:ListView ID="ListView1" runat="server" DataSourceID="SqlDataSource1" DataKeyNames="BusinessEntityID,PhoneNumber,PhoneNumberTypeID" GroupItemCount="2" GroupPlaceholderID="itemPlaceholderContainer" ItemPlaceholderID="itemContainer">
이것으로 ListView를 Group 화할 준비가 다 되었습니다. 추가적으로 Item 이 그룹으로 나뉘어져 있음을 눈으로 확인하기 위해 GroupSeparatorTemplate 요소를 추가하여 구분자를 생성하도록 합니다.
<GroupSeparatorTemplate>
    <tr runat="server">
        <td colspan="3"><hr /></td>
    </tr>
</GroupSeparatorTemplate>


ListView 를 Grouping 할때 한가지 주의할점은 데이터가 GroupItemCount 에 지정한 Group 수만큼 나누어 지지 않고 비어있는 부분이 생길수가 있다는 것입니다.


GroupItemCount 를 3으로 지정했는데 정작 Item 수는 4개뿐이라 마지막 2개 부분이 비어있는채로 표시되었습니다. 대부분의 경우 이것은 데이터가 없는 경우이므로 무시하면 되지만 경우에 따라 Table 의 레이아웃 문제로 인해 전체 레이아웃이 깨지는 경우가 생길 수 있으므로 이때는 어떻게 해서든 빈 부분을 임의로 채워줘야 합니다.
<EmptyItemTemplate>
    <tr runat="server">
        <td colspan="5">NO DATA</td>
    </tr>
</EmptyItemTemplate>
이런 경우 EmptyItemTemplate 을 사용할 수 있으며 비어있는 부분을 이 템플릿에 정의된 내용으로 대체할 수 있게됩니다.


 Command

ListView 는 기본적으로 데이터의 편집을 위한 EditItemTemplate, InsertItemTemplate 등의 템플릿을 포함하고 있습니다. 데이터 편집기능이 필요없다면 이들 템플릿은 삭제해도 되지만 여기서 주목해야 할 부분은 각 템플릿에 사용되는 버튼컨트롤입니다.
<EditItemTemplate>
    <tr style="">
        <td>
            <asp:Button ID="UpdateButton" runat="server" CommandName="Update" Text="Update" />
            <asp:Button ID="CancelButton" runat="server" CommandName="Cancel" Text="Cancel" />
        </td>
        <td>
            <asp:Label ID="BusinessEntityIDLabel1" runat="server" Text='<%# Eval("BusinessEntityID") %>' />
        </td>
        <td>
            <asp:Label ID="PhoneNumberLabel1" runat="server" Text='<%# Eval("PhoneNumber") %>' />
        </td>
        <td>
            <asp:Label ID="PhoneNumberTypeIDLabel1" runat="server" Text='<%# Eval("PhoneNumberTypeID") %>' />
        </td>
        <td>
            <asp:TextBox ID="ModifiedDateTextBox" runat="server" Text='<%# Bind("ModifiedDate") %>' />
        </td>
    </tr>
</EditItemTemplate>
<InsertItemTemplate>
    <tr style="">
        <td>
            <asp:Button ID="InsertButton" runat="server" CommandName="Insert" Text="Insert" />
            <asp:Button ID="CancelButton" runat="server" CommandName="Cancel" Text="Clear" />
        </td>
        <td>
            <asp:TextBox ID="BusinessEntityIDTextBox" runat="server" Text='<%# Bind("BusinessEntityID") %>' />
        </td>
        <td>
            <asp:TextBox ID="PhoneNumberTextBox" runat="server" Text='<%# Bind("PhoneNumber") %>' />
        </td>
        <td>
            <asp:TextBox ID="PhoneNumberTypeIDTextBox" runat="server" Text='<%# Bind("PhoneNumberTypeID") %>' />
        </td>
        <td>
            <asp:TextBox ID="ModifiedDateTextBox" runat="server" Text='<%# Bind("ModifiedDate") %>' />
        </td>
    </tr>
</InsertItemTemplate>
이들 컨트롤은 데이터의 편집명령을 수행하기 위한 버튼이며 CommandName 속성을 통해 각 버튼의 역활(동작)을 지정하고 있습니다.

 Paging (DataPager)

DataPager 컨트롤은 IPagableItemContainer 인터페이스를 구현하는 데이터를 바인딩 컨트롤(ListView)에서 사용자에게 순수히 페이징기능을 제공하기 위해 디자인된 컨트롤입니다.

ListView 에서 페이징기능을 제공하려면 DataPager 컨트롤을 사용해야 하는데 Configure ListView 에서 'Enable Paging' 을 체크하거나


직접 Layout Template 에서 DataPager 컨트롤을 추가하면 됩니다.
<asp:DataPager ID="DataPager1" runat="server">
    <Fields>
        <asp:NextPreviousPagerField ButtonType="Button" ShowFirstPageButton="True" ShowLastPageButton="True" />
    </Fields>
</asp:DataPager>
DataPager 는 Fields 요소안에 원하는 형태의 페이징 객체를 담을 수 있으며 예제에서 보이는 객체인 NextPreviousPagerField 는 말 그대로 이전, 다음 형식의 기능을 가진 페이징 기능을 의미합니다.

DataPager 는 이외에도 숫자형식의 페이징을 표시하는 NumericPagerField, 페이징 표시를 직접 커스텀할 수 있는 TemplatePagerField 등을 포함하고 있으며 각각의 Field 는 페이징구현에 필요한 다양한 속성을 포함하고 있습니다. 특히 TemplatePagerField 는 이 객체 스스로 표시할 수 있는 어떠한 내용도 가지고 있지 않지만 임의의 요소를 포함시켜 페이징 인터페이스 자체를 완벽히 디자인할 수 있도록 하고 있습니다.
<Fields>
    <asp:TemplatePagerField>
        <PagerTemplate>
            현재 페이지 <%# Container.TotalRowCount / Container.PageSize %> 중 <%# (Container.StartRowIndex/Container.PageSize) + 1 %> 번째
        </PagerTemplate>
    </asp:TemplatePagerField>
</Fields>
위 예제에서 보여진 Container 객체의 TotalRowCount 나 PageSize 등의 속성에 주목해 주세요. 예제는 단지 현재의 페이지 수를 표시하는데 그치지만 PagerTemplate 안에서 버튼과 같은 컨트롤을 배치하고 클릭등의 이벤트를 정의하여 직접 페이징기능을 구현하려고 할때 페이징 계산시 해당 속성을 활용할 수 있습니다.

DataPager 는 또한 다중 Field 를 정의하거나 하나의 Field 안에서 다수의 페이징 Field 를 포함시켜 사용자에게 여러개의 페이징 기능을 동시에 제공할 수도 있으며 GridView 에서의 페이징기능과는 달리 개별적인 컨트롤로서 분리되어 있기 때문에 LaoutTemplate 안에서 뿐만 아니라 웹폼 어디에서는 위치시킬 수 있습니다.
<form id="form1" runat="server">
    <asp:DataPager ID="DataPager1" runat="server" PagedControlID="ListView1">
        <Fields>
            <asp:TemplatePagerField>
                <PagerTemplate>
                    현재 페이지 <%# Container.TotalRowCount / Container.PageSize %> 중 <%# (Container.StartRowIndex/Container.PageSize) + 1 %> 번째
                </PagerTemplate>
            </asp:TemplatePagerField>
        </Fields>
    </asp:DataPager>
다만 DataPager 가 ListView 의 LaoutTemplate 을 벗어나는 경우 PagedControlID 속성을 통해 어느 컨트롤을 대상으로 페이징을 구현할 것인지를 알려줘야 합니다.

태그