【问题标题】:Datagridview virtualmode with heavy data load数据负载重的 Datagridview 虚拟模式
【发布时间】:2011-08-10 07:00:14
【问题描述】:

我想以虚拟模式在 datagridview 中显示“大量大量数据”。

我使用一个缓冲区来存储 n (2) 页数据,最初 2 个第一页存储在缓冲区中。当gridview滚动到缓冲区中不存在的行时,我加载当前的新页面并将其与旧页面交换。

为了模拟将大量数据从数据库加载到缓冲区中,我添加了这一行: System.Threading.Thread.Sleep(3000);
结果,当缓冲区更新自身时,网格在这段时间内被冻结。

现在,据我了解,在这里添加线程无济于事,因为只有当滚动到达无缓冲行时才会发生 1 个操作。

  private void dataGridView1_CellValueNeeded(object sender, DataGridViewCellValueEventArgs e)
    {

       e.Value = theBuffer.ReturnFromBuffer(e.RowIndex, e.ColumnIndex);
//I check there if row index is in buffer and if not i update the buffer with new page and return the value

    }

所以我正在考虑当它发生时在网格上方添加一个“纺车”动画。

我尝试将事件发送到表单以在更新缓冲区的方法中显示/隐藏图像:

if (ProgressEvent != null)
     ProgressEvent(true);

System.Threading.Thread.Sleep(3000);

if (ProgressEvent != null)
     ProgressEvent(false);

并在表单事件处理程序上设置了图像的可见属性,但结果并不像预期的那样,我猜睡眠发生在事件处理之前,所以图像在睡眠之前没有出现。

所以我想还是需要胎面的。但不知道什么时候应该开始,应该怎么做?

更新:

我厌倦了建议的后台工作人员:
在 CellValueNeeded 事件调用的方法中,我检查请求的行是否被缓冲,如果没有,我启动后台工作程序。

 if (!bgWorker.IsBusy) //otherwise worker will be started for every column of the row
 {
      bgWorker.DoWork += (sender, e) =>
         {
           UpdateBuffer(rowIndex);                           
          };
       bgWorker.RunWorkerAsync();
 }
 return "null"; 

还有这个:

bgWorker.RunWorkerCompleted += (sender, e) =>
{
    if (IvalidateEvent != null)
        IvalidateEvent();  //send event to form, where invalidate the gridview  dataGridView1.Invalidate();
};            

它可以工作,但有一些问题: 1)我的网格中有很多虚拟结果,即使时间不长 - 我不太喜欢它。 2)如果我保持原样,它甚至会花费更多时间,可能是因为 backgroundworker 消耗了一些时间,并且因为我实际上使网格无效并调用 CellValueNeeded 事件 2 次(第一次启动 backgroundworkerm 在完成它的工作时第二次)!

【问题讨论】:

  • 使整个网格无效有点像“矫枉过正”......它还将重新绘制所有具有正确值的单元格,而不仅仅是那些带有假人的单元格。在滚动网格时避免虚拟值的另一种方法是对滚动本身做出反应。尝试在请求之前获取接近显示行的行,在这种情况下,值可能会在 CellNeedsValue 请求它们之前就在那里
  • @DarkSquirrel42,CellNeedsValue 不仅缓冲所需的行,还缓冲前面的一堆行。当我需要加载新行时唯一的问题。我不介意冻结网格,但动画必须在它发生时出现。没有虚拟值
  • 当 datagridview 引发 cellvalueneeded 时,您可以指定要在单元格中显示的值。如果您没有该值,则无法指定它。所以你有2个选择:1)要么提供“加载”消息,要么什么都不提供(空字符串),在异步操作中填充缓冲区,当数据准备好时更新单元格。或选项 2) 同步填充缓冲区并返回所需的值。当然,选项 2 会阻塞 UI 线程,因此无法使用加载动画、表单重绘或用户交互(除非您执行 Application.DoEvents 之类的坏事——调试起来会很痛苦)
  • 问题在于,例如,当我多次单击 pagedown 时,虚拟值会填充很多行(直到加载数据)。为了测试,我使用数据集作为源和数据表,将页面保存在内存中。也许其他资源会更快?
  • 我不这么认为...尝试优化加载器进程...创建一个类来处理对需要加载数据的缓冲区的请求...存储此类请求以及何时您的工作线程运行,尝试收集不止一行(减少查询数量)......当工作人员完成工作时,让它查看请求列表并继续使用该工作人员而不是产生新工作人员,如果还有更多做... 跟踪滚动事件并确保下一个和上一个 X 行都在缓冲区中(因此滚动缓冲区后可以直接提供值)

标签: c# winforms multithreading datagridview


【解决方案1】:

当您到达无缓冲行时,调用您的代码以在第二个线程中获取所需数据(新线程、线程池、backgroundworker ... 选择一个)

在此异步操作开始后,使“正在加载...”控件可见

(在这里您可能希望在操作完成之前禁用部分 UI)

现在返回一个空字符串或其他一些虚拟值...

您的异步操作应该获取所需的数据,然后调用 ui 线程的另一个方法(在 Form 的对象上调用 Invoke(...))

需要invoke调用来避免跨线程UI交互

在调用的方法中,您应该使用获取的数据来填充缓冲区,隐藏“正在加载...”控件,使相应的行或单元格无效(datagridview 具有执行此操作的方法)和最后但并非最不重要的一点是在必要时重新启用您的用户界面

【讨论】:

  • 是的,我考虑过,但有一个问题我会尝试解释:当新线程将数据加载到缓冲区中时,可能会出现这样一种情况,用户可以再次滚动网格并且新线程将开始!这会造成很多混乱。我不认为我想锁定滚动。
  • 而且,CellValueNeeded 等待它的值如何使用线程?它不会等待线程完成
  • 第一个:等待的 cellValueNeeded 处理程序......不要让它等待......返回一个虚拟值,例如。 “加载中……”。稍后当异步操作完成并且数据在缓冲区中时,使行/单元格无效。这将再次提高 CellValueNeeded,然后您可以获得真正的价值。第二:一次只有一个工作人员,您可以编写一个处理数据请求的类。当请求数据并且当前没有获取数据时,启动线程。当前提取数据时,将请求排入队列...在您的提取过程结束时检查等待请求...
  • @DarkSquirrel42,“使行/单元格无效”是什么意思。此外,我认为我不能返回虚拟值,因为它会将其放入单元格中。
  • 当需要重绘控件(如 DataGridViewCell)时,您可以使其客户区无效,因此下次 UI 线程进入空闲状态时,它将重绘该区域。通常你在控件上调用 Invalidate() 来触发它。对于 datagridview-cell/-row,这是从 datagridview-object 完成的(如果我没记错的话,该方法在虚拟模式下名为 InvalidateRow(...)/InvalidateCell(...)),datagridview 将在它可以之前引发 CellValueNeeded重绘一个单元格。因此,当在后台获取数据时,您在该单元格中拥有该虚拟值,然后出现真实数据
猜你喜欢
  • 2011-06-11
  • 2012-10-14
  • 2012-01-05
  • 1970-01-01
  • 1970-01-01
  • 2019-03-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多