共用方式為


逐步解說:在 Windows Form DataGridView 控制項中實作虛擬模式

當您想要在 DataGridView 控制項中顯示非常大量的表格式資料時,您可以將 VirtualMode 屬性設定為 true,並明確管理控制項與其資料存放區的互動。 這可讓您在此情況中微調控制項的效能。

DataGridView 控制項提供數個事件,可讓您用來與自訂資料存放區互動。 本逐步解說會引導您完成實作這些事件處理常式的程序。 為了提供說明,本主題中的程式碼範例會使用非常簡單的資料來源。 在生產設定中,您通常只會將需要顯示的資料列載入至快取,並處理 DataGridView 事件以與快取互動並更新快取。 如需詳細資訊,請參閱在 Windows Forms DataGridView 控制項中使用 Just-In-Time 資料載入實作虛擬模式

若要將本主題中的程式碼複製為單一清單,請參閱操作說明:在 Windows Forms DataGridView 控制項中實作虛擬模式

建立表單

實作虛擬模式

  1. 建立衍生自 Form 的類別,並包含 DataGridView 控制項。

    下列程式碼包含一些基本初始化。 它會宣告一些將在後續步驟中使用的變數,提供 Main 方法,並在類別建構函式中提供簡單的表單配置。

    #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. 實作表單的 Load 事件的處理常式,初始化 DataGridView 控制項並以範例值填入資料存放區。

    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::DisplayedCells;
       
       // 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.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;
    }
    
    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.DisplayedCells
    
        ' 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. 實作 CellValueNeeded 事件的處理常式,從資料存放區或目前編輯的 Customer 物件擷取要求的儲存格值。

    每當 DataGridView 控制項需要繪製儲存格時,就會發生此事件。

    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. 實作 CellValuePushed 事件的處理常式,將已編輯的儲存格值儲存在代表已編輯資料列的 Customer 物件中。 每當使用者認可儲存格值變更時,就會發生此事件。

    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. 實作 NewRowNeeded 事件的處理程序,建立代表新建立的資料列的新 Customer 物件。

    每當使用者輸入新記錄的資料列時,就會發生此事件。

    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 事件的處理常式,將新的或修改的資料列儲存至資料存放區。

    每當使用者變更目前的資料列時,就會發生此事件。

    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. 實作 RowDirtyStateNeeded 事件的處理常式,指出當使用者在編輯模式中按兩次或在編輯模式外按一次 ESC 鍵來發出資料列還原訊號時,是否要發生 CancelRowEdit 事件。

    根據預設,除非在 CancelRowEdit 事件處理常式中將 QuestionEventArgs.Response 屬性設定為 true,否則當目前資料列中的任何儲存格都已修改時,就會在資料列還原時發生 RowDirtyStateNeeded。 當認可範圍是在執行階段判斷時,此事件很有用。

    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. 實作 CancelRowEdit 事件的處理常式,捨棄代表目前資料列的 Customer 物件的值。

    當使用者在編輯模式中按兩次或在編輯模式外按一次 ESC 鍵來發出資料列還原訊號時,就會發生此事件。 如果目前資料列中的儲存格未修改,或 QuestionEventArgs.Response 事件處理常式中 false 屬性的值已設定為 RowDirtyStateNeeded,則不會發生此事件。

    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. 實作 UserDeletingRow 事件的處理常式,從資料存放區中刪除現有的 Customer 物件,或捨棄代表新建立的資料列的未儲存 Customer 物件。

    每當使用者按一下資料列標頭並按下 DELETE 鍵來刪除資料列時,就會發生此事件。

    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 類別,表示此程式碼範例所使用的資料項目。

    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
    

測試應用程式

您現在可以測試表單,以確保其如預期般運作。

測試表單

  • 編譯並執行應用程式。

    您會看到 DataGridView 控制項已填入三筆客戶記錄。 您可以修改資料列中多個儲存格的值,並在編輯模式中按兩次或在編輯模式外按一次 ESC 鍵,將整個資料列還原為其原始值。 當您修改、新增或刪除控制項中的資料列時,也會修改、新增或刪除資料存放區中的 Customer 物件。

後續步驟

此應用程式可讓您基本了解您必須處理的事件,才能在 DataGridView 控制項中實作虛擬模式。 您可以透過數種方式來改善此基本應用程式:

  • 實作資料存放區,從外部資料庫快取值。 快取應該視需要擷取和捨棄值,使其只包含在用戶端電腦上取用少量記憶體時顯示所需的量。

  • 根據您的需求微調資料存放區的效能。 例如,您可能想要使用較大的快取大小並將資料庫查詢數目降到最低來補償緩慢的網路連線,而非用戶端電腦記憶體限制。

如需從外部資料庫快取值的詳細資訊,請參閱操作說明:在 Windows Forms DataGridView 控制項中使用 Just-In-Time 資料載入實作虛擬模式

另請參閱