Export (0) Print
Expand All

Walkthrough: Implementing Virtual Mode in the Windows Forms DataGridView Control

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.

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. 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. For more information, see Implementing Virtual Mode with Just-In-Time Data Loading in the Windows Forms DataGridView Control

To copy the code in this topic as a single listing, see How to: Implement Virtual Mode in the Windows Forms DataGridView Control.

To implement virtual mode

  1. Create a class that derives from Form and contains a DataGridView control.

    The following code contains some basic initialization. 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.

    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
    
  2. Implement a handler for your form's Load event that initializes the DataGridView control and populates the data store with sample values.

    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. Implement a handler for the CellValueNeeded event that retrieves the requested cell value from the data store or the Customer object currently in edit.

    This event occurs whenever the DataGridView control needs to paint a cell.

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

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

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

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

    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.

    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. Implement a handler for the CancelRowEdit event that discards the values of the Customer object representing the current row.

    This event occurs when the user signals row reversion by pressing ESC twice in edit mode or once outside of edit mode. 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.

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

    This event occurs whenever the user deletes a row by clicking a row header and pressing the DELETE key.

    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. Implement a simple Customers class to represent the data items used by this code example.

    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
    

You can now test the form to make sure it behaves as expected.

To test the form

  • Compile and run the application.

    You will see a DataGridView control populated with three customer records. 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. When you modify, add, or delete rows in the control, Customer objects in the data store are modified, added, or deleted as well.

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.

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.

Show:
© 2014 Microsoft