사용자 지정된 정렬 사용자 인터페이스 만들기(C#)

작성자 : Scott Mitchell

PDF 다운로드

정렬된 데이터의 긴 목록을 표시할 때 구분 기호 행을 도입하여 관련 데이터를 그룹화하면 매우 유용할 수 있습니다. 이 자습서에서는 이러한 정렬 사용자 인터페이스를 만드는 방법을 알아보세요.

소개

정렬된 열에 소수의 다른 값만 있는 정렬된 데이터의 긴 목록을 표시할 때 최종 사용자는 차이점 경계가 발생하는 위치를 정확히 파악하기 어려울 수 있습니다. 예를 들어 데이터베이스에는 81개의 제품이 있지만 9개의 다른 범주 선택 항목(8개의 고유 범주와 NULL 옵션)만 있습니다. 해산물 범주에 속하는 제품을 검사하려는 사용자의 경우를 고려합니다. 단일 GridView의 모든 제품을 나열하는 페이지에서 사용자는 모든 해산물 제품을 함께 그룹화할 범주별로 결과를 정렬하는 것이 가장 좋은 선택이라고 결정할 수 있습니다. 범주별로 정렬한 후 사용자는 목록을 헌팅하여 해산물 그룹화된 제품이 시작되고 끝나는 위치를 찾습니다. 결과는 범주 이름으로 사전순으로 정렬되므로 해산물 제품을 찾는 것은 어렵지 않지만 그리드의 항목 목록을 면밀히 검사해야 합니다.

정렬된 그룹 간의 경계를 강조 표시하기 위해 많은 웹 사이트에서는 이러한 그룹 간에 구분 기호를 추가하는 사용자 인터페이스를 사용합니다. 그림 1에 표시된 것과 같은 구분 기호를 사용하면 사용자가 특정 그룹을 더 빠르게 찾고 해당 경계를 식별하고 데이터에 존재하는 고유 그룹을 확인할 수 있습니다.

각 범주 그룹이 명확하게 식별됨

그림 1: 각 범주 그룹이 명확하게 식별됨(전체 크기 이미지를 보려면 클릭)

이 자습서에서는 이러한 정렬 사용자 인터페이스를 만드는 방법을 알아보세요.

1단계: 표준 정렬 가능한 GridView 만들기

GridView를 보강하여 향상된 정렬 인터페이스를 제공하는 방법을 살펴보기 전에 먼저 제품을 나열하는 정렬 가능한 표준 GridView를 만들어 보겠습니다. 먼저 폴더에서 CustomSortingUI.aspxPagingAndSorting 페이지를 엽니다. 페이지에 GridView를 추가하고, 해당 ID 속성을 로 ProductList설정하고, 새 ObjectDataSource에 바인딩합니다. 레코드를 선택하는 데 클래스의 GetProducts() 메서드를 ProductsBLL 사용하도록 ObjectDataSource를 구성합니다.

다음으로, , , 및 SupplierNameCategoryNameBoundFields ProductNameUnitPrice 중단된 CheckBoxField만 포함되도록 GridView를 구성합니다. 마지막으로 GridView의 스마트 태그에서 정렬 사용 확인란을 선택하거나 속성을 로 설정하여 정렬을 지원하도록 GridView를 AllowSortingtrue구성합니다. 이러한 페이지를 추가 CustomSortingUI.aspx 한 후 선언적 태그는 다음과 유사하게 표시됩니다.

<asp:GridView ID="ProductList" runat="server" AllowSorting="True"
    AutoGenerateColumns="False" DataKeyNames="ProductID"
    DataSourceID="ObjectDataSource1" EnableViewState="False">
    <Columns>
        <asp:BoundField DataField="ProductName" HeaderText="Product"
            SortExpression="ProductName" />
        <asp:BoundField DataField="CategoryName" HeaderText="Category"
            ReadOnly="True" SortExpression="CategoryName" />
        <asp:BoundField DataField="SupplierName" HeaderText="Supplier"
            ReadOnly="True" SortExpression="SupplierName" />
        <asp:BoundField DataField="UnitPrice" DataFormatString="{0:C}"
            HeaderText="Price" HtmlEncode="False" SortExpression="UnitPrice" />
        <asp:CheckBoxField DataField="Discontinued" HeaderText="Discontinued"
            SortExpression="Discontinued" />
    </Columns>
</asp:GridView>
<asp:ObjectDataSource ID="ObjectDataSource1" runat="server"
    OldValuesParameterFormatString="original_{0}" SelectMethod="GetProducts"
    TypeName="ProductsBLL"></asp:ObjectDataSource>

잠시 시간을 내어 브라우저에서 지금까지 진행 상황을 봅니다. 그림 2는 데이터가 사전순으로 범주별로 정렬될 때 정렬 가능한 GridView를 보여줍니다.

Sortable GridView의 데이터는 범주별로 정렬됩니다.

그림 2: Sortable GridView의 데이터가 범주별로 정렬됨(전체 크기 이미지를 보려면 클릭)

2단계: 구분 기호 행을 추가하기 위한 기술 탐색

정렬 가능한 제네릭 GridView가 완료되면 각 고유한 정렬된 그룹 앞에 GridView의 구분 기호 행을 추가할 수 있습니다. 그러나 이러한 행을 GridView에 어떻게 삽입할 수 있을까요? 기본적으로 GridView 행을 반복하고 정렬된 열의 값 간에 차이가 발생하는 위치를 확인한 다음 적절한 구분 기호 행을 추가해야 합니다. 이 문제를 생각할 때 솔루션이 GridView의 RowDataBound 이벤트 처리기 어딘가에 있는 것은 자연스러운 일입니다. 데이터 기반 사용자 지정 서식 자습서에서 설명한 것처럼 이 이벤트 처리기는 행 데이터를 기반으로 행 수준 서식을 적용할 때 일반적으로 사용됩니다. 그러나 RowDataBound 이 이벤트 처리기에서 프로그래밍 방식으로 GridView에 행을 추가할 수 없으므로 이벤트 처리기는 여기에 대한 솔루션이 아닙니다. 실제로 GridView의 Rows 컬렉션은 읽기 전용입니다.

GridView에 행을 더 추가하려면 다음 세 가지 옵션을 선택할 수 있습니다.

  • GridView에 바인딩된 실제 데이터에 이러한 메타데이터 구분 기호 행 추가
  • GridView가 데이터에 바인딩된 후 GridView의 컨트롤 컬렉션에 추가 TableRow 인스턴스를 추가합니다.
  • GridView 컨트롤을 확장하고 GridView 구조 생성을 담당하는 메서드를 재정의하는 사용자 지정 서버 컨트롤 만들기

이 기능이 많은 웹 페이지 또는 여러 웹 사이트에서 필요한 경우 사용자 지정 서버 컨트롤을 만드는 것이 가장 좋습니다. 그러나 꽤 많은 코드와 GridView 내부 작업의 깊이에 대한 철저한 탐색이 수반됩니다. 따라서 이 자습서에서는 해당 옵션을 고려하지 않습니다.

GridView에 바인딩되는 실제 데이터에 구분 기호 행을 추가하고 바인딩된 후 GridView의 컨트롤 컬렉션을 조작하는 다른 두 옵션은 문제를 다르게 공격하고 토론할 수 있습니다.

GridView에 바인딩된 데이터에 행 추가

GridView가 데이터 원본에 바인딩되면 데이터 원본에서 반환되는 GridViewRow 각 레코드에 대해 을 만듭니다. 따라서 GridView에 바인딩하기 전에 데이터 원본에 구분 기호 레코드를 추가하여 필요한 구분 기호 행을 삽입할 수 있습니다. 그림 3에서는 이 개념을 보여 줍니다.

데이터 원본에 구분 기호 행 추가와 관련된 한 가지 기술

그림 3: 데이터 원본에 구분 기호 행 추가를 포함하는 한 가지 기술

특수 구분 기호 레코드가 없으므로 따옴표로 구분 기호 레코드라는 용어를 사용합니다. 대신 데이터 원본의 특정 레코드가 일반 데이터 행이 아닌 구분 기호로 사용되도록 플래그를 지정해야 합니다. 예제에서는 로 구성된 GridView에 instance 바인딩 ProductsDataTable 합니다ProductRows. 속성을 -1 로 설정하여 레코드를 구분 기호 행으로 플래그를 설정할 CategoryID 수 있습니다(이러한 값이 정상적으로 존재할 수 없으므로).

이 기술을 활용하려면 다음 단계를 수행해야 합니다.

  1. GridView에 바인딩할 데이터를 프로그래밍 방식으로 검색합니다(ProductsDataTableinstance).
  2. GridView SortExpressionSortDirection 속성에 따라 데이터 정렬
  3. 에서 ProductsDataTable를 반복 ProductsRows 하여 정렬된 열의 차이점이 있는 위치를 찾습니다.
  4. 각 그룹 경계에서 구분 기호 레코드 ProductsRow instance DataTable CategoryID-1 에 삽입합니다( 또는 레코드를 구분 기호 레코드로 표시하도록 결정된 지정).
  5. 구분 기호 행을 삽입한 후 프로그래밍 방식으로 GridView에 데이터를 바인딩합니다.

이러한 5단계 외에도 GridView RowDataBound 이벤트에 대한 이벤트 처리기를 제공해야 합니다. 여기서는 각각 DataRow 을 검사 설정이 인 구분 기호 행 CategoryID-1인지 확인합니다. 그렇다면 해당 서식이나 셀에 표시된 텍스트를 조정하려고 할 것입니다.

정렬 그룹 경계를 삽입하기 위해 이 기술을 사용하려면 GridView Sorting 이벤트에 대한 이벤트 처리기를 제공하고 및 SortDirection 값을 추적 SortExpression 해야 하므로 위에서 설명한 것보다 더 많은 작업이 필요합니다.

데이터 바인딩된 후 GridView의 컨트롤 컬렉션 조작

GridView에 바인딩하기 전에 데이터를 메시징하는 대신 데이터가 GridView에 바인딩된 구분 기호 행을 추가할 수 있습니다. 데이터 바인딩 프로세스는 GridView의 컨트롤 계층 구조를 구축합니다. 실제로는 단순히 Table 행 컬렉션으로 구성된 instance, 각 행은 셀 컬렉션으로 구성됩니다. 특히 GridView의 컨트롤 컬렉션에는 루트의 개체, GridViewRow GridView에 바인딩된 각 레코드 DataSource 에 대한 (클래스에서 TableRow 파생된) 및 TableCell 의 각 데이터 필드에 대한 각 GridViewRow instance 개체가 DataSource포함 Table 됩니다.

각 정렬 그룹 간에 구분 기호 행을 추가하려면 이 컨트롤 계층이 만들어지면 직접 조작할 수 있습니다. GridView의 컨트롤 계층 구조는 페이지가 렌더링될 때까지 마지막으로 생성되었다고 확신할 수 있습니다. 따라서 이 방법은 클래스의 메서드를 Page 재정의 Render 합니다. 이때 GridView의 최종 제어 계층 구조가 필요한 구분 기호 행을 포함하도록 업데이트됩니다. 그림 4에서는 이 프로세스를 보여 줍니다.

GridView의 컨트롤 계층 구조를 조작하는 대체 기술

그림 4: GridView의 컨트롤 계층 구조를 조작하는 대체 기술(전체 크기 이미지를 보려면 클릭)

이 자습서에서는 이 후자의 접근 방식을 사용하여 정렬 사용자 환경을 사용자 지정합니다.

참고

이 자습서에서 제시하는 코드는 Teemu Keiski 의 블로그 항목인 GridView 정렬 그룹화로 비트 재생에 제공된 예제를 기반으로 합니다.

3단계: GridView의 컨트롤 계층 구조에 구분 기호 행 추가

해당 페이지 방문에서 마지막으로 컨트롤 계층 구조를 만들고 만든 후에만 GridView 컨트롤 계층 구조에 구분 기호 행을 추가하려고 하므로 페이지 수명 주기의 끝에서 실제 GridView 컨트롤 계층이 HTML로 렌더링되기 전에 이 추가를 수행하려고 합니다. 이 작업을 수행할 수 있는 최신 지점은 다음 메서드 서명을 사용하여 코드 숨김 클래스에서 재정의할 수 있는 클래스의 Render 이벤트입니다Page.

protected override void Render(HtmlTextWriter writer)
{
    // Add code to manipulate the GridView control hierarchy
    base.Render(writer);
}

Page 클래스의 원래 Render 메서드가 호출 base.Render(writer) 되면 페이지의 각 컨트롤이 렌더링되어 해당 컨트롤 계층 구조에 따라 태그가 생성됩니다. 따라서 페이지가 렌더링되고 를 호출base.Render(writer)하기 전에 GridView의 컨트롤 계층 구조를 조작하여 구분 기호 행이 렌더링되기 전에 GridView의 컨트롤 계층 구조에 추가되도록 둘 다 를 호출base.Render(writer)해야 합니다.

정렬 그룹 헤더를 삽입하려면 먼저 사용자가 데이터를 정렬하도록 요청했는지 확인해야 합니다. 기본적으로 GridView의 콘텐츠는 정렬되지 않으므로 그룹 정렬 헤더를 입력할 필요가 없습니다.

참고

페이지가 처음 로드될 때 GridView를 특정 열별로 정렬하려면 첫 번째 페이지 방문 시 GridView 메서드 Sort 를 호출합니다(후속 포스트백에서는 아님). 이 작업을 수행하려면 조건부 내의 Page_Load 이벤트 처리기에 이 호출을 if (!Page.IsPostBack) 추가합니다. 메서드에 대한 자세한 내용은 페이징 및 보고서 데이터 정렬 자습서 정보를 Sort 참조하세요.

데이터가 정렬되었다고 가정하면 다음 작업은 데이터가 정렬된 열을 확인한 다음 행에서 해당 열 값의 차이를 찾는 것입니다. 다음 코드는 데이터가 정렬되었는지 확인하고 데이터가 정렬된 열을 찾습니다.

protected override void Render(HtmlTextWriter writer)
{
    // Only add the sorting UI if the GridView is sorted
    if (!string.IsNullOrEmpty(ProductList.SortExpression))
    {
        // Determine the index and HeaderText of the column that
        //the data is sorted by
        int sortColumnIndex = -1;
        string sortColumnHeaderText = string.Empty;
        for (int i = 0; i < ProductList.Columns.Count; i++)
        {
            if (ProductList.Columns[i].SortExpression.CompareTo(ProductList.SortExpression)
                == 0)
            {
                sortColumnIndex = i;
                sortColumnHeaderText = ProductList.Columns[i].HeaderText;
                break;
            }
        }
        // TODO: Scan the rows for differences in the sorted column�s values
}

GridView를 아직 정렬하지 않은 경우 GridView의 SortExpression 속성이 설정되지 않습니다. 따라서 이 속성에 값이 있는 경우에만 구분 기호 행을 추가하려고 합니다. 이 경우 다음으로 데이터가 정렬된 열의 인덱스 확인이 필요합니다. 이 작업은 GridView의 Columns 컬렉션을 반복하고 속성이 GridView의 SortExpression 속성과 SortExpression 같은 열을 검색하여 수행됩니다. 열의 인덱스 외에도 구분 기호 행을 HeaderText 표시할 때 사용되는 속성을 가져옵니다.

데이터가 정렬되는 열의 인덱스를 사용하여 마지막 단계는 GridView의 행을 열거하는 것입니다. 각 행에 대해 정렬된 열 값이 이전 행의 정렬된 열 값과 다른지 확인해야 합니다. 그렇다면 새 GridViewRow instance 컨트롤 계층 구조에 삽입해야 합니다. 이 작업은 다음 코드로 수행됩니다.

protected override void Render(HtmlTextWriter writer)
{
    // Only add the sorting UI if the GridView is sorted
    if (!string.IsNullOrEmpty(ProductList.SortExpression))
    {
        // ... Code for finding the sorted column index removed for brevity ...
        // Reference the Table the GridView has been rendered into
        Table gridTable = (Table)ProductList.Controls[0];
        // Enumerate each TableRow, adding a sorting UI header if
        // the sorted value has changed
        string lastValue = string.Empty;
        foreach (GridViewRow gvr in ProductList.Rows)
        {
            string currentValue = gvr.Cells[sortColumnIndex].Text;
            if (lastValue.CompareTo(currentValue) != 0)
            {
                // there's been a change in value in the sorted column
                int rowIndex = gridTable.Rows.GetRowIndex(gvr);
                // Add a new sort header row
                GridViewRow sortRow = new GridViewRow(rowIndex, rowIndex,
                    DataControlRowType.DataRow, DataControlRowState.Normal);
                TableCell sortCell = new TableCell();
                sortCell.ColumnSpan = ProductList.Columns.Count;
                sortCell.Text = string.Format("{0}: {1}",
                    sortColumnHeaderText, currentValue);
                sortCell.CssClass = "SortHeaderRowStyle";
                // Add sortCell to sortRow, and sortRow to gridTable
                sortRow.Cells.Add(sortCell);
                gridTable.Controls.AddAt(rowIndex, sortRow);
                // Update lastValue
                lastValue = currentValue;
            }
        }
    }
    base.Render(writer);
}

이 코드는 GridView 컨트롤 계층의 루트에 있는 개체를 Table 프로그래밍 방식으로 참조하고 라는 lastValue문자열 변수를 만드는 것으로 시작합니다. lastValue 는 현재 행의 정렬된 열 값을 이전 행의 값과 비교하는 데 사용됩니다. 다음으로 GridView의 Rows 컬렉션이 열거되고 각 행에 대해 정렬된 열의 값이 변수에 currentValue 저장됩니다.

참고

특정 행의 정렬된 열 값을 확인하려면 셀의 Text 속성을 사용합니다. 이는 BoundFields에서 잘 작동하지만 TemplateFields, CheckBoxFields 등에 대해 원하는 대로 작동하지 않습니다. 곧 대체 GridView 필드를 어떻게 설명할지 살펴보겠습니다.

currentValue 그런 다음 및 lastValue 변수를 비교합니다. 다른 경우 컨트롤 계층에 새 구분 기호 행을 추가해야 합니다. 이 작업은 개체 컬렉션에서 의 GridViewRow 인덱스를 확인하고, 새 GridViewRowTableCell 인스턴스를 만든 다음, 및 GridViewRow 를 컨트롤 계층 구조에 추가하여 TableCell 수행 Table 됩니다.Rows

구분 기호 행은 GridView의 TableCell 전체 너비에 걸쳐 있고, CSS 클래스를 사용하여 SortHeaderRowStyle 형식이 지정되고, 정렬 그룹 이름(예: Category) 및 Text 그룹 값(예: 음료)을 모두 표시할 수 있도록 해당 속성이 있도록 형식이 지정됩니다. 마지막으로 는 lastValue 값으로 업데이트됩니다 currentValue.

정렬 그룹 머리글 행 SortHeaderRowStyle 의 서식을 지정하는 데 사용되는 CSS 클래스를 파일에 지정 Styles.css 해야 합니다. 어떤 스타일 설정도 자유롭게 사용할 수 있습니다. 다음을 사용했습니다.

.SortHeaderRowStyle
{
    background-color: #c00;
    text-align: left;
    font-weight: bold;
    color: White;
}

현재 코드를 사용하여 정렬 인터페이스는 BoundField를 기준으로 정렬할 때 정렬 그룹 헤더를 추가합니다(공급자별로 정렬할 때 스크린샷을 보여 주는 그림 5 참조). 그러나 다른 필드 형식(예: CheckBoxField 또는 TemplateField)을 기준으로 정렬하면 정렬 그룹 헤더를 찾을 수 없습니다(그림 6 참조).

BoundFields로 정렬할 때 정렬 인터페이스에 정렬 그룹 머리글이 포함됨

그림 5: BoundFields로 정렬할 때 정렬 그룹 머리글이 정렬 인터페이스에 포함됨(전체 크기 이미지를 보려면 클릭)

CheckBoxField를 정렬할 때 그룹 머리글 정렬이 누락됨

그림 6: CheckBoxField를 정렬할 때 그룹 머리글 정렬이 누락됨(전체 크기 이미지를 보려면 클릭)

CheckBoxField를 기준으로 정렬할 때 정렬 그룹 머리글이 누락된 이유는 코드가 현재 s Text 속성만 TableCell 사용하여 각 행에 대해 정렬된 열의 값을 결정하기 때문입니다. CheckBoxFields의 TableCell 경우 s Text 속성은 빈 문자열입니다. 대신 값은 컬렉션 Controls 내에 있는 CheckBox 웹 컨트롤을 TableCell 통해 사용할 수 있습니다.

BoundFields 이외의 필드 형식을 처리하려면 컬렉션에 CheckBox TableCellControls 가 존재하기 위해 변수가 검사 할당되는 currentValue 코드를 보강해야 합니다. 을 사용하는 currentValue = gvr.Cells[sortColumnIndex].Text대신 이 코드를 다음으로 바꿉다.

string currentValue = string.Empty;
if (gvr.Cells[sortColumnIndex].Controls.Count > 0)
{
    if (gvr.Cells[sortColumnIndex].Controls[0] is CheckBox)
    {
        if (((CheckBox)gvr.Cells[sortColumnIndex].Controls[0]).Checked)
            currentValue = "Yes";
        else
            currentValue = "No";
    }
    // ... Add other checks here if using columns with other
    //      Web controls in them (Calendars, DropDownLists, etc.) ...
}
else
    currentValue = gvr.Cells[sortColumnIndex].Text;

이 코드는 현재 행의 정렬된 열을 TableCell 검사하여 컬렉션에 Controls 컨트롤이 있는지 확인합니다. 이 있고 첫 번째 컨트롤이 CheckBox인 경우 변수는 CheckBox currentValueChecked 속성에 따라 예 또는 아니요로 설정됩니다. 그렇지 않으면 값은 s Text 속성에서 TableCell 가져옵니다. 이 논리는 GridView에 있을 수 있는 모든 TemplateFields에 대한 정렬을 처리하도록 복제할 수 있습니다.

위의 코드 추가를 사용하면 중단된 CheckBoxField를 기준으로 정렬할 때 정렬 그룹 헤더가 표시됩니다(그림 7 참조).

CheckBoxField를 정렬할 때 그룹 머리글 정렬이 표시됩니다.

그림 7: CheckBoxField를 정렬할 때 정렬 그룹 머리글이 표시됩니다(전체 크기 이미지를 보려면 클릭).

참고

, SupplierID또는 필드에 대한 CategoryID데이터베이스 값이 있는 NULL 제품이 있는 경우 해당 값은 기본적으로 GridView에 빈 문자열로 표시됩니다. 즉, 값이 있는 제품에 NULL 대한 구분 기호 행 텍스트는 범주: (범주: 음료와 같이 범주 다음에 이름이 없음)와 UnitPrice 같이 읽습니다. 여기에 값을 표시하려면 BoundFields NullDisplayText 속성을 표시할 텍스트로 설정하거나 를 구분 기호 행의 Text 속성에 할당 currentValue 할 때 Render 메서드에 조건문을 추가할 수 있습니다.

요약

GridView에는 정렬 인터페이스를 사용자 지정하기 위한 많은 기본 제공 옵션이 포함되어 있지 않습니다. 그러나 약간의 하위 수준 코드를 사용하면 GridView의 컨트롤 계층 구조를 조정하여 보다 사용자 지정된 인터페이스를 만들 수 있습니다. 이 자습서에서는 정렬 가능한 GridView에 대한 정렬 그룹 구분 기호 행을 추가하는 방법을 알아보았습니다. 이 행은 고유 그룹 및 해당 그룹 경계를 보다 쉽게 식별합니다. 사용자 지정된 정렬 인터페이스의 추가 예제를 보려면 Scott Guthrie몇 가지 ASP.NET 2.0 GridView 정렬 팁 및 요령 블로그 항목을 검사.

행복한 프로그래밍!

저자 정보

7개의 ASP/ASP.NET 책의 저자이자 4GuysFromRolla.com 창립자인 Scott Mitchell은 1998년부터 Microsoft 웹 기술로 작업해 왔습니다. Scott은 독립 컨설턴트, 트레이너 및 작가로 일합니다. 그의 최신 책은 샘스 티치 유어셀프 ASP.NET 24시간 만에 2.0입니다. 그는 에서mitchell@4GuysFromRolla.com 또는 에서 찾을 http://ScottOnWriting.NET수있는 자신의 블로그를 통해 도달 할 수 있습니다.