연습: Windows Forms DataGridView 컨트롤에서 가상 모드 구현Walkthrough: Implementing Virtual Mode in the Windows Forms DataGridView Control

DataGridView 컨트롤에 매우 많은 양의 테이블 형식 데이터를 표시 하려는 경우 VirtualMode 속성을 true로 설정 하 고 컨트롤의 데이터 저장소와의 상호 작용을 명시적으로 관리할 수 있습니다.When you want to display very large quantities of tabular data in a DataGridView control, you can set the VirtualMode property to true and explicitly manage the control's interaction with its data store. 이렇게 하면이 상황에서 컨트롤의 성능을 세밀 하 게 조정할 수 있습니다.This lets you fine-tune the performance of the control in this situation.

DataGridView 컨트롤은 사용자 지정 데이터 저장소와 상호 작용 하기 위해 처리할 수 있는 몇 가지 이벤트를 제공 합니다.The DataGridView control provides several events that you can handle to interact with a custom data store. 이 연습에서는 이러한 이벤트 처리기를 구현 하는 과정을 안내 합니다.This walkthrough guides you through the process of implementing these event handlers. 이 항목의 코드 예제에서는 매우 간단한 데이터 원본을 설명 하기 위해 사용 합니다.The code example in this topic uses a very simple data source for illustration purposes. 프로덕션 설정에서는 일반적으로 캐시에 표시 해야 하는 행만 로드 하 고 캐시를 상호 작용 하 고 업데이트 하는 DataGridView 이벤트를 처리 합니다.In a production setting, you will typically load only the rows you need to display into a cache, and handle DataGridView events to interact with and update the cache. 자세한 내용은 Windows Forms DataGridView 컨트롤에서 Just-in-time 데이터 로드를 사용 하 여 가상 모드 구현 을 참조 하세요.For more information, see Implementing Virtual Mode with Just-In-Time Data Loading in the Windows Forms DataGridView Control

이 항목의 코드를 단일 목록으로 복사 하려면 방법: DataGridView 컨트롤 Windows Forms에서 가상 모드 구현을 참조 하세요.To copy the code in this topic as a single listing, see How to: Implement Virtual Mode in the Windows Forms DataGridView Control.

폼 만들기Creating the Form

