65.9K
CodeProject 正在变化。 阅读更多。
Home

在 NET 2.0 中缓存 WinForms DataGridView 中的数据

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.76/5 (13投票s)

2006年5月4日

3分钟阅读

viewsIcon

161226

downloadIcon

2420

一篇关于如何使用 DataGridView 的虚拟模式进行分页显示大量记录的文章。

Win Form DataGridView Caching

引言

当您想在 DataGrid 中显示数百万条记录时,本文中描述的技术将非常有用。桌面应用程序的主要问题之一是,不像 Web 应用程序那样可以使用缓存。但在 Visual Studio 2005 中,Microsoft 在 DataGrid 中提供了非常好的支持,允许向用户显示数百万条数据记录。本文的灵感来自 MSDN 文章:在 Windows 窗体 DataGridView 控件中实现具有实时数据加载的虚拟模式

背景

当您想向用户显示大量数据,并且不想一次性将所有数据加载到内存中时,可以使用本文背后的基本思想。这在 DataGridView 中是可行的,因为它具有虚拟模式,并且在虚拟模式下,您可以在每次重绘时编写每个单元格的值。因此,当您向下滚动 DataGridView 时,每个单元格将被绘制,并且为此,将调用 CellValueNeeded 事件。因此,虚拟模式下的 DataGridView 非常用户友好,因为您可以根据需要进行工作。

使用代码

基本上,此代码基于上面提到的 MSDN 文章。但是我让它对您来说更容易一些,现在我将向您解释它是如何实际工作的。

在这里,数据缓存背后的基本思想是,当需要单元格值时,您应该获取它并显示它。因此,屏幕背后发生的事情是

Interface -> IDataPageRetriver
Public Interface IDataPageRetriever
    Function SupplyPageOfData( _
            ByVal lowerPageBoundary As Integer, _
            ByVal rowsPerPage As Integer) _
            As DataTable
End Interface

此接口有一个 SupplyPageOfData 方法,该方法返回特定范围的 DataTable

类 - Cache

这是解决方案的核心。要理解这个基本思想,可以想象一本书中的一页,它有页码,以及上行号和下行号,这将像页面的上索引和下索引。因此,现在在页面中,您可以存储数据并记录。

在此类中,定义了一个结构 DataPage

Public Structure DataPage

    Public table As DataTable
    Private lowestIndexValue As Integer
    Private highestIndexValue As Integer
 
    Public Sub New(ByVal table As DataTable, ByVal rowIndex As Integer)
        Me.table = table
        lowestIndexValue = MapToLowerBoundary(rowIndex)
        highestIndexValue = MapToUpperBoundary(rowIndex)
        System.Diagnostics.Debug.Assert(lowestIndexValue >= 0)
        System.Diagnostics.Debug.Assert(highestIndexValue >= 0)
    End Sub
 
    Public ReadOnly Property LowestIndex() As Integer
        Get
            Return lowestIndexValue
        End Get
    End Property
 
    Public ReadOnly Property HighestIndex() As Integer
        Get
            Return highestIndexValue
        End Get
    End Property
 
    Public Shared Function MapToLowerBoundary( _
        ByVal rowIndex As Integer) As Integer
 
        ' Return the lowest index of a page
        ' containing the given index.
        Return (rowIndex \ RowsPerPage) * RowsPerPage
 
    End Function
 
    Private Shared Function MapToUpperBoundary( _
        ByVal rowIndex As Integer) As Integer
 
        ' Return the highest index of a page
        ' containing the given index.
        Return MapToLowerBoundary(rowIndex) + RowsPerPage - 1
 
    End Function
 
End Structure

结构成员是 DataTable、下索引和上索引。创建此页面的对象时,DataTable 将分配给它,并设置下索引和上索引。

现在,当创建一个 Cache 类的对象时,它会创建两个默认页面。然后从数据库获取给定 PageSizeDataTable 并加载这两个页面。

Public Sub New(ByVal dataSupplier As IDataPageRetriever, _
        ByVal rowsPerPage As Integer)
 
    dataSupply = dataSupplier
    Cache.RowsPerPage = rowsPerPage
    LoadFirstTwoPages()

End Sub

并且当调用 CellValueNeeded 事件时,将调用具有行索引和列索引的 RetrieveElement 方法。

