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 點選