가상 모드를 구현 하려면To implement virtual mode

  1. Form에서 파생 되 고 DataGridView 컨트롤을 포함 하는 클래스를 만듭니다.Create a class that derives from Form and contains a DataGridView control.

    다음 코드에는 몇 가지 기본 초기화가 포함 되어 있습니다.The following code contains some basic initialization. 이후 단계에서 사용 되는 일부 변수를 선언 하 고, Main 메서드를 제공 하며, 클래스 생성자에서 간단한 양식 레이아웃을 제공 합니다.It declares some variables that will be used in later steps, provides a Main method, and provides a simple form layout in the class constructor.

    #using <System.Drawing.dll>
    #using <System.dll>
    #using <System.Windows.Forms.dll>
    
    using namespace System;
    using namespace System::Windows::Forms;
    
    public ref class Customer
    {
    private:
       String^ companyNameValue;
       String^ contactNameValue;
    
    public:
       Customer()
       {
          
          // Leave fields empty.
       }
    
       Customer( String^ companyName, String^ contactName )
       {
          companyNameValue = companyName;
          contactNameValue = contactName;
       }
    
    
       property String^ CompanyName 
       {
          String^ get()
          {
             return companyNameValue;
          }
    
          void set( String^ value )
          {
             companyNameValue = value;
          }
    
       }
    
       property String^ ContactName 
       {
          String^ get()
          {
             return contactNameValue;
          }
    
          void set( String^ value )
          {
             contactNameValue = value;
          }
    
       }
    
    };
    
    public ref class Form1: public Form
    {
    private:
       DataGridView^ dataGridView1;
    
       // Declare an ArrayList to serve as the data store. 
       System::Collections::ArrayList^ customers;
    
       // Declare a Customer object to store data for a row being edited.
       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. 
       int rowInEdit;
    
       // Declare a variable to indicate the commit scope. 
       // Set this value to false to use cell-level commit scope. 
       bool rowScopeCommit;
    
    public:
       static void Main()
       {
          Application::Run( gcnew Form1 );
       }
    
       Form1()
       {
          dataGridView1 = gcnew DataGridView;
          customers = gcnew System::Collections::ArrayList;
          rowInEdit = -1;
          rowScopeCommit = true;
          
          // Initialize the form.
          this->dataGridView1->Dock = DockStyle::Fill;
          this->Controls->Add( this->dataGridView1 );
          this->Load += gcnew EventHandler( this, &Form1::Form1_Load );
       }
    
    private:
    
    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)";
        }
    
    Imports System.Windows.Forms
    
    Public Class Form1
        Inherits Form
    
        Private WithEvents dataGridView1 As New DataGridView()
    
        ' Declare an ArrayList to serve as the data store. 
        Private customers As New System.Collections.ArrayList()
    
        ' Declare a Customer object to store data for a row being edited.
        Private customerInEdit As Customer
    
        ' 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 rowInEdit As Integer = -1
    
        ' Declare a variable to indicate the commit scope. 
        ' Set this value to false to use cell-level commit scope. 
        Private rowScopeCommit As Boolean = True
    
        <STAThreadAttribute()> _
        Public Shared Sub Main()
            Application.Run(New Form1())
        End Sub
    
        Public Sub New()
            ' Initialize the form.
            Me.dataGridView1.Dock = DockStyle.Fill
            Me.Controls.Add(Me.dataGridView1)
            Me.Text = "DataGridView virtual-mode demo (row-level commit scope)"
        End Sub
    
    };
    
    int main()
    {
       Form1::Main();
    }
    
    }
    
    
    End Class
    
  2. DataGridView 컨트롤을 초기화 하 고 데이터 저장소를 샘플 값으로 채우는 폼의 Load 이벤트에 대 한 처리기를 구현 합니다.Implement a handler for your form's Load event that initializes the DataGridView control and populates the data store with sample values.

    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 += gcnew
           DataGridViewCellValueEventHandler( this, &Form1::dataGridView1_CellValueNeeded );
       this->dataGridView1->CellValuePushed += gcnew
           DataGridViewCellValueEventHandler( this, &Form1::dataGridView1_CellValuePushed );
       this->dataGridView1->NewRowNeeded += gcnew
           DataGridViewRowEventHandler( this, &Form1::dataGridView1_NewRowNeeded );
       this->dataGridView1->RowValidated += gcnew
           DataGridViewCellEventHandler( this, &Form1::dataGridView1_RowValidated );
       this->dataGridView1->RowDirtyStateNeeded += gcnew
           QuestionEventHandler( this, &Form1::dataGridView1_RowDirtyStateNeeded );
       this->dataGridView1->CancelRowEdit += gcnew
           QuestionEventHandler( this, &Form1::dataGridView1_CancelRowEdit );
       this->dataGridView1->UserDeletingRow += gcnew
           DataGridViewRowCancelEventHandler( this, &Form1::dataGridView1_UserDeletingRow );
       
       // Add columns to the DataGridView.
       DataGridViewTextBoxColumn^ companyNameColumn = gcnew DataGridViewTextBoxColumn;
       companyNameColumn->HeaderText = L"Company Name";
       companyNameColumn->Name = L"Company Name";
       DataGridViewTextBoxColumn^ contactNameColumn = gcnew DataGridViewTextBoxColumn;
       contactNameColumn->HeaderText = L"Contact Name";
       contactNameColumn->Name = L"Contact Name";
       this->dataGridView1->Columns->Add( companyNameColumn );
       this->dataGridView1->Columns->Add( contactNameColumn );
       this->dataGridView1->AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode::AllCells;
       
       // Add some sample entries to the data store. 
       this->customers->Add( gcnew Customer( L"Bon app'",L"Laurence Lebihan" ) );
       this->customers->Add( gcnew Customer( L"Bottom-Dollar Markets",L"Elizabeth Lincoln" ) );
       this->customers->Add( gcnew Customer( L"B's Beverages",L"Victoria Ashworth" ) );
       
       // Set the row count, including the row for new records.
       this->dataGridView1->RowCount = 4;
    }
    
    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.AllCells;
    
        // 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;
    }
    
    Private Sub Form1_Load(ByVal sender As Object, ByVal e As EventArgs) _
        Handles Me.Load
    
        ' Enable virtual mode.
        Me.dataGridView1.VirtualMode = True
    
        ' Add columns to the DataGridView.
        Dim companyNameColumn As New DataGridViewTextBoxColumn()
        With companyNameColumn
            .HeaderText = "Company Name"
            .Name = "Company Name"
        End With
        Dim contactNameColumn As New DataGridViewTextBoxColumn()
        With contactNameColumn
            .HeaderText = "Contact Name"
            .Name = "Contact Name"
        End With
        Me.dataGridView1.Columns.Add(companyNameColumn)
        Me.dataGridView1.Columns.Add(contactNameColumn)
        Me.dataGridView1.AutoSizeColumnsMode = _
            DataGridViewAutoSizeColumnsMode.AllCells
    
        ' Add some sample entries to the data store. 
        Me.customers.Add(New Customer("Bon app'", "Laurence Lebihan"))
        Me.customers.Add(New Customer("Bottom-Dollar Markets", _
            "Elizabeth Lincoln"))
        Me.customers.Add(New Customer("B's Beverages", "Victoria Ashworth"))
    
        ' Set the row count, including the row for new records.
        Me.dataGridView1.RowCount = 4
    
    End Sub
    
  3. 현재 편집 중인 데이터 저장소 또는 Customer 개체에서 요청 된 셀 값을 검색 하는 CellValueNeeded 이벤트에 대 한 처리기를 구현 합니다.Implement a handler for the CellValueNeeded event that retrieves the requested cell value from the data store or the Customer object currently in edit.

    이 이벤트는 DataGridView 컨트롤이 셀을 그려야 할 때마다 발생 합니다.This event occurs whenever the DataGridView control needs to paint a cell.

    void dataGridView1_CellValueNeeded( Object^ /*sender*/,
        System::Windows::Forms::DataGridViewCellValueEventArgs^ e )
    {
       Customer^ customerTmp = nullptr;
       
       // Store a reference to the Customer object for the row being painted.
       if ( e->RowIndex == rowInEdit )
       {
          customerTmp = this->customerInEdit;
       }
       else
       {
          customerTmp = dynamic_cast<Customer^>(this->customers[ e->RowIndex ]);
       }
       
       // Set the cell value to paint using the Customer object retrieved.
       int switchcase = 0;
       if ( (this->dataGridView1->Columns[ e->ColumnIndex ]->Name)->Equals( L"Company Name" ) )
             switchcase = 1;
       else
       if ( (this->dataGridView1->Columns[ e->ColumnIndex ]->Name)->Equals( L"Contact Name" ) )
             switchcase = 2;
    
    
       switch ( switchcase )
       {
          case 1:
             e->Value = customerTmp->CompanyName;
             break;
    
          case 2:
             e->Value = customerTmp->ContactName;
             break;
       }
    }
    
    
    
    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;
        }
    }
    
    Private Sub dataGridView1_CellValueNeeded(ByVal sender As Object, _
        ByVal e As System.Windows.Forms.DataGridViewCellValueEventArgs) _
        Handles dataGridView1.CellValueNeeded
    
        ' If this is the row for new records, no values are needed.
        If e.RowIndex = Me.dataGridView1.RowCount - 1 Then
            Return
        End If
    
        Dim customerTmp As Customer = Nothing
    
        ' Store a reference to the Customer object for the row being painted.
        If e.RowIndex = rowInEdit Then
            customerTmp = Me.customerInEdit
        Else
            customerTmp = CType(Me.customers(e.RowIndex), Customer)
        End If
    
        ' Set the cell value to paint using the Customer object retrieved.
        Select Case Me.dataGridView1.Columns(e.ColumnIndex).Name
            Case "Company Name"
                e.Value = customerTmp.CompanyName
    
            Case "Contact Name"
                e.Value = customerTmp.ContactName
        End Select
    
    End Sub
    
  4. 편집 된 행을 나타내는 Customer 개체에 편집 된 셀 값을 저장 하는 CellValuePushed 이벤트에 대 한 처리기를 구현 합니다.Implement a handler for the CellValuePushed event that stores an edited cell value in the Customer object representing the edited row. 이 이벤트는 사용자가 셀 값 변경 내용을 커밋할 때마다 발생 합니다.This event occurs whenever the user commits a cell value change.

    void dataGridView1_CellValuePushed( Object^ /*sender*/,
        System::Windows::Forms::DataGridViewCellValueEventArgs^ e )
    {
       Customer^ customerTmp = nullptr;
       
       // 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.
          if ( this->customerInEdit == nullptr )
          {
             this->customerInEdit = gcnew Customer(
                 (dynamic_cast<Customer^>(this->customers[ e->RowIndex ]))->CompanyName,
                 (dynamic_cast<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.
       int switchcase = 0;
       if ( (this->dataGridView1->Columns[ e->ColumnIndex ]->Name)->Equals( L"Company Name" ) )
             switchcase = 1;
       else
       if ( (this->dataGridView1->Columns[ e->ColumnIndex ]->Name)->Equals( L"Contact Name" ) )
             switchcase = 2;
    
    
       switch ( switchcase )
       {
          case 1:
             customerTmp->CompanyName = dynamic_cast<String^>(e->Value);
             break;
    
          case 2:
             customerTmp->ContactName = dynamic_cast<String^>(e->Value);
             break;
       }
    }
    
    
    
    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;
        }
    }
    
    Private Sub dataGridView1_CellValuePushed(ByVal sender As Object, _
        ByVal e As System.Windows.Forms.DataGridViewCellValueEventArgs) _
        Handles dataGridView1.CellValuePushed
    
        Dim customerTmp As Customer = Nothing
    
        ' Store a reference to the Customer object for the row being edited.
        If e.RowIndex < Me.customers.Count Then
    
            ' If the user is editing a new row, create a new Customer object.
            If Me.customerInEdit Is Nothing Then
                Me.customerInEdit = New Customer( _
                    CType(Me.customers(e.RowIndex), Customer).CompanyName, _
                    CType(Me.customers(e.RowIndex), Customer).ContactName)
            End If
            customerTmp = Me.customerInEdit
            Me.rowInEdit = e.RowIndex
    
        Else
            customerTmp = Me.customerInEdit
        End If
    
        ' Set the appropriate Customer property to the cell value entered.
        Dim newValue As String = TryCast(e.Value, String)
        Select Case Me.dataGridView1.Columns(e.ColumnIndex).Name
            Case "Company Name"
                customerTmp.CompanyName = newValue
            Case "Contact Name"
                customerTmp.ContactName = newValue
        End Select
    
    End Sub
    
  5. 새로 만든 행을 나타내는 새 Customer 개체를 만드는 NewRowNeeded 이벤트에 대 한 처리기를 구현 합니다.Implement a handler for the NewRowNeeded event that creates a new Customer object representing a newly created row.

    이 이벤트는 사용자가 새 레코드에 대 한 행을 입력할 때마다 발생 합니다.This event occurs whenever the user enters the row for new records.

    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 = gcnew Customer;
       this->rowInEdit = this->dataGridView1->Rows->Count - 1;
    }
    
    
    
    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;
    }
    
    Private Sub dataGridView1_NewRowNeeded(ByVal sender As Object, _
        ByVal e As System.Windows.Forms.DataGridViewRowEventArgs) _
        Handles dataGridView1.NewRowNeeded
    
        ' Create a new Customer object when the user edits
        ' the row for new records.
        Me.customerInEdit = New Customer()
        Me.rowInEdit = Me.dataGridView1.Rows.Count - 1
    
    End Sub
    
  6. 새 행 또는 수정 된 행을 데이터 저장소에 저장 하는 RowValidated 이벤트에 대 한 처리기를 구현 합니다.Implement a handler for the RowValidated event that saves new or modified rows to the data store.

    이 이벤트는 사용자가 현재 행을 변경할 때마다 발생 합니다.This event occurs whenever the user changes the current row.

    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 = nullptr;
          this->rowInEdit = -1;
       }
       else
       if ( this->customerInEdit != nullptr && e->RowIndex < this->customers->Count )
       {
          
          // Save the modified Customer object in the data store.
          this->customers[ e->RowIndex ] = this->customerInEdit;
          this->customerInEdit = nullptr;
          this->rowInEdit = -1;
       }
       else
       if ( this->dataGridView1->ContainsFocus )
       {
          this->customerInEdit = nullptr;
          this->rowInEdit = -1;
       }
    }
    
    
    
    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;
        }
    }
    
    Private Sub dataGridView1_RowValidated(ByVal sender As Object, _
        ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) _
        Handles dataGridView1.RowValidated
    
        ' Save row changes if any were made and release the edited 
        ' Customer object if there is one.
        If e.RowIndex >= Me.customers.Count AndAlso _
            e.RowIndex <> Me.dataGridView1.Rows.Count - 1 Then
    
            ' Add the new Customer object to the data store.
            Me.customers.Add(Me.customerInEdit)
            Me.customerInEdit = Nothing
            Me.rowInEdit = -1
    
        ElseIf (Me.customerInEdit IsNot Nothing) AndAlso _
            e.RowIndex < Me.customers.Count Then
    
            ' Save the modified Customer object in the data store.
            Me.customers(e.RowIndex) = Me.customerInEdit
            Me.customerInEdit = Nothing
            Me.rowInEdit = -1
    
        ElseIf Me.dataGridView1.ContainsFocus Then
    
            Me.customerInEdit = Nothing
            Me.rowInEdit = -1
    
        End If
    
    End Sub
    
  7. 사용자가 편집 모드에서 ESC 키를 두 번 누르거나 편집 모드 외부에서 변경할지 때 CancelRowEdit 이벤트가 발생 하는지 여부를 나타내는 RowDirtyStateNeeded 이벤트에 대 한 처리기를 구현 합니다.Implement a handler for the RowDirtyStateNeeded event that indicates whether the CancelRowEdit event will occur when the user signals row reversion by pressing ESC twice in edit mode or once outside of edit mode.

    기본적으로 CancelRowEdit QuestionEventArgs.Response 속성이 RowDirtyStateNeeded 이벤트 처리기에서 true로 설정 되지 않은 경우 현재 행의 셀이 수정 되 면 행 변경할지 때 발생 합니다.By default, CancelRowEdit occurs upon row reversion when any cells in the current row have been modified unless the QuestionEventArgs.Response property is set to true in the RowDirtyStateNeeded event handler. 이 이벤트는 커밋 범위가 런타임에 결정 되는 경우에 유용 합니다.This event is useful when the commit scope is determined at run time.

    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;
       }
    }
    
    
    
    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;
        }
    }
    
    Private Sub dataGridView1_RowDirtyStateNeeded(ByVal sender As Object, _
        ByVal e As System.Windows.Forms.QuestionEventArgs) _
        Handles dataGridView1.RowDirtyStateNeeded
    
        If Not rowScopeCommit Then
    
            ' In cell-level commit scope, indicate whether the value
            ' of the current cell has been modified.
            e.Response = Me.dataGridView1.IsCurrentCellDirty
    
        End If
    
    End Sub
    
  8. 현재 행을 나타내는 Customer 개체의 값을 삭제 하는 CancelRowEdit 이벤트에 대 한 처리기를 구현 합니다.Implement a handler for the CancelRowEdit event that discards the values of the Customer object representing the current row.

    사용자가 편집 모드에서 ESC 키를 두 번 누르거나 편집 모드 외부에서 변경할지 때이 이벤트가 발생 합니다.This event occurs when the user signals row reversion by pressing ESC twice in edit mode or once outside of edit mode. 현재 행의 셀이 수정 되지 않았거나 QuestionEventArgs.Response 속성 값이 RowDirtyStateNeeded 이벤트 처리기에서 false로 설정 된 경우에는이 이벤트가 발생 하지 않습니다.This event does not occur if no cells in the current row have been modified or if the value of the QuestionEventArgs.Response property has been set to false in a RowDirtyStateNeeded event handler.

    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 = gcnew Customer;
       }
       else
       {
          
          // If the user has canceled the edit of an existing row, 
          // release the corresponding Customer object.
          this->customerInEdit = nullptr;
          this->rowInEdit = -1;
       }
    }
    
    
    
    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;
        }
    }
    
    Private Sub dataGridView1_CancelRowEdit(ByVal sender As Object, _
        ByVal e As System.Windows.Forms.QuestionEventArgs) _
        Handles dataGridView1.CancelRowEdit
    
        If Me.rowInEdit = Me.dataGridView1.Rows.Count - 2 AndAlso _
            Me.rowInEdit = Me.customers.Count Then
    
            ' If the user has canceled the edit of a newly created row, 
            ' replace the corresponding Customer object with a new, empty one.
            Me.customerInEdit = New Customer()
    
        Else
    
            ' If the user has canceled the edit of an existing row, 
            ' release the corresponding Customer object.
            Me.customerInEdit = Nothing
            Me.rowInEdit = -1
    
        End If
    
    End Sub
    
  9. 데이터 저장소에서 기존 Customer 개체를 삭제 하거나 새로 만든 행을 나타내는 저장 되지 않은 Customer 개체를 삭제 하는 UserDeletingRow 이벤트에 대 한 처리기를 구현 합니다.Implement a handler for the UserDeletingRow event that deletes an existing Customer object from the data store or discards an unsaved Customer object representing a newly created row.

    이 이벤트는 사용자가 행 머리글을 클릭 하 고 DELETE 키를 눌러 행을 삭제할 때마다 발생 합니다.This event occurs whenever the user deletes a row by clicking a row header and pressing the DELETE key.

    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 = nullptr;
       }
    }
    
    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;
        }
    }
    
    Private Sub dataGridView1_UserDeletingRow(ByVal sender As Object, _
        ByVal e As System.Windows.Forms.DataGridViewRowCancelEventArgs) _
        Handles dataGridView1.UserDeletingRow
    
        If e.Row.Index < Me.customers.Count Then
    
            ' If the user has deleted an existing row, remove the 
            ' corresponding Customer object from the data store.
            Me.customers.RemoveAt(e.Row.Index)
    
        End If
    
        If e.Row.Index = Me.rowInEdit Then
    
            ' If the user has deleted a newly created row, release
            ' the corresponding Customer object. 
            Me.rowInEdit = -1
            Me.customerInEdit = Nothing
    
        End If
    
    End Sub
    
  10. 이 코드 예제에서 사용 하는 데이터 항목을 나타내는 간단한 Customers 클래스를 구현 합니다.Implement a simple Customers class to represent the data items used by this code example.

    public ref class Customer
    {
    private:
       String^ companyNameValue;
       String^ contactNameValue;
    
    public:
       Customer()
       {
          
          // Leave fields empty.
       }
    
       Customer( String^ companyName, String^ contactName )
       {
          companyNameValue = companyName;
          contactNameValue = contactName;
       }
    
    
       property String^ CompanyName 
       {
          String^ get()
          {
             return companyNameValue;
          }
    
          void set( String^ value )
          {
             companyNameValue = value;
          }
    
       }
    
       property String^ ContactName 
       {
          String^ get()
          {
             return contactNameValue;
          }
    
          void set( String^ value )
          {
             contactNameValue = value;
          }
    
       }
    
    };
    
    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;
            }
        }
    }
    
    Public Class Customer
    
        Private companyNameValue As String
        Private contactNameValue As String
    
        Public Sub New()
            ' Leave fields empty.
        End Sub
    
        Public Sub New(ByVal companyName As String, ByVal contactName As String)
            companyNameValue = companyName
            contactNameValue = contactName
        End Sub
    
        Public Property CompanyName() As String
            Get
                Return companyNameValue
            End Get
            Set(ByVal value As String)
                companyNameValue = value
            End Set
        End Property
    
        Public Property ContactName() As String
            Get
                Return contactNameValue
            End Get
            Set(ByVal value As String)
                contactNameValue = value
            End Set
        End Property
    
    End Class
    

