演练:在 Windows 窗体 DataGridView 控件中实现虚拟模式

如果要在 DataGridView 控件中显示非常大量的表格数据,可以将 VirtualMode 属性设置为 true,并显式管理控件与其数据存储区的交互。 这样,您便可以在这种情况下精细调整该控件的性能。

DataGridView 控件提供若干事件,您可以通过处理这些事件来与自定义数据存储区进行交互。 本演练引导您完成实现这些事件处理程序的过程。 为便于说明,本主题中的代码示例使用一个非常简单的数据源。 在生产设置中,通常仅将需要显示的行加载到缓存中,并处理 DataGridView 事件以便与缓存进行交互和更新缓存。 有关更多信息,请参见 在 Windows 窗体 DataGridView 控件中实现实时数据加载的虚拟模式

若要以单个列表的形式复制本主题中的代码,请参见 如何:在 Windows 窗体 DataGridView 控件中实现虚拟模式

创建窗体

实现虚拟模式

  1. 创建一个从 Form 派生并包含 DataGridView 控件的类。

    下面的代码包含一些简单的初始化操作。 它将声明一些将在后续步骤中使用的变量,提供 Main 方法,并在类的构造函数中提供一个简单的窗体布局。

    Imports System
    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
    
    
    ...
    
    
    
    End Class
    
    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)";
        }
    
    
    ...
    
    
    }
    
    #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:
    
    
    ...
    
    
    };
    
    int main()
    {
       Form1::Main();
    }
    
  2. 为窗体的 Load 事件实现一个处理程序,该处理程序初始化 DataGridView 控件并用示例值填充数据存储区。

    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
    
    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;
    }
    
       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;
       }
    
  3. CellValueNeeded 事件实现一个处理程序,从数据存储区或当前正在编辑的 Customer 对象中检索请求的单元格值。

    只要 DataGridView 控件需要绘制单元格,就会发生此事件。

    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
    
    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;
        }
    }
    
       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;
          }
       }
    
    
    
  4. CellValuePushed 事件实现一个处理程序,将编辑后的单元格值存储在代表已编辑的行的 Customer 对象中。 只要用户提交对单元格值的更改,就会发生此事件。

    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
    
    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.
            if (this.customerInEdit == null)
            {
                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;
        }
    }
    
       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;
          }
       }
    
    
    
  5. NewRowNeeded 事件实现一个处理程序,创建一个代表新建行的新 Customer 对象。

    只要用户进入新的记录行,就会发生此事件。

    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
    
    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;
    }
    
       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;
       }
    
    
    
  6. RowValidated 事件实现一个处理程序,将新的或修改后的行保存到数据存储区中。

    只要用户更改当前行,就会发生此事件。

    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
    
    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;
        }
    }
    
       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;
          }
       }
    
    
    
  7. RowDirtyStateNeeded 事件实现一个处理程序,指示当用户通过在编辑模式下按 Esc 两次或在非编辑模式下按 Esc 一次来指示行恢复时,是否发生 CancelRowEdit 事件。

    默认情况下,除非 RowDirtyStateNeeded 事件处理程序中的 QuestionEventArgs.Response 属性设置为 true,否则,只要当前行中有任何单元格被修改,就会在恢复行时发生 CancelRowEdit 事件。 如果提交范围是在运行时确定的,则此事件非常有用。

    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
    
    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;
        }
    }
    
       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. CancelRowEdit 事件实现一个处理程序,丢弃表示当前行的 Customer 对象的值。

    当用户通过在编辑模式下按 Esc 两次或在非编辑模式下按 Esc 一次来指示行恢复时,会发生此事件。 如果当前行中没有任何单元格被修改,或者,QuestionEventArgs.Response 属性的值已在 RowDirtyStateNeeded 事件处理程序中设置为 false,则不会发生此事件。

    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
    
    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;
        }
    }
    
       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;
          }
       }
    
    
    
  9. UserDeletingRow 事件实现一个处理程序,该事件将现有的 Customer 对象从数据存储区中删除,或丢弃表示新建行的未保存的 Customer 对象。

    只要用户通过单击行标题再按 Delete 键来删除行,就会发生此事件。

    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
    
    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;
        }
    }
    
    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;
       }
    }
    
  10. 实现一个简单的 Customers 类来表示此代码示例使用的数据项。

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

测试应用程序

现在可以测试窗体,以确保它的行为与预期相同。

测试窗体

  • 编译并运行应用程序。

    您将看到填入了三个客户记录的 DataGridView 控件。 您可以修改一行中的多个单元格的值,然后在编辑模式下按 Esc 两次或在非编辑模式下按 Esc 一次,将整行恢复为原始值。 当在控件中修改、添加或删除行时,数据存储区中的 Customer 对象也会相应地修改、添加或删除。

后续步骤

此应用程序使您可以基本了解在 DataGridView 控件中实现虚拟模式所必须处理的事件。 可以通过许多方法来改进这个简单的应用程序:

  • 实现用于缓存来自外部数据库的值的数据存储区。 此缓存应根据需要检索和丢弃值,以便仅仅包含必须显示的内容,从而确保只占用客户端计算机上的少量内存。

  • 根据实际要求精细调整数据存储区的性能。 例如,您可能要使用更大规模的缓存并将数据库查询的数目控制在最少,从而补偿慢速网络连接,而不是根据客户端计算机的内存限制进行调整。

有关缓存来自外部数据库的值的更多信息,请参见 如何:在 Windows 窗体 DataGridView 控件中实现实时数据加载的虚拟模式

请参见

任务

如何:在 Windows 窗体 DataGridView 控件中实现虚拟模式

参考

DataGridView

VirtualMode

CellValueNeeded

CellValuePushed

NewRowNeeded

RowValidated

RowDirtyStateNeeded

CancelRowEdit

UserDeletingRow

概念

缩放 Windows 窗体 DataGridView 控件的最佳做法

在 Windows 窗体 DataGridView 控件中实现实时数据加载的虚拟模式

其他资源

Windows 窗体 DataGridView 控件中的性能优化