Private Sub grdFunctions_CellValueNeeded(ByVal sender As System.Object, _
        ByVal e As System.Windows.Forms.DataGridViewCellValueEventArgs) _
        Handles grdFunctions.CellValueNeeded
    If blnStopRepaint Then
        If e.RowIndex < dtRetrive.RowCount Then
            e.Value = memoryCache.RetrieveElement(e.RowIndex, e.ColumnIndex)
        End If
    End If
End Sub

现在,此方法将检查数据是否已缓存。如果在缓存中,则返回这些值,否则它将从数据库中检索新数据并根据每个页面的下索引和上索引填充最接近的页面,即第零页或第一页。

Public Function RetrieveElement(ByVal rowIndex As Integer, _
        ByVal columnIndex As Integer) As String
 
    Dim element As String = Nothing
    If IfPageCached_ThenSetElement(rowIndex, _
                 columnIndex, element) Then
        Return element
    Else
        UpdateCahnges()
        Return RetrieveData_CacheIt_ThenReturnElement( _
            rowIndex, columnIndex)
    End If
 
End Function

现在,当您从网格更新或删除记录时,又出现了一个大问题。因为我们使用页面缓存和 CellValueNeeded 事件,所以我们需要在每次更新时更新单元格。因此,当用户更新任何单元格时,我会更新缓存 DataTable

Private Sub grdFunctions_CellValuePushed(ByVal sender _
        As System.Object, ByVal e As _
        System.Windows.Forms.DataGridViewCellValueEventArgs) _
        Handles grdFunctions.CellValuePushed
    memoryCache.SetRowElement(e.RowIndex, e.ColumnIndex, e.Value)
End Sub

Public Sub SetRowElement(ByVal rowIndex As Integer, _
           ByVal colIndex As Integer, ByVal cellValue As String)
    If IsRowCachedInPage(0, rowIndex) Then
        cachePages(0).table.Rows(rowIndex _
            Mod RowsPerPage).Item(colIndex) = cellValue
    ElseIf IsRowCachedInPage(1, rowIndex) Then
        cachePages(1).table.Rows(rowIndex _
            Mod RowsPerPage).Item(colIndex) = cellValue
    End If
End Sub

滚动时,当用户到达 Cache 类决定从数据库页面替换缓存页面时,我将获得更新的 DataSet 并引发事件,这将使网格用户更新数据库。

Private Sub UpdateCahnges()
    Dim dtUpdate As DataTable = cachePages(0).table.GetChanges()
    If Not dtUpdate Is Nothing AndAlso dtUpdate.Rows.Count > 0 Then
        Dim _updateArgs As New UpdateDataArgs(dtUpdate)
        RaiseEvent UpdateChangesToDB(Me, _updateArgs)
        'MessageBox.Show(dtUpdate.Rows.Count.ToString & _
        '     " Rows are chaged in Page 0")
    End If
    dtUpdate = cachePages(1).table.GetChanges()
    If Not dtUpdate Is Nothing AndAlso dtUpdate.Rows.Count > 0 Then
        Dim _updateArgs As New UpdateDataArgs(dtUpdate)
        RaiseEvent UpdateChangesToDB(Me, _updateArgs)
        'MessageBox.Show(dtUpdate.Rows.Count.ToString & _
        '     " Rows are chaged in Page 1")
    End If
End Sub

但是在删除操作的情况下,我们无法应用此方法,因此我们必须从数据库中删除记录并重新加载缓存页面。

Private Sub grdFunctions_UserDeletingRow(ByVal sender As System.Object, _
       ByVal e As System.Windows.Forms.DataGridViewRowCancelEventArgs) _
       Handles grdFunctions.UserDeletingRow
    If blnStopRepaint Then
        'Remove from the database first here
        'DatabaseOP.DeleteRecord(connectionString, _
        '   grdFunctions.Rows(e.Row.Index).
        '   Cells(0).Value.ToString())
        memoryCache.RemoveRow(e.Row.Index)
        grdFunctions.InvalidateRow(e.Row.Index)
    End If
End Sub

关注点

Microsoft 提供了一个非常好的控件,用于在 VS-2005 中使用 DataGridView

历史

这是此 DataGrid 缓存解决方案的第一个版本,并且是使用 Oracle 数据库完成的,因为它有点难以使用。

在下一个版本中,我将致力于 DataGrid 控件,使其独立于任何数据库。我的意思是说,我将提供一个具有缓存解决方案的 DataGridView 控件,它将独立于数据库。

© . All rights reserved.