애플리케이션 테스트Testing the Application

이제 폼을 테스트 하 여 예상 대로 동작 하는지 확인할 수 있습니다.You can now test the form to make sure it behaves as expected.

폼을 테스트 하려면To test the form

  • 애플리케이션을 컴파일하고 실행합니다.Compile and run the application.

    3 개의 고객 레코드로 채워진 DataGridView 컨트롤이 표시 됩니다.You will see a DataGridView control populated with three customer records. 행에서 여러 셀의 값을 수정 하 고 편집 모드에서 ESC 키를 두 번 누르고 편집 모드 외부에서 한 번을 눌러 전체 행을 원래 값으로 되돌릴 수 있습니다.You can modify the values of multiple cells in a row and press ESC twice in edit mode and once outside of edit mode to revert the entire row to its original values. 컨트롤에서 행을 수정, 추가 또는 삭제 하는 경우 데이터 저장소의 Customer 개체도 수정, 추가 또는 삭제 됩니다.When you modify, add, or delete rows in the control, Customer objects in the data store are modified, added, or deleted as well.

다음 단계Next Steps

이 응용 프로그램은 DataGridView 컨트롤에서 가상 모드를 구현 하기 위해 처리 해야 하는 이벤트에 대 한 기본적인 이해를 제공 합니다.This application gives you a basic understanding of the events you must handle to implement virtual mode in the DataGridView control. 여러 가지 방법으로이 기본 응용 프로그램을 향상 시킬 수 있습니다.You can improve this basic application in a number of ways:

  • 외부 데이터베이스의 값을 캐시 하는 데이터 저장소를 구현 합니다.Implement a data store that caches values from an external database. 캐시는 클라이언트 컴퓨터에서 적은 양의 메모리를 사용 하는 동안 표시에 필요한 항목만 포함 하도록 필요에 따라 값을 검색 하 고 삭제 해야 합니다.The cache should retrieve and discard values as necessary so that it only contains what is necessary for display while consuming a small amount of memory on the client computer.

  • 요구 사항에 따라 데이터 저장소의 성능을 미세 조정 합니다.Fine-tune the performance of the data store depending on your requirements. 예를 들어 더 큰 캐시 크기를 사용 하 고 데이터베이스 쿼리 수를 최소화 하 여 클라이언트 컴퓨터 메모리 제한 보다 저속 네트워크 연결을 보정 하려고 할 수 있습니다.For example, you might want to compensate for slow network connections rather than client-computer memory limitations by using a larger cache size and minimizing the number of database queries.

외부 데이터베이스에서 값을 캐싱하는 방법에 대 한 자세한 내용은 방법: Windows Forms DataGridView 컨트롤에서 Just-in-time 데이터 로드를 사용 하 여 가상 모드 구현을 참조 하세요.For more information about caching values from an external database, see How to: Implement Virtual Mode with Just-In-Time Data Loading in the Windows Forms DataGridView Control.

참고 항목See also