DataGridView 虚拟模式,使用简单列表作为源

本文关键字:列表 简单 虚拟 模式 DataGridView | 更新日期: 2023-09-27 18:33:31

以前我问了一个关于我的 dataGridView 性能的问题,因为它必须显示大量基于传入流添加的行。给出了多种解决方案,其中一种是启用虚拟模式。MSDN 有一篇关于该主题的文章,但它感觉比我需要的更复杂,因为它使用数据库和可编辑字段。我的 DataGridView 仅用于显示,我显示的数据放置在列表中。

在我接受答案后,我收到了这个链接:http://www.codeproject.com/Articles/23937/Paging-Data-with-DataGridView-in-VirtualMode。即使它使用数据库示例,它也更适合我需要的东西。将包含要显示的数据的"我的列表"声明如下:

List<ResultRow> captureResults = new List<ResultRow>();

对象定义如下:

/* Simplified */
public class ResultRow
{
    private int first = 0;
    private string second = "";
    private UInt64 third = 0;
    private IPAddress fourth = null;
    /* etc */
    public ResultRow()
    {
    }
    public void Set (<the values>) //In actuallity a KeyValuePair
    {
        //field gets set here
    }
    public UInt64 Third
    {
        get { return third; }
        set { third = value; }
    }
    /* etc. */

}

按照上面提到的文章,我创建了一个ResultRowCache。该对象按如下方式制作:

/* Page size set to 100. */
ResultRowCache _cache   = new ResultRowCache(PAGE_SIZE, captureResults);

在我的表单加载事件上,我执行以下操作(与此问题有关。我还添加了一个事件处理程序,尽管这是使用 IDE 完成的,因此不直接显示在此代码中。定义如下!

dataGrid.VirtualMode = true;
_cache = new ResultRowCache(PAGE_SIZE, captureResults);
dataGrid.Columns.Add("FirstColumn"  , "First column header");
dataGrid.Columns.Add("Second Column", "Second column header");
/* Etc. Adding all columns. (Every member or ResultRow has it's own column. */
dataGrid.RowCount = (int)_cache.TotalCount;

我想知道的一件事是如何在这里初始化 RowCount。它可能是 0(由于 ResultRowCache 的构造函数调用(见下文((,但它似乎再也没有被更改过。此作业算作参考吗?它如何自我更新?

无论如何,从我所拥有的开始,ResultRowCache定义如下:

public class ResultRowCache
{
    public int  PageSize    = 100;
    public long TotalCount;
    public List<ResultRow> CachedData = null;
    private List<ResultRow> FullData;
    int _lastRowIndex = -1;
    public ResultRowCache (int pageSize, List<ResultRow> total)
    {
        PageSize = pageSize;
        FullData = total;
        LoadPage( 0 );
    }
    public void LoadPage (int rowIndex)
    {
         int lastRowIndex = rowIndex - ( rowIndex % PageSize );
         /* Page already loaded */
         if( lastRowIndex == _lastRowIndex ) return;
         /* New page */
         _lastRowIndex = lastRowIndex;
         /* Create a new cashes data object */
         if( CachedData == null ) CachedData = new List<ResultRow>();
         /* If cached data already existed, clear */
         CachedData.Clear();
         /* The index is valid (there is data */
         if (lastRowIndex < FullData.Count)
         {
             /* Not a full page */
             if (lastRowIndex + PageSize > FullData.Count)
             {
                 CachedData = FullData.GetRange(lastRowIndex, ((lastRowIndex + PageSize) - 1) - FullData.Count);
             }
            /* Full page */
            else
            {
                CachedData = FullData.GetRange(lastRowIndex, PageSize);
            }
        }
        TotalCount = CachedData.Count;
    }
    }
}

最后,数据网格的 CellValueNeeded 事件定义如下:

void DataGridCellValueNeededEvent(object sender, DataGridViewCellValueEventArgs e)
{
    _cache.LoadPage(e.RowIndex);
    int rowIndex = e.RowIndex % _cache.PageSize;
    switch (dataGrid.Columns[e.ColumnIndex].Name)
    {
        /* Not actual names, example */
    case "FirstColumn":   e.Value = _cache.CachedData[rowIndex].First;  break;
        case "SecondColumn":  e.Value = _cache.CachedData[rowIndex].Second; break;
        /* Rest of the possibly columns/ResultRow values */
    }
}

问题:即使"捕获结果"列表已填满,我的数据网格仍为空。这是我到目前为止尝试的:

  • 事件中切换后更新数据网格的 RowCount 成员。
  • 声明包含缓存中结果总数的列表,以确保它始终是最新的。(我担心"外部修改"不会进入我在缓存构造函数中传递的列表,即使它是一个引用。(C#相当新((
  • 在窗体的加载事件中,将数据网格的行计数设置为 100(硬值(。
  • 在向捕获结果列表添加内容后,向数据网格添加了"Update(("调用。(这发生在一个特殊的线程中,该线程调用了一个向列表中添加内容的函数(

以上都没有改变任何东西。网格保持为空。我想我在这里错过了一些很明显的东西。有什么建议吗?

-编辑- 添加了一些我试图让它工作的东西。

DataGridView 虚拟模式,使用简单列表作为源

我觉得使用缓存使过程有些复杂(尽管在向您发送以这种方式实现的msdn链接后,我确实感到有责任(。

我建议作为起点是:

  1. 扔掉缓存(如果您遇到内存问题,这可能会在以后有用,但现在让我们填充您的数据网格(

  2. List<ResultsRow>存储在实例变量中。

  3. 确保dataGrid.VirtualMode = true;(或等效(

  4. 实现 CellValueNeed,如下所示:

        private void gridContacts_CellValueNeeded(object sender,  DataGridViewCellValueEventArgs e)
        {
           ResultRow dataObject = resultRows[e.RowIndex];
           switch(e.ColumnIndex)
           {
               case 0:
                   e.Value = dataObject.First;
                   break;
               case 1 :
                   e.Value = dataObject.Second;
                   break;
               //etc..
           }
        }
    

注意:您需要在DataObject中公开一些额外的公共属性,以便它们可以在方法中设置为值。

看看你是如何做到的。如果在 CellValueRequired 方法中设置了一些断点,这些断点应该有助于调试任何进一步的意外行为。祝你好运。