영어로 읽기

다음을 통해 공유


연습: Windows Forms DataGridView 컨트롤에서 가상 모드 구현

DataGridView 컨트롤에 매우 많은 테이블 형식 데이터를 표시하려는 경우 VirtualMode 속성을 true로 설정하고 이 컨트롤과 해당 데이터 저장소의 상호 작용을 명시적으로 관리할 수 있습니다. 이렇게 하면 이 상황에서 컨트롤의 성능을 미세 조정할 수 있습니다.

DataGridView 컨트롤은 사용자 지정 데이터 저장소와 상호 작용하기 위해 처리할 수 있는 여러 이벤트를 제공합니다. 이 연습에서는 관련 이벤트 처리기를 구현하는 프로세스를 안내합니다. 이 항목의 코드 예제에서는 설명 목적으로 매우 간단한 데이터 원본을 사용합니다. 프로덕션 설정에서는 일반적으로 캐시에 표시해야 하는 행만 로드하며 DataGridView 이벤트를 처리하여 캐시와 상호 작용하고 캐시를 업데이트합니다. 자세한 내용은 Windows Forms DataGridView 컨트롤에서 Just-In-Time 데이터 로드를 사용하여 가상 모드 구현을 참조하세요.

이 항목의 코드를 단일 목록으로 복사하려면 방법: Windows Forms DataGridView 컨트롤에서 가상 모드 구현을 참조하세요.

폼 만들기

