缩放 Windows 窗体 DataGridView 控件的最佳做法
DataGridView 控件旨在提供最大的可伸缩性。 如果需要显示大量数据,则应遵循本主题中所述的准则,以避免消耗大量内存或降低用户界面 (UI) 的响应能力。 本主题讨论以下问题:
高效使用单元格样式
高效使用快捷菜单
高效使用自动调整大小
高效使用选定的单元格、行和列集合
使用共享行
防止行变成非共享行
如果你有特殊的性能需求,可以实现虚拟模式并提供自己的数据管理操作。 有关详细信息,请参阅 Windows 窗体 DataGridView 控件中的数据显示模式。
高效使用单元格样式
每个单元格、行和列都可以有自己的样式信息。 样式信息存储在 DataGridViewCellStyle 对象中。 为许多单独的 DataGridView 元素创建单元格样式对象可能效率低下,尤其是在处理大量数据时。 为避免性能影响,请遵循以下准则:
避免为单个 DataGridViewCell 或 DataGridViewRow 对象设置单元格样式属性。 这包括由 RowTemplate 属性指定的行对象。 从行模板克隆的每个新行都将收到自己的模板单元格样式对象副本。 为了获得最大的可伸缩性,请在 DataGridView 级别设置单元格样式属性。 例如,设置 DataGridView.DefaultCellStyle 属性而不是 DataGridViewCell.Style 属性。
如果某些单元格需要默认格式设置以外的格式设置,请在各组单元格、行或列中使用相同的 DataGridViewCellStyle 实例。 避免为单个单元格、行和列直接设置 DataGridViewCellStyle 类型的属性。 有关单元格样式共享的示例,请参阅如何:设置 Windows 窗体 DataGridView 控件的默认单元格样式。 还可以通过处理 CellFormatting 事件处理器来避免单独设置单元格样式时的性能损失。 有关示例,请参阅如何:自定义 Windows 窗体 DataGridView 控件中的数据格式设置。
确定单元格样式时,使用 DataGridViewCell.InheritedStyle 属性而不是 DataGridViewCell.Style 属性。 如果尚未使用 Style 属性,访问该属性会创建 DataGridViewCellStyle 类的新实例。 此外,如果某些样式继承自行、列或控件,则此对象可能不包含单元格的完整样式信息。 有关单元格样式继承的详细信息,请参阅 Windows 窗体 DataGridView 控件中的单元格样式。
高效使用快捷菜单
每个单元格、行和列都可以有自己的快捷菜单。 DataGridView 控件中的快捷菜单由 ContextMenuStrip 控件表示。 与单元格样式对象一样,为许多单独的 DataGridView 元素创建快捷菜单会对性能产生负面影响。 为避免这种性能损失,请遵循以下准则:
避免为单个单元格和行创建快捷菜单。 这包括行模板,向控件添加新行时,该模板连同其快捷菜单一起被克隆。 为了获得最大的可伸缩性,请仅使用控件的 ContextMenuStrip 属性为整个控件指定一个快捷菜单。
如果需要多个行或单元格的多个快捷菜单,请处理 CellContextMenuStripNeeded 或 RowContextMenuStripNeeded 事件。 你可以使用这些事件自行管理快捷菜单对象,从而优化性能。
高效使用自动调整大小
行、列和标题可以随着单元格内容的变化自动调整大小,以便显示单元格的全部内容而不进行剪裁。 更改调整大小模式也可以调整行、列和标题的大小。 若要确定正确的大小,DataGridView 控件必须检查它必须容纳的每个单元格的值。 在处理大型数据集时,此分析可能会在自动调整大小时对控件的性能产生负面影响。 为避免性能损失,请遵循以下准则:
避免在包含大量行的 DataGridView 控件上使用自动调整大小。 如果使用自动调整大小,请仅根据显示的行调整大小。 在虚拟模式下也只使用显示的行。
对于行和列,使用 DataGridViewAutoSizeRowsMode、DataGridViewAutoSizeColumnsMode 和 DataGridViewAutoSizeColumnMode 枚举的
DisplayedCells
或DisplayedCellsExceptHeaders
字段。对于行标题,使用 DataGridViewRowHeadersWidthSizeMode 枚举的 AutoSizeToDisplayedHeaders 或 AutoSizeToFirstHeader 字段。
为了获得最大的可伸缩性,请关闭自动调整大小并使用程序化调整大小。
有关详细信息,请参阅在 Windows 窗体 DataGridView 控件中调整选项大小。
高效使用选定的单元格、行和列集合
在选定了大量单元格的情况下,SelectedCells 集合无法高效执行。 SelectedRows 和 SelectedColumns 集合也可能效率低下,不过程度较低,因为在典型的 DataGridView 控件中,行数远少于单元格数,而列数远少于行数。 为了避免在使用这些集合时出现性能损失,请遵循以下准则:
若要在访问 SelectedCells 集合的内容之前确定是否已选择 DataGridView 中的所有单元格,请检查 AreAllCellsSelected 方法的返回值。 但是,请注意,此方法可能会导致行变成非共享行。 有关详细信息,请参阅下一部分。
避免使用 System.Windows.Forms.DataGridViewSelectedCellCollection 的 Count 属性来确定所选单元格的数量。 相反,应使用 DataGridView.GetCellCount 方法并传入 DataGridViewElementStates.Selected 值。 同样,使用 DataGridViewRowCollection.GetRowCount 和 DataGridViewColumnCollection.GetColumnCount 方法来确定所选元素的数量,而不是访问所选行和列集合。
避免使用基于单元格的选择模式。 相反,应将 DataGridView.SelectionMode 属性设置为 DataGridViewSelectionMode.FullRowSelect 或 DataGridViewSelectionMode.FullColumnSelect。
使用共享行
DataGridView 控件通过共享行实现了高效的内存使用。 通过共享 DataGridViewRow 类的实例,行将尽可能多地共享其外观和行为的相关信息。
虽然共享行实例可以节省内存,但行很容易变成非共享行。 例如,每当用户直接与单元格交互时,它的行就会变成非共享行。 因为这是无法避免的,所以本主题中的准则仅适用于以下情况:处理大量数据,并且每次运行程序时用户与相对较少的一部分数据进行交互。
如果行的任何单元格包含值,则该行不能在未绑定的 DataGridView 控件中共享。 当 DataGridView 控件绑定到外部数据源时,或当你实现虚拟模式并提供自己的数据源时,单元格值存储在控件外部而不是单元格对象中,从而允许共享行。
仅当可以从行的状态和包含单元格的列的状态确定行对象的所有单元格的状态时,才能共享行对象。 如果更改单元格的状态,使其无法再从其行和列的状态推断出来,则无法共享该行。
例如,在以下任一情况下,无法共享行:
行包含一个未在选定列中的选定单元格。
行包含一个设置了 ToolTipText 或 ContextMenuStrip 属性的单元格。
行包含一个设置了 Items 属性的 DataGridViewComboBoxCell。
在绑定模式或虚拟模式下,可以通过处理 CellToolTipTextNeeded 和 CellContextMenuStripNeeded 事件,为各个单元格提供工具提示和快捷菜单。
每当向 DataGridViewRowCollection 添加行时,DataGridView 控件都会自动尝试使用共享行。 使用以下准则来确保行可共享:
避免调用 DataGridView.Rows 集合的 Add 方法的
Add(Object[])
重载和 Insert 方法的Insert(Object[])
重载。 这些重载会自动创建非共享行。请确保在以下情况下可以共享 DataGridView.RowTemplate 属性中指定的行:
调用 DataGridView.Rows 集合的 Add 方法的
Add()
或Add(Int32)
重载或 Insert 方法的Insert(Int32,Int32)
重载时。增大 DataGridView.RowCount 属性值时。
设置 DataGridView.DataSource 属性时。
确保在调用 DataGridView.Rows 集合的 AddCopy、AddCopies、InsertCopy 和 InsertCopies 方法时,可以共享
indexSource
参数指示的行。确保在调用 DataGridView.Rows 集合的 Add 方法的
Add(DataGridViewRow)
重载、AddRange 方法、Insert 方法的Insert(Int32,DataGridViewRow)
重载和 InsertRange 方法时,可以共享指定的行。
若要确定行是否共享,请使用 DataGridViewRowCollection.SharedRow 方法检索行对象,然后检查对象的 Index 属性。 共享行的 Index 属性值始终为 –1。
防止行变成非共享行
由于代码或用户操作,共享行可能会变成非共享行。 为了避免性能影响,应避免导致行变成非共享行。 在应用程序开发期间,可以处理 RowUnshared 事件以确定行何时变成非共享行。 这在调试行共享问题时很有用。
若要防止行变成非共享行,请遵循以下准则:
避免为 Rows 集合编制索引或使用
foreach
循环对其进行循环访问。 通常不需要直接访问行。 对行进行操作的 DataGridView 方法使用行索引参数而不是行实例。 此外,与行相关的事件的处理程序接收具有行属性的事件参数对象,你可以使用这些对象来操作行,而不会导致它们变成非共享行。如果需要访问行对象,请使用 DataGridViewRowCollection.SharedRow 方法并传入行的实际索引。 但是,请注意,修改通过此方法检索到的共享行对象将修改共享此对象的所有行。 但是,新记录的行不与其他行共享,因此当你修改任何其他行时,它不会受到影响。 另请注意,由共享行表示的不同行可能具有不同的快捷菜单。 若要从共享行实例中检索正确的快捷菜单,请使用 GetContextMenuStrip 方法并传入行的实际索引。 如果改为访问共享行的 ContextMenuStrip 属性,它将使用共享行索引 -1 并且不会检索正确的快捷菜单。
避免为 DataGridViewRow.Cells 集合编制索引。 直接访问单元格将导致其父行变成非共享行,从而实例化新的 DataGridViewRow。 与单元格相关的事件的处理程序接收具有单元格属性的事件参数对象,你可以使用这些对象来操作单元格,而不会导致行变成非共享行。 你还可以使用 CurrentCellAddress 属性来检索当前单元格的行索引和列索引,而无需直接访问单元格。
避免使用基于单元格的选择模式。 这些模式会导致行变成非共享行。 相反,应将 DataGridView.SelectionMode 属性设置为 DataGridViewSelectionMode.FullRowSelect 或 DataGridViewSelectionMode.FullColumnSelect。
不要处理 DataGridViewRowCollection.CollectionChanged 或 DataGridView.RowStateChanged 事件。 这些事件会导致行变成非共享行。 此外,不要调用引发这些事件的 DataGridViewRowCollection.OnCollectionChanged 或 DataGridView.OnRowStateChanged 方法。
当 DataGridView.SelectionMode 属性值为 FullColumnSelect、ColumnHeaderSelect、FullRowSelect 或 RowHeaderSelect 时,不要访问 DataGridView.SelectedCells 集合。 这会导致所有选定行变成非共享行。
不要调用 DataGridView.AreAllCellsSelected 方法。 此方法可能会导致行变成非共享行。
当 DataGridView.SelectionMode 属性值为 CellSelect 时,不要调用 DataGridView.SelectAll 方法。 这会导致所有行变成非共享行。
当单元格所在列中的相应属性设置为
true
时,不要将单元格的 ReadOnly 或 Selected 属性设置为false
。 这会导致所有行变成非共享行。不要访问 DataGridViewRowCollection.List 属性。 这会导致所有行变成非共享行。
不要调用 Sort 方法的
Sort(IComparer)
重载。 使用自定义比较器进行排序会导致所有行变成非共享行。
另请参阅
反馈
https://aka.ms/ContentUserFeedback。
即将发布:在整个 2024 年,我们将逐步淘汰作为内容反馈机制的“GitHub 问题”,并将其取代为新的反馈系统。 有关详细信息,请参阅:提交和查看相关反馈