Implémentation du mode virtuel avec le chargement de données juste-à-temps dans le contrôle DataGridView Windows Forms
Une raison d'implémenter le mode virtuel dans le contrôle DataGridView est de récupérer des données uniquement quand elles sont nécessaires Cela est appelé le chargement de données juste-à-temps.
Si vous utilisez une table très grande dans une base de données distante, par exemple, que vous pouvez souhaiter éviter des délais de démarrage en récupérant uniquement les données qui sont nécessaires pour l'affichage et l'extraction de données supplémentaires uniquement lorsque l'utilisateur fait défiler de nouvelles lignes dans l'affichage. Si les ordinateurs clients qui exécutent votre application ont une quantité de mémoire disponible limitée pour stocker des données, vous pouvez souhaiter également ignorer les données inutilisées lors de la récupération de nouvelles valeurs de la base de données.
Les sections suivantes décrivent comment utiliser un contrôle DataGridView avec un cache juste-à-temps.
Pour copier le code dans cette rubrique sous forme de liste unique, consultez Comment : implémenter le mode virtuel avec le chargement de données juste-à-temps dans le contrôle DataGridView Windows Forms.
Le formulaire
L'exemple de code suivant définit un formulaire contenant un contrôle DataGridView en lecture seule qui interagit avec un objet Cache via un gestionnaire d'événements CellValueNeeded. L'objet Cache gère les valeurs stockées localement et utilise un objet DataRetriever pour récupérer des valeurs de la table Orders de l'exemple de base de données Northwind. L'objet DataRetriever, qui implémente l'interface IDataPageRetriever requise par la classe Cache, est également utilisé pour initialiser les lignes et les colonnes du contrôle DataGridView.
Les types IDataPageRetriever, DataRetriever et Cache sont décrits ultérieurement dans cette rubrique.
Notes
Le stockage d'informations sensibles, par exemple, un mot de passe, dans la chaîne de connexion peut affecter la sécurité de votre application. L'utilisation de l'authentification Windows (également appelée sécurité intégrée) offre un moyen plus sûr de contrôler l'accès à une base de données. Pour plus d'informations, consultez Protection des informations de connexion (ADO.NET).
Imports System.Data
Imports System.Data.SqlClient
Imports System.Drawing
Imports System.Windows.Forms
Public Class VirtualJustInTimeDemo
Inherits System.Windows.Forms.Form
Private WithEvents dataGridView1 As New DataGridView()
Private memoryCache As Cache
' Specify a connection string. Replace the given value with a
' valid connection string for a Northwind SQL Server sample
' database accessible to your system.
Private connectionString As String = _
"Initial Catalog=NorthWind;Data Source=localhost;" & _
"Integrated Security=SSPI;Persist Security Info=False"
Private table As String = "Orders"
Private Sub VirtualJustInTimeDemo_Load( _
ByVal sender As Object, ByVal e As EventArgs) _
Handles Me.Load
' Initialize the form.
With Me
.AutoSize = True
.Controls.Add(Me.dataGridView1)
.Text = "DataGridView virtual-mode just-in-time demo"
End With
' Complete the initialization of the DataGridView.
With Me.dataGridView1
.Size = New Size(800, 250)
.Dock = DockStyle.Fill
.VirtualMode = True
.ReadOnly = True
.AllowUserToAddRows = False
.AllowUserToOrderColumns = False
.SelectionMode = DataGridViewSelectionMode.FullRowSelect
End With
' Create a DataRetriever and use it to create a Cache object
' and to initialize the DataGridView columns and rows.
Try
Dim retriever As New DataRetriever(connectionString, table)
memoryCache = New Cache(retriever, 16)
For Each column As DataColumn In retriever.Columns
dataGridView1.Columns.Add( _
column.ColumnName, column.ColumnName)
Next
Me.dataGridView1.RowCount = retriever.RowCount
Catch ex As SqlException
MessageBox.Show("Connection could not be established. " & _
"Verify that the connection string is valid.")
Application.Exit()
End Try
' Adjust the column widths based on the displayed values.
Me.dataGridView1.AutoResizeColumns( _
DataGridViewAutoSizeColumnsMode.DisplayedCells)
End Sub
Private Sub dataGridView1_CellValueNeeded( _
ByVal sender As Object, ByVal e As DataGridViewCellValueEventArgs) _
Handles dataGridView1.CellValueNeeded
e.Value = memoryCache.RetrieveElement(e.RowIndex, e.ColumnIndex)
End Sub
<STAThreadAttribute()> _
Public Shared Sub Main()
Application.Run(New VirtualJustInTimeDemo())
End Sub
End Class
using System;
using System.Data;
using System.Data.SqlClient;
using System.Drawing;
using System.Windows.Forms;
public class VirtualJustInTimeDemo : System.Windows.Forms.Form
{
private DataGridView dataGridView1 = new DataGridView();
private Cache memoryCache;
// Specify a connection string. Replace the given value with a
// valid connection string for a Northwind SQL Server sample
// database accessible to your system.
private string connectionString =
"Initial Catalog=NorthWind;Data Source=localhost;" +
"Integrated Security=SSPI;Persist Security Info=False";
private string table = "Orders";
protected override void OnLoad(EventArgs e)
{
// Initialize the form.
this.AutoSize = true;
this.Controls.Add(this.dataGridView1);
this.Text = "DataGridView virtual-mode just-in-time demo";
// Complete the initialization of the DataGridView.
this.dataGridView1.Size = new Size(800, 250);
this.dataGridView1.Dock = DockStyle.Fill;
this.dataGridView1.VirtualMode = true;
this.dataGridView1.ReadOnly = true;
this.dataGridView1.AllowUserToAddRows = false;
this.dataGridView1.AllowUserToOrderColumns = false;
this.dataGridView1.SelectionMode =
DataGridViewSelectionMode.FullRowSelect;
this.dataGridView1.CellValueNeeded += new
DataGridViewCellValueEventHandler(dataGridView1_CellValueNeeded);
// Create a DataRetriever and use it to create a Cache object
// and to initialize the DataGridView columns and rows.
try
{
DataRetriever retriever =
new DataRetriever(connectionString, table);
memoryCache = new Cache(retriever, 16);
foreach (DataColumn column in retriever.Columns)
{
dataGridView1.Columns.Add(
column.ColumnName, column.ColumnName);
}
this.dataGridView1.RowCount = retriever.RowCount;
}
catch (SqlException)
{
MessageBox.Show("Connection could not be established. " +
"Verify that the connection string is valid.");
Application.Exit();
}
// Adjust the column widths based on the displayed values.
this.dataGridView1.AutoResizeColumns(
DataGridViewAutoSizeColumnsMode.DisplayedCells);
base.OnLoad(e);
}
private void dataGridView1_CellValueNeeded(object sender,
DataGridViewCellValueEventArgs e)
{
e.Value = memoryCache.RetrieveElement(e.RowIndex, e.ColumnIndex);
}
[STAThreadAttribute()]
public static void Main()
{
Application.Run(new VirtualJustInTimeDemo());
}
}
L'interface IDataPageRetriever
L'exemple de code suivant définit l'interface IDataPageRetriever qui est implémentée par la classe DataRetriever. La seule méthode déclarée dans cette interface est la méthode SupplyPageOfData, qui requiert un index de ligne initial et un décompte du nombre de lignes dans une seule page de données. Ces valeurs sont utilisées par l'implémenteur pour récupérer un sous-ensemble de données d'une source de données.
Un objet Cache utilise une implémentation de cette interface pendant la construction pour charger deux pages initiales de données. Toutes les fois qu'une valeur non mise en cache est exigée, le cache ignore l'une de ces pages et demande une nouvelle page contenant la valeur de IDataPageRetriever.
Public Interface IDataPageRetriever
Function SupplyPageOfData( _
ByVal lowerPageBoundary As Integer, ByVal rowsPerPage As Integer) _
As DataTable
End Interface
public interface IDataPageRetriever
{
DataTable SupplyPageOfData(int lowerPageBoundary, int rowsPerPage);
}
La classe DataRetriever
L'exemple de code suivant définit la classe DataRetriever, qui implémente l'interface IDataPageRetriever, pour récupérer des pages de données d'un serveur. La classe DataRetriever fournit également des propriétés Columns et RowCount, que le contrôle DataGridView utilise pour créer les colonnes nécessaires et ajouter le nombre approprié de lignes vides à la collection Rows. L'ajout de lignes vides est nécessaire pour que le contrôle se comporte comme s'il contient toutes les données de la table. Cela signifie que la case de défilement dans la barre de défilement aura la taille appropriée, et que l'utilisateur sera à même d'accéder à toute ligne dans la table. Les lignes sont remplies par le gestionnaire d'événements CellValueNeeded uniquement lorsqu'elles défilent dans l'affichage.
Public Class DataRetriever
Implements IDataPageRetriever
Private tableName As String
Private command As SqlCommand
Public Sub New( _
ByVal connectionString As String, ByVal tableName As String)
Dim connection As New SqlConnection(connectionString)
connection.Open()
command = connection.CreateCommand()
Me.tableName = tableName
End Sub
Private rowCountValue As Integer = -1
Public ReadOnly Property RowCount() As Integer
Get
' Return the existing value if it has already been determined.
If Not rowCountValue = -1 Then
Return rowCountValue
End If
' Retrieve the row count from the database.
command.CommandText = "SELECT COUNT(*) FROM " & tableName
rowCountValue = CInt(command.ExecuteScalar())
Return rowCountValue
End Get
End Property
Private columnsValue As DataColumnCollection
Public ReadOnly Property Columns() As DataColumnCollection
Get
' Return the existing value if it has already been determined.
If columnsValue IsNot Nothing Then
Return columnsValue
End If
' Retrieve the column information from the database.
command.CommandText = "SELECT * FROM " & tableName
Dim adapter As New SqlDataAdapter()
adapter.SelectCommand = command
Dim table As New DataTable()
table.Locale = System.Globalization.CultureInfo.InvariantCulture
adapter.FillSchema(table, SchemaType.Source)
columnsValue = table.Columns
Return columnsValue
End Get
End Property
Private commaSeparatedListOfColumnNamesValue As String = Nothing
Private ReadOnly Property CommaSeparatedListOfColumnNames() As String
Get
' Return the existing value if it has already been determined.
If commaSeparatedListOfColumnNamesValue IsNot Nothing Then
Return commaSeparatedListOfColumnNamesValue
End If
' Store a list of column names for use in the
' SupplyPageOfData method.
Dim commaSeparatedColumnNames As New System.Text.StringBuilder()
Dim firstColumn As Boolean = True
For Each column As DataColumn In Columns
If Not firstColumn Then
commaSeparatedColumnNames.Append(", ")
End If
commaSeparatedColumnNames.Append(column.ColumnName)
firstColumn = False
Next
commaSeparatedListOfColumnNamesValue = _
commaSeparatedColumnNames.ToString()
Return commaSeparatedListOfColumnNamesValue
End Get
End Property
' Declare variables to be reused by the SupplyPageOfData method.
Private columnToSortBy As String
Private adapter As New SqlDataAdapter()
Public Function SupplyPageOfData( _
ByVal lowerPageBoundary As Integer, ByVal rowsPerPage As Integer) _
As DataTable Implements IDataPageRetriever.SupplyPageOfData
' Store the name of the ID column. This column must contain unique
' values so the SQL below will work properly.
If columnToSortBy Is Nothing Then
columnToSortBy = Me.Columns(0).ColumnName
End If
If Not Me.Columns(columnToSortBy).Unique Then
Throw New InvalidOperationException(String.Format( _
"Column {0} must contain unique values.", columnToSortBy))
End If
' Retrieve the specified number of rows from the database, starting
' with the row specified by the lowerPageBoundary parameter.
command.CommandText = _
"Select Top " & rowsPerPage & " " & _
CommaSeparatedListOfColumnNames & " From " & tableName & _
" WHERE " & columnToSortBy & " NOT IN (SELECT TOP " & _
lowerPageBoundary & " " & columnToSortBy & " From " & _
tableName & " Order By " & columnToSortBy & _
") Order By " & columnToSortBy
adapter.SelectCommand = command
Dim table As New DataTable()
table.Locale = System.Globalization.CultureInfo.InvariantCulture
adapter.Fill(table)
Return table
End Function
End Class
public class DataRetriever : IDataPageRetriever
{
private string tableName;
private SqlCommand command;
public DataRetriever(string connectionString, string tableName)
{
SqlConnection connection = new SqlConnection(connectionString);
connection.Open();
command = connection.CreateCommand();
this.tableName = tableName;
}
private int rowCountValue = -1;
public int RowCount
{
get
{
// Return the existing value if it has already been determined.
if (rowCountValue != -1)
{
return rowCountValue;
}
// Retrieve the row count from the database.
command.CommandText = "SELECT COUNT(*) FROM " + tableName;
rowCountValue = (int)command.ExecuteScalar();
return rowCountValue;
}
}
private DataColumnCollection columnsValue;
public DataColumnCollection Columns
{
get
{
// Return the existing value if it has already been determined.
if (columnsValue != null)
{
return columnsValue;
}
// Retrieve the column information from the database.
command.CommandText = "SELECT * FROM " + tableName;
SqlDataAdapter adapter = new SqlDataAdapter();
adapter.SelectCommand = command;
DataTable table = new DataTable();
table.Locale = System.Globalization.CultureInfo.InvariantCulture;
adapter.FillSchema(table, SchemaType.Source);
columnsValue = table.Columns;
return columnsValue;
}
}
private string commaSeparatedListOfColumnNamesValue = null;
private string CommaSeparatedListOfColumnNames
{
get
{
// Return the existing value if it has already been determined.
if (commaSeparatedListOfColumnNamesValue != null)
{
return commaSeparatedListOfColumnNamesValue;
}
// Store a list of column names for use in the
// SupplyPageOfData method.
System.Text.StringBuilder commaSeparatedColumnNames =
new System.Text.StringBuilder();
bool firstColumn = true;
foreach (DataColumn column in Columns)
{
if (!firstColumn)
{
commaSeparatedColumnNames.Append(", ");
}
commaSeparatedColumnNames.Append(column.ColumnName);
firstColumn = false;
}
commaSeparatedListOfColumnNamesValue =
commaSeparatedColumnNames.ToString();
return commaSeparatedListOfColumnNamesValue;
}
}
// Declare variables to be reused by the SupplyPageOfData method.
private string columnToSortBy;
private SqlDataAdapter adapter = new SqlDataAdapter();
public DataTable SupplyPageOfData(int lowerPageBoundary, int rowsPerPage)
{
// Store the name of the ID column. This column must contain unique
// values so the SQL below will work properly.
if (columnToSortBy == null)
{
columnToSortBy = this.Columns[0].ColumnName;
}
if (!this.Columns[columnToSortBy].Unique)
{
throw new InvalidOperationException(String.Format(
"Column {0} must contain unique values.", columnToSortBy));
}
// Retrieve the specified number of rows from the database, starting
// with the row specified by the lowerPageBoundary parameter.
command.CommandText = "Select Top " + rowsPerPage + " " +
CommaSeparatedListOfColumnNames + " From " + tableName +
" WHERE " + columnToSortBy + " NOT IN (SELECT TOP " +
lowerPageBoundary + " " + columnToSortBy + " From " +
tableName + " Order By " + columnToSortBy +
") Order By " + columnToSortBy;
adapter.SelectCommand = command;
DataTable table = new DataTable();
table.Locale = System.Globalization.CultureInfo.InvariantCulture;
adapter.Fill(table);
return table;
}
}
La classe Cache
L'exemple de code suivant définit la classe Cache qui gère deux pages de données remplies via une implémentation IDataPageRetriever. La classe Cache définit une structure DataPage interne qui contient un DataTable pour stocker les valeurs dans une seule page de cache qui calcule les index de ligne représentant les limites supérieures et inférieures de la page.
La classe Cache charge deux pages de données au moment de la construction. Toutes les fois que l'événement CellValueNeeded demande une valeur, l'objet Cache détermine si la valeur est disponible dans l'une de ses deux pages et, le cas échéant, la retourne. Si la valeur est non disponible localement, l'objet Cache détermine laquelle de ses deux pages est la plus éloignée des lignes affichées actuellement et remplace la page par une nouvelle page contenant la valeur demandée qu'il retourne ensuite.
En supposant que le nombre de lignes dans une page de données est le même que le nombre de lignes pouvant être affichées à la fois à l'écran, ce modèle permet aux utilisateurs qui paginent à travers la table de retourner efficacement à la dernière page affichée.
Public Class Cache
Private Shared RowsPerPage As Integer
' Represents one page of data.
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
Private cachePages As DataPage()
Private dataSupply As IDataPageRetriever
Public Sub New(ByVal dataSupplier As IDataPageRetriever, _
ByVal rowsPerPage As Integer)
dataSupply = dataSupplier
Cache.RowsPerPage = rowsPerPage
LoadFirstTwoPages()
End Sub
' Sets the value of the element parameter if the value is in the cache.
Private Function IfPageCached_ThenSetElement(ByVal rowIndex As Integer, _
ByVal columnIndex As Integer, ByRef element As String) As Boolean
If IsRowCachedInPage(0, rowIndex) Then
element = cachePages(0).table.Rows(rowIndex Mod RowsPerPage) _
.Item(columnIndex).ToString()
Return True
ElseIf IsRowCachedInPage(1, rowIndex) Then
element = cachePages(1).table.Rows(rowIndex Mod RowsPerPage) _
.Item(columnIndex).ToString()
Return True
End If
Return False
End Function
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
Return RetrieveData_CacheIt_ThenReturnElement( _
rowIndex, columnIndex)
End If
End Function
Private Sub LoadFirstTwoPages()
cachePages = New DataPage() { _
New DataPage(dataSupply.SupplyPageOfData( _
DataPage.MapToLowerBoundary(0), RowsPerPage), 0), _
New DataPage(dataSupply.SupplyPageOfData( _
DataPage.MapToLowerBoundary(RowsPerPage), _
RowsPerPage), RowsPerPage) _
}
End Sub
Private Function RetrieveData_CacheIt_ThenReturnElement( _
ByVal rowIndex As Integer, ByVal columnIndex As Integer) As String
' Retrieve a page worth of data containing the requested value.
Dim table As DataTable = dataSupply.SupplyPageOfData( _
DataPage.MapToLowerBoundary(rowIndex), RowsPerPage)
' Replace the cached page furthest from the requested cell
' with a new page containing the newly retrieved data.
cachePages(GetIndexToUnusedPage(rowIndex)) = _
New DataPage(table, rowIndex)
Return RetrieveElement(rowIndex, columnIndex)
End Function
' Returns the index of the cached page most distant from the given index
' and therefore least likely to be reused.
Private Function GetIndexToUnusedPage(ByVal rowIndex As Integer) _
As Integer
If rowIndex > cachePages(0).HighestIndex AndAlso _
rowIndex > cachePages(1).HighestIndex Then
Dim offsetFromPage0 As Integer = _
rowIndex - cachePages(0).HighestIndex
Dim offsetFromPage1 As Integer = _
rowIndex - cachePages(1).HighestIndex
If offsetFromPage0 < offsetFromPage1 Then
Return 1
End If
Return 0
Else
Dim offsetFromPage0 As Integer = _
cachePages(0).LowestIndex - rowIndex
Dim offsetFromPage1 As Integer = _
cachePages(1).LowestIndex - rowIndex
If offsetFromPage0 < offsetFromPage1 Then
Return 1
End If
Return 0
End If
End Function
' Returns a value indicating whether the given row index is contained
' in the given DataPage.
Private Function IsRowCachedInPage( _
ByVal pageNumber As Integer, ByVal rowIndex As Integer) As Boolean
Return rowIndex <= cachePages(pageNumber).HighestIndex AndAlso _
rowIndex >= cachePages(pageNumber).LowestIndex
End Function
End Class
public class Cache
{
private static int RowsPerPage;
// Represents one page of data.
public struct DataPage
{
public DataTable table;
private int lowestIndexValue;
private int highestIndexValue;
public DataPage(DataTable table, int rowIndex)
{
this.table = table;
lowestIndexValue = MapToLowerBoundary(rowIndex);
highestIndexValue = MapToUpperBoundary(rowIndex);
System.Diagnostics.Debug.Assert(lowestIndexValue >= 0);
System.Diagnostics.Debug.Assert(highestIndexValue >= 0);
}
public int LowestIndex
{
get
{
return lowestIndexValue;
}
}
public int HighestIndex
{
get
{
return highestIndexValue;
}
}
public static int MapToLowerBoundary(int rowIndex)
{
// Return the lowest index of a page containing the given index.
return (rowIndex / RowsPerPage) * RowsPerPage;
}
private static int MapToUpperBoundary(int rowIndex)
{
// Return the highest index of a page containing the given index.
return MapToLowerBoundary(rowIndex) + RowsPerPage - 1;
}
}
private DataPage[] cachePages;
private IDataPageRetriever dataSupply;
public Cache(IDataPageRetriever dataSupplier, int rowsPerPage)
{
dataSupply = dataSupplier;
Cache.RowsPerPage = rowsPerPage;
LoadFirstTwoPages();
}
// Sets the value of the element parameter if the value is in the cache.
private bool IfPageCached_ThenSetElement(int rowIndex,
int columnIndex, ref string element)
{
if (IsRowCachedInPage(0, rowIndex))
{
element = cachePages[0].table
.Rows[rowIndex % RowsPerPage][columnIndex].ToString();
return true;
}
else if (IsRowCachedInPage(1, rowIndex))
{
element = cachePages[1].table
.Rows[rowIndex % RowsPerPage][columnIndex].ToString();
return true;
}
return false;
}
public string RetrieveElement(int rowIndex, int columnIndex)
{
string element = null;
if (IfPageCached_ThenSetElement(rowIndex, columnIndex, ref element))
{
return element;
}
else
{
return RetrieveData_CacheIt_ThenReturnElement(
rowIndex, columnIndex);
}
}
private void LoadFirstTwoPages()
{
cachePages = new DataPage[]{
new DataPage(dataSupply.SupplyPageOfData(
DataPage.MapToLowerBoundary(0), RowsPerPage), 0),
new DataPage(dataSupply.SupplyPageOfData(
DataPage.MapToLowerBoundary(RowsPerPage),
RowsPerPage), RowsPerPage)};
}
private string RetrieveData_CacheIt_ThenReturnElement(
int rowIndex, int columnIndex)
{
// Retrieve a page worth of data containing the requested value.
DataTable table = dataSupply.SupplyPageOfData(
DataPage.MapToLowerBoundary(rowIndex), RowsPerPage);
// Replace the cached page furthest from the requested cell
// with a new page containing the newly retrieved data.
cachePages[GetIndexToUnusedPage(rowIndex)] = new DataPage(table, rowIndex);
return RetrieveElement(rowIndex, columnIndex);
}
// Returns the index of the cached page most distant from the given index
// and therefore least likely to be reused.
private int GetIndexToUnusedPage(int rowIndex)
{
if (rowIndex > cachePages[0].HighestIndex &&
rowIndex > cachePages[1].HighestIndex)
{
int offsetFromPage0 = rowIndex - cachePages[0].HighestIndex;
int offsetFromPage1 = rowIndex - cachePages[1].HighestIndex;
if (offsetFromPage0 < offsetFromPage1)
{
return 1;
}
return 0;
}
else
{
int offsetFromPage0 = cachePages[0].LowestIndex - rowIndex;
int offsetFromPage1 = cachePages[1].LowestIndex - rowIndex;
if (offsetFromPage0 < offsetFromPage1)
{
return 1;
}
return 0;
}
}
// Returns a value indicating whether the given row index is contained
// in the given DataPage.
private bool IsRowCachedInPage(int pageNumber, int rowIndex)
{
return rowIndex <= cachePages[pageNumber].HighestIndex &&
rowIndex >= cachePages[pageNumber].LowestIndex;
}
}
Considérations supplémentaires
Les exemples de code antérieurs sont fournis comme démonstration de chargement de données juste-à-temps. Vous devrez modifier le code pour vos propres besoins pour obtenir le rendement maximal. Au minimum, vous devrez choisir une valeur appropriée pour le nombre de lignes par page de données dans le cache. Cette valeur est passée dans le constructeur Cache. Le nombre de lignes par page ne doit pas être inférieur au nombre des lignes qui peuvent être affichées simultanément dans votre contrôle DataGridView.
Pour des résultats optimaux, vous devrez effectuer des tests de performance et de facilité d'utilisation afin de déterminer les besoins de votre système et de vos utilisateurs. Parmi les différents facteurs que vous devez prendre en considération figurent la quantité de mémoire dans les ordinateurs clients qui exécutent votre application, la bande passante disponible de la connexion réseau utilisée et la latence du serveur utilisé. La bande passante et la latence doivent être déterminées aux moments de l'utilisation maximale.
Pour améliorer les performances de défilement de votre application, vous pouvez augmenter la quantité de données stockées localement. Pour améliorer le temps de démarrage, toutefois, vous devez éviter de charger trop de données initialement. Vous pouvez modifier la classe Cache pour augmenter le nombre de pages de données qu'elle peut stocker. L'utilisation d'un plus grand nombre de données peut améliorer le rendement de défilement, mais vous devrez déterminer le nombre idéal de lignes dans une page de données, selon la bande passante disponible et la latence serveur. Avec des pages moins volumineuses, le serveur fera fréquemment l'objet d'un accès, mais prendra moins de temps pour retourner les données demandées. Si la latence représente davantage un problème que la bande passante, vous pouvez utiliser des pages de données plus volumineuses.
Voir aussi
Tâches
Procédure pas à pas : implémentation du mode virtuel dans le contrôle DataGridView Windows Forms
Référence
Concepts
Meilleures pratiques pour la mise à l'échelle du contrôle DataGridView Windows Forms
Mode virtuel dans le contrôle DataGridView Windows Forms
Autres ressources
Réglage des performances dans le contrôle DataGridView Windows Forms