가상 모드를 구현하려면

  1. Form에서 파생되고 DataGridView 컨트롤을 포함하는 클래스를 만듭니다.

    다음 코드에는 일부 기본 초기화가 포함됩니다. 이 코드는 이후 단계에서 사용할 일부 변수를 선언하고, Main 메서드를 제공하고, 클래스 생성자에서 간단한 양식 레이아웃을 제공합니다.

    using System;
    using System.Windows.Forms;
    
    public class Form1 : Form
    {
        private DataGridView dataGridView1 = new DataGridView();
    
        // Declare an ArrayList to serve as the data store.
        private System.Collections.ArrayList customers =
            new System.Collections.ArrayList();
    
        // Declare a Customer object to store data for a row being edited.
        private Customer customerInEdit;
    
        // Declare a variable to store the index of a row being edited.
        // A value of -1 indicates that there is no row currently in edit.
        private int rowInEdit = -1;
    
        // Declare a variable to indicate the commit scope.
        // Set this value to false to use cell-level commit scope.
        private bool rowScopeCommit = true;
    
        [STAThreadAttribute()]
        public static void Main()
        {
            Application.Run(new Form1());
        }
    
        public Form1()
        {
            // Initialize the form.
            this.dataGridView1.Dock = DockStyle.Fill;
            this.Controls.Add(this.dataGridView1);
            this.Load += new EventHandler(Form1_Load);
            this.Text = "DataGridView virtual-mode demo (row-level commit scope)";
        }
    
    }
    
  2. DataGridView 컨트롤을 초기화하고 데이터 저장소를 샘플 값으로 채우는 양식의 Load 이벤트에 대한 처리기를 구현합니다.

    private void Form1_Load(object sender, EventArgs e)
    {
        // Enable virtual mode.
        this.dataGridView1.VirtualMode = true;
    
        // Connect the virtual-mode events to event handlers.
        this.dataGridView1.CellValueNeeded += new
            DataGridViewCellValueEventHandler(dataGridView1_CellValueNeeded);
        this.dataGridView1.CellValuePushed += new
            DataGridViewCellValueEventHandler(dataGridView1_CellValuePushed);
        this.dataGridView1.NewRowNeeded += new
            DataGridViewRowEventHandler(dataGridView1_NewRowNeeded);
        this.dataGridView1.RowValidated += new
            DataGridViewCellEventHandler(dataGridView1_RowValidated);
        this.dataGridView1.RowDirtyStateNeeded += new
            QuestionEventHandler(dataGridView1_RowDirtyStateNeeded);
        this.dataGridView1.CancelRowEdit += new
            QuestionEventHandler(dataGridView1_CancelRowEdit);
        this.dataGridView1.UserDeletingRow += new
            DataGridViewRowCancelEventHandler(dataGridView1_UserDeletingRow);
    
        // Add columns to the DataGridView.
        DataGridViewTextBoxColumn companyNameColumn = new
            DataGridViewTextBoxColumn();
        companyNameColumn.HeaderText = "Company Name";
        companyNameColumn.Name = "Company Name";
        DataGridViewTextBoxColumn contactNameColumn = new
            DataGridViewTextBoxColumn();
        contactNameColumn.HeaderText = "Contact Name";
        contactNameColumn.Name = "Contact Name";
        this.dataGridView1.Columns.Add(companyNameColumn);
        this.dataGridView1.Columns.Add(contactNameColumn);
        this.dataGridView1.AutoSizeColumnsMode =
            DataGridViewAutoSizeColumnsMode.DisplayedCells;
    
        // Add some sample entries to the data store.
        this.customers.Add(new Customer(
            "Bon app'", "Laurence Lebihan"));
        this.customers.Add(new Customer(
            "Bottom-Dollar Markets", "Elizabeth Lincoln"));
        this.customers.Add(new Customer(
            "B's Beverages", "Victoria Ashworth"));
    
        // Set the row count, including the row for new records.
        this.dataGridView1.RowCount = 4;
    }
    
  3. 현재 편집 중인 Customer 개체 또는 데이터 저장소에서 요청된 셀 값을 검색하는 CellValueNeeded 이벤트에 대한 처리기를 구현합니다.

    이 이벤트는 DataGridView 컨트롤이 셀을 그려야 할 때마다 발생합니다.

    private void dataGridView1_CellValueNeeded(object sender,
        System.Windows.Forms.DataGridViewCellValueEventArgs e)
    {
        // If this is the row for new records, no values are needed.
        if (e.RowIndex == this.dataGridView1.RowCount - 1) return;
    
        Customer customerTmp = null;
    
        // Store a reference to the Customer object for the row being painted.
        if (e.RowIndex == rowInEdit)
        {
            customerTmp = this.customerInEdit;
        }
        else
        {
            customerTmp = (Customer)this.customers[e.RowIndex];
        }
    
        // Set the cell value to paint using the Customer object retrieved.
        switch (this.dataGridView1.Columns[e.ColumnIndex].Name)
        {
            case "Company Name":
                e.Value = customerTmp.CompanyName;
                break;
    
            case "Contact Name":
                e.Value = customerTmp.ContactName;
                break;
        }
    }
    
  4. 편집된 행을 나타내는 Customer 개체에 편집된 셀 값을 저장하는 CellValuePushed 이벤트에 대한 처리기를 구현합니다. 이 이벤트는 사용자가 셀 값 변경을 커밋할 때마다 발생합니다.

    private void dataGridView1_CellValuePushed(object sender,
        System.Windows.Forms.DataGridViewCellValueEventArgs e)
    {
        Customer customerTmp = null;
    
        // Store a reference to the Customer object for the row being edited.
        if (e.RowIndex < this.customers.Count)
        {
            // If the user is editing a new row, create a new Customer object.
            this.customerInEdit ??= new Customer(
                ((Customer)this.customers[e.RowIndex]).CompanyName,
                ((Customer)this.customers[e.RowIndex]).ContactName);
            customerTmp = this.customerInEdit;
            this.rowInEdit = e.RowIndex;
        }
        else
        {
            customerTmp = this.customerInEdit;
        }
    
        // Set the appropriate Customer property to the cell value entered.
        String newValue = e.Value as String;
        switch (this.dataGridView1.Columns[e.ColumnIndex].Name)
        {
            case "Company Name":
                customerTmp.CompanyName = newValue;
                break;
    
            case "Contact Name":
                customerTmp.ContactName = newValue;
                break;
        }
    }
    
  5. 새로 만든 행을 나타내는 새 Customer 개체를 만드는 NewRowNeeded 이벤트에 대한 처리기를 구현합니다.

    이 이벤트는 사용자가 새 레코드의 행을 입력할 때마다 발생합니다.

    private void dataGridView1_NewRowNeeded(object sender,
        System.Windows.Forms.DataGridViewRowEventArgs e)
    {
        // Create a new Customer object when the user edits
        // the row for new records.
        this.customerInEdit = new Customer();
        this.rowInEdit = this.dataGridView1.Rows.Count - 1;
    }
    
  6. 새 행이나 수정된 행을 데이터 저장소에 저장하는 RowValidated 이벤트에 대한 처리기를 구현합니다.

    이 이벤트는 사용자가 현재 행을 변경할 때마다 발생합니다.

    private void dataGridView1_RowValidated(object sender,
        System.Windows.Forms.DataGridViewCellEventArgs e)
    {
        // Save row changes if any were made and release the edited
        // Customer object if there is one.
        if (e.RowIndex >= this.customers.Count &&
            e.RowIndex != this.dataGridView1.Rows.Count - 1)
        {
            // Add the new Customer object to the data store.
            this.customers.Add(this.customerInEdit);
            this.customerInEdit = null;
            this.rowInEdit = -1;
        }
        else if (this.customerInEdit != null &&
            e.RowIndex < this.customers.Count)
        {
            // Save the modified Customer object in the data store.
            this.customers[e.RowIndex] = this.customerInEdit;
            this.customerInEdit = null;
            this.rowInEdit = -1;
        }
        else if (this.dataGridView1.ContainsFocus)
        {
            this.customerInEdit = null;
            this.rowInEdit = -1;
        }
    }
    
  7. 사용자가 ESC 키를 편집 모드에서 두 번 누르거나 편집 모드 외부에 한 번 눌러 행 되돌리기 신호를 보낼 때 CancelRowEdit 이벤트가 발생할지 여부를 나타내는 RowDirtyStateNeeded 이벤트에 대한 처리기를 구현합니다.

    기본적으로 QuestionEventArgs.Response 속성이 RowDirtyStateNeeded 이벤트 처리기에서 true로 설정되지 않는 한 CancelRowEdit 이벤트는 현재 행의 셀이 수정된 경우 행을 되돌릴 때 발생합니다. 이 이벤트는 커밋 범위가 런타임에 결정되는 경우에 유용합니다.

    private void dataGridView1_RowDirtyStateNeeded(object sender,
        System.Windows.Forms.QuestionEventArgs e)
    {
        if (!rowScopeCommit)
        {
            // In cell-level commit scope, indicate whether the value
            // of the current cell has been modified.
            e.Response = this.dataGridView1.IsCurrentCellDirty;
        }
    }
    
  8. 현재 행을 나타내는 Customer 개체의 값을 무시하는 CancelRowEdit 이벤트에 대한 처리기를 구현합니다.

    이 이벤트는 사용자가 ESC 키를 편집 모드에서 두 번 누르거나 편집 모드 외부에서 한 번 눌러 행 되돌리기 신호를 보낼 때 발생합니다. 현재 행에 있는 셀이 수정되지 않았거나 QuestionEventArgs.Response 속성 값이 RowDirtyStateNeeded 이벤트 처리기에서 false로 설정된 경우에는 이 이벤트가 발생하지 않습니다.

    private void dataGridView1_CancelRowEdit(object sender,
        System.Windows.Forms.QuestionEventArgs e)
    {
        if (this.rowInEdit == this.dataGridView1.Rows.Count - 2 &&
            this.rowInEdit == this.customers.Count)
        {
            // If the user has canceled the edit of a newly created row,
            // replace the corresponding Customer object with a new, empty one.
            this.customerInEdit = new Customer();
        }
        else
        {
            // If the user has canceled the edit of an existing row,
            // release the corresponding Customer object.
            this.customerInEdit = null;
            this.rowInEdit = -1;
        }
    }
    
  9. 데이터 저장소에서 기존 Customer 개체를 삭제하거나 새로 만든 행을 나타내는 저장되지 않은 Customer 개체를 무시하는 UserDeletingRow 이벤트에 대한 처리기를 구현합니다.

    이 이벤트는 사용자가 행 머리글을 클릭하고 Delete 키를 눌러 행을 삭제할 때마다 발생합니다.

    private void dataGridView1_UserDeletingRow(object sender,
        System.Windows.Forms.DataGridViewRowCancelEventArgs e)
    {
        if (e.Row.Index < this.customers.Count)
        {
            // If the user has deleted an existing row, remove the
            // corresponding Customer object from the data store.
            this.customers.RemoveAt(e.Row.Index);
        }
    
        if (e.Row.Index == this.rowInEdit)
        {
            // If the user has deleted a newly created row, release
            // the corresponding Customer object.
            this.rowInEdit = -1;
            this.customerInEdit = null;
        }
    }
    
  10. 이 코드 예제에서 사용하는 데이터 항목을 나타내는 간단한 Customers 클래스를 구현합니다.

    public class Customer
    {
        private String companyNameValue;
        private String contactNameValue;
    
        public Customer()
        {
            // Leave fields empty.
        }
    
        public Customer(String companyName, String contactName)
        {
            companyNameValue = companyName;
            contactNameValue = contactName;
        }
    
        public String CompanyName
        {
            get
            {
                return companyNameValue;
            }
            set
            {
                companyNameValue = value;
            }
        }
    
        public String ContactName
        {
            get
            {
                return contactNameValue;
            }
            set
            {
                contactNameValue = value;
            }
        }
    }
    

