Windows窗体DataGridView控件的性能调优.net 4.5
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 = -1; // 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 = 4; }
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 - 1) 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 - 1; }
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 - 1) { // Add the new Customer object to the data store. this.customers.Add(this.customerInEdit); this.customerInEdit = null; this.rowInEdit = -1; } 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 = -1; } else if (this.dataGridView1.ContainsFocus) { this.customerInEdit = null; this.rowInEdit = -1; } }
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 - 2 && 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 = -1; } }
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 = -1; 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控件 。
1 using System; 2 using System.Data; 3 using System.Data.SqlClient; 4 using System.Drawing; 5 using System.Windows.Forms; 6 7 public class VirtualJustInTimeDemo : System.Windows.Forms.Form 8 { 9 private DataGridView dataGridView1 = new DataGridView(); 10 private Cache memoryCache; 11 12 // Specify a connection string. Replace the given value with a 13 // valid connection string for a Northwind SQL Server sample 14 // database accessible to your system. 15 private string connectionString = 16 "Initial Catalog=NorthWind;Data Source=localhost;" + 17 "Integrated Security=SSPI;Persist Security Info=False"; 18 private string table = "Orders"; 19 20 protected override void OnLoad(EventArgs e) 21 { 22 // Initialize the form. 23 this.AutoSize = true; 24 this.Controls.Add(this.dataGridView1); 25 this.Text = "DataGridView virtual-mode just-in-time demo"; 26 27 // Complete the initialization of the DataGridView. 28 this.dataGridView1.Size = new Size(800, 250); 29 this.dataGridView1.Dock = DockStyle.Fill; 30 this.dataGridView1.VirtualMode = true; 31 this.dataGridView1.ReadOnly = true; 32 this.dataGridView1.AllowUserToAddRows = false; 33 this.dataGridView1.AllowUserToOrderColumns = false; 34 this.dataGridView1.SelectionMode = 35 DataGridViewSelectionMode.FullRowSelect; 36 this.dataGridView1.CellValueNeeded += new 37 DataGridViewCellValueEventHandler(dataGridView1_CellValueNeeded); 38 39 // Create a DataRetriever and use it to create a Cache object 40 // and to initialize the DataGridView columns and rows. 41 try 42 { 43 DataRetriever retriever = 44 new DataRetriever(connectionString, table); 45 memoryCache = new Cache(retriever, 16); 46 foreach (DataColumn column in retriever.Columns) 47 { 48 dataGridView1.Columns.Add( 49 column.ColumnName, column.ColumnName); 50 } 51 this.dataGridView1.RowCount = retriever.RowCount; 52 } 53 catch (SqlException) 54 { 55 MessageBox.Show("Connection could not be established. " + 56 "Verify that the connection string is valid."); 57 Application.Exit(); 58 } 59 60 // Adjust the column widths based on the displayed values. 61 this.dataGridView1.AutoResizeColumns( 62 DataGridViewAutoSizeColumnsMode.DisplayedCells); 63 64 base.OnLoad(e); 65 } 66 67 private void dataGridView1_CellValueNeeded(object sender, 68 DataGridViewCellValueEventArgs e) 69 { 70 e.Value = memoryCache.RetrieveElement(e.RowIndex, e.ColumnIndex); 71 } 72 73 [STAThreadAttribute()] 74 public static void Main() 75 { 76 Application.Run(new VirtualJustInTimeDemo()); 77 } 78 79 } 80 81 public interface IDataPageRetriever 82 { 83 DataTable SupplyPageOfData(int lowerPageBoundary, int rowsPerPage); 84 } 85 86 public class DataRetriever : IDataPageRetriever 87 { 88 private string tableName; 89 private SqlCommand command; 90 91 public DataRetriever(string connectionString, string tableName) 92 { 93 SqlConnection connection = new SqlConnection(connectionString); 94 connection.Open(); 95 command = connection.CreateCommand(); 96 this.tableName = tableName; 97 } 98 99 private int rowCountValue = -1; 100 101 public int RowCount 102 { 103 get 104 { 105 // Return the existing value if it has already been determined. 106 if (rowCountValue != -1) 107 { 108 return rowCountValue; 109 } 110 111 // Retrieve the row count from the database. 112 command.CommandText = "SELECT COUNT(*) FROM " + tableName; 113 rowCountValue = (int)command.ExecuteScalar(); 114 return rowCountValue; 115 } 116 } 117 118 private DataColumnCollection columnsValue; 119 120 public DataColumnCollection Columns 121 { 122 get 123 { 124 // Return the existing value if it has already been determined. 125 if (columnsValue != null) 126 { 127 return columnsValue; 128 } 129 130 // Retrieve the column information from the database. 131 command.CommandText = "SELECT * FROM " + tableName; 132 SqlDataAdapter adapter = new SqlDataAdapter(); 133 adapter.SelectCommand = command; 134 DataTable table = new DataTable(); 135 table.Locale = System.Globalization.CultureInfo.InvariantCulture; 136 adapter.FillSchema(table, SchemaType.Source); 137 columnsValue = table.Columns; 138 return columnsValue; 139 } 140 } 141 142 private string commaSeparatedListOfColumnNamesValue = null; 143 144 private string CommaSeparatedListOfColumnNames 145 { 146 get 147 { 148 // Return the existing value if it has already been determined. 149 if (commaSeparatedListOfColumnNamesValue != null) 150 { 151 return commaSeparatedListOfColumnNamesValue; 152 } 153 154 // Store a list of column names for use in the 155 // SupplyPageOfData method. 156 System.Text.StringBuilder commaSeparatedColumnNames = 157 new System.Text.StringBuilder(); 158 bool firstColumn = true; 159 foreach (DataColumn column in Columns) 160 { 161 if (!firstColumn) 162 { 163 commaSeparatedColumnNames.Append(", "); 164 } 165 commaSeparatedColumnNames.Append(column.ColumnName); 166 firstColumn = false; 167 } 168 169 commaSeparatedListOfColumnNamesValue = 170 commaSeparatedColumnNames.ToString(); 171 return commaSeparatedListOfColumnNamesValue; 172 } 173 } 174 175 // Declare variables to be reused by the SupplyPageOfData method. 176 private string columnToSortBy; 177 private SqlDataAdapter adapter = new SqlDataAdapter(); 178 179 public DataTable SupplyPageOfData(int lowerPageBoundary, int rowsPerPage) 180 { 181 // Store the name of the ID column. This column must contain unique 182 // values so the SQL below will work properly. 183 if (columnToSortBy == null) 184 { 185 columnToSortBy = this.Columns[0].ColumnName; 186 } 187 188 if (!this.Columns[columnToSortBy].Unique) 189 { 190 throw new InvalidOperationException(String.Format( 191 "Column {0} must contain unique values.", columnToSortBy)); 192 } 193 194 // Retrieve the specified number of rows from the database, starting 195 // with the row specified by the lowerPageBoundary parameter. 196 command.CommandText = "Select Top " + rowsPerPage + " " + 197 CommaSeparatedListOfColumnNames + " From " + tableName + 198 " WHERE " + columnToSortBy + " NOT IN (SELECT TOP " + 199 lowerPageBoundary + " " + columnToSortBy + " From " + 200 tableName + " Order By " + columnToSortBy + 201 ") Order By " + columnToSortBy; 202 adapter.SelectCommand = command; 203 204 DataTable table = new DataTable(); 205 table.Locale = System.Globalization.CultureInfo.InvariantCulture; 206 adapter.Fill(table); 207 return table; 208 } 209 210 } 211 212 public class Cache 213 { 214 private static int RowsPerPage; 215 216 // Represents one page of data. 217 public struct DataPage 218 { 219 public DataTable table; 220 private int lowestIndexValue; 221 private int highestIndexValue; 222 223 public DataPage(DataTable table, int rowIndex) 224 { 225 this.table = table; 226 lowestIndexValue = MapToLowerBoundary(rowIndex); 227 highestIndexValue = MapToUpperBoundary(rowIndex); 228 System.Diagnostics.Debug.Assert(lowestIndexValue >= 0); 229 System.Diagnostics.Debug.Assert(highestIndexValue >= 0); 230 } 231 232 public int LowestIndex 233 { 234 get 235 { 236 return lowestIndexValue; 237 } 238 } 239 240 public int HighestIndex 241 { 242 get 243 { 244 return highestIndexValue; 245 } 246 } 247 248 public static int MapToLowerBoundary(int rowIndex) 249 { 250 // Return the lowest index of a page containing the given index. 251 return (rowIndex / RowsPerPage) * RowsPerPage; 252 } 253 254 private static int MapToUpperBoundary(int rowIndex) 255 { 256 // Return the highest index of a page containing the given index. 257 return MapToLowerBoundary(rowIndex) + RowsPerPage - 1; 258 } 259 } 260 261 private DataPage[] cachePages; 262 private IDataPageRetriever dataSupply; 263 264 public Cache(IDataPageRetriever dataSupplier, int rowsPerPage) 265 { 266 dataSupply = dataSupplier; 267 Cache.RowsPerPage = rowsPerPage; 268 LoadFirstTwoPages(); 269 } 270 271 // Sets the value of the element parameter if the value is in the cache. 272 private bool IfPageCached_ThenSetElement(int rowIndex, 273 int columnIndex, ref string element) 274 { 275 if (IsRowCachedInPage(0, rowIndex)) 276 { 277 element = cachePages[0].table 278 .Rows[rowIndex % RowsPerPage][columnIndex].ToString(); 279 return true; 280 } 281 else if (IsRowCachedInPage(1, rowIndex)) 282 { 283 element = cachePages[1].table 284 .Rows[rowIndex % RowsPerPage][columnIndex].ToString(); 285 return true; 286 } 287 288 return false; 289 } 290 291 public string RetrieveElement(int rowIndex, int columnIndex) 292 { 293 string element = null; 294 295 if (IfPageCached_ThenSetElement(rowIndex, columnIndex, ref element)) 296 { 297 return element; 298 } 299 else 300 { 301 return RetrieveData_CacheIt_ThenReturnElement( 302 rowIndex, columnIndex); 303 } 304 } 305 306 private void LoadFirstTwoPages() 307 { 308 cachePages = new DataPage[]{ 309 new DataPage(dataSupply.SupplyPageOfData( 310 DataPage.MapToLowerBoundary(0), RowsPerPage), 0), 311 new DataPage(dataSupply.SupplyPageOfData( 312 DataPage.MapToLowerBoundary(RowsPerPage), 313 RowsPerPage), RowsPerPage)}; 314 } 315 316 private string RetrieveData_CacheIt_ThenReturnElement( 317 int rowIndex, int columnIndex) 318 { 319 // Retrieve a page worth of data containing the requested value. 320 DataTable table = dataSupply.SupplyPageOfData( 321 DataPage.MapToLowerBoundary(rowIndex), RowsPerPage); 322 323 // Replace the cached page furthest from the requested cell 324 // with a new page containing the newly retrieved data. 325 cachePages[GetIndexToUnusedPage(rowIndex)] = new DataPage(table, rowIndex); 326 327 return RetrieveElement(rowIndex, columnIndex); 328 } 329 330 // Returns the index of the cached page most distant from the given index 331 // and therefore least likely to be reused. 332 private int GetIndexToUnusedPage(int rowIndex) 333 { 334 if (rowIndex > cachePages[0].HighestIndex && 335 rowIndex > cachePages[1].HighestIndex) 336 { 337 int offsetFromPage0 = rowIndex - cachePages[0].HighestIndex; 338 int offsetFromPage1 = rowIndex - cachePages[1].HighestIndex; 339 if (offsetFromPage0 < offsetFromPage1) 340 { 341 return 1; 342 } 343 return 0; 344 } 345 else 346 { 347 int offsetFromPage0 = cachePages[0].LowestIndex - rowIndex; 348 int offsetFromPage1 = cachePages[1].LowestIndex - rowIndex; 349 if (offsetFromPage0 < offsetFromPage1) 350 { 351 return 1; 352 } 353 return 0; 354 } 355 356 } 357 358 // Returns a value indicating whether the given row index is contained 359 // in the given DataPage. 360 private bool IsRowCachedInPage(int pageNumber, int rowIndex) 361 { 362 return rowIndex <= cachePages[pageNumber].HighestIndex && 363 rowIndex >= cachePages[pageNumber].LowestIndex; 364 } 365 366 }
编译的代码
-
引用到系统,系统。 数据系统。 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 点击