Windows Forms DataGridView是否实现真正的虚拟模式? [英] Does the Windows Forms DataGridView implement a true virtual mode?

查看:102
本文介绍了Windows Forms DataGridView是否实现真正的虚拟模式?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述



有一个特定的用户需求来呈现一个可排序的网格,显示所有的行没有寻呼。用户期望通过使用滚动条可以很快地从一行到另一列从上到下。



我熟悉虚拟模式网格,只显示整个数据的可见子集。他们可以提供卓越的UI性能和最小的内存要求(我甚至在几年前甚至实现了使用这种技术的应用程序)。



Windows窗体DataGridView提供了一个虚拟模式看起来应该是答案。但是,与其他遇到的虚拟模式不同,它仍然为每一行分配内存(在ProcessExplorer中已确认)。显然,这导致整体内存使用量不必要地大大增加,而在分配这些行时,会有明显的延迟。滚动性能也受到100万行的影响。



真正的虚拟模式不需要为没有显示的行分配任何内存。您只需给出总行数(例如1,000,000),并且所有网格都相应地缩放滚动条。当首次显示网格时,只需要先查询n(例如30)可见行数据即可显示数据。



当用户滚动网格时,偏移量和可见行数被提供,可用于从数据存储中检索数据。



以下是我目前使用的DataGridView代码示例: / p>

  public void AddVirtualRows(int rowCount)
{
dataGridList.ColumnCount = 4;


dataGridList.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.None;
dataGridList.AutoSizeRowsMo​​de = DataGridViewAutoSizeRowsMo​​de.None;

dataGridList.VirtualMode = true;

dataGridList.RowCount = rowCount;

dataGridList.CellValueNeeded + = new DataGridViewCellValueEventHandler(dataGridList_CellValueNeeded);


}
void dataGridList_CellValueNeeded(object sender,DataGridViewCellValueEventArgs e)
{
e.Value = e.RowIndex;
}

我在这里缺少任何东西,或者是DataGridView的虚拟模式真的不是真的吗?



[更新]



看起来好的旧ListView完全实现了排序的虚拟模式我在寻找。但不幸的是ListView没有DataGridView的单元格格式化功能,所以我不能使用它。对于其他可能能够使用四列ListView(详细模式),VirtualMode = True和VirtualListSize = 100,000,000行的其他人来说,



列表立即显示,前30行可见。我可以随时快速滚动到列表的底部。内存使用量始终为10 MB。

解决方案

我们只是有一个类似的要求,可以显示任意的,无索引的1M +行表在我们的应用程序中具有非常好的性能,使用库存 DataGridView 。起初我以为这是不可能的,但是有了足够的头痛,我们想出了在Reflector和.NET Profiler花费数天之后,效果很好的东西。



我们解决这个问题的方法是创建一个实现 ITypedList IBindingList (可以称之为 LargeTableView ,例如)来管理来自数据库的异步检索和缓存信息。我们还创建了一个PropertyDescriptor继承类(例如 LargeTableColumnDescriptor ),以从每列中检索数据。



DataGridView.DataSource 属性设置为实现 IBindingList 的类,它进入伪虚拟模式与常规VirtualMode不同,当每一行被绘制时(例如当用户滚动时),DataGridView访问 IBindingList 上的索引器[]每个列的 PropertyDescriptor 各自的 GetValue 方法根据需要检索值。不会引发 CellValueNeeded 事件。在我们的例子中,我们在访问索引器时访问数据库,然后缓存该值,以便后续的重新绘制不会打到数据库。



我执行了类似的测试re:内存使用。 DataGridView 会分配一个数组,该数组是列表的大小(即1M行),但是数组中的每个项目最初都引用了一个DataGridViewRow,所以内存使用是可以接受的。当VirtualMode为true时,我不确定该行为是否相同。 通过在 GetValue 方法中立即返回 String.Empty ,我们可以消除滚动滞后如果行没有缓存,然后异步执行数据库查询。当异步请求完成时,您可以提出 IBindingList.ListChanged 事件来发信号给DataGridView它应该重新绘制单元格,除了这个时候从缓存中读取很容易可用。这样,用户界面永远不会被阻止等待数据库调用。



有一件事我们注意到,如果将DataSource或虚拟行数设置为之前将DataGridView添加到窗体 - 它将初始化时间缩短了一半。此外,请确保您将Row和Column自动调整设置为 None ,否则您将遇到额外的性能问题。



<旁注:我们在.NET应用程序中完成加载这样一个大表的方式是通过在SQL服务器上创建一个临时表,列出所需排序顺序中的主键以及IDENTITY(行)数字),然后持续连接以用于后续行请求。这自然需要时间来初始化(在相当快的SQL服务器上大约3-5秒),但是没有可用索引的知识,我们没有更好的选择。然后,在我们的ITypedList实现中,我们请求100行的页面,其中第50行是被绘制的行,这样我们限制每次访问索引器时执行的查询数量,并且我们给出外观在我们的应用程序中提供所有数据。



进一步阅读:



http://msdn.microsoft.com/en-us/library/ms404298.aspx



