사용자 지정된 정렬 사용자 인터페이스 만들기(C#)Creating a Customized Sorting User Interface (C#)

Scott Mitchellby Scott Mitchell

샘플 앱 다운로드 또는 PDF 다운로드Download Sample App or Download PDF

정렬 된 데이터의 긴 목록을 표시 하는 경우 구분 기호 행을 도입 하 여 관련 데이터를 그룹화 하는 것이 매우 유용할 수 있습니다.When displaying a long list of sorted data, it can be very helpful to group related data by introducing separator rows. 이 자습서에서는 이러한 정렬 사용자 인터페이스를 만드는 방법을 알아봅니다.In this tutorial we'll see how to create such a sorting user interface.

소개Introduction

정렬 된 열에 몇 개의 다른 값만 있는 경우 정렬 된 데이터의 긴 목록을 표시 하는 경우 최종 사용자는 정확히 차이가 발생 하는 위치를 정확 하 게 파악 하는 것이 어려울 수 있습니다.When displaying a long list of sorted data where there are only a handful of different values in the sorted column, an end user might find it hard to discern where, exactly, the difference boundaries occur. 예를 들어 데이터베이스에는 81 개의 제품이 있지만 9 개의 범주를 선택할 수 있습니다 (고유 범주 8 개와 NULL 옵션).For example, there are 81 products in the database, but only nine different category choices (eight unique categories plus the NULL option). 해산물 범주에 속하는 제품을 검사 하려는 사용자의 경우를 고려 합니다.Consider the case of a user who is interested in examining the products that fall under the Seafood category. 단일 GridView의 모든 제품을 나열 하는 페이지에서 사용자는 범주를 기준으로 결과를 정렬 하는 것이 가장 좋습니다. 모든 해산물 제품을 함께 그룹화 하는 것이 좋습니다.From a page that lists all of the products in a single GridView, the user might decide her best bet is to sort the results by category, which will group together all of the Seafood products together. 범주별로 정렬 한 후에 사용자는 목록을 검색 하 여 해산물 그룹 제품이 시작 및 종료 되는 위치를 확인 해야 합니다.After sorting by category, the user then needs to hunt through the list, looking for where the Seafood-grouped products start and end. 결과는 범주 이름을 기준으로 사전순으로 정렬 되므로 해산물 제품을 찾는 것은 어렵지 않지만 표에서 항목 목록을 자세히 검색 해야 합니다.Since the results are ordered alphabetically by the category name finding the Seafood products is not difficult, but it still requires closely scanning the list of items in the grid.

정렬 된 그룹 간의 경계를 강조 하기 위해 많은 웹 사이트에서는 이러한 그룹 사이에 구분 기호를 추가 하는 사용자 인터페이스를 사용 합니다.To help highlight the boundaries between sorted groups, many websites employ a user interface that adds a separator between such groups. 그림 1에 표시 된 것과 같은 구분 기호를 사용 하면 사용자가 특정 그룹을 더 빠르게 찾고 해당 경계를 식별할 수 있을 뿐만 아니라 데이터에 존재 하는 고유한 그룹을 확인할 수 있습니다.Separators like the ones shown in Figure 1 enables a user to more quickly find a particular group and identify its boundaries, as well as ascertain what distinct groups exist in the data.

각 범주 그룹을 명확 하 게 식별 Each Category Group is Clearly Identified

그림 1: 각 범주 그룹을 명확 하 게 식별 (전체 크기 이미지를 보려면 클릭)Figure 1: Each Category Group is Clearly Identified (Click to view full-size image)

이 자습서에서는 이러한 정렬 사용자 인터페이스를 만드는 방법을 알아봅니다.In this tutorial we'll see how to create such a sorting user interface.

1 단계: 정렬 가능한 표준 GridView 만들기Step 1: Creating a Standard, Sortable GridView

GridView를 보강 하 여 향상 된 정렬 인터페이스를 제공 하는 방법을 살펴보기 전에 먼저 제품을 나열 하는 정렬 가능한 표준 GridView를 만들어 보겠습니다.Before we explore how to augment the GridView to provide the enhanced sorting interface, let s first create a standard, sortable GridView that lists the products. 먼저 PagingAndSorting 폴더에서 CustomSortingUI.aspx 페이지를 엽니다.Start by opening the CustomSortingUI.aspx page in the PagingAndSorting folder. 페이지에 GridView를 추가 하 고, 해당 ID 속성을 ProductList로 설정 하 고, 새 ObjectDataSource에 바인딩합니다.Add a GridView to the page, set its ID property to ProductList, and bind it to a new ObjectDataSource. ProductsBLL 클래스 s GetProducts() 메서드를 사용 하 여 레코드를 선택 하도록 ObjectDataSource를 구성 합니다.Configure the ObjectDataSource to use the ProductsBLL class s GetProducts() method for selecting records.

그런 다음 ProductName, CategoryName, SupplierName, UnitPrice BoundFields 및 단종 된 CheckBoxField 포함 하도록 GridView를 구성 합니다.Next, configure the GridView such that it only contains the ProductName, CategoryName, SupplierName, and UnitPrice BoundFields and the Discontinued CheckBoxField. 마지막으로 GridView의 스마트 태그에서 정렬 사용 확인란을 선택 하 여 (또는 해당 AllowSorting 속성을 true로 설정) 정렬을 지원 하도록 GridView를 구성 합니다.Finally, configure the GridView to support sorting by checking the Enable Sorting checkbox in the GridView s smart tag (or by setting its AllowSorting property to true). CustomSortingUI.aspx 페이지를 추가 하 고 나면 선언적 태그가 다음과 같이 표시 됩니다.After making these additions to the CustomSortingUI.aspx page, the declarative markup should look similar to the following:

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

잠시 후 브라우저에서 지금까지 진행 상황을 확인 하세요.Take a moment to view our progress thus far in a browser. 그림 2에서는 데이터를 범주별로 사전순으로 정렬할 때 정렬 가능한 GridView를 보여 줍니다.Figure 2 shows the sortable GridView when its data is sorted by category in alphabetical order.

정렬할 수 있는 GridView s 데이터를 범주별로 정렬 The Sortable GridView s Data is Ordered by Category

그림 2: 정렬 가능한 GridView s 데이터를 범주별로 정렬 (전체 크기 이미지를 보려면 클릭)Figure 2: The Sortable GridView s Data is Ordered by Category (Click to view full-size image)

2 단계: 구분 기호 행을 추가 하는 기술 탐색Step 2: Exploring Techniques for Adding the Separator Rows

정렬할 수 있는 일반 GridView를 사용 하는 경우 각 고유 정렬 된 그룹 앞에 GridView의 구분 기호 행을 추가할 수 있습니다.With the generic, sortable GridView complete, all that remains is to be able to add the separator rows in the GridView before each unique sorted group. 그러나 이러한 행을 GridView에 삽입 하려면 어떻게 해야 하나요?But how can such rows be injected into the GridView? 기본적으로 GridView의 행을 반복 하 고 정렬 된 열의 값 간에 차이가 발생 하는 위치를 확인 한 다음 적절 한 구분 기호 행을 추가 해야 합니다.Essentially, we need to iterate through the GridView s rows, determine where the differences occur between the values in the sorted column, and then add the appropriate separator row. 이 문제에 대해 생각 하는 경우 솔루션이 GridView s RowDataBound 이벤트 처리기의 어딘가에 있는 것으로 자연스럽 게 보입니다.When thinking about this problem, it seems natural that the solution lies somewhere in the GridView s RowDataBound event handler. 데이터에 따라 사용자 지정 서식 지정 자습서에서 설명한 대로이 이벤트 처리기는 행 데이터를 기반으로 행 수준 서식을 적용할 때 일반적으로 사용 됩니다.As we discussed in the Custom Formatting Based Upon Data tutorial, this event handler is commonly used when applying row-level formatting based on the row s data. 그러나이 이벤트 처리기에서 프로그래밍 방식으로 GridView에 행을 추가할 수 없으므로 RowDataBound 이벤트 처리기는 솔루션이 아닙니다.However, the RowDataBound event handler is not the solution here, as rows cannot be added to the GridView programmatically from this event handler. GridView s Rows 컬렉션은 실제로 읽기 전용입니다.The GridView s Rows collection, in fact, is read-only.

GridView에 행을 추가 하려면 다음 세 가지 옵션 중에서 선택할 수 있습니다.To add additional rows to the GridView we have three choices:

  • GridView에 바인딩된 실제 데이터에 이러한 메타 데이터 구분 기호 행을 추가 합니다.Add these metadata separator rows to the actual data that is bound to the GridView
  • GridView를 데이터에 바인딩한 후에 TableRow 인스턴스를 GridView 컨트롤 컬렉션에 추가 합니다.After the GridView has been bound to the data, add additional TableRow instances to the GridView s control collection
  • GridView 컨트롤을 확장 하 고 GridView 구조를 생성 하는 메서드를 재정의 하는 사용자 지정 서버 컨트롤을 만듭니다.Create a custom server control that extends the GridView control and overrides those methods responsible for constructing the GridView s structure

이 기능이 여러 웹 페이지 또는 여러 웹 사이트에 필요한 경우 사용자 지정 서버 컨트롤을 만드는 것이 가장 좋은 방법입니다.Creating a custom server control would be the best approach if this functionality was needed on many web pages or across several websites. 그러나 매우 많은 코드와 GridView의 내부 작동 수준에 대 한 철저 한 탐색이 수반 됩니다.However, it would entail quite a bit of code and a thorough exploration into the depths of the GridView s internal workings. 따라서이 자습서에서는이 옵션을 고려 하지 않습니다.Therefore, we'll not consider that option for this tutorial.

다른 두 옵션은 GridView에 바인딩되는 실제 데이터에 구분 기호 행을 추가 하 고 GridView의 컨트롤 컬렉션을 조작 하 여 문제를 다르게 공격 하 고 논의 하는 것입니다.The other two options adding separator rows to the actual data being bound to the GridView and manipulating the GridView s control collection after its been bound - attack the problem differently and merit a discussion.

GridView에 바인딩된 데이터에 행 추가Adding Rows to the Data Bound to the GridView

GridView는 데이터 원본에 바인딩될 때 데이터 원본에서 반환 하는 각 레코드에 대 한 GridViewRow를 만듭니다.When the GridView is bound to a data source, it creates a GridViewRow for each record returned by the data source. 따라서 GridView에 바인딩하기 전에 데이터 원본에 구분 기호 레코드를 추가 하 여 필요한 구분 기호 행을 삽입할 수 있습니다.Therefore, we can inject the needed separator rows by adding separator records to the data source before binding it to the GridView. 그림 3에서는 이러한 개념을 보여 줍니다.Figure 3 illustrates this concept.

한 가지 방법으로 데이터 원본에 구분 기호 행 추가

그림 3: 데이터 원본에 구분 기호 행 추가를 포함 하는 한 가지 방법Figure 3: One Technique Involves Adding Separator Rows to the Data Source

특별 한 구분 기호가 없으므로 따옴표로 구분 된 용어 구분 기호 레코드를 사용 합니다. 대신 데이터 원본의 특정 레코드가 일반 데이터 행이 아닌 구분 기호로 사용 된다는 플래그를 지정 해야 합니다.I use the term separator records in quotes because there is no special separator record; rather, we must somehow flag that a particular record in the data source serves as a separator rather than a normal data row. 이 예제에서는 ProductRows로 구성 된 GridView에 ProductsDataTable 인스턴스를 다시 바인딩합니다.For our examples, we re binding a ProductsDataTable instance to the GridView, which is composed of ProductRows. CategoryID 속성을 -1로 설정 하 여 레코드를 구분 기호로 플래그를 설정할 수 있습니다. 이러한 값은 정상적으로 존재할 수 없기 때문입니다.We might flag a record as a separator row by setting its CategoryID property to -1 (since such a value couldn't exist normally).

이 기술을 활용 하기 위해 다음 단계를 수행 해야 합니다.To utilize this technique we d need to perform the following steps:

  1. GridView에 바인딩할 데이터를 프로그래밍 방식으로 검색 (ProductsDataTable 인스턴스)Programmatically retrieve the data to bind to the GridView (a ProductsDataTable instance)
  2. GridView s SortExpression를 기준으로 데이터를 정렬 하 고 SortDirection 속성을 설정 합니다.Sort the data based on the GridView s SortExpression and SortDirection properties
  3. ProductsDataTable에서 ProductsRows을 반복 하 여 정렬 된 열의 차이가 있는 위치를 찾습니다.Iterate through the ProductsRows in the ProductsDataTable, looking for where the differences in the sorted column lie
  4. 각 그룹 경계에서 DataTable ProductsRow 인스턴스를 DataTable에 삽입 합니다. 하나는 -1 (또는 레코드를 구분 기호로 표시 하도록 결정 된 모든 지정) CategoryID.At each group boundary, inject a separator record ProductsRow instance into the DataTable, one that has it s CategoryID set to -1 (or whatever designation was decided upon to mark a record as a separator record )
  5. 구분 기호 행을 삽입 한 후 데이터를 프로그래밍 방식으로 GridView에 바인딩합니다.After injecting the separator rows, programmatically bind the data to the GridView

이러한 다섯 단계 외에도 GridView s RowDataBound 이벤트에 대 한 이벤트 처리기를 제공 해야 합니다.In addition to these five steps, we d also need to provide an event handler for the GridView s RowDataBound event. 여기서는 각 DataRow를 확인 하 고 구분 행 인지 확인 합니다 .이 행은 CategoryID 설정이 -1되었습니다.Here, we d check each DataRow and determine if it was a separator row, one whose CategoryID setting was -1. 이 경우 셀에 표시 되는 텍스트 서식 또는 텍스트를 조정 하는 것이 좋습니다.If so, we d probably want to adjust its formatting or the text displayed in the cell(s).

이 기법을 사용 하 여 정렬 그룹 경계를 삽입 하는 데는 GridView s Sorting 이벤트에 대 한 이벤트 처리기를 제공 하 고 SortExpressionSortDirection 값을 추적 해야 하므로 위에 설명 된 것 보다 약간 더 많은 작업이 필요 합니다.Using this technique for injecting the sorting group boundaries requires a bit more work than outlined above, as you need to also provide an event handler for the GridView s Sorting event and keep track of the SortExpression and SortDirection values.

데이터 바인딩된 후 GridView 컨트롤 컬렉션 조작Manipulating the GridView s Control Collection After It s Been Databound

GridView에 바인딩하기 전에 데이터를 전달 하는 대신 데이터를 GridView에 바인딩한 에 구분 기호 행을 추가할 수 있습니다.Rather than messaging the data before binding it to the GridView, we can add the separator rows after the data has been bound to the GridView. 데이터 바인딩 프로세스는 GridView의 컨트롤 계층 구조를 구성 합니다. 즉, 실제로는 셀 컬렉션으로 구성 된 행의 컬렉션으로 구성 된 Table 인스턴스입니다.The process of data binding builds up the GridView s control hierarchy, which in reality is simply a Table instance composed of a collection of rows, each of which is composed of a collection of cells. 특히 GridView의 컨트롤 컬렉션에는 GridViewRow (TableRow 클래스에서 파생 됨)의 루트에 있는 Table 개체와, GridView에 바인딩된 DataSource의 각 레코드에 대 한 TableCell 개체와 GridViewRow의 각 데이터 필드에 대 한 각 DataSource인스턴스의 개체가 포함 되어 있습니다.Specifically, the GridView s control collection contains a Table object at its root, a GridViewRow (which is derived from the TableRow class) for each record in the DataSource bound to the GridView, and a TableCell object in each GridViewRow instance for each data field in the DataSource.

각 정렬 그룹 사이에 구분 기호 행을 추가 하려면이 컨트롤 계층 구조를 만든 후 직접 조작할 수 있습니다.To add separator rows between each sorting group, we can directly manipulate this control hierarchy once it has been created. 페이지를 렌더링 하는 시간을 기준으로 GridView의 컨트롤 계층이 마지막으로 생성 되었음을 확신할 수 있습니다.We can be confident that the GridView s control hierarchy has been created for the last time by the time the page is being rendered. 따라서이 방법은 Page 클래스 Render 메서드를 재정의 합니다 .이 시점에서 GridView s 최종 컨트롤 계층 구조가 필요한 구분 기호 행을 포함 하도록 업데이트 됩니다.Therefore, this approach overrides the Page class s Render method, at which point the GridView s final control hierarchy is updated to include the needed separator rows. 그림 4에서는이 프로세스를 보여 줍니다.Figure 4 illustrates this process.

GridView의 컨트롤 계층 구조를 조작 하는 대체 기술An Alternate Technique Manipulates the GridView s Control Hierarchy

그림 4: 대체 기술이 GridView의 컨트롤 계층 구조를 조작 (전체 크기 이미지를 보려면 클릭)Figure 4: An Alternate Technique Manipulates the GridView s Control Hierarchy (Click to view full-size image)

이 자습서에서는이 두 가지 방법을 사용 하 여 사용자 환경 정렬을 사용자 지정 합니다.For this tutorial, we'll use this latter approach to customize the sorting user experience.

Note

이 자습서에서 설명 하는 코드는 GridView 정렬 그룹화를 사용 하 여 비트를 재생하는 teemu Keiski s 블로그 항목에 제공 된 예제를 기반으로 합니다.The code I m presenting in this tutorial is based on the example provided in Teemu Keiski s blog entry, Playing a Bit with GridView Sort Grouping.

3 단계: GridView 컨트롤 계층 구조에 구분 기호 행 추가Step 3: Adding the Separator Rows to the GridView s Control Hierarchy

컨트롤 계층 구조를 만들어 해당 페이지에서 마지막으로 만든 후에는 GridView의 컨트롤 계층에 구분 기호 행만 추가 하려고 하기 때문에 페이지 수명 주기 끝에서, 실제 GridView c 이전에이 추가를 수행 하려고 합니다. o 계층이 HTML로 렌더링 되었습니다.Since we only want to add the separator rows to the GridView s control hierarchy after its control hierarchy has been created and created for the last time on that page visit, we want to perform this addition at the end of the page lifecycle, but before the actual GridView control hierarchy has been rendered into HTML. 이 작업을 수행할 수 있는 최신 시점으로, 다음 메서드 시그니처를 사용 하 여 코드 숨김이 클래스에서 재정의할 수 있는 Page 클래스 Render 이벤트입니다.The latest possible point at which we can accomplish this is the Page class s Render event, which we can override in our code-behind class using the following method signature:

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

Page 클래스의 원래 Render 메서드가 base.Render(writer) 호출 되 면 페이지의 각 컨트롤이 렌더링 되어 컨트롤 계층 구조를 기반으로 태그가 생성 됩니다.When the Page class s original Render method is invoked base.Render(writer) each of the controls in the page will be rendered, generating the markup based on their control hierarchy. 따라서 페이지가 렌더링 되기 전에 base.Render(writer)를 호출 하 고, base.Render(writer)를 호출 하기 전에 GridView의 컨트롤 계층 구조를 조작 하는 것이 필수적입니다. 따라서 구분 기호 행이 렌더링 되기 전에 GridView의 컨트롤 계층 구조에 추가 됩니다.Therefore it is imperative that we both call base.Render(writer), so that the page is rendered, and that we manipulate the GridView s control hierarchy prior to calling base.Render(writer), so that the separator rows have been added to the GridView s control hierarchy before it s been rendered.

정렬 그룹 헤더를 삽입 하려면 먼저 사용자가 데이터를 정렬 하도록 요청 했는지 확인 해야 합니다.To inject the sort group headers we first need to ensure that the user has requested that the data be sorted. 기본적으로 GridView s의 콘텐츠는 정렬 되지 않으므로 그룹 정렬 헤더를 입력할 필요가 없습니다.By default, the GridView s contents are not sorted, and therefore we don t need to enter any group sorting headers.

Note

페이지가 처음 로드 될 때 특정 열을 기준으로 GridView를 정렬 하려면 첫 번째 페이지에서 GridView s Sort 메서드를 호출 합니다 (이후 포스트백의 경우는 제외).If you want the GridView to be sorted by a particular column when the page is first loaded, call the GridView s Sort method on the first page visit (but not on subsequent postbacks). 이를 수행 하려면 if (!Page.IsPostBack) 조건부 내의 Page_Load 이벤트 처리기에이 호출을 추가 합니다.To accomplish this, add this call in the Page_Load event handler within an if (!Page.IsPostBack) conditional. Sort 방법에 대 한 자세한 내용은 보고서 데이터 페이징 및 정렬 자습서 정보를 참조 하세요.Refer back to the Paging and Sorting Report Data tutorial information for more on the Sort method.

데이터가 정렬 된 것으로 가정 하면 다음 태스크는 데이터가 정렬 된 열을 확인 한 다음 해당 열 값의 차이를 찾는 행을 검색 하는 것입니다.Assuming that the data has been sorted, our next task is to determine what column the data was sorted by and then to scan the rows looking for differences in that column s values. 다음 코드는 데이터가 정렬 되었는지 확인 하 고 데이터가 정렬 된 기준이 되는 열을 찾습니다.The following code ensures that the data has been sorted and finds the column by which the data has been sorted:

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 s SortExpression 속성이 설정 되지 않습니다.If the GridView has yet to be sorted, the GridView s SortExpression property will not have been set. 따라서이 속성에 값이 있는 경우에만 구분 기호 행을 추가 하려고 합니다.Therefore, we only want to add the separator rows if this property has some value. 이 경우 다음에 데이터를 정렬 하는 기준이 되는 열의 인덱스를 결정 해야 합니다.If it does, we next need to determine the index of the column by which the data was sorted. 이는 GridView의 Columns 컬렉션을 반복 하 여 SortExpression 속성이 GridView s SortExpression 속성과 같은 열을 검색 하는 방식으로 수행 됩니다.This is accomplished by looping through the GridView s Columns collection, searching for the column whose SortExpression property equals the GridView s SortExpression property. 열 s 인덱스 외에도 구분 기호 행을 표시할 때 사용 되는 HeaderText 속성을 가져옵니다.In addition to the column s index, we also grab the HeaderText property, which is used when displaying the separator rows.

데이터를 정렬 하는 데 사용 되는 열의 인덱스를 사용 하 여 마지막 단계는 GridView의 행을 열거 하는 것입니다.With the index of the column by which the data is sorted, the final step is to enumerate the rows of the GridView. 각 행에 대해 정렬 된 열 값이 이전 행의 정렬 된 열 값과 다른 지 여부를 확인 해야 합니다.For each row we need to determine whether the sorted column s value differs from the previous row s sorted column s value. 그렇다면 새 GridViewRow 인스턴스를 컨트롤 계층 구조에 삽입 해야 합니다.If so, we need to inject a new GridViewRow instance into the control hierarchy. 이 작업은 다음 코드를 사용 하 여 수행 됩니다.This is accomplished with the following code:

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 s 컨트롤 계층의 루트에 있는 Table 개체를 프로그래밍 방식으로 참조 하 고 lastValue라는 문자열 변수를 만듭니다.This code starts by programmatically referencing the Table object found at the root of the GridView s control hierarchy and creating a string variable named lastValue. lastValue를 사용 하 여 현재 행의 정렬 된 열 값을 이전 행의 값과 비교할 수 있습니다.lastValue is used to compare the current row s sorted column value with the previous row s value. 그런 다음 GridView s Rows 컬렉션을 열거 하 고 각 행에 대해 정렬 된 열의 값이 currentValue 변수에 저장 됩니다.Next, the GridView s Rows collection is enumerated and for each row the value of the sorted column is stored in the currentValue variable.

Note

특정 행의 정렬 된 열 값을 확인 하려면 셀 Text 속성을 사용 합니다.To determine the value of the particular row s sorted column I use the cell s Text property. 이는 BoundFields에서 잘 작동 하지만, 템플릿 필드, CheckBoxFields 등에 대해 원하는 대로 작동 하지 않습니다.This works well for BoundFields, but will not work as desired for TemplateFields, CheckBoxFields, and so on. 대체 GridView 필드를 고려 하는 방법을 살펴보겠습니다.We'll look at how to account for alternate GridView fields shortly.

그런 다음 currentValuelastValue 변수를 비교 합니다.The currentValue and lastValue variables are then compared. 서로 다른 경우 컨트롤 계층 구조에 새 구분 기호 행을 추가 해야 합니다.If they differ we need to add a new separator row to the control hierarchy. 이렇게 하려면 Table 개체의 Rows 컬렉션에서 GridViewRow 인덱스를 확인 하 고, 새 GridViewRowTableCell 인스턴스를 만든 다음, TableCellGridViewRow를 컨트롤 계층 구조에 추가 합니다.This is accomplished by determining the index of the GridViewRow in the Table object s Rows collection, creating new GridViewRow and TableCell instances, and then adding the TableCell and GridViewRow to the control hierarchy.

구분 기호 행 s 유일한 TableCell은 GridView의 전체 너비에 적용 되 고 SortHeaderRowStyle CSS 클래스를 사용 하 여 서식이 지정 되며, 정렬 그룹 이름 (예: 범주) 및 그룹 값 (예: 음료)을 모두 보여 주는 Text 속성이 있습니다.Note that the separator row s lone TableCell is formatted such that it spans the entire width of the GridView, is formatted using the SortHeaderRowStyle CSS class, and has its Text property such that it shows both the sort group name (such as Category ) and the group s value (such as Beverages ). 마지막으로 lastValue currentValue값으로 업데이트 됩니다.Finally, lastValue is updated to the value of currentValue.

정렬 그룹 머리글 행 SortHeaderRowStyle의 서식을 지정 하는 데 사용 되는 CSS 클래스를 Styles.css 파일에 지정 해야 합니다.The CSS class used to format the sorting group header row SortHeaderRowStyle needs to be specified in the Styles.css file. 자유롭게 사용할 수 있는 스타일 설정을 자유롭게 사용할 수 있습니다. 다음을 사용 했습니다.Feel free to use whatever style settings appeal to you; I used the following:

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

현재 코드를 사용 하 여 정렬 인터페이스는 BoundField 정렬할 때 정렬 그룹 헤더를 추가 합니다 (공급 업체 별로 정렬할 때 스크린샷을 보여 주는 그림 5 참조).With the current code, the sorting interface adds sort group headers when sorting by any BoundField (see Figure 5, which shows a screenshot when sorting by supplier). 그러나 다른 필드 형식 (예: CheckBoxField 또는 Templatefield로 변환)을 기준으로 정렬 하는 경우 정렬 그룹 머리글이 검색 되지 않습니다 (그림 6 참조).However, when sorting by any other field type (such as a CheckBoxField or TemplateField), the sort group headers are nowhere to be found (see Figure 6).

BoundFields 기준으로 정렬할 때 정렬 인터페이스에 정렬 그룹 머리글이 포함 The Sorting Interface Includes Sort Group Headers When Sorting by BoundFields

그림 5: BoundFields 기준으로 정렬할 때 정렬 인터페이스에 정렬 그룹 머리글 포함 (전체 크기 이미지를 보려면 클릭)Figure 5: The Sorting Interface Includes Sort Group Headers When Sorting by BoundFields (Click to view full-size image)

CheckBoxField을 정렬할 때 정렬 그룹 헤더가 누락 The Sort Group Headers are Missing When Sorting a CheckBoxField

그림 6: CheckBoxField을 정렬할 때 정렬 그룹 머리글 누락 (전체 크기 이미지를 보려면 클릭)Figure 6: The Sort Group Headers are Missing When Sorting a CheckBoxField (Click to view full-size image)

CheckBoxField 정렬할 때 정렬 그룹 머리글이 누락 되는 이유는 코드에서 현재 TableCell s Text 속성만 사용 하 여 각 행에 대해 정렬 된 열의 값을 결정 하기 때문입니다.The reason the sort group headers are missing when sorting by a CheckBoxField is because the code currently uses just the TableCell s Text property to determine the value of the sorted column for each row. CheckBoxFields의 TableCell s Text 속성은 빈 문자열입니다. 대신 TableCell s Controls 컬렉션에 있는 CheckBox 웹 컨트롤을 통해 값을 사용할 수 있습니다.For CheckBoxFields, the TableCell s Text property is an empty string; instead, the value is available through a CheckBox Web control that resides within the TableCell s Controls collection.

BoundFields 이외의 필드 형식을 처리 하려면 TableCell s Controls 컬렉션에 CheckBox가 있는지 확인 하기 위해 currentValue 변수가 할당 된 코드를 보강 해야 합니다.To handle field types other than BoundFields, we need to augment the code where the currentValue variable is assigned to check for the existence of a CheckBox in the TableCell s Controls collection. currentValue = gvr.Cells[sortColumnIndex].Text를 사용 하는 대신이 코드를 다음 코드로 바꿉니다.Instead of using currentValue = gvr.Cells[sortColumnIndex].Text, replace this code with the following:

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 컬렉션에 컨트롤이 있는지 확인 합니다.This code examines the sorted column TableCell for the current row to determine if there are any controls in the Controls collection. 이 있는 경우 첫 번째 컨트롤이 CheckBox 이면 currentValue 변수는 CheckBox의 Checked 속성에 따라 예 또는 아니요로 설정 됩니다.If there are, and the first control is a CheckBox, the currentValue variable is set to Yes or No, depending on the CheckBox s Checked property. 그렇지 않으면 TableCell s Text 속성에서 값을 가져옵니다.Otherwise, the value is taken from the TableCell s Text property. GridView에 있을 수 있는 모든 템플릿 필드의 정렬을 처리 하기 위해이 논리를 복제할 수 있습니다.This logic can be replicated to handle sorting for any TemplateFields that may exist in the GridView.

위의 코드 추가 기능을 사용 하면 더 이상 사용 되지 않는 CheckBoxField 정렬할 때 정렬 그룹 머리글이 표시 됩니다 (그림 7 참조).With the above code addition, the sort group headers are now present when sorting by the Discontinued CheckBoxField (see Figure 7).

CheckBoxField 정렬할 때 정렬 그룹 머리글이 현재 표시 The Sort Group Headers are Now Present When Sorting a CheckBoxField

그림 7: 이제 CheckBoxField을 정렬할 때 정렬 그룹 머리글이 표시 됩니다 (전체 크기 이미지를 보려면 클릭).Figure 7: The Sort Group Headers are Now Present When Sorting a CheckBoxField (Click to view full-size image)

Note

CategoryID, SupplierID또는 UnitPrice 필드에 대 한 NULL 데이터베이스 값을 포함 하는 제품이 있는 경우 이러한 값은 기본적으로 GridView에 빈 문자열로 표시 됩니다. 즉, NULL 값이 있는 해당 제품에 대 한 구분 기호 행 s 텍스트는 category와 같이 표시 됩니다. 즉, 범주 뒤에는 category: 음료와 같이 이름이 없습니다.If you have products with NULL database values for the CategoryID, SupplierID, or UnitPrice fields, those values will appear as empty strings in the GridView by default, meaning the separator row s text for those products with NULL values will read like Category: (that is, there s no name after Category: like with Category: Beverages ). 여기에 값을 표시 하려는 경우 BoundFields NullDisplayText 속성 을 표시 하려는 텍스트로 설정 하거나, currentValue를 구분 기호 행의 Text 속성에 할당할 때 Render 메서드에서 조건문을 추가할 수 있습니다.If you want a value displayed here you can either set the BoundFields NullDisplayText property to the text you want displayed or you can add a conditional statement in the Render method when assigning the currentValue to the separator row s Text property.

요약Summary

GridView에는 정렬 인터페이스를 사용자 지정 하기 위한 여러 가지 기본 제공 옵션이 포함 되어 있지 않습니다.The GridView does not include many built-in options for customizing the sorting interface. 그러나 약간의 하위 수준 코드를 사용 하는 경우 GridView의 컨트롤 계층 구조를 조정 하 여 더 사용자 지정 된 인터페이스를 만들 수 있습니다.However, with a bit of low-level code, it s possible to tweak the GridView s control hierarchy to create a more customized interface. 이 자습서에서는 정렬 가능한 GridView에 대 한 정렬 그룹 구분 기호 행을 추가 하 여 고유 그룹과 해당 그룹 경계를 보다 쉽게 식별 하는 방법을 살펴보았습니다.In this tutorial we saw how to add a sort group separator row for a sortable GridView, which more easily identifies the distinct groups and those groups boundaries. 사용자 지정 된 정렬 인터페이스의 추가 예를 보려면 Scott Guthrie s ASP.NET 2.0 GridView 정렬 팁과 트릭 블로그 항목을 확인 하세요.For additional examples of customized sorting interfaces, check out Scott Guthrie s A Few ASP.NET 2.0 GridView Sorting Tips and Tricks blog entry.

행복 한 프로그래밍Happy Programming!

저자 정보About the Author

Scott Mitchell(7 개의 ASP/ASP. NET books 및 4GuysFromRolla.com창립자)은 1998부터 Microsoft 웹 기술을 사용 하 여 작업 했습니다.Scott Mitchell, author of seven ASP/ASP.NET books and founder of 4GuysFromRolla.com, has been working with Microsoft Web technologies since 1998. Scott은 독립 컨설턴트, 강사 및 기록기로 작동 합니다.Scott works as an independent consultant, trainer, and writer. 최신 책은 24 시간 이내에 ASP.NET 2.0을 sams teach yourself것입니다.His latest book is Sams Teach Yourself ASP.NET 2.0 in 24 Hours. mitchell@4GuysFromRolla.com에 도달할 수 있습니다 .He can be reached at mitchell@4GuysFromRolla.com. 또는 블로그를 통해 http://ScottOnWriting.NET에서 찾을 수 있습니다.or via his blog, which can be found at http://ScottOnWriting.NET.