最佳实践扩展Windows窗体DataGridView控件 .net 4.5 附示例代码 1.最佳实践扩展Windows窗体DataGridView控件 2.虚拟模式的Windows窗体DataGridView控件 3.介绍:在Windows窗体DataGridView控件中实现虚拟模式 4.如何:实现虚拟模式的即时数据加载Windows窗体DataGridView控制
.net 4.5
控制可以消耗大量的内存开销,除非你仔细地使用它。 在客户有限的内存,你可以避免一些这方面的开销,避免内存成本高的特性。 你也可以管理的部分或全部数据维护和检索任务自己使用虚拟模式为了定制的内存使用情况。
1.最佳实践扩展Windows窗体DataGridView控件
控制的目的是提供最大的可伸缩性。 如果你需要显示大量数据,您应该遵循的指导方针中描述这个话题避免消耗大量内存或有辱人格的用户界面(UI)的响应能力。 本主题讨论以下问题:
-
有效使用单元格样式
-
有效使用快捷菜单
-
有效地使用自动调整
-
使用选定的单元格、行和列的集合
-
使用共享的行
-
防止行成为非共享
。
类。
可以很容易地共享行实例节省内存,行成为非共享。 例如,每当用户直接与细胞相互作用,成为非共享它的行。 因为这无法避免,这一主题的指导方针是有用的只有当处理大量数据,只有当用户将与一个相对较小的数据每次运行你的程序的一部分。
控件绑定到一个外部数据源或当你实现虚拟模式,提供自己的数据源,细胞外的值存储控制,而不是在细胞对象,允许行共享。
对象只能共享一行如果可以确定所有的细胞状态的行和列的状态包含细胞。 如果你改变一个细胞的状态,这样就可以不再是推断从国家的行和列,行不能共享。
例如,一行不能共享在下列情况下:
-
的行包含一个选择单元选择的列。
-
属性集。
-
属性集。
事件。
。 使用以下指南以确保行共享:
-
收集。 这些过载自动创建专有的行。
-
房地产可以共享在下列情况下:
-
收集。
-
财产。
-
财产。
-
-
收集。
-
收集。
属性值为1。
事件确定当行成为非共享。 这是有用的调试row-sharing问题时。
为了防止行成为非共享,使用以下指南:
-
方法,操作行采取行索引参数而不是行实例。 此外,row-related事件接收事件参数处理程序对象与行属性,您可以使用它们来操纵行没有使它们成为非共享。
-
属性相反,它将使用共享的行索引1和不会检索正确的快捷菜单。
-
属性检索当前单元格的行和列索引没有直接访问单元。
-
。
-
方法,提高这些事件。
-
。 这将导致所有选定行成为非共享。
-
方法。 这种方法会导致行成为非共享。
-
。 这将导致所有行成为非共享。
-
。 这将导致所有行成为非共享。
-
财产。 这将导致所有行成为非共享。
-
方法。 排序与一个定制的比较器使所有行成为非共享。
2.虚拟模式的Windows窗体DataGridView控件
事件,使控制查找数据缓存中的值。
事件可能是唯一事件需要处理。 额外的虚拟方式事件让你使特定功能用户编辑、添加和删除行,行级的事务。
事件(如当用户添加或删除行发生的事件,或当细胞值编辑,解析,验证,或格式化的)是有用的在虚拟模式,。 你也可以处理事件,让你保持值不是通常存储在一个绑定数据源,如细胞提示文本,细胞和行错误文本,细胞和行快捷菜单数据,和行高数据。
。
属性引用主题。
。
事件 |
描述 |
---|---|
控制使用的检索数据缓存的一个细胞值显示。 这个事件只发生在未绑定列细胞。 |
|
使用的控制提交用户输入一个细胞到数据缓存。 这个事件只发生在未绑定列细胞。 事件处理程序,以确保当前值显示在控制和自动分级模式目前在应用任何效果。 |
|
使用的控制来表示数据缓存需要一个新行。 |
|
连续使用的控制,以确定任何未提交的更改。 |
|
控制用于显示一行应该回归其缓存值。 |
属性设置。
事件 |
描述 |
---|---|
使用的控制指示当行被删除或添加,让你更新相应数据缓存。 |
|
使用的控制格式的单元格值显示,解析和验证用户输入。 |
|
。 。 |
|
。 方法当你改变细胞或行错误文本,以确保当前值显示在控制。 。 |
|
。 |
|
事件处理程序,以确保当前值的显示控制。 |
3.介绍:在Windows窗体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)"; } ... }
控制和填充数据存储与样本值。
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; }
当前在编辑对象。
控制细胞需要油漆。
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.对象代表行进行编辑。 这个事件发生时只要用户提交一个细胞值变化。
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. 对象代表一个新创建的行。
这个事件发生时用户输入新记录的行。
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. 事件,保存新的或修改的行数据存储。
这个事件发生时用户更改当前行。
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. 事件将发生,当用户信号连续降级在编辑模式下按下ESC两次或一次以外的编辑模式。
事件处理程序。 这个事件非常有用当提交范围是在运行时确定的。
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; } }
对象代表当前行。
事件处理程序。
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.
对象代表一个新创建的行。
这事件发生时每当用户点击一行删除一行标题,按删除键。
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; } } }
4.如何:实现虚拟模式的即时数据加载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安装的示例数据库。
- 运行如图
。
本文地址: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 点击