Windows窗体DataGridView控件的性能调优.net 4.5
在处理大量数据时, DataGridView 控制可以消耗大量的内存开销,除非你仔细地使用它。 在客户有限的内存,你可以避免一些这方面的开销,避免内存成本高的特性。 你也可以管理的部分或全部数据维护和检索任务自己使用虚拟模式为了定制的内存使用情况。
在这一节中
- 最佳实践扩展Windows窗体DataGridView控件
-
描述如何使用 DataGridView 控制,避免了不必要的内存使用和在处理大量数据时的性能损失。
- 虚拟模式的Windows窗体DataGridView控件
-
描述如何使用虚拟模式来补充或替换标准的数据绑定机制。
- 介绍:在Windows窗体DataGridView控件中实现虚拟模式
-
描述如何实现事件处理程序几个虚拟方式。 还演示了如何实现行级回滚并为用户编辑提交。
- 实现虚拟模式的即时数据加载Windows窗体DataGridView控件
-
描述如何加载数据的需求,这是非常有用的,当你有更多的数据比可用来显示客户端内存可以存储。
1.最佳实践扩展Windows窗体DataGridView控件
DataGridView控制的目的是提供最大的可伸缩性。 如果你需要显示大量数据,您应该遵循的指导方针中描述这个话题避免消耗大量内存或有辱人格的用户界面(UI)的响应能力。 本主题讨论以下问题:
有效使用单元格样式
有效使用快捷菜单
有效地使用自动调整
使用选定的单元格、行和列的集合
使用共享的行
防止行成为非共享
如果你有特殊的性能需求,您可以实现虚拟模式,提供自己的数据管理操作。 有关更多信息,请参见 数据显示模式在Windows窗体DataGridView控件。
有效使用单元格样式
每个单元格、行和列可以有它自己的样式信息。 信息存储在风格 DataGridViewCellStyle对象。 创建单元格样式对象对于许多个人DataGridView元素可以是低效的,特别是当处理大量的数据。 为了避免影响性能,使用以下指南:
避免个人设置单元格样式属性 DataGridViewCell或 DataGridViewRow对象。 这包括指定的row对象 RowTemplate财产。 每个新行克隆从行模板将收到自己的模板复制的单元格样式对象。 最大的可伸缩性,在设置单元格样式属性 DataGridView的水平。 例如,设置 DataGridView 。 DefaultCellStyle财产而不是 DataGridViewCell 。 风格财产。
如果默认格式以外的一些细胞需要格式化,使用相同的 DataGridViewCellStyle跨组单元实例、行或列。 避免直接设置属性的类型DataGridViewCellStyle在单个细胞、行和列。 单元格样式共享的一个示例,请参阅 如何:设置默认细胞为Windows窗体DataGridView控件风格。 你也可以避免性能损失在设置单元格样式时单独处理 CellFormatting事件处理程序。 例如,看到的 如何:自定义数据格式在Windows窗体DataGridView控件。
在确定细胞的风格,使用 DataGridViewCell 。 InheritedStyle财产而不是 DataGridViewCell 。 风格财产。 访问 风格房地产的创建一个新的实例 DataGridViewCellStyle类如果属性尚未使用。 此外,该对象可能不包含细胞的完整样式信息如果一些风格继承了行,列,或控制。 单元格样式继承的更多信息,请参阅 细胞在Windows窗体DataGridView控件风格。
有效使用快捷菜单
每个单元格、行和列可以有它自己的快捷菜单。 快捷菜单中 DataGridView控制用 contextmenustrip控制。 与单元格样式对象一样,许多个人创建快捷方式菜单 DataGridView元素将对性能造成负面影响。 为了避免这种惩罚,使用以下指南:
避免创建快捷方式菜单为单个细胞和行。 这包括行模板,克隆及其快捷菜单当新行添加到控制。 最大的可伸缩性,只使用控制的contextmenustrip财产为整个控制指定一个快捷菜单。
如果你需要多个快捷方式菜单为多个行或细胞,处理 CellContextMenuStripNeeded或 RowContextMenuStripNeeded事件。 这些事件让你管理自己的快捷菜单对象,允许您调整性能。
有效地使用自动调整
行,列,可以自动调整大小和标题单元格内容更改,这样整个细胞显示没有剪切的内容。 改变分级模式还可以调整行、列和标题。 确定正确的尺寸, DataGridView控制必须检查每个单元格的值,它必须适应。 在处理大型数据集时,这种分析可能会对性能造成负面影响的控制自动调整发生时。 为了避免性能损失,使用以下指南:
-
避免使用自动分级 DataGridView控制大量的行。 如果你使用自动分级,只有调整基于显示的行。 只使用在虚拟显示的行模式。
行和列,使用 DisplayedCells 或 DisplayedCellsExceptHeaders 场的 DataGridViewAutoSizeRowsMode,DataGridViewAutoSizeColumnsMode, DataGridViewAutoSizeColumnMode枚举。
行标题,使用 AutoSizeToDisplayedHeaders或 AutoSizeToFirstHeader场的 DataGridViewRowHeadersWidthSizeMode枚举。
最大的可伸缩性,关掉自动分级和使用程序化的调整。
有关更多信息,请参见 在Windows窗体DataGridView控件大小的选择。
使用选定的单元格、行和列的集合
的 SelectedCells不执行有效地选择大集合。 的 SelectedRows和 SelectedColumns集合也可以是低效的,虽然从一个较小的程度上,因为有很多行比细胞在一个典型的少 DataGridView控制,许多列少于行。 为了避免性能损失在处理这些集合时,使用以下指南:
确定所有的细胞 DataGridView被选在你访问的内容 SelectedCells收集、检查的返回值 AreAllCellsSelected方法。 但是请注意,这个方法会导致行成为非共享。 有关更多信息,请参见下一节。
避免使用 数财产的 System.Windows.Forms 。 DataGridViewSelectedCellCollection确定选择的细胞的数量。 相反,使用DataGridView 。 GetCellCount方法和通过 DataGridViewElementStates 。 选择价值。 同样,使用 DataGridViewRowCollection 。getrowcount和 DataGridViewColumnCollection 。 GetColumnCount方法来确定选定元素的数量,而不是访问选定的行和列的集合。
避免细胞选择模式。 相反,设置 DataGridView 。 SelectionMode财产 DataGridViewSelectionMode 。 FullRowSelect或DataGridViewSelectionMode 。 FullColumnSelect。
使用共享的行
中实现高效的内存使用 DataGridView控制通过共享行。 行将分享尽可能多的信息关于他们的外观和行为通过共享的实例DataGridViewRow类。
可以很容易地共享行实例节省内存,行成为非共享。 例如,每当用户直接与细胞相互作用,成为非共享它的行。 因为这无法避免,这一主题的指导方针是有用的只有当处理大量数据,只有当用户将与一个相对较小的数据每次运行你的程序的一部分。
一行不能在一个共享的 DataGridView如果它的任何细胞包含值控制。 当 DataGridView控件绑定到一个外部数据源或当你实现虚拟模式,提供自己的数据源,细胞外的值存储控制,而不是在细胞对象,允许行共享。
对象只能共享一行如果可以确定所有的细胞状态的行和列的状态包含细胞。 如果你改变一个细胞的状态,这样就可以不再是推断从国家的行和列,行不能共享。
例如,一行不能共享在下列情况下:
的行包含一个选择单元选择的列。
的行包含一个单元 ToolTipText或 contextmenustrip属性集。
一行包含一个 DataGridViewComboBoxCell与它的 项目属性集。
在绑定模式或虚拟模式,您可以为单个细胞提供工具提示和快捷菜单处理 CellToolTipTextNeeded和 CellContextMenuStripNeeded事件。
的 DataGridView只要控制将自动尝试使用共享一排排被添加到 DataGridViewRowCollection。 使用以下指南以确保行共享:
避免调用 Add(Object[]) 超载的 添加方法和 插入(Object[]) 超载的 插入的方法 DataGridView 。 行收集。 这些过载自动创建专有的行。
-
确保在指定的行 DataGridView 。 RowTemplate房地产可以共享在下列情况下:
当调用 add() 或 添加(Int32) 过载的 添加法或者 插入(Int32,Int32) 超载的 插入的方法 DataGridView 。 行收集。
当增加的价值 DataGridView 。 rowcount财产。
当设置 DataGridView 。 数据源财产。
确保的行表示 indexSource 调用时参数可以共享 AddCopy, AddCopies, InsertCopy, InsertCopies的方法 DataGridView 。 行收集。
确保指定行或行调用时可以共享 添加(DataGridViewRow) 超载的 添加方法, addrange方法, 插入(Int32,DataGridViewRow) 超载的 插入方法, InsertRange的方法 DataGridView 。 行收集。
确定一行是否共享,使用 DataGridViewRowCollection 。 SharedRow方法检索行对象,然后检查对象的 指数财产。 总是有一个共享的行 指数属性值为1。
防止行成为非共享
共享行可以成为非共享的代码或用户操作。 为了避免性能影响,你应该避免造成行成为非共享。 在应用程序开发期间,您可以处理的RowUnshared事件确定当行成为非共享。 这是有用的调试row-sharing问题时。
为了防止行成为非共享,使用以下指南:
避免索引 行或者遍历集合 foreach 循环。 您通常不需要直接访问的行。 DataGridView方法,操作行采取行索引参数而不是行实例。 此外,row-related事件接收事件参数处理程序对象与行属性,您可以使用它们来操纵行没有使它们成为非共享。
如果你需要访问对象,连续使用 DataGridViewRowCollection 。 SharedRow方法,并通过实际的行索引。 但是请注意,修改共享行对象检索通过这种方法将修改的所有行,分享这个对象。 行新记录不是共享与其他行,然而,所以它将不会受到影响,当你修改其他行。 还要注意不同的行代表一个共享行可能有不同的快捷菜单。 获取正确的快捷菜单从共享行实例,使用 GetContextMenuStrip方法,并通过实际的行索引。 如果你访问的共享行 contextmenustrip属性相反,它将使用共享的行索引1和不会检索正确的快捷菜单。
避免索引 DataGridViewRow 。 细胞收集。 直接访问一个细胞将会导致母公司行成为非共享,实例化一个新的 DataGridViewRow。 闲暇的事件接收事件参数处理程序对象与细胞属性,您可以使用它们来操纵细胞不会造成行成为非共享。 你也可以使用CurrentCellAddress属性检索当前单元格的行和列索引没有直接访问单元。
避免细胞选择模式。 这些模式导致行成为非共享。 相反,设置 DataGridView 。 SelectionMode财产 DataGridViewSelectionMode。 FullRowSelect或 DataGridViewSelectionMode 。 FullColumnSelect。
不处理 DataGridViewRowCollection 。 CollectionChanged或 DataGridView 。 RowStateChanged事件。 这些事件导致行变得非共享。 同时,不打电话 DataGridViewRowCollection 。 OnCollectionChanged或 DataGridView 。 OnRowStateChanged方法,提高这些事件。
不能访问 DataGridView 。 SelectedCells集合的时候 DataGridView 。 SelectionMode属性值是 FullColumnSelect,ColumnHeaderSelect, FullRowSelect,或 RowHeaderSelect。 这将导致所有选定行成为非共享。
不叫 DataGridView 。 AreAllCellsSelected方法。 这种方法会导致行成为非共享。
不叫 DataGridView 。 selectAll方法时, DataGridView 。 SelectionMode属性值是 CellSelect。 这将导致所有行成为非共享。
不能访问 DataGridViewRowCollection 。 列表财产。 这将导致所有行成为非共享。
不叫 排序(左) 超载的 排序方法。 排序与一个定制的比较器使所有行成为非共享。
2.虚拟模式的Windows窗体DataGridView控件
使用虚拟模式,您可以管理之间的交互 DataGridView控制和自定义数据缓存。 实现虚拟模式,设置 virtualmode财产 真正的 和处理一个或多个事件中描述的这个话题。 你通常会处理至少 CellValueNeeded 事件,使控制查找数据缓存中的值。
绑定模式和虚拟模式
虚拟模式是必要的,只有当你需要补充或更换绑定模式。 在绑定模式下,设置 数据源产权和控制自动加载数据从指定源和提交用户更改回去。 您可以控制显示哪些列绑定的,和数据源本身通常处理操作,如排序。
补充绑定模式
可以补充绑定模式通过显示的列的列。 这是有时被称为“混合模式”,是用于显示诸如计算值或用户界面(UI)控件。
因为未绑定列外的数据源,他们忽视了数据源的排序操作。 因此,当你在混合模式启用排序,您必须在本地缓存管理的数据和实现让虚拟模式DataGridView控制与之交互。
关于使用虚拟模式的更多信息维护未绑定列中的值,看到的例子 DataGridViewCheckBoxColumn 。 ThreeState财产和System.Windows.Forms 。 DataGridViewComboBoxColumn类引用主题。
更换绑定模式
如果绑定模式不能满足您的性能需求,您可以通过虚拟方式管理你所有的数据在一个自定义缓存事件处理程序。 例如,您可以使用虚拟模式来实现即时数据加载机制,从网络数据库检索只有尽可能多的数据作为最优性能是必要的。 这个场景是特别有用的在处理大量数据时在一个缓慢的网络连接或与客户端机器的数量有限的内存或者存储空间。
关于使用虚拟模式的更多信息在实时的情况下,看到的 实现虚拟模式的即时数据加载Windows窗体DataGridView控件。
虚拟方式事件
如果您的数据是只读的, CellValueNeeded 事件可能是唯一事件需要处理。 额外的虚拟方式事件让你使特定功能用户编辑、添加和删除行,行级的事务。
一些标准 DataGridView事件(如当用户添加或删除行发生的事件,或当细胞值编辑,解析,验证,或格式化的)是有用的在虚拟模式,。 你也可以处理事件,让你保持值不是通常存储在一个绑定数据源,如细胞提示文本,细胞和行错误文本,细胞和行快捷菜单数据,和行高数据。
为更多的信息关于实现虚拟模式管理读/写数据行级提交范围,明白了 介绍:在Windows窗体DataGridView控件中实现虚拟模式。
为例,实现了虚拟模式具有承诺范围,看到 virtualmode属性引用主题。
只有当发生以下事件 virtualmode属性设置为 真正的 。
事件 |
描述 |
---|---|
控制使用的检索数据缓存的一个细胞值显示。 这个事件只发生在未绑定列细胞。 |
|
使用的控制提交用户输入一个细胞到数据缓存。 这个事件只发生在未绑定列细胞。 调用 UpdateCellValue当改变缓存值之外的一个方法 CellValuePushed事件处理程序,以确保当前值显示在控制和自动分级模式目前在应用任何效果。 |
|
使用的控制来表示数据缓存需要一个新行。 |
|
连续使用的控制,以确定任何未提交的更改。 |
|
控制用于显示一行应该回归其缓存值。 |
以下事件是有用的在虚拟模式,但可以不管 virtualmode属性设置。
事件 |
描述 |
---|---|
使用的控制指示当行被删除或添加,让你更新相应数据缓存。 |
|
使用的控制格式的单元格值显示,解析和验证用户输入。 |
|
控制用于检索时细胞提示文本 数据源属性设置或 virtualmode属性是 真正的 。 细胞只有当显示工具提示 ShowCellToolTips属性值是 真正的 。 |
|
控制检索单元格或行错误使用的文本的时候 数据源属性设置或 virtualmode属性是 真正的 。 调用 UpdateCellErrorText法或者 UpdateRowErrorText方法当你改变细胞或行错误文本,以确保当前值显示在控制。 细胞和行错误符号时显示 ShowCellErrors和 ShowRowErrors属性值是 真正的 。 |
|
使用的控制来检索一个细胞或行 contextmenustrip当控制 数据源属性设置或 virtualmode属性是真正的 。 |
|
使用的控制来检索或存储行高信息在数据缓存。 调用 UpdateRowHeightInfo方法当改变缓存行之外的高度信息 RowHeightInfoPushed事件处理程序,以确保当前值的显示控制。 |
最佳实践在虚拟模式
如果你是实现虚拟模式,以有效地处理大量的数据,你也想确保高效的工作 DataGridView控制自己。 更多信息的有效利用细胞风格,自动分级,选择,和行共享,明白了 最佳实践扩展Windows窗体DataGridView控件。
3.介绍:在Windows窗体DataGridView控件中实现虚拟模式
当你想要显示非常大量的表格数据 DataGridView控制,您可以设置 virtualmode财产 真正的 和显式地管理控制的交互数据存储。 这允许您调整控制在这种情况下的性能。
的 DataGridView控制提供了一些事件,你可以处理与自定义数据存储交互。 这个介绍将指导您完成这些事件处理程序实现的过程。 这个话题中的代码示例使用一个非常简单的数据源出于演示目的。 在生产环境中,您通常会只行你需要显示加载到缓存中,并处理 DataGridView事件进行交互和更新缓存。 有关更多信息,请参见 实现虚拟模式的即时数据加载Windows窗体DataGridView控件
复制这个主题作为一个清单中的代码,看看 如何:实现虚拟模式在Windows窗体DataGridView控件。
创建表单
实现虚拟模式
-
创建一个类,来源于 形式并包含一个 DataGridView控制。
下面的代码包含了一些基本的初始化。 它声明一些变量,将在后面使用步骤,提供了一个 主要 方法,并提供了一个简单的表单布局在类的构造函数。
using System;
using System.Windows.Forms; public class Form1 : Form
{
private DataGridView dataGridView1 = new DataGridView(); // Declare an ArrayList to serve as the data store.
private System.Collections.ArrayList customers =
new System.Collections.ArrayList(); // Declare a Customer object to store data for a row being edited.
private Customer customerInEdit; // Declare a variable to store the index of a row being edited.
// A value of -1 indicates that there is no row currently in edit.
private int rowInEdit = -; // Declare a variable to indicate the commit scope.
// Set this value to false to use cell-level commit scope.
private bool rowScopeCommit = true; [STAThreadAttribute()]
public static void Main()
{
Application.Run(new Form1());
} public Form1()
{
// Initialize the form.
this.dataGridView1.Dock = DockStyle.Fill;
this.Controls.Add(this.dataGridView1);
this.Load += new EventHandler(Form1_Load);
this.Text = "DataGridView virtual-mode demo (row-level commit scope)";
} ... }
2. 实现一个表单的处理程序 负载事件,初始化 DataGridView控制和填充数据存储与样本值。
private void Form1_Load(object sender, EventArgs e)
{
// Enable virtual mode.
this.dataGridView1.VirtualMode = true; // Connect the virtual-mode events to event handlers.
this.dataGridView1.CellValueNeeded += new
DataGridViewCellValueEventHandler(dataGridView1_CellValueNeeded);
this.dataGridView1.CellValuePushed += new
DataGridViewCellValueEventHandler(dataGridView1_CellValuePushed);
this.dataGridView1.NewRowNeeded += new
DataGridViewRowEventHandler(dataGridView1_NewRowNeeded);
this.dataGridView1.RowValidated += new
DataGridViewCellEventHandler(dataGridView1_RowValidated);
this.dataGridView1.RowDirtyStateNeeded += new
QuestionEventHandler(dataGridView1_RowDirtyStateNeeded);
this.dataGridView1.CancelRowEdit += new
QuestionEventHandler(dataGridView1_CancelRowEdit);
this.dataGridView1.UserDeletingRow += new
DataGridViewRowCancelEventHandler(dataGridView1_UserDeletingRow); // Add columns to the DataGridView.
DataGridViewTextBoxColumn companyNameColumn = new
DataGridViewTextBoxColumn();
companyNameColumn.HeaderText = "Company Name";
companyNameColumn.Name = "Company Name";
DataGridViewTextBoxColumn contactNameColumn = new
DataGridViewTextBoxColumn();
contactNameColumn.HeaderText = "Contact Name";
contactNameColumn.Name = "Contact Name";
this.dataGridView1.Columns.Add(companyNameColumn);
this.dataGridView1.Columns.Add(contactNameColumn);
this.dataGridView1.AutoSizeColumnsMode =
DataGridViewAutoSizeColumnsMode.AllCells; // Add some sample entries to the data store.
this.customers.Add(new Customer(
"Bon app'", "Laurence Lebihan"));
this.customers.Add(new Customer(
"Bottom-Dollar Markets", "Elizabeth Lincoln"));
this.customers.Add(new Customer(
"B's Beverages", "Victoria Ashworth")); // Set the row count, including the row for new records.
this.dataGridView1.RowCount = ;
}
3. 实现一个处理程序 CellValueNeeded事件,从数据存储中检索请求的细胞值或 客户 当前在编辑对象。
这个事件发生时 DataGridView控制细胞需要油漆。
private void dataGridView1_CellValueNeeded(object sender,
System.Windows.Forms.DataGridViewCellValueEventArgs e)
{
// If this is the row for new records, no values are needed.
if (e.RowIndex == this.dataGridView1.RowCount - ) return; Customer customerTmp = null; // Store a reference to the Customer object for the row being painted.
if (e.RowIndex == rowInEdit)
{
customerTmp = this.customerInEdit;
}
else
{
customerTmp = (Customer)this.customers[e.RowIndex];
} // Set the cell value to paint using the Customer object retrieved.
switch (this.dataGridView1.Columns[e.ColumnIndex].Name)
{
case "Company Name":
e.Value = customerTmp.CompanyName;
break; case "Contact Name":
e.Value = customerTmp.ContactName;
break;
}
}
4.实现一个处理程序 CellValuePushed事件中存储一个编辑单元格值 客户 对象代表行进行编辑。 这个事件发生时只要用户提交一个细胞值变化。
private void dataGridView1_CellValuePushed(object sender,
System.Windows.Forms.DataGridViewCellValueEventArgs e)
{
Customer customerTmp = null; // Store a reference to the Customer object for the row being edited.
if (e.RowIndex < this.customers.Count)
{
// If the user is editing a new row, create a new Customer object.
if (this.customerInEdit == null)
{
this.customerInEdit = new Customer(
((Customer)this.customers[e.RowIndex]).CompanyName,
((Customer)this.customers[e.RowIndex]).ContactName);
}
customerTmp = this.customerInEdit;
this.rowInEdit = e.RowIndex;
}
else
{
customerTmp = this.customerInEdit;
} // Set the appropriate Customer property to the cell value entered.
String newValue = e.Value as String;
switch (this.dataGridView1.Columns[e.ColumnIndex].Name)
{
case "Company Name":
customerTmp.CompanyName = newValue;
break; case "Contact Name":
customerTmp.ContactName = newValue;
break;
}
}
5. 实现一个处理程序 NewRowNeeded创建一个新的事件 客户 对象代表一个新创建的行。
这个事件发生时用户输入新记录的行。
private void dataGridView1_NewRowNeeded(object sender,
System.Windows.Forms.DataGridViewRowEventArgs e)
{
// Create a new Customer object when the user edits
// the row for new records.
this.customerInEdit = new Customer();
this.rowInEdit = this.dataGridView1.Rows.Count - ;
}
6. 实现一个处理程序 RowValidated事件,保存新的或修改的行数据存储。
这个事件发生时用户更改当前行。
private void dataGridView1_RowValidated(object sender,
System.Windows.Forms.DataGridViewCellEventArgs e)
{
// Save row changes if any were made and release the edited
// Customer object if there is one.
if (e.RowIndex >= this.customers.Count &&
e.RowIndex != this.dataGridView1.Rows.Count - )
{
// Add the new Customer object to the data store.
this.customers.Add(this.customerInEdit);
this.customerInEdit = null;
this.rowInEdit = -;
}
else if (this.customerInEdit != null &&
e.RowIndex < this.customers.Count)
{
// Save the modified Customer object in the data store.
this.customers[e.RowIndex] = this.customerInEdit;
this.customerInEdit = null;
this.rowInEdit = -;
}
else if (this.dataGridView1.ContainsFocus)
{
this.customerInEdit = null;
this.rowInEdit = -;
}
}
7. 实现一个处理程序 RowDirtyStateNeeded事件表明是否 CancelRowEdit事件将发生,当用户信号连续降级在编辑模式下按下ESC两次或一次以外的编辑模式。
默认情况下, CancelRowEdit发生在连续降级当任何细胞已经被修改,除非在当前行 QuestionEventArgs 。 响应属性设置为 真正的 在 RowDirtyStateNeeded事件处理程序。 这个事件非常有用当提交范围是在运行时确定的。
private void dataGridView1_RowDirtyStateNeeded(object sender,
System.Windows.Forms.QuestionEventArgs e)
{
if (!rowScopeCommit)
{
// In cell-level commit scope, indicate whether the value
// of the current cell has been modified.
e.Response = this.dataGridView1.IsCurrentCellDirty;
}
}
8. 实现一个处理程序 CancelRowEdit事件,丢弃的值 客户 对象代表当前行。
这个事件发生在用户信号连续降级在编辑模式下按下ESC两次或一次以外的编辑模式。 这个事件不发生如果没有修改当前行细胞或者的价值 QuestionEventArgs 。 响应属性被设置 假 在一个RowDirtyStateNeeded事件处理程序。
private void dataGridView1_CancelRowEdit(object sender,
System.Windows.Forms.QuestionEventArgs e)
{
if (this.rowInEdit == this.dataGridView1.Rows.Count - &&
this.rowInEdit == this.customers.Count)
{
// If the user has canceled the edit of a newly created row,
// replace the corresponding Customer object with a new, empty one.
this.customerInEdit = new Customer();
}
else
{
// If the user has canceled the edit of an existing row,
// release the corresponding Customer object.
this.customerInEdit = null;
this.rowInEdit = -;
}
}
9.
实现一个处理程序 UserDeletingRow如果删除一个现有的 客户 从数据存储对象或丢弃未保存的 客户 对象代表一个新创建的行。
这事件发生时每当用户点击一行删除一行标题,按删除键。
private void dataGridView1_UserDeletingRow(object sender,
System.Windows.Forms.DataGridViewRowCancelEventArgs e)
{
if (e.Row.Index < this.customers.Count)
{
// If the user has deleted an existing row, remove the
// corresponding Customer object from the data store.
this.customers.RemoveAt(e.Row.Index);
} if (e.Row.Index == this.rowInEdit)
{
// If the user has deleted a newly created row, release
// the corresponding Customer object.
this.rowInEdit = -;
this.customerInEdit = null;
}
}
10.实现一个简单的 客户 类来代表这段代码示例所使用的数据项。
public class Customer
{
private String companyNameValue;
private String contactNameValue; public Customer()
{
// Leave fields empty.
} public Customer(String companyName, String contactName)
{
companyNameValue = companyName;
contactNameValue = contactName;
} public String CompanyName
{
get
{
return companyNameValue;
}
set
{
companyNameValue = value;
}
} public String ContactName
{
get
{
return contactNameValue;
}
set
{
contactNameValue = value;
}
}
}
测试应用程序
您现在可以测试形式,以确保它的行为。
测试表单
-
编译并运行应用程序。
您将看到一个 DataGridView控制填充三个客户记录。 您可以修改多个单元的值并按ESC连续两次在编辑模式以外的编辑模式,一旦恢复整个行其原始值。 当你修改、添加或删除行控制, 客户 对象数据存储的修改、添加或删除。
下一个步骤
这个应用程序的事件给你一个基本的了解,你必须处理来实现虚拟模式 DataGridView控制。 你可以改善这个基本的应用程序在许多方面:
实现一个数据存储,从外部数据库缓存值。 缓存应该必要的检索和丢弃的值,以便显示只包含什么是必要的同时消耗少量的内存在客户端计算机。
调整数据存储的性能取决于您的需求。 例如,您可能想要弥补缓慢的网络连接,而不是端计算机内存限制通过使用一个更大的缓存大小和最小化数据库查询的数量。
关于缓存值的更多信息从外部数据库,明白了 如何:实现虚拟模式的即时数据加载Windows窗体DataGridView控制。
4.如何:实现虚拟模式的即时数据加载Windows窗体DataGridView控制
以下代码示例展示了如何使用虚拟模式 DataGridView控制与数据缓存,从服务器加载数据只有当它是必要的。 本例中详细描述 实现虚拟模式的即时数据加载Windows窗体DataGridView控件。
using System;
using System.Data;
using System.Data.SqlClient;
using System.Drawing;
using System.Windows.Forms; public class VirtualJustInTimeDemo : System.Windows.Forms.Form
{
private DataGridView dataGridView1 = new DataGridView();
private Cache memoryCache; // Specify a connection string. Replace the given value with a
// valid connection string for a Northwind SQL Server sample
// database accessible to your system.
private string connectionString =
"Initial Catalog=NorthWind;Data Source=localhost;" +
"Integrated Security=SSPI;Persist Security Info=False";
private string table = "Orders"; protected override void OnLoad(EventArgs e)
{
// Initialize the form.
this.AutoSize = true;
this.Controls.Add(this.dataGridView1);
this.Text = "DataGridView virtual-mode just-in-time demo"; // Complete the initialization of the DataGridView.
this.dataGridView1.Size = new Size(, );
this.dataGridView1.Dock = DockStyle.Fill;
this.dataGridView1.VirtualMode = true;
this.dataGridView1.ReadOnly = true;
this.dataGridView1.AllowUserToAddRows = false;
this.dataGridView1.AllowUserToOrderColumns = false;
this.dataGridView1.SelectionMode =
DataGridViewSelectionMode.FullRowSelect;
this.dataGridView1.CellValueNeeded += new
DataGridViewCellValueEventHandler(dataGridView1_CellValueNeeded); // Create a DataRetriever and use it to create a Cache object
// and to initialize the DataGridView columns and rows.
try
{
DataRetriever retriever =
new DataRetriever(connectionString, table);
memoryCache = new Cache(retriever, );
foreach (DataColumn column in retriever.Columns)
{
dataGridView1.Columns.Add(
column.ColumnName, column.ColumnName);
}
this.dataGridView1.RowCount = retriever.RowCount;
}
catch (SqlException)
{
MessageBox.Show("Connection could not be established. " +
"Verify that the connection string is valid.");
Application.Exit();
} // Adjust the column widths based on the displayed values.
this.dataGridView1.AutoResizeColumns(
DataGridViewAutoSizeColumnsMode.DisplayedCells); base.OnLoad(e);
} private void dataGridView1_CellValueNeeded(object sender,
DataGridViewCellValueEventArgs e)
{
e.Value = memoryCache.RetrieveElement(e.RowIndex, e.ColumnIndex);
} [STAThreadAttribute()]
public static void Main()
{
Application.Run(new VirtualJustInTimeDemo());
} } public interface IDataPageRetriever
{
DataTable SupplyPageOfData(int lowerPageBoundary, int rowsPerPage);
} public class DataRetriever : IDataPageRetriever
{
private string tableName;
private SqlCommand command; public DataRetriever(string connectionString, string tableName)
{
SqlConnection connection = new SqlConnection(connectionString);
connection.Open();
command = connection.CreateCommand();
this.tableName = tableName;
} private int rowCountValue = -; public int RowCount
{
get
{
// Return the existing value if it has already been determined.
if (rowCountValue != -)
{
return rowCountValue;
} // Retrieve the row count from the database.
command.CommandText = "SELECT COUNT(*) FROM " + tableName;
rowCountValue = (int)command.ExecuteScalar();
return rowCountValue;
}
} private DataColumnCollection columnsValue; public DataColumnCollection Columns
{
get
{
// Return the existing value if it has already been determined.
if (columnsValue != null)
{
return columnsValue;
} // Retrieve the column information from the database.
command.CommandText = "SELECT * FROM " + tableName;
SqlDataAdapter adapter = new SqlDataAdapter();
adapter.SelectCommand = command;
DataTable table = new DataTable();
table.Locale = System.Globalization.CultureInfo.InvariantCulture;
adapter.FillSchema(table, SchemaType.Source);
columnsValue = table.Columns;
return columnsValue;
}
} private string commaSeparatedListOfColumnNamesValue = null; private string CommaSeparatedListOfColumnNames
{
get
{
// Return the existing value if it has already been determined.
if (commaSeparatedListOfColumnNamesValue != null)
{
return commaSeparatedListOfColumnNamesValue;
} // Store a list of column names for use in the
// SupplyPageOfData method.
System.Text.StringBuilder commaSeparatedColumnNames =
new System.Text.StringBuilder();
bool firstColumn = true;
foreach (DataColumn column in Columns)
{
if (!firstColumn)
{
commaSeparatedColumnNames.Append(", ");
}
commaSeparatedColumnNames.Append(column.ColumnName);
firstColumn = false;
} commaSeparatedListOfColumnNamesValue =
commaSeparatedColumnNames.ToString();
return commaSeparatedListOfColumnNamesValue;
}
} // Declare variables to be reused by the SupplyPageOfData method.
private string columnToSortBy;
private SqlDataAdapter adapter = new SqlDataAdapter(); public DataTable SupplyPageOfData(int lowerPageBoundary, int rowsPerPage)
{
// Store the name of the ID column. This column must contain unique
// values so the SQL below will work properly.
if (columnToSortBy == null)
{
columnToSortBy = this.Columns[].ColumnName;
} if (!this.Columns[columnToSortBy].Unique)
{
throw new InvalidOperationException(String.Format(
"Column {0} must contain unique values.", columnToSortBy));
} // Retrieve the specified number of rows from the database, starting
// with the row specified by the lowerPageBoundary parameter.
command.CommandText = "Select Top " + rowsPerPage + " " +
CommaSeparatedListOfColumnNames + " From " + tableName +
" WHERE " + columnToSortBy + " NOT IN (SELECT TOP " +
lowerPageBoundary + " " + columnToSortBy + " From " +
tableName + " Order By " + columnToSortBy +
") Order By " + columnToSortBy;
adapter.SelectCommand = command; DataTable table = new DataTable();
table.Locale = System.Globalization.CultureInfo.InvariantCulture;
adapter.Fill(table);
return table;
} } public class Cache
{
private static int RowsPerPage; // Represents one page of data.
public struct DataPage
{
public DataTable table;
private int lowestIndexValue;
private int highestIndexValue; public DataPage(DataTable table, int rowIndex)
{
this.table = table;
lowestIndexValue = MapToLowerBoundary(rowIndex);
highestIndexValue = MapToUpperBoundary(rowIndex);
System.Diagnostics.Debug.Assert(lowestIndexValue >= );
System.Diagnostics.Debug.Assert(highestIndexValue >= );
} public int LowestIndex
{
get
{
return lowestIndexValue;
}
} public int HighestIndex
{
get
{
return highestIndexValue;
}
} public static int MapToLowerBoundary(int rowIndex)
{
// Return the lowest index of a page containing the given index.
return (rowIndex / RowsPerPage) * RowsPerPage;
} private static int MapToUpperBoundary(int rowIndex)
{
// Return the highest index of a page containing the given index.
return MapToLowerBoundary(rowIndex) + RowsPerPage - ;
}
} private DataPage[] cachePages;
private IDataPageRetriever dataSupply; public Cache(IDataPageRetriever dataSupplier, int rowsPerPage)
{
dataSupply = dataSupplier;
Cache.RowsPerPage = rowsPerPage;
LoadFirstTwoPages();
} // Sets the value of the element parameter if the value is in the cache.
private bool IfPageCached_ThenSetElement(int rowIndex,
int columnIndex, ref string element)
{
if (IsRowCachedInPage(, rowIndex))
{
element = cachePages[].table
.Rows[rowIndex % RowsPerPage][columnIndex].ToString();
return true;
}
else if (IsRowCachedInPage(, rowIndex))
{
element = cachePages[].table
.Rows[rowIndex % RowsPerPage][columnIndex].ToString();
return true;
} return false;
} public string RetrieveElement(int rowIndex, int columnIndex)
{
string element = null; if (IfPageCached_ThenSetElement(rowIndex, columnIndex, ref element))
{
return element;
}
else
{
return RetrieveData_CacheIt_ThenReturnElement(
rowIndex, columnIndex);
}
} private void LoadFirstTwoPages()
{
cachePages = new DataPage[]{
new DataPage(dataSupply.SupplyPageOfData(
DataPage.MapToLowerBoundary(), RowsPerPage), ),
new DataPage(dataSupply.SupplyPageOfData(
DataPage.MapToLowerBoundary(RowsPerPage),
RowsPerPage), RowsPerPage)};
} private string RetrieveData_CacheIt_ThenReturnElement(
int rowIndex, int columnIndex)
{
// Retrieve a page worth of data containing the requested value.
DataTable table = dataSupply.SupplyPageOfData(
DataPage.MapToLowerBoundary(rowIndex), RowsPerPage); // Replace the cached page furthest from the requested cell
// with a new page containing the newly retrieved data.
cachePages[GetIndexToUnusedPage(rowIndex)] = new DataPage(table, rowIndex); return RetrieveElement(rowIndex, columnIndex);
} // Returns the index of the cached page most distant from the given index
// and therefore least likely to be reused.
private int GetIndexToUnusedPage(int rowIndex)
{
if (rowIndex > cachePages[].HighestIndex &&
rowIndex > cachePages[].HighestIndex)
{
int offsetFromPage0 = rowIndex - cachePages[].HighestIndex;
int offsetFromPage1 = rowIndex - cachePages[].HighestIndex;
if (offsetFromPage0 < offsetFromPage1)
{
return ;
}
return ;
}
else
{
int offsetFromPage0 = cachePages[].LowestIndex - rowIndex;
int offsetFromPage1 = cachePages[].LowestIndex - rowIndex;
if (offsetFromPage0 < offsetFromPage1)
{
return ;
}
return ;
} } // Returns a value indicating whether the given row index is contained
// in the given DataPage.
private bool IsRowCachedInPage(int pageNumber, int rowIndex)
{
return rowIndex <= cachePages[pageNumber].HighestIndex &&
rowIndex >= cachePages[pageNumber].LowestIndex;
} }
VirtualJustInTimeDemo
编译的代码
引用到系统,系统。 数据系统。 Xml和System.Windows。 表单组件。
访问服务器与罗斯文SQL server安装的示例数据库。
- 运行如图
从命令行信息建设这个例子对Visual Basic或Visual c#,明白了 建筑从命令行(Visual Basic)或 命令行构建与csc.exe。 你也可以在Visual Studio构建这个例子的代码粘贴到一个新项目。 也看到 如何:编译和运行一个完整的Windows窗体代码示例使用Visual Studio。
本文地址:http://www.cnblogs.com/endv/p/4234537.html
官方地址:http://msdn.microsoft.com/en-us/library/ms171621.aspx
译者Q:77811970
源代码下载:http://files.cnblogs.com/endv/DataGridViewFrom.rar 点击