애플리케이션 테스트

이제 양식을 테스트하여 예상대로 동작하는지 확인할 수 있습니다.

양식 테스트

  • 애플리케이션을 컴파일하고 실행합니다.

    세 개의 고객 레코드로 채워진 DataGridView 컨트롤이 표시됩니다. 한 행에 있는 여러 셀의 값을 수정하고 ESC를 편집 모드에서 두 번 누르고 편집 모드 외부에서 한 번 눌러 전체 행을 원래 값으로 되돌릴 수 있습니다. 컨트롤에서 행을 수정, 추가 또는 삭제하면 데이터 저장소의 Customer 개체도 수정, 추가 또는 삭제됩니다.

다음 단계

이 애플리케이션은 DataGridView 컨트롤에서 가상 모드를 구현하기 위해 처리해야 하는 이벤트에 대한 기본적인 이해를 제공합니다. 다음과 같은 여러 가지 방법으로 이 기본 애플리케이션을 개선할 수 있습니다.

  • 외부 데이터베이스의 값을 캐시하는 데이터 저장소를 구현합니다. 캐시는 클라이언트 컴퓨터에서 적은 양의 메모리를 사용하는 동안 표시에 필요한 항목만 포함되도록 필요에 따라 값을 검색하고 삭제해야 합니다.

  • 요구 사항에 따라 데이터 저장소의 성능을 미세 조정합니다. 예를 들어, 더 큰 캐시 크기를 사용하고 데이터베이스 쿼리 수를 최소화하여 클라이언트-컴퓨터 메모리를 제한하기 보다는 느려진 네트워크 연결을 해결하려고 할 수 있습니다.

외부 데이터베이스에서 값을 캐시하는 방법에 관한 자세한 내용은 방법: Windows Forms DataGridView 컨트롤에서 Just-In-Time 데이터 로드를 사용하여 가상 모드 구현을 참조하세요.

참고 항목