在 Windows 控件中显示大量数据
演示了一种在 Windows 可视化控件中显示大量数据的技术,以 ListView 为例。
引言
本文及附带的代码旨在演示一种在可视化控件中显示大量数据且性能下降极少的技术。
背景
在处理大量数据时,通常需要将这些数据呈现给用户。列表和树经常用于此目的。很多时候,程序员会尝试将整个数据集加载到一个控件中。当向这些控件添加的项目数量不断增加时,问题就会出现;内存使用量增加,性能下降,直到该解决方案不再可行。
本文提出了一种显示大量数据集且无性能下降的技术。
这项技术可以轻松地改编到各种 .NET 控件以及其他平台,例如 Java Swing 控件。
在 ListView 中显示大量数据的策略
此示例的方法是使用一个列表控件,该控件仅包含对用户可见的数据项,而不是整个数据集。使用了现有的 .NET ListView
组件,并结合了 VScrollBar
,该滚动条被“手动”调整以反映所表示的数据量。由于 ListView
只包含可以显示的数据项 (ListViewItem
) 的确切数量,因此内部垂直滚动条永远不应显示。因此,有必要管理一个单独的滑块,该滑块准确地反映所表示的数据量。这个控件可以被看作是一个在大量数据上上下移动的滑动窗口。
当 ListViewItem
移出视图时,它们会被删除;当新的 ListViewItem
进入视图时,它们会被添加,因此列表最多只包含用户可见的项目数量。
零碎的组件
HighPerformanceListView
是一个封装了 .NET ListView
控件和 VScrollBar
控件的新复合控件。此控件可以添加到工具箱并拖放到窗体上。为了以通用的方式以编程方式提供数据,该控件使用实现 IHighPerformanceListViewProvider
接口的对象。此对象提供了 ListView
控件所需的补充功能,用于确定正在显示的列标题,并提供代表一个数据项的 ListViewItem
。
public interface IHighPerformanceListViewProvider
{
List<ColumnHeaders> { get; }
void SortDataList(int sortColumnNumber, SortOrder sortOrder);
ListViewItem ConvertDataItemToListViewItem(int dataIndex);
int DataCount { get; }
HighPerformanceListView ControllingListView { get; set; }
}
提供了一个抽象类 ListViewProviderBase
,它实现了该接口的 ControllingListView
属性。
Using the Code
以下代码片段演示了如何与 HighPerformanceListView
控件进行交互
// Create a provider to supply data items, columns, etc. to
// the HighPerformanceListView
listViewProvider = new ListViewExampleProvider();
// Associate the new provider with the list view control:
highPerformanceListView.ListViewProvider = listViewProvider;
// List of data items to display in the list view control
// (This list is specific to the ListViewExampleProvider,
// and can be any type of data source in 'real-life'
List<DataItem> dataList;
// Set the application specific data list in the provider.
listViewProvider.DataList = dataList;
关注点
传递事件、方法和属性
由于 HighPerformanceListView
控件是封装了 .NET ListView
的 UserControl
,因此 ListView
特定的事件和属性被隐藏了。必须为用户控件创建传递属性、事件和方法,以便将信息传递给实际的 ListView
控件。传递项并不代表对 ListView
公开的项的完全重新创建,而仅仅是为了满足此示例的需要。
传递项组织在 HighPerformanceListView.PassThrough
文件中,该文件包含列表视图类的部分类定义。
传递事件示例
public event DrawListViewColumnHeaderEventHandler DrawColumnHeader
{
// Pass the DrawColumnHeader event through
// to the ListView.DrawColumnHeader
add { listView.DrawColumnHeader += value; }
remove { listView.DrawColumnHeader -= value; }
}
滑动窗口
HighPerformanceListView
中“滑动窗口”的概念由以下三个变量管理
// Index into the data list that represents the first item
// to be displayed in the list view.
// This represents the starting position of our
// sliding window moved over the data list.
private int displayStartIdx = 0;
// Number of rows from the data list that are being displayed
private int rowsDisplayed = 0;
// Maximum number of items (rows) that can be displayed in the list view
// (Depends on size of control on screen)
private int maxDisplayLines = 0;
刷新 ListView 中的项目
当控件被调整大小时、列被重新排序时,或视图滚动时,ListView
中包含的显示项需要更新。第一次尝试通过添加进入视图的项目或删除移出视图的项目来优化该过程。此过程导致了不可接受的闪烁。
相反,每次添加或删除项目时,整个视图都会刷新。这极大地提高了视觉性能。(参见:HighPerformanceListView.RefreshListViewItems()
)。
维护不在视图中的多个选定项目
当前控件不跟踪已移出视图的选定项目,但如果需要,可以轻松添加此功能。
ListView.VirtualMode
属性
本文的目的是提出一些处理大量数据的想法,无论使用何种控件。应该注意的是,ListView
有一个 VirtualMode
属性。通过使用此属性并提供额外的事件代码,可以实现类似的性能提升。
有关此方法的示例,请参阅:ListView in VirtualMode and checkboxes 作者:Alphons van der Heijden。
历史
- 2009 年 6 月 15 日:创建用户控件和示例项目。
- 2009 年 7 月 1 日:更新文章以引用 Alphons van der Heijden 关于使用
ListView.VirtualMode
的文章。