2012-05-09 32 views
7

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

在我接受答案后,我收到此链接:http://www.codeproject.com/Articles/23937/Paging-Data-with-DataGridView-in-VirtualMode。尽管它使用数据库示例,但它更适合我所需。我的列表将包含我想要显示如下声明的数据:

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

一个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); 

在我的窗体的Load事件中,我做了以下(与此相关的问题,我还添加了一个事件处理程序,尽管这是一个使用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; 
    } 
    } 
} 

最后,我为DataGrid 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 */ 
    } 
} 

的问题:我的即使“captureResults”列表被填充,datagrid仍为空。这是我到目前为止所尝试的:

  • 更新事件中切换后的datagrid的RowCount成员。
  • 用缓存中的结果总数声明该列表,以确保它始终处于最新状态。 (我担心“外部修改”不会通过缓存的构造函数传递给我的列表,尽管它是一个参考。(与C#相同)
  • 将datagrid的RowCount设置为100(硬值)在窗体的加载事件中。
  • 在向CaptureResults列表添加内容后,向datagrid添加了“Update()”调用。 (这发生在一个特殊的线程中,这个调用的功能会添加一些东西到列表中)

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

-edit-增加了一些我试图让它工作的东西。

回答

2

我觉得缓存的使用会让这个过程变得复杂一些(尽管在发送链接到以这种方式实现的msdn之后,我确实感到负责)。

我会建议作为出发点是:

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

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

  3. 确保dataGrid.VirtualMode = true;(或equivilant)

  4. 实施CellValueNeeded如下:

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

注意:您需要在数据对象暴露了一些额外的公共属性,使他们可以在方法中设置为值。

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

+0

我设法让它在没有缓存的情况下工作,通过在每次添加后更新RowCount并使用我的列表大小。然而性能似乎更糟糕。 (也许是因为我每次增加更新表单,而不是后台工作人员每隔 ms)我的应用程序在处理完数据后也会相对快速地冻结。这是虚拟模式的错误实现还是其他问题? (因为某些原因,绘制行(可以有不同的背景颜色)对于这个系统来说是非常困难的。我的笔记本电脑(功能更强大)可以更轻松地完成这项任务。 – Arnold4107176

+0

您是如何在每次添加后更新RowCount的?分享这段代码,因为我不确定它是如何工作的,而且听起来不太正确。如果将列表分配给bindingSource,然后将其分配给Grid.DataSource,那么应该充分跟踪rowCounts和内部列表。 –

+0

啊,我认为这是我对概念缺乏理解的结果,当我启用虚拟模式时,我没有为DataGridView设置数据源,因为我认为这是性能问题,如果我保持这种唯一区别是我将VirtualMode属性设置为true,并且定义了CellValueNeeded事件,这是否正确?我不太明白为什么这会消除你将会得到的性能。对大型集合来说效率低下的事件的默认实现?用虚拟模式关闭它可能不起作用,我必须阅读。 – Arnold4107176

相关问题