http://msdn.microsoft.com/en-us/library/system.componentmodel.ibindinglist.aspx


I have a SQL table containing currently 1 million rows that will grow over time.

There is a specific user requirement to present a sortable grid that displays all rows without paging. The user expects to be able to very quickly jump from row to row and top to bottom by using the scrollbar.

I am familiar with "virtual mode" grids that only present a visible subset of the overall data. They can provide excellent UI performance and minimal memory requirements, (I've even implemented an application using this technique many years ago).

The Windows Forms DataGridView provides a virtual mode that looks like it should be the answer. However unlike other virtual modes I've encountered, it still allocates memory for every row (confirmed in ProcessExplorer). Obviously this causes overall memory usage to needlessly greatly increase and, while allocating these rows, there is a noticeable delay. Scrolling performance also suffers on 1 million + rows.

A real virtual mode would have no need to allocate any memory for rows not being displayed. You just give it the total row count (eg 1,000,000) and all the grid does is scale the scrollbar accordingly. When it is first displayed the grid simply asks for data the first n (say 30) visible rows only, instantaneous display.

When the user scrolls the grid, a simple row offset and the number of visible rows are provided and can be used to retrieve data from the data store.

Here's an example of the DataGridView code I'm currently using:

public void AddVirtualRows(int rowCount)
{
    dataGridList.ColumnCount = 4;


    dataGridList.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.None;
    dataGridList.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.None;

    dataGridList.VirtualMode = true;

    dataGridList.RowCount = rowCount;

    dataGridList.CellValueNeeded += new DataGridViewCellValueEventHandler(dataGridList_CellValueNeeded);


}
void dataGridList_CellValueNeeded(object sender, DataGridViewCellValueEventArgs e)
{
    e.Value = e.RowIndex;
}

Am I missing anything here, or is the "virtual" mode of the DataGridView not really virtual at all?

[Update]

It looks like the good old ListView implements exactly the sort of virtual mode I'm looking for. But unfortunately the ListView does not have the cell formatting capabilities of the DataGridView, so I can't use it.

For others that might be able to, I tested it with a four column ListView (in Detail mode), VirtualMode= True and VirtualListSize =100,000,000 rows.

The list is displayed immediately with the first 30 rows visible. I can then scroll rapidly to the bottom of the list with no delay. The memory usage is constant 10 MB at all times.

解决方案

We just had a similar requirement to be able to display arbitrary, unindexed 1M+ row tables in our application with "very good" performance, using the stock DataGridView. At first I thought it wasn't possible, but with enough head scratching, we came up with something that works very well after spending days pouring over Reflector and .NET Profiler. This was difficult to do, but the results were well worth it.

The way we tackled this problem was by creating a class that implements ITypedList and IBindingList (you can call it LargeTableView, for example) to manage the asynchronous retrieval and caching of information from the database. We also created a single PropertyDescriptor-inheriting class (e.g. LargeTableColumnDescriptor) to retrieve data from each column.

When the DataGridView.DataSource property is set to a class implementing IBindingList, it goes into a pseudo-virtual mode that differs from regular VirtualMode, where as when each row is painted (such as when the user scrolls), the DataGridView accesses the indexer [] on the IBindingList and the respective GetValue methods on each column's PropertyDescriptor to retrieve the values as needed. The CellValueNeeded event is not raised. In our case, we access the database when the indexer is accessed, and then cache the value, so that subsequent re-paints don't hit the database.

I performed similar tests re: memory usage. The DataGridView does allocate an array that is the size of the list (i.e. 1M rows), however each item in the array initially references a single DataGridViewRow, so memory usage is acceptible. I am not sure if the behavior is the same when VirtualMode is true. We were able to eliminate scroll lag by immediately returning String.Empty in the GetValue method if the row is not cached, and then performing the database query asynchronously. When the async request is finished, you can raise the IBindingList.ListChanged event to signal to the DataGridView that it should repaint the cells, except this time reading from the cache which is readily available. That way, the UI is never blocked waiting for database calls.

One thing we noticed is that performance is significantly better if you set the DataSource or number of virtual rows before adding the DataGridView to the form - it cut initialization time in half. Also, make sure that you have both Row and Column autosizing set to None or else you will have additional performance problems.

Side note: the way we accomplished "loading" such a large table in our .NET application was by creating a temporary table on the SQL server that listed the primary keys in the desired sort order along with an IDENTITY (row number), and then persisting the connection for subsequent row requests. This naturally takes time to initialize (approx. 3-5s on a reasonably fast SQL server), but without knowledge of available indexes, we have no better alternative. Then, in our ITypedList implementation, we request rows in pages of 100 rows, where the 50th row is the row that is being painted, so that we limit the number of queries performed each time the indexer is accessed, and that we give the appearance of having all of the data available in our application.

Further reading:

http://msdn.microsoft.com/en-us/library/ms404298.aspx

http://msdn.microsoft.com/en-us/library/system.componentmodel.ibindinglist.aspx

这篇关于Windows Forms DataGridView是否实现真正的虚拟模式?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