一、單元格內容的操作 // 取得當前單元格內容 Console.WriteLine(DataGridView1.CurrentCell.Value); // 取得當前單元格的列 Index Console.WriteLine(DataGridView1.CurrentCell.ColumnIndex); // 取得當前單元格的行 Index Console.WriteLine(DataGridView1.CurrentCell.RowIndex); 另外,使用 DataGridView.CurrentCellAddress 屬性(而不是直接訪問單元格)來確定單元格所在的行: DataGridView.CurrentCellAddress.Y 和列: DataGridView.CurrentCellAddress.X 。這對於避免取消共享行的共享非常有用。 當前的單元格可以通過設定 DataGridView 對象的 CurrentCell 來改變。可以通過 CurrentCell 來設定 DataGridView 的激活單元格。將 CurrentCell 設為 Nothing(null) 可以取消激活的單元格。 // 設定 (0, 0) 為當前單元格 DataGridView1.CurrentCell = DataGridView1[0, 0]; 在整行選中模式開啟時,你也可以通過 CurrentCell 來設定選定行。 /// <summary> /// 向下遍歷 /// </summary> /// <param ></param> /// <param ></param> private void button4_Click(object sender, EventArgs e) ...{ int row = this.dataGridView1.CurrentRow.Index + 1; if (row > this.dataGridView1.RowCount - 1) row = 0; this.dataGridView1.CurrentCell = this.dataGridView1[0, row]; } /// <summary> /// 向上遍歷 /// </summary> /// <param ></param> /// <param ></param> private void button5_Click(object sender, EventArgs e) ...{ int row = this.dataGridView1.CurrentRow.Index - 1; if (row < 0) row = this.dataGridView1.RowCount - 1; this.dataGridView1.CurrentCell = this.dataGridView1[0, row]; } * 注意: this.dataGridView 的索引器的參數是: columnIndex, rowIndex 或是 columnName, rowIndex 這與習慣不同。 ********DataGridView 設定單元格只讀: 1) 使用 ReadOnly 屬性 ? 如果希望,DataGridView 內所有單元格都不可編輯, 那么只要: // 設置 DataGridView1 為只讀 DataGridView1.ReadOnly = true;此時,用戶的新增行操作和刪除行操作也被屏蔽了。 ******如果希望,DataGridView 內某個單元格不可編輯, 那么只要: // 設置 DataGridView1 的第2列整列單元格為只讀 DataGridView1.Columns[1].ReadOnly = true; // 設置 DataGridView1 的第3行整行單元格為只讀 DataGridView1.Rows[2].ReadOnly = true; // 設置 DataGridView1 的[0,0]單元格為只讀 DataGridView1[0, 0].ReadOnly = true; DataGridView 行頭列頭的單元格 // 改變DataGridView1的第一列列頭內容 DataGridView1.Columns[0].HeaderCell.Value = "第一列"; // 改變DataGridView1的第一行行頭內容 DataGridView1.Rows[0].HeaderCell.Value = "第一行"; // 改變DataGridView1的左上頭部單元內容 DataGridView1.TopLeftHeaderCell.Value = "左上"; 另外你也可以通過 HeaderText 來改變他們的內容。 // 改變DataGridView1的第一列列頭內容 DataGridView1.Columns[0].HeaderText = "第一列"; DataGridView 單元格的ToolTip的設置 DataGridView.ShowCellToolTips = True 的情況下, 單元格的 ToolTip 可以表示出來。對於單元格窄小,無法完全顯示的單元格, ToolTip 可以顯示必要的信息。 1) 設定單元格的ToolTip內容 // 設定單元格的ToolTip內容 DataGridView1[0, 0].ToolTipText = "該單元格的內容不能修改"; // 設定列頭的單元格的ToolTip內容 DataGridView1.Columns[0].ToolTipText = "該列只能輸入數字"; // 設定行頭的單元格的ToolTip內容 DataGridView1.Rows[0].HeaderCell.ToolTipText = "該行單元格內容不能修改"; 2) CellToolTipTextNeeded 事件 在批量的單元格的 ToolTip 設定的時候,一個一個指定那么設定的效率比較低, 這時候可以利用 CellToolTipTextNeeded 事件。當單元格的 ToolTipText 變化的時候也會引發該事件。但是,當DataGridView的DataSource被指定且VirualMode=True的時候,該事件不會被引發。 // CellToolTipTextNeeded事件處理方法 private void DataGridView1_CellToolTipTextNeeded(object sender, DataGridViewCellToolTipTextNeededEventArgs e) { e.ToolTipText = e.ColumnIndex.ToString() + ", " + e.RowIndex.ToString(); } DataGridView 的單元格的邊框、 網格線樣式的設定 1) DataGridView 的邊框線樣式的設定 DataGridView 的邊框線的樣式是通過 DataGridView.BorderStyle 屬性來設定的。 BorderStyle 屬性設定值是一個 BorderStyle 枚舉: FixedSingle(單線,默認)、Fixed3D、None。 2) 單元格的邊框線樣式的設定 單元格的邊框線的樣式是通過 DataGridView.CellBorderStyle 屬性來設定的。 CellBorderStyle 屬性設定值是 DataGridViewCellBorderStyle 枚舉。(詳細參見 MSDN) 另外,通過 DataGridView.ColumnHeadersBorderStyle 和 RowHeadersBorderStyle 屬性可以修改 DataGridView 的頭部的單元格邊框線樣式。 屬性設定值是 DataGridViewHeaderBorderStyle 枚舉。(詳細參見 MSDN) 3) 單元格的邊框顏色的設定 單元格的邊框線的顏色可以通過 DataGridView.GridColor 屬性來設定的。默認是 ControlDarkDark 。但是只有在 CellBorderStyle 被設定為 Single、SingleHorizontal、SingleVertical 的條件下才能改變其邊框線的顏色。同樣,ColumnHeadersBorderStyle 以及 RowHeadersBorderStyle 只有在被設定為 Single 時,才能改變顏色。 4) 單元格的上下左右的邊框線式樣的單獨設定 CellBorderStyle只能設定單元格全部邊框線的式樣。要單獨改變單元格某一邊邊框式樣的話,需要用到DataGridView.AdvancedCellBorderStyle屬性。如示例: ' 單元格的上邊和左邊線設為二重線 ' 單元格的下邊和右邊線設為單重線 DataGridView1.AdvancedCellBorderStyle.Top = _ DataGridViewAdvancedCellBorderStyle.InsetDouble DataGridView1.AdvancedCellBorderStyle.Right = _ DataGridViewAdvancedCellBorderStyle.Inset DataGridView1.AdvancedCellBorderStyle.Bottom = _ DataGridViewAdvancedCellBorderStyle.Inset DataGridView1.AdvancedCellBorderStyle.Left = _ DataGridViewAdvancedCellBorderStyle.InsetDouble 同樣,設定行頭單元格的屬性是: AdvancedRowHeadersBorderStyle, 設定列頭單元格屬性是:AdvancedColumnHeadersBorderStyle。 DataGridView 單元格表示值的自定義 通過CellFormatting事件,可以自定義單元格的表示值。(比如:值為Error的時候,單元格被設定為紅色) 下面的示例:將“Colmn1”列的值改為大寫。 //CellFormatting 事件處理方法 private void DataGridView1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e) { DataGridView dgv = (DataGridView)sender; // 如果單元格是“Column1”列的單元格 if (dgv.Columns[e.ColumnIndex].Name == "Column1" && e.Value is string) { // 將單元格值改為大寫 string str = e.Value.ToString(); e.Value = str.ToUpper(); // 應用該Format,Format完畢。 e.FormattingApplied = true; } } CellFormatting事件的DataGridViewCellFormattingEventArgs對象的Value屬性一開始保存着未被格式化的值。當Value屬性被設定表示用的文本之后,把FormattingApplied屬性做為True,告知DataGridView文本已經格式化完畢。如果不這樣做的話,DataGridView會根據已經設定的Format,NullValue,DataSourceNullValue,FormatProvider屬性會將Value屬性會被重新格式化一遍。 DataGridView 用戶輸入時,單元格輸入值的設定 通過 DataGridView.CellParsing 事件可以設定用戶輸入的值。下面的示例:當輸入英文文本內容的時候,立即被改變為大寫。 //CellParsing 事件處理方法 private void DataGridView1_CellParsing(object sender, DataGridViewCellParsingEventArgs e) { DataGridView dgv = (DataGridView)sender; //單元格列為“Column1”時 if (dgv.Columns[e.ColumnIndex].Name == "Column1" && e.DesiredType == typeof(string)) { //將單元格值設為大寫 e.Value = e.Value.ToString().ToUpper(); //解析完畢 e.ParsingApplied = true; } } 二、行/列的操作 DataGridView 不顯示最下面的新行: 通常 DataGridView 的最下面一行是用戶新追加的行(行頭顯示 * )。如果不想讓用戶新追加行即不想顯示該新行,可以將 DataGridView 對象的 AllowUserToAddRows 屬性設置為 False。 // 設置用戶不能手動給 DataGridView1 添加新行 DataGridView1.AllowUserToAddRows = false; 但是,可以通過程序: DataGridViewRowCollection.Add 為 DataGridView 追加新行。 補足: 如果 DataGridView 的 DataSource 綁定的是 DataView, 還可以通過設置 DataView.AllowAdd 屬性為 False 來達到同樣的效果。 DataGridView 判斷新增行: DataGridView的AllowUserToAddRows屬性為True時也就是允許用戶追加新行的場合下,DataGridView的最后一行就是新追加的行(*行)。使用 DataGridViewRow.IsNewRow 屬性可以判斷哪一行是新追加的行。另外,通過DataGridView.NewRowIndex 可以獲取新行的行序列號。在沒有新行的時候,NewRowIndex = -1。 If (DataGridView1.CurrentRow.IsNewRow) Console.WriteLine("當前行為新追加行。") ; Else Console.WriteLine("當前行不是新追加行。") ; DataGridView 行的用戶刪除操作的自定義: 1) 無條件的限制行刪除操作。 默認時,DataGridView 是允許用戶進行行的刪除操作的。如果設置 DataGridView對象的AllowUserToDeleteRows屬性為 False 時, 用戶的行刪除操作就被禁止了。 // 禁止DataGridView1的行刪除操作。 DataGridView1.AllowUserToDeleteRows = false; 但是,通過 DataGridViewRowCollection.Remove 還是可以進行行的刪除。 補足: 如果 DataGridView 綁定的是 DataView 的話,通過 DataView.AllowDelete 也可以控制行的刪除。 行刪除時的條件判斷處理。 用戶在刪除行的時候,將會引發 DataGridView.UserDeletingRow 事件。 在這個事件里,可以判斷條件並取消刪除操作。 // DataGridView1 的 UserDeletingRow 事件 private void DataGridView1_UserDeletingRow( object sender, DataGridViewRowCancelEventArgs e) { // 刪除前的用戶確認。 if (MessageBox.Show("確認要刪除該行數據嗎?", "刪除確認", MessageBoxButtons.OKCancel, MessageBoxIcon.Question) != DialogResult.OK) { // 如果不是 OK,則取消。 e.Cancel = true; } } DataGridView 行、列的隱藏和刪除: 1) 行、列的隱藏 // DataGridView1的第一列隱藏 DataGridView1.Columns[0].Visible = false; // DataGridView1的第一行隱藏 DataGridView1.Rows[0].Visible = false; 2) 行頭、列頭的隱藏 // 列頭隱藏 DataGridView1.ColumnHeadersVisible = false; // 行頭隱藏 DataGridView1.RowHeadersVisible = false; 3) 行和列的刪除 ' 刪除名為"Column1"的列 DataGridView1.Columns.Remove("Column1"); ' 刪除第一列 DataGridView1.Columns.RemoveAt(0); ' 刪除第一行 DataGridView1.Rows.RemoveAt(0); 4) 刪除選中行 foreach (DataGridViewRow r in DataGridView1.SelectedRows) { if (!r.IsNewRow) { DataGridView1.Rows.Remove(r); } } DataGridView 禁止列或者行的Resize: 1) 禁止所有的列或者行的Resize // 禁止用戶改變DataGridView1的所有列的列寬 DataGridView1.AllowUserToResizeColumns = false; //禁止用戶改變DataGridView1の所有行的行高 DataGridView1.AllowUserToResizeRows = false; 但是可以通過 DataGridViewColumn.Width 或者 DataGridViewRow.Height 屬性設定列寬和行高。 2) 禁止指定行或者列的Resize // 禁止用戶改變DataGridView1的第一列的列寬 DataGridView1.Columns[0].Resizable = DataGridViewTriState.False; // 禁止用戶改變DataGridView1的第一列的行寬 DataGridView1.Rows[0].Resizable = DataGridViewTriState.False; 關於 NoSet 當 Resizable 屬性設為 DataGridViewTriState.NotSet 時, 實際上會默認以 DataGridView 的 AllowUserToResizeColumns 和 AllowUserToResizeRows 的屬性值進行設定。比如: DataGridView.AllowUserToResizeColumns = False 且 Resizable 是 NoSet 設定時,Resizable = False 。 判斷 Resizable 是否是繼承設定了 DataGridView 的 AllowUserToResizeColumns 和 AllowUserToResizeRows 的屬性值, 可以根據 State 屬性判斷。如果 State 屬性含有 ResizableSet,那么說明沒有繼承設定。 3) 列寬和行高的最小值的設定 // 第一列的最小列寬設定為 100 DataGridView1.Columns[0].MinimumWidth = 100; // 第一行的最小行高設定為 50 DataGridView1.Rows[0].MinimumHeight = 50; 4) 禁止用戶改變行頭的寬度以及列頭的高度 // 禁止用戶改變列頭的高度 DataGridView1.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.DisableResizing; // 禁止用戶改變行頭的寬度 DataGridView1.RowHeadersWidthSizeMode = DataGridViewRowHeadersWidthSizeMode.EnableResizing; DataGridView 列寬和行高自動調整的設定: // 設定包括Header和所有單元格的列寬自動調整 DataGridView1.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells; // 設定包括Header和所有單元格的行高自動調整 DataGridView1.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.AllCells; AutoSizeColumnsMode 屬性的設定值枚舉請參照 msdn 的 DataGridViewAutoSizeRowsMode 說明。 2)指定列或行自動調整 // 第一列自動調整 DataGridView1.Columns[0].AutoSizeMode =DataGridViewAutoSizeColumnMode.DisplayedCells; AutoSizeMode 設定為 NotSet 時, 默認繼承的是 DataGridView.AutoSizeColumnsMode 屬性。 3) 設定列頭的高度和行頭的寬度自動調整 // 設定列頭的寬度可以自由調整 DataGridView1.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize; // 設定行頭的寬度可以自由調整 DataGridView1.RowHeadersWidthSizeMode = DataGridViewRowHeadersWidthSizeMode.AutoSizeToAllHeaders; 4) 隨時自動調整 a, 臨時的,讓列寬自動調整,這和指定AutoSizeColumnsMode屬性一樣。 // 讓 DataGridView1 的所有列寬自動調整一下。 DataGridView1.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.AllCells); // 讓 DataGridView1 的第一列的列寬自動調整一下。 DataGridView1.AutoResizeColumn(0, DataGridViewAutoSizeColumnMode.AllCells);上面調用的 AutoResizeColumns 和 AutoResizeColumn 當指定的是DataGridViewAutoSizeColumnMode.AllCells 的時候, 參數可以省略。即: DataGridView1.AutoResizeColumn(0) 和 DataGridView1.AutoResizeColumns() b,臨時的,讓行高自動調整 // 讓 DataGridView1 的所有行高自動調整一下。 DataGridView1.AutoResizeRows(DataGridViewAutoSizeRowsMode.AllCells); //讓 DataGridView1 的第一行的行高自動調整一下。 DataGridView1.AutoResizeRow(0, DataGridViewAutoSizeRowMode.AllCells);上面調用的 AutoResizeRows 和 AutoResizeRow 當指定的是DataGridViewAutoSizeRowMode.AllCells 的時候, 參數可以省略。即:DataGridView1.AutoResizeRow (0) 和 DataGridView1.AutoResizeRows() c,臨時的,讓行頭和列頭自動調整 // 列頭高度自動調整 DataGridView1.AutoResizeColumnHeadersHeight(); // 行頭寬度自動調整 DataGridView1.AutoResizeRowHeadersWidth( DataGridViewRowHeadersWidthSizeMode.AutoSizeToAllHeaders); 關於性能: 通過 AutoSizeColumnsMode 或者 AutoSizeRowsMode 屬性所指定的單元格進行自動調整時,如果調整次數過於多那么將可能導致性能下降,尤其是在行和列數比較多的情況下。在這時用 DisplayedCells 代替 AllCells 能減少非所見的單元格的調整,從而提高性能。 DataGridView 凍結列或行 1) 列凍結 DataGridViewColumn.Frozen 屬性為 True 時, 該列左側的所有列被固定, 橫向滾動時固定列不隨滾動條滾動而左右移動。這對於重要列固定顯示很有用。 // DataGridView1的左側2列固定 DataGridView1.Columns[1].Frozen = true; 但是,DataGridView.AllowUserToOrderColumns = True 時,固定列不能移動到非固定列, 反之亦然。 2) 行凍結 DataGridViewRow.Frozen 屬性為 True 時, 該行上面的所有行被固定, 縱向滾動時固定行不隨滾動條滾動而上下移動。 // DataGridView1 的上3行固定 DataGridView1.Rows[2].Frozen = true; DataGridView 列順序的調整 設定 DataGridView 的 AllowUserToOrderColumns 為 True 的時候, 用戶可以自由調整列的順序。 當用戶改變列的順序的時候,其本身的 Index 不會改變,但是 DisplayIndex 改變了。你也可以通過程序改變 DisplayIndex 來改變列的順序。 列順序發生改變時會引發 ColumnDisplayIndexChanged 事件: // DataGridView1的ColumnDisplayIndexChanged事件處理方法 private void DataGridView1_ColumnDisplayIndexChanged(object sender, DataGridViewColumnEventArgs e) { Console.WriteLine("{0} 的位置改變到 {1} ", e.Column.Name, e.Column.DisplayIndex); } DataGridView 新加行的默認值的設定 需要指定新加行的默認值的時候,可以在DataGridView.DefaultValuesNeeded事件里處理。在該事件中處理除了可以設定默認值以外,還可以指定某些特定的單元格的ReadOnly屬性等。 // DefaultValuesNeeded 事件處理方法 private void DataGridView1_DefaultValuesNeeded(object sender, DataGridViewRowEventArgs e) { // 設定單元格的默認值 e.Row.Cells["Column1"].Value = 0; e.Row.Cells["Column2"].Value = "-"; } 三、針對datagridview全局屬性的設置 使用 EditMode 屬性 DataGridView.EditMode 屬性被設置為 DataGridViewEditMode.EditProgrammatically 時,用戶就不能手動編輯單元格的內容了。但是可以通過程序,調用 DataGridView.BeginEdit 方法,使單元格進入編輯模式進行編輯。 DataGridView1.EditMode = DataGridViewEditMode.EditProgrammatically; 根據條件設定單元格的不可編輯狀態 當一個一個的通過單元格坐標設定單元格 ReadOnly 屬性的方法太麻煩的時候,你可以通過 CellBeginEdit 事件來取消單元格的編輯。 CellBeginEdit 事件處理方法 private void DataGridView1_CellBeginEdit(object sender, DataGridViewCellCancelEventArgs e) { DataGridView dgv = (DataGridView)sender; //是否可以進行編輯的條件檢查 if (dgv.Columns[e.ColumnIndex].Name == "Column1" && !(bool)dgv["Column2", e.RowIndex].Value) { // 取消編輯 e.Cancel = true; } } DataGridView 剪切板的操作 DataGridView.ClipboardCopyMode 屬性被設定為 DataGridViewClipboardCopyMode.Disable 以外的情況時,「Ctrl + C」 按下的時候,被選擇的單元格的內容會拷貝到系統剪切板內。格式有: Text, UnicodeText,Html, CommaSeparatedValue。可以直接粘貼到 Excel 內。 ClipboardCopyMode 還可以設定 Header部分是否拷貝: EnableAlwaysIncludeHeaderText 拷貝Header部分、EnableWithoutHeaderText 則不拷貝。默認是 EnableWithAutoHeaderText , Header 如果選擇了的話,就拷貝。 1) 編程方式實現剪切板的拷貝 Clipboard.SetDataObject(DataGridView1.GetClipboardContent()) 2) DataGridView 的數據粘貼 實現剪切板的拷貝比較容易,但是實現 DataGridView 的直接粘貼就比較難了。「Ctrl + V」按下進行粘貼時,DataGridView 沒有提供方法,只能自己實現。 以下,是粘貼時簡單的事例代碼,將拷貝數據粘貼到以選擇單元格開始的區域內。 //當前單元格是否選擇的判斷 if (DataGridView1.CurrentCell == null) return; int insertRowIndex = DataGridView1.CurrentCell.RowIndex; // 獲取剪切板的內容,並按行分割 string pasteText = Clipboard.GetText(); if (string.IsNullOrEmpty(pasteText)) return; pasteText = pasteText.Replace(" ", " "); pasteText = pasteText.Replace(' ', ' '); pasteText.TrimEnd(new char[] { ' ' }); string[] lines = pasteText.Split(' '); bool isHeader = true; foreach (string line in lines) { // 是否是列頭 if (isHeader) { isHeader = false; continue; } // 按 Tab 分割數據 string[] vals = line.Split(' '); // 判斷列數是否統一 if (vals.Length - 1 != DataGridView1.ColumnCount) throw new ApplicationException("粘貼的列數不正確。"); DataGridViewRow row = DataGridView1.Rows[insertRowIndex]; // 行頭設定 row.HeaderCell.Value = vals[0]; // 單元格內容設定 for (int i = 0; i < row.Cells.Count; i++) { row.Cells[i].Value = vals[i + 1]; } // DataGridView的行索引+1 insertRowIndex++; } DataGridView 的右鍵菜單(ContextMenuStrip) DataGridView, DataGridViewColumn, DataGridViewRow, DataGridViewCell 有 ContextMenuStrip 屬性。可以通過設定 ContextMenuStrip 對象來控制 DataGridView 的右鍵菜單的顯示。 DataGridViewColumn 的 ContextMenuStrip 屬性設定了 除了列頭以外的單元格的右鍵菜單。 DataGridViewRow 的 ContextMenuStrip 屬性設定了除了行頭以外的單元格的右鍵菜單。DataGridViewCell 的 ContextMenuStrip 屬性設定了指定單元格的右鍵菜單。 // DataGridView 的 ContextMenuStrip 設定 DataGridView1.ContextMenuStrip = this.ContextMenuStrip1; // 列的 ContextMenuStrip 設定 DataGridView1.Columns[0].ContextMenuStrip = this.ContextMenuStrip2; // 列頭的 ContextMenuStrip 設定 DataGridView1.Columns[0].HeaderCell.ContextMenuStrip = this.ContextMenuStrip2; // 行的 ContextMenuStrip 設定 DataGridView1.Rows[0].ContextMenuStrip = this.ContextMenuStrip3; // 單元格的 ContextMenuStrip 設定 DataGridView1[0, 0].ContextMenuStrip = this.ContextMenuStrip4; 對於單元格上的右鍵菜單的設定,優先順序是: Cell > Row > Column > DataGridView CellContextMenuStripNeeded、RowContextMenuStripNeeded 事件 利用 CellContextMenuStripNeeded 事件可以設定單元格的右鍵菜單,尤其但需要右鍵菜單根據單元格值的變化而變化的時候。比起使用循環遍歷,使用該事件來設定右鍵菜單的效率更高。但是,在DataGridView使用了DataSource綁定而且是VirtualMode的時候,該事件將不被引發。 // CellContextMenuStripNeeded事件處理方法 private void DataGridView1_CellContextMenuStripNeeded(object sender, DataGridViewCellContextMenuStripNeededEventArgs e) { DataGridView dgv = (DataGridView)sender; if (e.RowIndex < 0) { // 列頭的ContextMenuStrip設定 e.ContextMenuStrip = this.ContextMenuStrip1; } else if (e.ColumnIndex < 0) { // 行頭的ContextMenuStrip設定 e.ContextMenuStrip = this.ContextMenuStrip2; } else if (dgv[e.ColumnIndex, e.RowIndex].Value is int) { // 如果單元格值是整數時 e.ContextMenuStrip = this.ContextMenuStrip3; } } 同樣,可以通過 RowContextMenuStripNeeded 事件來設定行的右鍵菜單。 // RowContextMenuStripNeeded事件處理方法 private void DataGridView1_RowContextMenuStripNeeded(object sender, DataGridViewRowContextMenuStripNeededEventArgs e) { DataGridView dgv = (DataGridView)sender; // 當"Column1"列是Bool型且為True時、設定其的ContextMenuStrip object boolVal = dgv["Column1", e.RowIndex].Value; Console.WriteLine(boolVal); if (boolVal is bool && (bool)boolVal) { e.ContextMenuStrip = this.ContextMenuStrip1; } } CellContextMenuStripNeeded 事件處理方法的參數中、「e.ColumnIndex=-1」表示行頭、「e.RowIndex=-1」表示列頭。RowContextMenuStripNeeded則不存在「e.RowIndex=-1」的情況。 四、針對觸發事件的一些介紹 我認為只要記住常用的即可,比如鼠標的操作,一些常見的點擊觸發事件;比如_CellParsing()一般在編輯狀態結束的時候發生。 其他的用到的時候算查即可,時間長了掌握的也就多了。 ----------- ((DataGridViewComboBoxCell)dgvStudentList.Rows[0].Cells["Student"]).DataSource = list; ((DataGridViewComboBoxCell)dgvStudentList.Rows[0].Cells["Student"]).ValueMember = "StuNum"; ((DataGridViewComboBoxCell)dgvStudentList.Rows[0].Cells["Student"]).DisplayMember = "StuName"; ------------- 讓DataGridView的列寬自適應 就一行代碼: Columns[i].AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells; 這就搞定了,效果就是列寬會根據內容以及表頭寬度自行判斷,最后調整到合適的寬度。 AutoSizeMode這個屬性並不顯示在“屬性”窗口中。 如果你的DataGridView是動態綁定數據的, 那就只能在代碼中設置。 如果你的DataGridView列名是設計好的,那么可以在DataGridView上點擊右鍵,選擇“編輯列”,然后選中你要設置自適應的列,在它的屬性列表中的“布局”選項卡里面選擇AutoSizeMode的類型。 AutoSizeMode屬性可以設置的值還有不少: 成員名稱 說明 NotSet 列的大小調整行為從DataGridView.AutoSizeColumnsMode 屬性繼承。 None 列寬不會自動調整。 AllCells 調整列寬,以適合該列中的所有單元格的內容,包括標題單元格。 AllCellsExceptHeader 調整列寬,以適合該列中的所有單元格的內容,不包括標題單元格。 DisplayedCells 調整列寬,以適合當前屏幕上顯示的行的列中的所有單元格的內容,包括標題單元格。 DisplayedCellsExceptHeader 調整列寬,以適合當前屏幕上顯示的行的列中的所有單元格的內容,不包括標題單元格。 ColumnHeader 調整列寬,以適合列標題單元格的內容。 Fill 調整列寬,使所有列的寬度正好填充控件的顯示區域,只需要水平滾動保證列寬在DataGridViewColumn.MinimumWidth屬性值以上。相對列寬由相對DataGridViewColumn.FillWeight屬性值決定。 另外介紹: DataGridView 有一個屬性是AutoSizeColumnMode,他有幾個枚舉值: 1、AllCells 調整列寬,以適合該列中的所有單元格的內容,包括標題單元格。 2、AllCellsExceptHeader 調整列寬,以適合該列中的所有單元格的內容,不包括標題單元格。 3、ColumnHeader 調整列寬,以適合列標題單元格的內容。 4、DisplayedCells 調整列寬,以適合當前屏幕上顯示的行的列中的所有單元格的內容,包括標題單元格。 5、DisplayedCellsExceptHeader 調整列寬,以適合當前屏幕上顯示的行的列中的所有單元格的內容,不包括標題單元格。 6、Fill 調整列寬,使所有列的寬度正好填充控件的顯示區域,只需要水平滾動保證列寬在DataGridViewColumn.MinimumWidth 屬性值以上。相對列寬由相對 DataGridViewColumn.FillWeight 屬性值決定。 7、None 列寬不會 自動調整。 8、NotSet 列的大小調整行為從 DataGridView.AutoSizeColumnsMode 屬性繼承。 注:如果想要自動設置列寬.在這里就必須把DataGridView的值設置為Fill this.dataGridView.DataSource = ds.Tables[0]; this.dataGridView.Columns[0].FillWeight = 10; //第一列的相對寬度為10% this.dataGridView.Columns[1].FillWeight = 20; //第二列的相對寬度為20% this.dataGridView.Columns[2].FillWeight = 30; //第三列的相對寬度為30% 這里的值是相對於DataGridView當前的總寬度的,所以窗體最大化和縮小的效果是不一樣的,但比例不變 注:如何給每列設置標頭 設置標題字段(先把ColumnsHeadersVisible設置為true) this.dataGridView.Columns[0].HeaderText = "編號"; this.dataGridView.Columns[1].HeaderText = "日期"; this.dataGridView.Columns[2].HeaderText = "標題"; ----------- // 設定包括Header和所有單元格的列寬自動調整 DataGridView1.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells; // 設定包括Header和所有單元格的行高自動調整 DataGridView1.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.AllCells; ---------- 在winform中,使用DataGridView時,想在某一列,值為“true”時,將這列顏色改變,並且將值也改變,需要用到如下方法: private void gdvData_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e) { if (e.ColumnIndex == 8 ) //哪一列 { if (object.Equals(e.Value, "true")) { e.Value="成功"; e.CellStyle.ForeColor = Color.Green; } else { e.Value="失敗"; e.CellStyle.ForeColor = Color.Red; } } } ---------- DataGridView控件 DataGridView是用於Windows Froms 2.0的新網格控件。它可以取代先前版本中DataGrid控件,它易於使用並高度可定制,支持很多我們的用戶需要的特性。 1 何為DataGridView 通過DataGridView控件,可以顯示和編輯表格式的數據,而這些數據可以取自多種不同類型的數據源。 DataGridView控件具有很高的的可配置性和可擴展性,提供了大量的屬性、方法和事件,可以用來對該控件的外觀和行為進行自定義。當你需要在WinForm應用程序中顯示表格式數據時,可以優先考慮DataGridView(相比於DataGrid等其它控件)。如果你要在小型網格中顯示只讀數據,或者允許用戶編輯數以百萬計的記錄,DataGridView將為你提供一個易於編程和良好性能的解決方案。 DataGridView 用來替換先前版本中的DataGrid,擁有較DataGrid更多的功能;但DataGrid仍然得到保留,以備向后兼容和將來使用。如果你要在兩者中選擇,可以參考下面給出的DataGrid 和DataGridView之間區別的細節信息。 1.1 DataGridView和DataGrid 之間的區別 DataGridView提供了大量的DataGrid所不具備的基本功能和高級功能。此外,DataGridView 的結構使得它較之DataGrid控件更容易擴展和自定義。 下表描述了DataGridView提供而DataGrid未提供的幾個主要功能。 DataGridView功能 描述 多種列類型 與DataGrid相比,DataGridView 提供了更多的內置列類型。這些列類型能夠滿足大部分常見需要,而且比DataGrid中的列類型易於擴展或替換。 多種數據顯示方式 DataGrid僅限於顯示外部數據源的數據。而DataGridView則能夠顯示非綁定的數據,綁定的數據源,或者同時顯示綁定和非綁定的數據。你也可以在DataGridView中實現virtual mode,實現自定義的數據管理。 用於自定義數據顯示的多種方式 DataGridView提供了很多屬性和事件,用於數據的格式化和顯示。比如,你可以根據單元格、行和列的內容改變其外觀,或者使用一種類型的數據替代另一種類型的數據。 用於更改單元格、行、列、表頭外觀和行為的多個選項 DataGridView使你能夠以多種方式操作單個網格組件。比如,你可以凍結行和列,避免它們因滾動而不可見;隱藏行、列、表頭;改變行、列、表頭尺寸的調整方式;為單個的單元格、行和列提供工具提示(ToolTip)和快捷菜單。 唯一的一個DataGrid提供而DataGridView未提供的特性是兩個相關表中數據的分層次顯示(比如常見的主從表顯示)。你必須使用兩個DataGridView來顯示具有主從關系的兩個表的數據。 1.2 DataGridView的亮點 下表着重顯示了DataGridView的主要特性,稍后會介紹它們的詳細信息。 DataGridView控件特性 描述 多種列類型 DataGridView提供有TextBox、CheckBox、Image、Button、ComboBox和Link類型的列及相應的單元格類型。 多種數據顯示方式 DataGrid僅限於顯示外部數據源的數據。而DataGridView則能夠顯示非綁定的數據,綁定的數據源,或者同時顯示綁定和非綁定的數據。你也可以在DataGridView中實現virtual mode,實現自定義的數據管理。 自定義數據的顯示和操作的多種方式 DataGridView提供了很多屬性和事件,用於數據的格式化和顯示。 此外,DataGridView提供了操作數據的多種方式,比如,你可以: 對數據排序,並顯示相應的排序符號(帶方向的箭頭表示升降序) 對行、列和單元格的多種選擇模式;多項選擇和單項選擇 以多種格式將數據拷貝到剪貼板,包括text,CSV (以逗號隔開的值) 和 HTML 改變用戶編輯單元格內容的方式 用於更改單元格、行、列、表頭外觀和行為的多個選項 DataGridView使你能夠以多種方式操作單個網格組件。比如,你可以: 凍結行和列,避免它們因滾動而不可見; 隱藏行、列、表頭; 改變行、列、表頭尺寸的調整方式; 改變用戶對行、列、單元格的選擇模式; 為單個的單元格、行和列提供工具提示(ToolTip)和快捷菜單。 自定義單元格、行和列的邊框樣式。 提供豐富的可擴展性的支持 DataGridView提供易於對網格進行擴展和自定義的基礎結構,比如: 處理自定義的繪制事件可以為單元格、列和行提供自定義的觀感; 繼承一個內置的單元格類型以為其提供更多的行為; 實現自定義的接口以提供新的編輯體驗。 2 DataGridView的結構 DataGridView及其相關類被設計為用於顯示和編輯表格數據式數據的靈活的、可擴展的體系。這些類都位於system.Windows.Forms命名空間,它們的名稱也都有共同的前綴"DataGridView"。 2.1 結構元素(Architecture Elements) 主要的DataGridView相關類繼承自DataGridViewElement類。 DataGridViewElement類有兩個屬性,一是DataGridView,該屬性提供了對其所屬的DataGridView的引用;二是State,該屬性表示當前的狀態,其值為DataGridViewElementStates枚舉,該枚舉支持位運算,這意味着可以設置組合狀態。 2.2 單元格和組(Cells and Bands) DataGridView由兩種基本的對象組成:單元格(cell)和組(band)。所有的單元格都繼承自DataGridViewCell基類。 兩種類型的組(或稱集合)DataGridViewColumn和DataGridViewRow都繼承自DataGridViewBand 基類,表示一組結合在一起的單元格。 DataGridView會與一些類進行互操作,但最常打交道的則是如下三個:DataGridViewCell, DataGridViewColumn,DataGridViewRow。 2.3 DataGridView的單元格 (DataGridViewCell) 單元格(cell)是操作DataGridView的基本單位。Display is centered on cells, and data entry is often performed through cells。可以通過DataGridViewRow 類的Cells 集合屬性訪問一行包含的單元格,通過DataGridView的SelectedCells集合屬性訪問當前選中的單元格,通過DataGridView的CurrentCell屬性訪問當前的單元格。 DataGridViewCell 類圖 Cell 相關類和屬性 DataGridViewCell是一個抽象基類,所有的單元格類型都繼承於此。DataGridViewCell及其繼承類型並不是Windows Forms控件,但其中一些宿主於Windows Forms控件。單元格支持的編輯功能通常都由其宿主控件來處理。 DataGridViewCell對象不會像Windows Forms控件那樣控制自己的外觀和繪制(painting)特征,相反的,DataGridView會負責其包含的單元格的外觀。通過DataGridView 控件的屬性和事件,你可以深刻地影響單元格的外觀和行為。如果你對單元格定制有特殊要求,超出了DataGridView提供的功能,可以繼承DataGridViewCell或者它的某個子類來滿足這些要求。 2.3.1 DataGridViewCell的工作機制 理解DataGridView結構的一個重要部分是理解DataGridViewCell的工作機制: 單元格的值(A Cell’s Value) 單元格的值是其根本所在。如果單元格所在列不是綁定列,並且所在的DataGridView也不是Virtual Mode,那么它的值就由它本身所持有並維護。對於那些由綁定產生的單元格,它們壓根兒就不“知道”該持有什么值,當然也就不會去維護了;當DataGridView需要單元格的值的時候,它會到數據源中查詢該單元格應當顯示的值。在Virtual Mode下,除了會觸發CellValueNeeded事件以獲取相應單元格的值外,與數據綁定方式非常相似。在單元格級,所有這些由DataGridViewCell.GetValue() 方法來控制。 默認情況下,單元格的值的類型為object。當一個列被綁定后,會設置它的ValueType屬性,它包含的單元格的ValueType也隨之更新。而單元格的ValueType對於下一步的格式化非常重要。 格式化顯示(Formatting for Display) 注意:當DataGridView需要了解“如何顯示這個單元格”時,它需要的是單元格的FormattedValue ,而不是Value。這是一個復雜的過程,因為格式化屏幕上的一些內容通常需要將它轉換為字符串。例如,盡管你將單元格的值(Value)設置為整型值155,在顯示它的時候仍需要將其格式化。單元格和其所在的列的FormattedValueType 屬性決定了顯示它時所用的類型。多數列使用字符串類型,而Image和CheckBox類型的單元格/列則使用其它類型。Image類型的單元格和列使用Image作為默認的FormattedValueType,它的內置實現了解如何去顯示一個Image。CheckBox類型的單元格/列的FormattedValueType屬性則取決於屬性ThreeState的值。在單元格級,所有這些由DataGridViewCell.GetFormattedValue()控制。 默認情況下,DataGridView使用TypeConverter將單元格的值(Value)轉換為格式化的值(FormattedValue)。DataGridView會基於單元格的ValueType和FormattedValueType屬性來獲取合時的TypeConverter。 對於一個單元格,FormattedValue會得到多次請求(即會在多個地方用到):繪制單元格的時候,所在列根據單元格內容自動調整大小的時候,甚至是在判斷鼠標是否經過單元格內容時。每次需要FormattedValue的時候,DataGridView會觸發CellFormatting事件,這時你就有機會修改單元格的格式化顯示了。 如果單元格不能獲取它的格式化值,它會觸發DataError事件。 格式化顯示單元格還包含以怎樣的首選尺寸顯示它。這個首選尺寸是由單元格的FormattedValue,填充區域(padding),附加顯示和邊框合並而成。 繪制單元格的顯示(Painting the Display) 在獲得FormattedValue 后,單元格將負責繪制它的內容。單元格決定了繪制過程所使用的正確樣式(參見本文檔第五章的樣式部分)並進行繪制。記住:如果單元格不去繪制自己,那么該單元格將不會有任何內容得到繪制(即單元格的繪制只由它自己負責),行、列不會負責繪制任何內容,因此要確保至少要繪制單元格的背景(background),否則單元格所在的矩形區域仍然是無效的(即未經繪制)。 解析單元格的顯示(Parsing the Display) 用戶開始與單元格交互后,可能會編輯單元格的值。有一件事要記住,用戶編輯的實際上是單元格的FormattedValue。用戶提交所編輯的值時,FormattedValue需要轉換回單元格的值(Value),這個過程稱為解析(parsing)。在單元格級上,所有這些工作由單元格的DataGridViewCell.ParseFormattedValue(int rowIndex)方法控制。 默認情況下,會再次使用TypeConverter來將FormattedValue解析為單元格的真實值,這時會觸發DataGridView的CellParsing事件,這時你就有機會修改單元格的解析方式了。. 如果單元格不能得到正確地解析,會觸發DataError事件。 DataGridViewTextBoxColumn是一種通用的列類型,用於表示基於文本的值,比如數字和字符串。在編輯模式下,會有一個TextBox控件出現在當前活動單元格,用戶可以修改單元格的值。 單元格的值在顯示時會自動轉換為字符串。用戶輸入或修改的值在提交時則被自動解析為合適的數據類型以創建一個單元格的值。通過處理CellFoamatting和CellParsing事件,你可以自定義這些轉換的方式。比如將數據源的日期字段以特定的形式顯示,對某些特殊單元格作出特殊的標記。 一般情況下,CheckBox類型的單元格要么用於存儲數據,就像其它類型的數據一樣,要么用於進行一些重要操作。用戶點擊CheckBox單元格時,如果你希望對此立即做出反應,可以處理CellClick事件,但該事件發生在單元格的值更新之前。如果點擊之時就希望獲得新值,一種選擇是根據當前值計算點擊后的值;另一種方法是立即提交值的變化,然后在CellValueChanged事件處理函數中對此作出反應,而要在用戶點擊單元格時立即提交值的變化,你必須處理CurrentCellDirtyStateChanged事件,在這里,調用CommitEnd方法提交新值。 3.3 DataGridViewImageColumn DataGridViewImageColumn 類型的列用於顯示圖像。這種類型的列有三種方法生成:綁定到數據源時自動生成;為非綁定列手動生成;在CellFormatting事件處理函數(該事件發生在單元格顯示前)中動態生成。 綁定到數據源時自動生成Image列的方法適用於大量的圖像格式,包括.NET中Image類支持的各種格式,還有Access數據庫及Northwind范例數據庫使用的OLE圖片格式。 如果你想提供DataGridViewButtonColumn列的功能,又希望顯示自定義的外觀,手動生成Image列會很有用。在顯示后,你可以處理CellClick事件以處理用戶對單元格的點擊(模擬按鈕列)。 如果你要為計算值或非圖片的值提供圖片顯示,在CellFormatting事件處理函數中動態生成Image列的方法會很有用。比如,你有一個表示風險值的列,它的值可能是”high”、”middle”或”low”,可以為它們顯示不同的圖標作為警示;或者你有一個名為”Image”的列,它的值時圖片文件的位置而不是真實的圖片內容,也可以用這種方法。 3.4 DataGridViewButtonColumn 使用DataGridViewButtonColumn 列,可以在單元格內顯示按鈕。如果你要為用戶操作特定行提供一種簡單的方式,Button列會很有用,比如排序或在另一個窗體中顯示子表記錄。 在對DataGridView進行數據綁定時不會自動生成Button列,所以你必須手動創建它們,然后把它們添加到DataGridView控件的Columns集合中。 你可以處理CellClick事件以響應用戶的點擊動作。 3.5 DataGridViewComboBoxColumn 在DataGridViewComboBoxColumn類型的列中,你可以顯示包含下拉列表的單元格。這在僅允許用戶輸入一些特定值的時候顯得很有用,比如在SQL Server示例數據庫Northwind中Products表的Category列,它表示產品的種類,這個應只允許選擇現有的產品種類,此時就可以使用ComboBox列。 如果你了解如何為ComboBox控件生成下拉列表,就可以用相同的方式為ComboBox列中的所有單元格生成下拉列表。要么通過列的Items集合手動添加,要么通過DataSource,DisplayMember 和ValueMember屬性綁定到一個數據源。要了解其中的更多信息,可以參考WinForms中ComboBox空間的用法。 你可以將ComboBox列的單元格的實際值綁定到DataGridView控件本身的數據源(注意不是ComboBox列的數據源),這需要設置該列的DataPropertyName屬性(設置某個列的名稱)。 ComboBox列不會在數據綁定時自動生成,所以你必須手動創建它們,然后將其添加到Columns集合屬性中。另外,你也可以使用設計器,在設計時設置相應的屬性,這個過程類似於在設計器中ComboBox控件的使用。 3.5.1 DataError事件和ComboBox列 在使用DataGridViewComboBoxColumn 時,有時會修改單元格的值或啟動ComboBox控件的Items集合,這樣可能會引發DataError事件。這是ComboBox列的設計使然,ComboBox列的單元格會進行數據驗證。在ComboBox列的單元格嘗試繪制包含的內容時,它需要將包含的值進行格式化(見第二章第三節),在此轉換過程中,它會在ComboBox的Items集合中查找對應的值,如果查找失敗,就會引發DataError事件。忽略了DataError事件可能會使單元格不能進行正確的格式化。 3.6 DataGridViewLinkColumn 使用DataGridViewLinkColumn列,你可以顯示一列包含超鏈接的單元格。在顯示數據源中的URL值,或者替代按鈕列進行一些特殊行為,如打開另一個子記錄窗體時會很有用。 Link列也不會在DataGridView數據綁定時自動生成。要使用它,你還得手動創建,然后將它添加到DataGridView控件的Columns集合中。 你可以處理CellContentClick事件來相應用戶的點擊動作。這個事件不同於CellClick 和CellMouseClick 事件,后兩者在用戶點擊單元格任何位置(而不僅僅時鏈接)時都會觸發。 DataGridViewLinkColumn 類提供了幾個屬性,用來修改鏈接的外觀,包括點擊前,點擊時和點擊后(類似於網頁中的超鏈接)。 4 操作數據(Working with Data) 多數情況下,使用DataGridView的時候都需要跟數據打交道,這時有很多事情可能需要你去做。你需要驗證用戶輸入的數據,或者需要對數據進行格式化。DataGridView能夠以三種模式顯示數據:bound、unboundand 和virtual。每種模式都有自己的特性和存在的理由。不管是否是數據綁定模式,在操作數據時,如果發生錯誤,DataGridView通常會觸發DataError事件,理解該事件發生的原因能讓你更好地利用它。 4.1 數據輸入和驗證的相關事件 用戶輸入數據時-對其所在的行或單元格,你可能希望驗證這些數據,在遇到無效數據時通知用戶。就像常見的Windows Forms控件,DataGridView的行和單元格也有Validating和Validated事件,驗證事件可被取消。用戶在單元格/行間移動時會觸發Enter和Leave事件。最后,用戶在開始編輯單元格時也會觸發事件。了解所有這些程序的發生順序會對你很有幫助。 4.1.1 數據驗證相關事件的順序 下面列出validation,enter/leave和begin/end這些事件的順序(當EditMode為EditOnEnter時): 當從一個單元格移動至另一單元格(在同一行內): 1) Cell Leave (原來的單元格) 2) Cell Validating/ed (原來的單元格) 3) Cell EndEdit (原來的單元格) 4) Cell Enter (新的單元格) 5) Cell BeginEdit (新的單元格) 當從一行移動到另一行: 1) Cell Leave (原來的單元格),Row leave (原來的行) 2) Cell Validating/ed (原來的單元格) 3) Cell EndEdit (原來的單元格) 4) Row Validating/ed (原來的行) 5) Row Enter (新的行) 6) Cell Enter (新的單元格) 7) Cell BeginEdit (新的單元格) 4.1.2 驗證數據 驗證用戶輸入時,如果DataGridView采用非數據綁定模式,通常會對單元格進行驗證;而如果采用數據綁定模式,則一般會對行進行驗證。這與數據的組織方式密切相關,非數據綁定模式下,一行的單元格間關系一般比較“散”,而綁定模式下,數據源的數據一般以行來組織。但有時在數據綁定模式下會同時進行單元格級和行級的驗證。 4.1.2.1 顯示錯誤信息 一旦遭遇了無效的輸入數據,你通常需要通知用戶。這時有多種方式可以選擇,傳統的方式是使用信息對話框。DataGridView還能夠為行或單元格顯示一個錯誤圖標來通知用戶輸入了無效數據。錯誤圖標帶有一個工具提示,它提供了該錯誤的相關信息: 4.1.3 在新行中的數據輸入(Data Entry in the New Row) 當在程序中使用DataGridView來編輯數據時,你往往希望提供讓用戶添加新行數據的功能。DataGridView控件支持這個功能,提供了一個用於添加新記錄的行,而這一行總是顯示為最后一行,並在該行的標題單元格標以星號(*)。 下面的幾個小節會討論一些在程序中使用這個新行時需要考慮的內容。(下面總是以 新行 表示 用於添加新記錄的行 ) 4.1.3.1 顯示新行 使用AllowUserToAddRows屬性以指示是否顯示新行,其默認值為true。 新行處於網格的最后一行,標題帶有星號: 在數據綁定的情況下,當DataGridView控件的AllowUserToAddRows屬性和數據源的IBindingList.AllowNew 屬性都為true時,新行才會顯示,只要兩者有一個為false,新行就不會顯示。 4.1.3.2 為生成的新行添加默認值 當用戶選擇新行作為當前行,DataGridView會觸發DefaultValuesNeeded事件。在該事件中可以訪問新行,並為其生成默認值,為用戶輸入提供方便。 下面這段代碼演示了如何在DefaultValuesNeeded事件中為新行指定默認值。 private void dataGridView1_DefaultValuesNeeded(object sender, DataGridViewRowEventArgs e) { e.Row.Cells["Region"].Value = "WA"; e.Row.Cells["City"].Value = "Redmond"; e.Row.Cells["PostalCode"].Value = "98052-6399"; e.Row.Cells["Region"].Value = "NA"; e.Row.Cells["Country"].Value = "USA"; e.Row.Cells["CustomerID"].Value = NewCustomerId(); } 4.1.3.3 Rows集合與新行的關系 新行包含在DataGridView控件的Rows集合中,又因其總是處於最后一行,下面這行代碼會返回新行: DataGridViewRow row = dataGridView1.Rows[dataGridView1.Rows.Count - 1]; 盡管新行也包含在Rows集合中,它與Rows集合中其它行的行為卻不相同,表現在兩點: 不能以編程的方式將新行從Rows集合中移除,如果你嘗試這么做,會拋出InvalidOperationException類型的異常。用戶也不能刪除新行。DataGridViewRowCollection.Clear()方法也不能將新行從Rows集合中移除。 不能在新行之后添加行。如果你嘗試這么做,會拋出InvalidOperationException 類型的異常。這種特性的結果是,新行總處於DataGridView的最后一行。當新行顯示的時候,DataGridViewRowCollection 類中用於添加行的方法-Add,AddCopy以及AddCopies-在內部都調用用於插入的方法。 4.1.3.4 在新行中輸入數據 用戶開始在新行輸入數據之前,新行的IsNewRow屬性值為true;一旦用戶開始輸入,這一行就不再是新行了,DataGridView中會產生一個“新”的新行,看下面示意圖: 在添加“新”的新行時,會觸發UserAddedRow事件,它的事件處理函數的第二個參數有屬性Row,指定了這個“新”的新行。如果用戶此時按下Escape鍵,“新”的新行會被移除,這會觸發UserDeletingRow事件,它的事件處理函數的第二個參數的屬性Row指定了“新”的新行。 4.1.3.5 自定義新行的可視化效果 新行是基於RowTemplate模板創建的,如果沒有指定它的單元格的樣式,它們會采用繼承的樣式。要了解樣式繼承的更多信息,請參看第五章第一節的內容。 新行中單元格的初始值是由每個單元格的DefaultNewRowValue屬性決定的。對於DataGridViewImageCell類型的單元格,其初始值為一個占位圖片,其它類型的則為null。你可以重寫這個屬性以返回自定義值。但也可以在DefaultValuesNeeded事件處理函數中對默認值進行替換,該事件在焦點進入新行時觸發。 新行標題的標准圖標是箭頭或者星號,並沒有得到暴露。如果你要自定義這個圖標,就需要創建一個自定義的DataGridViewRowHeaderCell 類。 新行的標題的標准圖標使用標題單元格DataGridViewCellStyle的ForeColor屬性。注意:如果沒有足夠的空間,圖標就不會再顯示。 如果為標題單元格設置了字符串值(通過Value屬性),但沒有足夠的控件同時顯示文本和圖標,那么圖標會被首先截掉。 4.1.3.6 新行的排序 在非綁定模式下,新行總是添加在DataGridView的最后一行,即使已經對數據排序。用戶需要在添加新行后再次進行排序,以將新記錄放在合適的位置;這種行為方式類似於ListView控件。 在綁定模式或虛擬模式(Virtual Mode)下,如果已對數據排序,那么插入數據時的行為取決於數據模型的實現方式。對於ADO.NET,新加的行會被自動排序至合適的位置。 4.1.3.7 關於新行,還要注意: 你不能將新行的Visible屬性值設置為false,否則會觸發一個InvalidOperationException類型的異常。 新行在創建時總是處於非選中(unselected)狀態。 4.1.3.8 Virtual Mode下的新行 如果你正要實現虛擬模式(Virtual Mode),需要考慮數據模型添加新行和回滾添加操作的情況。該功能准確的實現方式取決於數據模型的實現方式及其事務機制,例如,提交的時候是針對單元格還是行。參看本文檔后面關於Virtual Mode的主題。 4.2 關於Null值 在使用數據源的時候,比如數據庫或業務對象,經常需要處理null值。null值可能是一個實際的null(VB中為Nothing),也可能是一個數據庫的”null”值(DBNull.Value),當你遭遇了這些值,就需要考慮如何顯示它們。另一方面,很多時候,你還需要向數據源寫入null值。使用單元格Style的NullValue屬性和DataSourceNullValue 屬性,你可以改變DataGridView處理null值的方式。 4.2.1 NullValue屬性 DataGridViewCellStyle.NullValue 屬性本來要被命名為FormattedNullValue 的,但是后來沒來得及作出這個更改。但它能給我們帶來一點提示——顧名思義,在格式化時會用到它。如果一個單元格的值為”null”(等於null或DBNull.Value),它會使用你設置的NullValue屬性來顯示。該屬性的默認值取決於所在列的類型,見下圖: DataGridView列類型 列的DefaultCellStyle.NullValue值 TextBoxColumn String.Empty (“”) ImageColumn 空的圖像( ) ComboBoxColumn String.Empty (“”) ButtonColumn String.Empty (“”) LinkColumn String.Empty (“”) CheckBoxColumn 默認值取決於ThreeState屬性的值,如果為true,默認值為CheckState.Indeterminate ,否則為unchecked。 有一點要了解,在用戶輸入數據時也會用到NullValue。例如,若用戶向TextBox類型單元格輸入了string.Empty,那么會將null作為該單元格的值。 查看下面的DataSourceNullValue屬性以了解究竟是輸入了什么作為單元格的值。 4.2.2 DataSourceNullValue屬性 DataGridViewCellStyle.DataSourceNullValue屬性要被命名為ParseNullValue的,如果NullValue屬性被命名為FormattedNullValue的話,但最后還是采用了DataSourceNullValue,這樣更直觀准確。在將null值寫入單元格的值時,就會用到DataSourceNullValue屬性。在數據綁定情形下,這個null值將被寫入數據庫或業務對象,此處需要進行控制,因為對於數據庫和業務對象來說,null的概念不盡相同。通常你會期望,使用業務對象時將DataSourceNullValue 設置為null,而使用數據庫時則將其設置為DBNullValue。DataSourceNullValue的默認值為DBNull.Value。 4.3 DataError事件 將DataError事件獨立出來作為一個主題,是因為在操作數據時,經常會遭遇DataError事件。在操作數據時,DataError主要發生在一下情況:不能讀/寫或轉換單元格的數據;在嘗試進行某種編輯操作時發生了異常。 編輯操作中的DataError 事件 下面的列表列出了可能會引發DataError事件的編輯操作: 取消編輯(Canceling an edit) 刷新一個編輯 (通過調用RefreshEdit方法) 嘗試將單元格的值寫入數據源 初始化編輯控件\單元格的值(通過設置單元格的FormattedValue屬性或調用單元格的InitializeEditingControl方法) 結束編輯(Ending an edit) 提交編輯(Committing an edit) 刪除一行(Deleting a row) DataError的上下文: 下面的列表顯示了不同的DataError上下文環境,然后進一步說明了這些上下文環境合適可能發生: 4.4 數據綁定模式(Databound modes) 4.4.1 非綁定模式(Unbound Mode) 如果你要在程序中管理數量相對較小的數據,那么非綁定模式會比較合適。此時你不是像綁定模式中那樣將DataGridView控件直接指向一個數據源,而是手動去生成控件。一般需要用到DataGridViewRowCollection.Add 方法(該方法向DGV中添加行)。 非綁定模式在處理靜態、只讀的數據時特別有用,也可以用在以自己的方式與外部數據源交互的情況,但實際上,如果你希望你的用戶與外部的數據源交互,一般還是用綁定模式(bound mode)更好。 4.4.2 綁定模式(Bound Mode) 如果你在程序中管理一些數據,並希望能與數據源自動進行交互,就應該使用綁定模式。此時你可以設置DataSource屬性,將數據源綁定到DataGridView控件。如果控件使用了綁定模式,就不需要你去顯式地對數據進行讀寫了。如果AutoGenerateColumns 屬性為true,數據源中的每一列都會在DataGridView中生成一個相應的列(根據列的數據類型),如果你希望創建自己的列,可以將該屬性設置為false,使用DataPropertyName屬性將一列綁定到數據源的一列,這在你不想用自動生成的列類型時很有用。 4.4.2.1 有效的數據源 將數據綁定到DataGridView非常簡單、直觀,很多情況下,你只需要設置它的DataSource屬性。如果使用的數據源包含多個列表(list)或數據表(table),你還需要設置控件的DataMember屬性,該屬性為字符串類型,用於指定要綁定的列表或數據表。 DataGridView控件支持標准的WinForm數據綁定模型,因此它可以綁定到下面列表中的類的實例: 任意實現了IList接口的類,包括一維數組; 任意實現了IListSource接口的類,比如DataTable和DataSet; 任意實現了IBindingList 接口的類,比如BindingList ; 任意實現了IBindingListView接口的類,比如BindingSource 。 列表更改通知(List Change Notification) 當你將數據綁定到列表時,最重要的功能之一便是支持列表更改通知了。這只有在你希望列表(即數據源)發生變化,如添加、修改和刪除,DataGridView能夠隨之更新的時候,該功能才顯得重要。只有實現了IBindingList接口的數據源支持更改通知。像數組和集合這樣的列表默認情況下不支持更改通知。 在選擇數據源時,BindingSource組件應該作為首選,因為它可以綁定到多種類型的數據源,並且能夠自動處理很多數據綁定相關的事務。一般情況下,應該將DataGridView綁定到BindingSource組件,並將BindingSource組件綁定真正的數據源(它的作用就像DGV和數據源間的橋梁)。 BindingList<T>類也可以在一個類的基礎上創建自定義列表(list)。 對象更改通知(Object Change Notification) 如果你有了一個數據源,那么數據源中的對象就可以實現對public屬性的更改通知。這需要你為相應屬性提供一個” PropertyNameChanged”事件,或者實現INotifyPropertyChanged接口。INotifyPropertyChanged 是在VS 2005 中新加的接口,可以與BindingList<T>一起使用來創建可綁定的列表(list)。但當你的數據源是BindingSource ,那就不用再額外實現更改通知了。 4.4.3 虛擬模式 使用虛擬模式,你可以實現自己的數據管理操作。在綁定模式下,如果要使用非綁定列,那么要想在對列排序時能夠維護非綁定列的值,就需要虛擬模式。但虛擬模式的最主要的用途還是在操作大量數據時優化性能。 你將DataGridView綁定到緩存的數據,然后用代碼控制數據行的存取。要保持使用內存量比較小,緩存的數據量應與當前要顯示的行數相當。當用戶滾動控件看到了新的行時,你的代碼就從緩存中請求新的數據,並從內存中清除舊的數據。 如果你正要實現虛擬模式(Virtual Mode),需要考慮數據模型添加新行和回滾添加操作的情況。該功能准確的實現方式取決於數據模型的實現方式及其事務機制,例如,提交的時候是針對單元格還是行。參看本文檔后面關於Virtual Mode的主題。 4.4.4 混合模式 – 綁定與非綁定模式 顯示在DataGridView中的數據通常來自於某種類型的數據源,但是你可能也希望顯示一個數據源之外的列。這種列稱為非綁定列。 你可以在綁定模式下添加非綁定列,在你希望顯示一個按鈕列或者鏈接列讓用戶操作一些特定行時這顯得很有用,另外也可以用非綁定列顯示一些由綁定列計算而得到的值。你可以在CellFormatting事件處理函數中生成計算列的值。不過如果你使用的數據源是DataSet或DataTable,你可能希望使用DataColumn.Expression 屬性來創建一個計算列,在這種情況下,在DGV看來,這一列就跟數據源中其它列是一樣的。 在綁定模式下根據非綁定列排序是不受支持的。如果你在綁定模式下創建了非綁定列,你必須實現虛擬模式,這樣在根據綁定列排序時可以維護非綁定列的值。 如果添加的非綁定列不能由數據源數據計算得來或者這些數據會頻繁更新,你就應該使用虛擬模式。要了解虛擬模式的更多信息,請參看本文檔后面的虛擬模式相關章節。 5 特性綜覽(Overview of features) 5.1 樣式(Styling) DataGridView使得定義單元格的基本外觀和格式化單元格顯示變得簡單。 您可以定義的外觀和在特定的列和行,或在通過各種設置DataGridView控件屬性訪問的DataGridViewCellStyle對象的屬性控制所有細胞的單個單元格的格式樣式。此外,您可以修改,如通過處理CellFormatting事件的單元格值因素的基礎上動態這些樣式。 DataGridView控件中的每一個細胞都可以擁有如文本格式,背景色,前景色和字體自己的風格。但是,通常多個單元格將分享獨特的風格特點。 細胞群體共享樣式可能包括在特定行或列的所有單元格包含特定值,或控件中的所有細胞的所有細胞。由於這些群體重疊,每個單元可能會從多個位置的樣式??信息。例如,您可能會希望每個在DataGridView控件使用相同的字體細胞,只有細胞貨幣列,但使用貨幣格式,負數和貨幣細胞只使用紅色前景色。 單擊單元格以選中它,行列標題不能用於選擇。 ColumnHeaderSelect 單擊單元格以選中它,單擊列標題選中整列。此時列標題不能用於排序。 FullColumnSelect 單擊單元格或列標題會選中它們所在的列,此時列標題不能用於排序。 FullRowSelect 單擊單元格或行標題會選中它們所在的行。 RowHeaderSelect DGV的默認選擇模式,單擊單元格選中該單元格,單擊行標題則選中整行。 注意: 在運行時改變選擇模式會自動清除當前選擇的內容。 5.4.1 Programmatic Selection 注意:改變CurrentCell屬性的值不會改變當前選擇的內容。 通過SelectedCells、SelectedRows和SelectedColumns屬性你可以訪問當前選中的單元格、行和列。不過當所有單元格都被選中的時候,使用這些屬性效率會比較低,為此可首先使用AreAllCellsSelected方法查看是否已選中全部單元格。此外,訪問這些屬性來查看選中單元格、行和列的數目效率也比較低,此時應該使用GetCellCount、GetRowCount和GetColumnCount方法,傳給它們的參數為DataGridViewElementStates.Selected。 5.5 滾動(Scrolling) DataGridView毫無疑問地會提供對水平和垂直滾動條的支持,它同時也支持使用鼠標滾輪進行垂直滾動。水平方向的滾動基於像素值,而垂直方向的滾動則基於行的索引,DataGridView不支持垂直方向的基於像素值的滾動。 你可以檢索各種屬性的DataGridView,DataGridViewColumn的,的DataGridViewRow,和DataGridViewCell類及其派生類DataGridViewCellStyle對象。如果其中一個屬性尚未設置,檢索其值將創建一個新的DataGridViewCellStyle對象。您還可以將自己的DataGridViewCellStyle對象,並將它們分配給這些屬性。 您可以通過共享的DataGridViewCellStyle避免不必要的在多個DataGridView元素對象的樣式信息的重復。因為在控制,列集的風格,和行各層面滲透到細胞水平的水平了,你還可以通過設置避免只在每個級別,從不同層次上的風格樣式屬性重復。這是進行了更詳細的樣式繼承節如下。 下表列出了獲取或設置DataGridViewCellStyle對象的主要屬性。 物業類的描述 的DefaultCellStyle的DataGridView,DataGridViewColumn的,的DataGridViewRow和派生類獲取或設置所有單元格中使用的整個控制(包括標題單元格)的默認風格,在一列,或在一排。 RowsDefaultCellStyle的DataGridView獲取或設置默認單元格的控件中的所有行使用的樣式。這不包括標題單元格。 AlternatingRowsDefaultCellStyle的DataGridView獲取或設置默認單元格的行交替使用的樣式的控制。用於創建一個總賬般的效果。 RowHeadersDefaultCellStyle的DataGridView獲取或設置該控件的默認單元格的行標題使用的樣式。由當前主題重寫如果啟用視覺樣式。 ColumnHeadersDefaultCellStyle的DataGridView獲取或設置該控件的默認單元格的列標題使用的樣式。由當前主題重寫如果啟用視覺樣式。 風格的DataGridViewCell和派生類獲取或設置在細胞水平上指定的樣式。這些樣式覆蓋上級繼承的。 InheritedStyle的DataGridViewCell,的DataGridViewRow,DataGridViewColumn和派生類獲取所有的風格,包括從上級繼承樣式應用到當前單元格,行或列。 如上所述,得到了一個樣式屬性的值會自動實例化一個新的DataGridViewCellStyle對象如果屬性尚未以前設置。為了避免不必要地創建這些對象,行和列類有一個HasDefaultCellStyle屬性,您可以檢查以確定是否DefaultCellStyle屬性已設置。同樣,細胞類具有HasStyle屬性,指示是否Style屬性已設置。 每個屬性的樣式上有一個相應的PropertyNameChanged DataGridView控件的事件。對於行,列和單元格屬性,事件名稱開頭“行”,“列”,或“細胞”(例如,RowDefaultCellStyleChanged)。這些事件發生時,每一個對應的樣式屬性設置為不同的DataGridViewCellStyle對象。這些事件不會發生當您檢索從樣式屬性的DataGridViewCellStyle對象,並修改其屬性值。為了應對變化的單元格樣式對象本身,處理CellStyleContentChanged事件。 5.1.3樣式繼承 每個DataGridViewCell的會從它的InheritedStyle屬性它的外觀。 DataGridViewCellStyle對象的此屬性返回繼承從類型DataGridViewCellStyle的屬性層次的價值。下面列出了這些屬性的順序在其中非頭細胞InheritedStyle獲取其值。 1。 DataGridViewCell.Style 2。 DataGridViewRow.DefaultCellStyle 3。 AlternatingRowsDefaultCellStyle(只適用於奇數行單元格指數) 4。 RowsDefaultCellStyle 5。 DataGridViewColumn.DefaultCellStyle 6。的DefaultCellStyle 對於行和列標題單元格,InheritedStyle屬性填充的值是從給定的順序在下面的列表源屬性。 1。 DataGridViewCell.Style 2。 ColumnHeadersDefaultCellStyle或RowHeadersDefaultCellStyle 3。的DefaultCellStyle 下圖演示了這個過程。 您還可以通過特定的行和列繼承的樣式。列InheritedStyle財產繼承了以下屬性的值。 1。 DataGridViewColumn.DefaultCellStyle 2。的DefaultCellStyle 該行InheritedStyle財產繼承了以下屬性的值。 1。 DataGridViewRow.DefaultCellStyle 2。 AlternatingRowsDefaultCellStyle(只適用於奇數行單元格指數) 3。 RowsDefaultCellStyle 4。的DefaultCellStyle 對於每一個由InheritedStyle屬性返回一個DataGridViewCellStyle對象屬性,屬性值是從第一個單元格樣式列表,在適當的相應的屬性設置為除DataGridViewCellStyle類的默認值等。 下表說明了一個例子細胞ForeColor屬性的值是從包含列繼承。 類型DataGridViewCellStyle的范例前景色為檢索對象的價值屬性 DataGridViewCell.Style Color.Empty DataGridViewRow.DefaultCellStyle Color.Red AlternatingRowsDefaultCellStyle Color.Empty RowsDefaultCellStyle Color.Empty DataGridViewColumn.DefaultCellStyle Color.DarkBlue 的DefaultCellStyle Color.Black 在這種情況下,從單元格的行System.Drawing.Color.Red值是第一個在名單上的實際價值。這成為該單元格的InheritedStyle ForeColor屬性值。 下圖說明了不同的DataGridViewCellStyle屬性可以繼承他們的價值觀不同的地方。 通過利用樣式繼承的優勢,可以提供,而無需指定相同的信息在多個地方為整個控制適當的樣式。 雖然標題單元格樣式繼承中所描述的身份參加,由DataGridView控件的ColumnHeadersDefaultCellStyle和RowHeadersDefaultCellStyle屬性返回的對象具有初始屬性值覆蓋由DefaultCellStyle屬性返回的對象的屬性值。如果你想由DefaultCellStyle屬性返回的對象設置為適用於行和列標題的屬性,你必須設置由ColumnHeadersDefaultCellStyle和RowHeadersDefaultCellStyle屬性返回的DataGridViewCellStyle類為默認顯示對象的相應屬性。 注:如果啟用視覺樣式,行和列標題(除TopLeftHeaderCell)會自動由當前的主題風格,覆蓋了這些屬性所指定的任何樣式。設置EnableHeadersVisualStyle屬性為false,如果你想標題不使用XP的視覺樣式。 該DataGridViewButtonColumn,DataGridViewImageColumn和DataGridViewCheckBoxColumn類型還初始化由列DefaultCellStyle屬性返回的對象的一些值。有關詳細信息,請參見這些類型的參考文件。 5.1.4設置樣式動態 要自定義,特別值的單元格的樣式,實施一項CellFormatting事件的處理程序。此事件的處理程序收到的DataGridViewCellFormattingEventArgs類型的參數。此對象包含的屬性,讓您確定單元格的值被格式化,其在DataGridView控制地沿。此對象還包含一個CellStyle屬性,初始化為單元格的InheritedStyle屬性值被格式化。您可以修改單元格樣式屬性來指定樣式的信息適合單元格的值和位置。 注:RowPrePaint和RowPostPaint事件還接收事件數據的DataGridViewCellStyle對象,但他們的案件,這是該行InheritedStyle屬性為只讀目的副本,以及它的變更不會影響控制。 您還可以動態改變以因應如CellMouseEnter和CellMouseLeave活動活動單個細胞的風格。例如,在為CellMouseEnter事件處理程序中,你可以存儲單元格的背景顏色(通過細胞的Style屬性檢索)的當前值,然后將其設置為一個新的色彩,將突出顯示單元格時在它的鼠標懸停。在為CellMouseLeave事件處理程序,然后就可以恢復到原來的背景顏色值。 注:緩存在細胞的Style屬性中存儲的值是重要的,無論是否設置特定的樣式值。如果您暫時替換樣式設置,恢復到原來的“未設置”國家保障,細胞會返回從更高的層次繼承的樣式設置。如果您需要確定在一個單元的實際效果風格的風格無論是繼承,使用單元格的InheritedStyle屬性。 5.2風俗畫 DataGridView控件提供了多個屬性,您可以用它來調整外觀和基本行為(外觀和感覺)的單元格,行和列。如果您有要求,超越的DataGridViewCellStyle類的功能的時候,你可以執行單元格或行的內容自定義繪制。單元格和行畫自己,你可以處理各種如RowPrePaint的DataGridView,CellPainting和RowPostPaint繪畫活動。 5.2.1油漆件 自定義繪制的一個重要部分是油漆部件的概念。該DataGridViewPainParts枚舉用於指定哪些部分細胞油漆。枚舉值可結合在一起,有一個單元不油漆塗料或特定部分。這里是不同的部分: PaintPart為例前景色為檢索對象的價值 所有的所有部件都畫 背景單元格的背景是畫使用單元格的背景顏色(1) 邊境的邊界是畫 ContentBackground單元格的內容是畫背景的一部分。 (2) ContentForeground單元格的內容的前景部分是畫(2) ErrorIcon錯誤圖標畫 重點加強對單元格焦點矩形畫 沒有任何部分是畫(1) SelectionBackground畫的背景是,如果選中該單元格被選中。 注釋 1)如果一個單元格不繪制其背景則沒有什么是畫。一個行或列執行任何作畫,確保至少細胞的背景畫,或者您執行您自己的自定義背景畫,否則仍然是無效的矩形(着色)。 2)每個單元確定什么前景為內容的背景和內容,如下面的列表描述的那樣塗料: 細胞類型的內容前景內容背景 文本框單元格的文字是畫沒有畫 扣式電池文字畫,畫按鈕 組合框單元格的文字是畫,畫組合框 選中復選框是畫沒有畫 鏈接單元格文本鏈接是沒有畫成畫 圖像細胞圖像是畫沒有畫 標題欄標題欄文字排序雕畫 行頭行頭文字Current行三角形,編輯鉛筆和新行的指標是畫 5.2.2行預油漆塗料和郵政業 您可以通過處理一個或DataGridView.RowPrePaint和DataGridView.RowPostPaint兩個事件的DataGridView行的外觀。這些活動的設計,讓你可以畫只有你想在DataGridView控制,而讓其余的油漆。例如,如果你想畫一個自定義的背景,你可以處理DataGridView.RowPrePaint事件,並讓自己的單個細胞塗料前景的內容。在RowPrePaint事件你可以設置PaintParts事件參數屬性來輕松定制的細胞如何油漆。例如,如果您想保留的任何選擇,或從繪畫的焦點細胞,你RowPrePaint事件將設置像這樣PaintParts屬性: e.PaintParts = DataGridViewPaintParts.All& ?(DataGridViewPaintParts.Focus | DataGridViewPaintParts.SelectionBackground); 這也可以寫成: e.PaintParts =(DataGridViewPaintParts.Background | DataGridViewPaintParts.Border | DataGridViewPaintParts.ContentBackground | DataGridViewPaintParts.ContentForeground | DataGridViewPaintParts.ErrorIcon); 或者,也可以讓自己和油漆的細胞中添加一個自定義事件處理程序的DataGridView.RowPostPaint前景的內容。您還可以禁用油漆和塗料的一切細胞在DataGridView.RowPrePaint自己的事件處理程序 5.3 Autosizing DataGridView控件提供了自定義的列和行的調整大小行為的許多選項。通常情況下,DataGridView單元格不調整的基礎上的內容。相反,她們還會給任何顯示值比電池大。如果內容可以作為一個字符串顯示,該單元格顯示在工具提示。 默認情況下,用戶可以用鼠標拖動來顯示更多信息行,列和標題分隔。用戶還可以雙擊一個分頻器來自動調整相關的行,列或標題帶其內容為基礎。列共享默認情況下,控制可用寬度??,所以,如果用戶可以調整控制,例如,如果它是一個可調整大小的對接形式,他們也可以更改列的所有可用的展示空間。 DataGridView控件提供的屬性,方法和事件,使您可以自定義或禁用這些用戶導向的所有行為。此外,您可以通過編程方式調整行,列和標題,以適合他們的內容,也可以將其配置為自動調整自己只要其內容的變化。 常見問題: 1)如何調整最后一列的寬度使其占據網格的剩余客戶區? 5.3.1在Windows窗體DataGridView控件調整大小選項 DataGridView行,列和標題可以改變許多不同的事件結果的大小。下表顯示了這些事件。 發生說明 用戶調整大小用戶可以通過拖動或雙擊行,列或標題分隔大小的調整。 控制調整在列填充模式,列寬度變化時,控制寬度的改變,例如,當控件停靠到其父形式和用戶調整的表格。 細胞在基於內容的自動調整大小模式值的變化,大小變化,以適應新的顯示值。 方法調用的方案內容為基礎的大小可以讓用戶調整大小的基礎上伺機在方法調用時單元格值。 屬性設置也可以設置特定的高度和寬度值。 默認情況下,啟用用戶調整大小,自動調整大小被禁用,是更廣泛的單元格值比列剪裁。 下表顯示的情況,你可以用它來調整預設的行為,或使用特定的調整大小選項來達到特定的效果。 方案實施 使用列填充顯示同樣,在一列,占據了整個寬度的控制數量相對較少,而不顯示水平滾動條大小的數據模式。 AutoSizeColumnsMode屬性設置為Fill。 使用列填充不同大小顯示值模式。 AutoSizeColumnsMode屬性設置為Fill。初始化設置列的FillWeight屬性或調用控件AutoResizeColumns灌裝后用數據控制方法相對列寬度。 使用列填充不同的重要性與價值模式。 AutoSizeColumnsMode屬性設置為Fill。設置大量列的MinimumWidth值,必須始終顯示的數據部分或使用一個尺寸的選擇以外填補特定列模式。 使用列填充模式,以避免顯示控件的背景。設置最后一列AutoSizeMode屬性為Fill和使用其他尺寸的其他列選項。 顯示一個固定寬度的列,如圖標或ID列。 AutoSizeMode設置為None,可調整大小為False的列。初始化設置width屬性,或者調用控件AutoResizeColumn后用數據填??充它的寬度控制方法。 大小時自動調整單元格內容的變化,以避免裁減和優化使用空間。設置一個自動調整大小屬性的值,表示一個基於內容的大小調整模式。為了避免性能下降時,大量的數據工作,使用一個尺寸模式,只計算顯示的行。 調整大小以適應顯示的行值,以避免性能下降時,許多行工作。使用自動或編程調整大小適當的調整大小模式枚舉值。要調整大小,以適應在新顯示的行滾動時,請在一個滾動的事件處理程序大小的方法價值。定制用戶雙擊調整大小,以便顯示的行的值只有在確定新的尺寸,要求在一個RowDividerDoubleClick或ColumnDividerDoubleClick事件處理程序大小的方法。 只有在特定時間調整大小以適應單元格內容,以避免性能罰款或啟用用戶調整大小。調用事件處理程序中的基於內容的大小的方法。例如,使用DataBindingComplete事件綁定后初始化大小和處理CellValidated或CellValueChanged事件調整大小,以彌補用戶編輯或綁定的數據源的變化。 調整多行單元格內容的行高。確保該列的寬度是用於顯示相應的文本段落並使用自動或編程的基於內容的行大小來調整高度。另外,還要確保與細胞顯示多內容使用的WrapMode細胞式的真實價值。 通常,你會使用自動調整大小模式,以維持列列寬或將其設置為特定寬度前行高進行調整。 5.3.2用鼠標調整大小 默認情況下,用戶可以調整行,列和標題不使用自動大小調整模式對細胞價值觀為基礎。為了防止其他模式,例如列填充模式,縮放用戶設置一個或以下的DataGridView屬性: ?AllowUserToResizeColumns ?AllowUserToResizeRows ?ColumnHeadersHeightSizeMode ?RowHeadersWidthSizeMode 您還可以防止大小設置其Resizable屬性由單個行或列的用戶。默認情況下,Resizable屬性值是基於對列AllowUserToResizeColumns屬性值和屬性值的行AllowUserToResizeRows。如果你明確地設置大小可調整為True或False,但是,指定的值控制值覆蓋該行或列中。設置調整大小to NotSet恢復繼承。 由於NotSet還原值繼承,Resizable屬性永遠不會返回NotSet值,除非該行或列並沒有被添加到一個DataGridView控制。如果您需要確定是否行或列Resizable屬性值繼承,審查其國家的財產。如果該國值包括ResizableSet標志,Resizable屬性值不繼承。 5.3.3自動調整大小 有兩種自動調整大小在DataGridView控制類型:列填充模式和基於內容的自動調整大小。 列填充模式導致在控件中可見列,以填補該控件的顯示區域的寬度。如需這個模式的詳細信息,請參閱列填充模式一節。 您還可以配置行,列和標題的大小自動調整以適應其單元格內容。在這種情況下,大小調整單元格內容時發生變化。 注意:如果你保持在自定義數據緩存單元格的值使用虛擬模式,自動調整大小時發生用戶編輯單元格值,但不會發生改變時,外面的一CellValuePushed事件處理緩存值。在這種情況下,調用UpdateCellValue方法強制控制更新單元格的顯示和應用當前的自動調整大小模式。 如果基於內容的自動調整大小僅用於也就是說,對於行,但不列,或列,但不是行和的WrapMode還啟用一維啟用,大小調整時,也會發生在其他方面的變化。例如,如果行,但不列自動調整大小和配置的WrapMode已啟用,用戶可以拖動列分隔來改變一個列和行高將自動調整使細胞內容仍然充分顯示寬度。 如果配置基於內容的自動調整大小行和列和的WrapMode啟用,DataGridView控件將調整單元格內容改變大小時,將使用一個理想的細胞高度對寬度的比例,當計算新的大小。 要配置標題和行和列不會覆蓋控制值,漿紗模式設置一個或多個以下的DataGridView屬性: ?ColumnHeadersHeightSizeMode ?RowHeadersWidthSizeMode ?AutoSizeColumnsMode ?AutoSizeRowsMo??de 若要重寫控件的列大小的單個列模式,將其AutoSizeMode屬性的值比NotSet等。一列大小調整模式實際上是取決於它的InheritedAutoSizeMode財產。這個屬性的值是基於列的AutoSizeMode屬性值,除非該值是NotSet,在這種情況下控制的AutoSizeColumnsMode值繼承。 請謹慎使用基於內容的自動調整大小時,大量數據的工作。為了避免性能下降,使用自動調整大小模式,而不是分析計算中的每一行控制的基礎上所顯示的行唯一的大小。為獲得最佳性能,使用編程調整大小,而不是讓你在特定的時間可以調整,如新的數據后立即加載。 基於內容的自動調整大小模式不會影響行,列或標題,你已經通過設置行或列的Visible屬性或控制RowHeadersVisible或ColumnHeadersVisible屬性為false隱藏。例如,如果列是隱藏后,它會自動調整以適應一個大單元格的值,隱藏的列將不會改變它的大小,如果大所在的行單元格的值將被刪除。自動調整大小時,不會出現能見度的變化,因此更改列的Visible屬性返回true,將不會強迫它重新計算其大小的當前內容為基礎。 方案內容為基礎的大小影響的行,列和標題不論其知名度。 5.3.4編程調整大小 禁用自動調整大小時,您可以通過編程設置精確的寬度通過下列屬性或行,列或標題的高度: ?RowHeadersWidth ?ColumnHeadersHeight ?DataGridViewRow.Height ?DataGridViewColumn.Width 您還可以通過編程調整行,列和標題,以適合他們的內容使用下列方法: ?AutoResizeColumn ?AutoResizeColumns ?AutoResizeColumnHeadersHeight ?AutoResizeRow ?AutoResizeRows ?AutoResizeRowHeadersWidth 這些方法將調整行,列或標題一次,而不是連續的大小配置它們。新的大小自動計算顯示沒有剪輯的所有單元格內容。當您以編程方式調整列有填充InheritedAutoSizeMode屬性值,但是,計算出的基於內容的寬度按比例用於調整列FillWeight屬性值,實際列寬,然后根據這些新的計算比例,讓所有列填充該控件的可用顯示區域。 編程調整大小可以有效避免連續調整大小的性能損失。它也為用戶提供有用的調整大小的行,列和標題的初始大小,列填充模式。 你通常會在特定時間調用的方案調整方法。例如,您可能編程加載數據后,立即調整所有列,或者你可能一個特定的編程方式調整后的行某單元格值已被修改。 5.3.5自定義基於內容的調整大小行為 您可以自定義大小的行為時,派生的DataGridView單元格,行和列類型的工作通過覆蓋DataGridViewCell.GetPreferredSize(),DataGridViewRow.GetPreferredHeight(),或DataGridViewColumn.GetPreferredWidth()方法或通過調用DataGridView的保護,在派生大小的方法重載控制。受保護的大小的方法重載的目的是在對工作,以實現理想的單元格高度與寬度的比例,避免過於寬或高的細胞。例如,如果調用AutoResizeRows(DataGridViewAutoSizeRowsMo??de,布爾)的AutoResizeRows方法重載並傳入一個虛假的布爾參數的值,過載將計算在該行細胞的理想的高度和寬度,但它會調整行高而已。然后,您必須調用AutoResizeColumns方法來調整列寬度以計算的理想選擇。 5.3.6基於內容的調整大小選項 由大小屬性和方法使用的枚舉有基於內容的大小相似的價值觀。有了這些值,你可以限制哪些細胞是用來計算首選大小。對於所有大小枚舉,其名稱是指顯示的單元格的值限制在他們的計算顯示的行的單元格。不包括行是有用的,以避免性能損失,當您使用的是大量的行工作。您還可以限制的計算,以在頁眉或nonheader細胞的細胞值。 5.4選擇模式 DataGridView控件提供了一系列用於配置用戶如何選擇單元格,行和列的多種選擇你。例如,您可以啟用單一或多重選擇,全行或列的選擇,當用戶單擊單元格,行或整列選擇或僅當用戶點擊他們的標題,也使小區選擇。如果您要提供您的選擇自己的用戶界面,您可以禁用普通的選擇和處理所有的編程選擇。此外,還可以讓用戶選定的值復制到剪貼板。 有時候你希望你的應用程序來執行的DataGridView控制范圍內用戶的選擇為基礎的行動。根據不同的操作,您可能希望限制的種類的選擇都是可能的。例如,假設你的應用程序可以打印出當前選中的記錄報告。在這種情況下,您可能需要配置的DataGridView控件,以便在連續點擊任何地方總是選擇整行,所以這只能有一個時間行可以被選中。 您可以通過設置SelectionMode屬性為下列DataGridViewSelectionMode枚舉值之一允許的選擇。 DataGridViewSelectionMode值描述 CellSelect單擊單元格以選中它,行列標題不能用於選擇。 ColumnHeaderSelect單擊單元格以選中它,單擊列標題選中整列。此時列標題不能用於排序。 FullColumnSelect單擊單元格或列標題會選中它們所在的列,此時列標題不能用於排序。 FullRowSelect單擊單元格或行標題會選中它們所在的行。 RowHeaderSelect DGV的默認選擇模式,單擊單元格選中該單元格,單擊行標題則選中整行。 注意:在運行時改變選擇模式會自動清除當前選擇的內容。 默認情況下,用戶可以選擇用鼠標拖動,按Ctrl或Shift的同時選擇延長或修改的選擇,或者點擊左上角的標題單元格來選擇控件中的所有細胞的多個行,列或單元格。為了防止這種行為,設置為false MultiSelect屬性。 該FullRowSelect和RowHeaderSelect模式允許用戶通過選擇刪除,再按DELETE鍵的行。用戶可以刪除行,只有在當前單元格不處於編輯模式,AllowUserToDeleteRows屬性設置為true,並且基礎數據源支持用戶驅動的行刪除。請注意,這些設置不會防止綱領性行刪除。 5.4.1編程選擇 目前的選擇模式限制了方案選擇,以及用戶的選擇行為。你可以改變當前選擇編程方式設置的任何單元格,行或列在DataGridView控制選錄的財產。您還可以選擇通過SelectAll方法控制所有單元格,選擇模式而定。要清除的選擇,使用ClearSelection方法。 如果MultiSelect屬性設置為true,則可以添加或刪除DataGridView元素從選擇通過改變這些元素的Selected屬性。否則,設置一個元素的Selected屬性為true自動刪除從選擇的其他因素。 注意:改變CurrentCell屬性的值不會改變當前選擇的內容。 通過SelectedCells,SelectedRows和的SelectedColumns屬性你可以訪問當前選中的單元格,行和列。不過當所有單元格都被選中的時候,使用這些屬性效率會比較低,為此可首先使用AreAllCellsSelected方法查看是否已選中全部單元格。此外,訪問這些屬性來查看選中單元格,行和列的數目效率也比較低,此時應該使用GetCellCount,GetRowCount和GetColumnCount方法,傳給它們的參數為DataGridViewElementStates.Selected。 5.5滾動(滾動) DataGridView中毫無疑問地會提供對水平和垂直滾動條的支持,它同時也支持使用鼠標滾輪進行垂直滾動。水平方向的滾動基於像素值,而垂直方向的滾動則基於行的索引,不支持垂直的DataGridView方向的基於像素值的滾動。 5.5.1 Scroll事件 當你滾動DataGridView的引發Scroll事件,讓您被通知滾動發生。對滾動事件參數定位屬性可以讓你知道滾動的方向。 5.5.2滾動條 DataGridView的滾動條可以訪問,它通過保護Horizo??ntalScrollBar和VerticalScrollBar屬性顯示。 ScrollBar控件直接訪問這些讓你擁有滾動更好的控制。 5.5.3滾動屬性 有許多的屬性,提供更大的詳細程度如何設??置DataGridView的滾動。該圖突出這些屬性和在這種狀態下它們的值。這些屬性的讀/寫除了FirstDisplayedScrollingColumnHiddenWidth和VerticalScrollingOffset屬性。 5.6排序 默認情況下,用戶可以按一下文字方塊的欄標題在DataGridView控件中的數據。您可以修改特定列SortMode屬性,允許用戶通過其他列類型進行排序時,這樣做是有道理的。您還可以通過編程對數據進行排序任何列或多個列。 DataGridView列有三種排序模式。每個列的排序模式是通過指定的列,它可以設置為以下DataGridViewColumnSortMode枚舉值之一SortMode屬性。 DataGridViewColumnSortMode值描述 自動默認為文本框列。除非列標頭用於選擇,單擊列標題此列自動排序,並顯示一個指示排序順序字形的DataGridView。 NotSortable默認非文本框列。您可以按該列編程,但是,它不適合排序,所以沒有空間為排序標志符號保留。 編程您可以按該列編程和空間是為排序標志符號保留。 您可能要更改的列,默認為NotSortable如果它包含可以有意義有序值的排序方式。例如,如果你有一個數據庫列包含表示項狀態的數字,你可以顯示一個圖像列綁定到數據庫列的這些數字對應的圖標。然后,您可以改變一個CellFormatting事件處理程序將圖像顯示值的數值單元格值。在這種情況下,設置SortMode屬性,使您的用戶自動排序列。自動分揀將使您的用戶組項目,具有相同的狀態,即使各國所對應的數字沒有一個自然順序。復選框列是另一個例子,自動排序分組,在同一國家的項目有用。 你可以在任何編程方式進行排序列中的值或多個列的DataGridView,無論SortMode設置。編程排序是有用的當您想為排序或當你想實現自己的自定義排序用戶界面(UI)。提供自己的排序用戶界面是有用的,例如,當您設置了DataGridView選擇模式,使列標題選擇。在這種情況下,雖然列標頭不能用於排序,你仍然想的標題來顯示相應的排序標志符號,所以你會設置SortMode屬性編程。 列設置為編程排序模式不會自動顯示排序標志符號。對於這些列,你必須顯示的字形通過設置DataGridViewColumnHeaderCell.SortGlyphDirection自己的財產。這是必要的,如果你想在自定義排序的靈活性。例如,如果按多列DataGridView的,你可能要顯示多個排序標志符號或無排序標志符號。 雖然您可以通過編程任意列進行排序的DataGridView,一些欄目,如按鈕列,可能不包含可以有意義的有序值。對於這些列,一個NotSortable SortMode屬性設置表示,它將永遠不會被用於排序的,所以沒有必要儲備為排序標志符號頭空間。 當DataGridView的排序,你可以同時確定排序列和通過檢查SortedColumn和SortOrder的屬性的值進行排序。這些值不是一個自定義排序操作??后,有意義的。有關自定義排序信息,請參見本主題中的自定義排序節后面。 當DataGridView控件同時包含綁定和未綁定列進行排序,在未綁定列的值不能自動維護。為了保持這些值,你必須執行VirtualMode屬性設置為true,並處理CellValueNeeded和CellValuePushed事件虛擬模式。 5.6.1編程排序 您可以排序的DataGridView編程方式調用它的排序方法。 本的Sort(DataGridViewColumn,ListSortDirection)Sort方法重載采用DataGridViewColumn和一個枚舉值作為參數ListSortDirection。此重載時非常有用,可以通過與有意義的命令,但你不想配置值的列自動分揀排序。當調用此重載並同一個DataGridViewColumnSortMode.Automatic的SortedColumn和SortOrder的性能SortMode屬性值列通過自動設置和相應的排序標志符號出現在列標題。 注意:當DataGridView控件綁定通過設置DataSource屬性到外部數據源,的Sort(DataGridViewColumn,ListSortDirection)方法重載不能用於未綁定列。此外,當VirtualMode屬性為true,則可以只綁定列調用此重載。要確定是否列是數據綁定,檢查IsDataBound屬性值。在綁定模式下未綁定列排序不受支持。 5.6.2自定義排序 您可以通過使用自定義的Sort(IComparer)Sort方法重載或通過處理DataGridView的SortCompare事件。 的Sort(IComparer)方法重載采用一個實現類作為參數的IComparer接口的實例。此重載很有用,當您要提供自定義排序,例如,當在一列中的值沒有自然排序順序或者當自然排序順序是不適當的。在這種情況下,您不能使用自動排序,但您可能仍然希望用戶通過點擊排序列標題。你還可以打電話為ColumnHeaderMouseClick此重載事件處理程序,如果你不使用選擇欄標題。 注意:的Sort(IComparer)方法重載僅當DataGridView控件未綁定到外部數據源和VirtualMode屬性值為false。要自定義綁定到外部數據源的列排序,你必須使用排序的數據源提供的操作。在虛擬模式下,你必須為自己的未綁定列排序操作。 要使用的Sort(IComparer)方法重載,您必須創建自己的類實現IComparer接口。此接口要求您的類來實現IComparer.Compare(Object)方法,對此,作為輸入傳遞時的DataGridView的Sort(IComparer)方法重載被稱為DataGridViewRow對象。有了這個,你可以計算出正確的行排序的基礎上在任一列的值。 的Sort(IComparer)方法重載不設置SortedColumn和SortOrder的屬性,所以你必須總是設置DataGridViewColumnHeaderCell.SortGlyphDirection屬性以顯示排序標志符號。 作為對的Sort(IComparer)方法重載替代方法,可以通過實施提供了SortCompare事件處理程序自定義排序。此事件發生在用戶單擊列或配置自動分揀頭當調用Sort方法的Sort(DataGridViewColumn,ListSortDirection)重載。事件發生時,每行一對在控制,使您能夠計算它們的正確順序。 注:SortCompare事件不會發生當DataSource屬性設置或當VirtualMode屬性值為true。 5.6.3常見問題及案例 1)如何避免用戶對列排序? 2)如何針對多個列排序? 5.7邊框樣式 使用DataGridView控件,您可以自定義該控件的邊框和網格線,以改善用戶體驗的外觀。您可以修改除了為細胞內控制邊境網格線的顏色和樣式的控件的邊框樣式。網格線顏色控制,通過GridColor財產。您還可以申請普通細胞,行標題單元格和列標題單元格不同的單元格邊框樣式。對於先進的邊框樣式的DataGridView提供先進的邊框樣式的屬性。 注:網格線顏色僅用於與DataGridViewCellBorderStyle枚舉和枚舉的DataGridViewHeaderBorderStyle單值單,SingleHorizo??ntal和SingleVertical值。這些枚舉的其他值使用由操作系統指定的顏色。此外,當視覺樣式的Windows XP及以上的啟用,GridColor屬性值不被使用。 5.7.1標准邊框樣式 邊框樣式控制標准通過CellBorderStyle,RowHeadersBorderStyle和ColumnHeadersBorderStyle屬性。 下表列出了標??准通過所提供的邊框樣式: 邊框值描述 Fixed3D一個三維邊框。 FixedSingle單行邊框。 無無邊框。 5.7.2高級邊境風格 DataGridView控件允許你完全自定義其外觀,包括細胞和頭的邊界。 DataGridView的有CellBorderStyle,ColumnHeadersBorderStyle和RowHeadersBorderStyle屬性,讓您設置單元格邊框的外觀。但是,如果您需要進一步定制邊界,DataGridViewAdvancedBorderStyle類允許您設置單元格的個人雙方的邊框樣式。對DataGridViewAdvancedBorderStyle左,右,頂部和底部屬性代表左,右,上,一個細胞和底部邊框,分別為。您可以設置在AdvancedCellBorderStyle,AdvancedColumnHeadersBorderStyle,AdvancedRowHeadersBorderStyle DataGridView的屬性這些屬性產生的細胞之間的邊界,展現多種風采。 下表列出了可用的先進的邊框樣式,可以設置為左,右,頂部和底部部分。請注意,某些組合是無效的。 邊框值描述 嵌入一??個三維邊框。 InsetDouble單行邊框。 無無邊框。 NotSet邊界是沒有設置 一開始就是單行凸起邊框 OutsetDouble一個雙線凸起邊框 OutsetPartial單行邊界包含凸起部分 單單行邊界 5.8輸入,編輯模式 默認情況下,用戶可以通過在編輯,或按F2鍵當前DataGridView的文本框格的內容。這使得在編輯模式下,如果下列條件全部得到滿足手機: ?基礎數據源支持編輯。 ?DataGridView控件已啟用。 ?將EditMode屬性值不是EditProgrammatically。 ?單元格,行,列的ReadOnly屬性和控制,都設置為false。 在編輯模式下,用戶可以更改單元格的值,然后按Enter鍵提交更改或ESC細胞恢復到其原始值。 您可以配置一個DataGridView控件,以使單元格進入編輯模式,一旦它成為當前單元格。該ENTER鍵和ESC鍵的行為在這種情況下保持不變,但細胞仍然處於編輯模式后,該值被提交或還原。您還可以配置控制,使細胞進入編輯模式僅當用戶鍵入單元格或只有當用戶按下F2鍵。最后,您可以阻止其進入編輯,除非你調用BeginEdit方法模式細胞。 下表描述了不同的編輯模式可供選擇: 編輯模式值描述 EditOnEnter編輯開始時,細胞接收焦點。這種模式是有用的當按下TAB鍵,進入跨越行值,或當按下回車鍵,進入下一個列值。 EditOnF2編輯開始時按下F2鍵時,單元格具有焦點。此模式放置在單元格內容的末尾的選擇點。 開始編輯EditOnKeystroke當任何字母數字鍵被按下,而細胞具有焦點。 EditOnKeystrokeOrF2編輯開始時,任何字母數字鍵或F2鍵被按下,而細胞具有焦點。 EditProgrammatically編輯時,才開始BeginEdit方法被調用。 5.9剪貼板拷貝模式 當你使細胞復制,你才能在DataGridView控件的數據很容易接觸到其他應用程序通過剪貼板。 DataGridView控件復制到選定的單元格的每個剪貼板的文本表示。此值是單元格的值轉換為圖像細胞,Description屬性的值的字符串或。其內容后加入為制表符分隔的文本值的剪貼簿在諸如記事本和Excel應用程序粘貼,並作為應用程序,如Word粘貼到HTML格式的表格。 您可以配置單元格值復制到復制只,包括在剪貼板上的數據行和列標題文本,或包含標題文本僅當用戶選擇整個行或列。 下表列出了不同的剪貼板復制模式: 剪貼板拷貝模式說明 禁用復制到剪貼板被禁用。 EnableAlwaysIncludeHeaderText所選單元格的文本值可以被復制到剪貼板。標題文字是否列入行和包含選定單元格的列。 EnableWithAutoHeaderText所選單元格的文本值可以被復制到剪貼板。行或列標題的文本包含或包含的行只選擇當SelectionMode屬性設置為RowHeaderSelect或ColumnHeaderSelect和至少一個頭被選中單元格的列。 EnableWithoutHeaderText所選單元格的文本值可以被復制到剪貼板。標題文字是否不包括在內。 在選擇模式的不同,用戶可以選擇多個不連續的細胞群。當用戶復制到剪貼板細胞,行和列,沒有選定的單元格不會被復制。所有其他行或列成為復制到剪貼板上的數據表的行和列。在這些行或列未選定的單元格被復制到剪貼板作為空白占位符。 當用戶復制內容時,DataGridView控件添加到剪貼板DataObject中。此數據對象是取自GetClipboardContent()方法。你可以調用這個方法時,您希望以編程方式將數據添加對象到剪貼板。該GetClipboardContent()方法通過調用DataGridViewCell.GetClipboardContent檢索()方法為個別單元格的值。你可以重寫派生類中任一這些方法或兩個自定義復制的單元格的布局,或支持格式的其他數據。 5.10凍結的列/行 當用戶查看數據有時他們需要參考一列或列集頻繁。例如,當顯示的客戶信息表,其中包含許多列,顯示是非常有用的在任何時候,客戶名稱,同時使其他列可見區域之外的滾動。 為了實現這一行為,您可以凍結在控制列。這是通過設置在列或行凍結的財產。當你凍結一列,所有列在它的左邊(或在從右到左的語言腳本右),凍結。凍結列留在原地,而所有其他列可以滾動。行以類似的方式行事:前行中的所有行被凍結的凍結,以及維持不變,而在非冰凍行可以滾動。 5.11實現自定義和編輯控制細胞/細胞 您可以實現在你的派生類來創建一個細胞的細胞類型具有編輯功能,但不承載的編輯模式控制IDataGridViewEditingCell接口。要創建一個控件,你可以在一個宿主細胞中的編輯模式,可以實現從Control派生的類IDataGridViewEditingControl接口。 5.11.1 IDataGridViewEditingControl 支持先進的單元格編輯功能通常使用一個托管控件是從Windows窗體控件派生的。此接口由編輯控件,如DataGridViewComboBoxEditingControl和DataGridViewTextBoxEditingControl,這是由相應的DataGridView單元格,如的DataGridViewComboBoxCell和DataGridViewTextBoxCell,當他們處於編輯模式主持。 單元格可以承載編輯控件設置其EditType屬性類型,表示一個類型的編輯控件的類型。 5.11.2 IDataGridViewEditingCell 此接口的類沒有提供存取指定的編輯控制值的用戶界面(UI)。在這種情況下用戶界面顯示無論是在細胞處於編輯模式。該DataGridViewCheckBoxCell的是一個細胞,它實現了IDataGridViewEditingCell接口的例子。 其他細胞類型,如的DataGridViewButtonCell,提供一個用戶界面,但不存儲用戶指定的值。在這種情況下,細胞類型不落實IDataGridViewEditingCell或主機一個編輯控制。 5.12虛擬模式 使用虛擬模式,您可以管理之間的DataGridView控件和自定義數據緩存交互。為了實現虛擬模式,設置VirtualMode屬性為true,並處理一個或本主題描述的事件更多。您通常處理至少CellValueNeeded事件,它使控件的外觀在數據緩存值。 5.12.1綁定模式和虛擬模式 虛擬模式只有當你需要補充或替換綁定模式。在綁定模式下,可以設置DataSource屬性和控制自動加載從指定的源數據和提交給它的用戶更改回來。您可以控制??哪些綁定列的顯示方式,和一般的數據源本身處理,如排序操作。 5.12.2補充綁定模式 您可以通過顯示補充隨着綁定列綁定列綁定模式。這有時也被稱為“混合模式”,是用來顯示像計算值或用戶界面(UI)控制的東西有用。 由於未綁定列之外的數據源,他們是忽視了數據源的排序操作。因此,當您在混合模式下啟用排序,你必須管理一個本地緩存中綁定數據,並實現虛擬模式,讓DataGridView控件交互。 5.12.3常見問題及案例 1)如何顯示綁定的數據綁定以及數據? 2)我怎樣的數據顯示,從兩個表來? 5.12.4更換綁定模式 如果綁定模式無法滿足您的性能需求,您可以通過虛擬管理模式的自定義事件處理程序緩存中的所有數據。例如,你可以使用虛擬模式來實現一個公正的實時數據加載的機制,只是從一個網絡數據庫,獲得最佳性能所必需的數據檢索。這種情況是非常有用的大量時,通過速度較慢的網絡連接或與客戶機的數據有一個內存或存儲空間有限的工作。 5.12.5虛擬模式事件 如果您的數據是只讀的,CellValueNeeded事件可能是唯一的事件,你將需要處理。額外的虛擬模式事件讓你啟用特定的功能,如用戶編輯,添加和刪除行和行級的交易。 一些標准的DataGridView事件(如發生的事件當用戶添加或刪除行,或在編輯單元格值時,解析,驗證,或者格式化)在虛擬模式中非常有用,以及。你也可以處理事件,讓你保持在一個通常不綁定的數據源中存儲的值,如細胞提示文本,單元格和行的錯誤文本,單元格和行的快捷菜單數據,和行高的數據。 下列事件發生時,才VirtualMode屬性設置為true。 事件描述 CellValueNeeded由控制用於檢索從顯示數據高速緩存單元格的值。此事件只發生在未綁定列細胞。 CellValuePushed由控制用於提交,可以向用戶輸入的數據高速緩存單元。此事件只發生在未綁定列細胞。 調用方法時UpdateCellValue更改之外的CellValuePushed事件處理緩存值,以確保當前值顯示在控件中的作用,並適用於目前所有自動調整大小模式。 NewRowNeeded由控件用來指示一個數據高速緩存中的新行的需要。 RowDirtyStateNeeded的控制,用來確定行是否有任何未提交的更改。 CancelRowEdit使用的控制,表明該行應恢復其緩存的值。 以下事件在虛擬模式中非常有用,但也可以使用了VirtualMode屬性設置無關。 事件的說明 UserDeletingRow UserDeletedRow RowsRemoved RowsAdded由控件用來指示行被刪除或添加,讓您更新相應的數據高速緩存。 CellFormatting CellParsing CellValidating CellValidated RowValidating RowValidated使用的顯示格式為單元格值和解析和驗證用戶輸入控制。 CellToolTipTextNeeded由控制單元用於檢索工具提示文本當DataSource屬性設置或VirtualMode屬性為true。 工具提示顯示細胞只有在ShowCellToolTips屬性值為true。 CellErrorTextNeeded RowErrorTextNeeded的控制,用來檢索單元格或行的錯誤文本當DataSource屬性設置或VirtualMode屬性為true。 調用方法或UpdateRowErrorText UpdateCellErrorText方法,當你更改單元格或行的錯誤文本,以確保當前值在控件中顯示。 細胞與行的錯誤標志符號時顯示ShowCellErrors和ShowRowErrors屬性值是正確的。 CellContextMenuStripNeeded RowContextMenuStripNeeded由控制用於檢索單元格或行的ContextMenuStrip當控件的DataSource屬性設置或VirtualMode屬性為true。 RowHeightInfoNeeded RowHeightInfoPushed由控制用於檢索或存儲數據的高速緩存行中高度信息。調用方法時改變UpdateRowHeightInfo緩存行之外的RowHeightInfoPushed事件處理的高度信息,以確保當前值在控制顯示器使用。 5.12.6在虛擬模式下的最佳實踐 如果要實現虛擬模式,以工作效率的大量數據,你也想確保您正在使用DataGridView控件本身的效率。請參閱下面的最佳做法的信息 5.13容量(容量) 一般來說,在DataGridView沒有硬編碼容量限制。網格的設計,使越來越多的內容可以添加的機器變得更快,並有更多的內存。盡管如此,格並不是用來處理大量列。如果您添加超過300行,您會開始注意到在隨着我們對電網的表現卻不是這樣的優化性能的退化。如果你需要一個大量的列格,然后在DataGridView可能不符合您的需求。關於支持的行數時,DataGridView是受內存限制。當使用虛擬模式,您可以輕松支持超過200萬行。看看你可以做的事情(不要做),以提高內存的使用情況和性能的最佳做法的信息,下面一節。 6個最佳實踐(最佳做法) DataGridView控件的設計提供最大的可擴展性。如果你需要顯示大量數據,你應該按照本主題中所述,以避免內存或有辱人格的用戶界面(UI)的響應消耗大量的指導方針。 6.1使用高效單元格樣式 每個單元格,行和列可以有自己的樣式信息。樣式信息存儲在DataGridViewCellStyle對象。創造許多個人DataGridView元素單元格樣式的對象可以是低效的,特別是當大量數據的工作。為了避免性能的影響,請遵循下列准則: ?避免為單個DataGridViewCell或DataGridViewRow對象的單元格樣式屬性。這包括由RowTemplate行對象屬性中指定。每個新行是從行模板克隆將接收其模板的單元格樣式對象的副本。為了獲得最大的可擴展性,設置在DataGridView的單元格樣式屬性的水平。例如,設置DefaultCellStyle屬性,而不是DataGridViewCell.Style財產。 ?如果某些細胞需要的格式以外的默認格式,在使用相同的單元格,行或列組的DataGridViewCellStyle實例。避免直接設置個別類型的單元格,行和列DataGridViewCellStyle屬性。對於一個單元格樣式共享的例子,請參見如何:設置單元格樣式的默認為Windows窗體DataGridView控件。您也可避免性能下降時,通過處理CellFormatting設置事件處理個別單元格樣式。有關示例,請參見如何:自定義的數據格式在Windows窗體DataGridView控件。 ?當確定一個單元格樣式,使用DataGridViewCell.InheritedStyle財產,而不是DataGridViewCell.Style財產。訪問Style屬性創建一個DataGridViewCellStyle類的新實例如果該屬性還沒有被使用。此外,這個對象可能不包含完整的樣式為單元格的信息,如果有些樣式從行,列或控件繼承。欲了解更多有關單元格樣式繼承的詳細信息,請參閱細胞在Windows窗體DataGridView控件樣式。 6.2使用高效快捷菜單 每個單元格,行和列可以有它自己的快捷菜單。在DataGridView控制快捷菜單ContextMenuStrip控件代表。這正好與單元格樣式對象作為,創造許多個人DataGridView元素的快捷菜單將產生負面影響性能。為了避免這種損失,請使用下列准則: ?避免為單個單元格和行的快捷菜單。這包括行模板,這是克隆了它的快捷方式菜單時,新行被添加到控件一起。為了獲得最大的可擴展性,僅使用控件的ContextMenuStrip屬性來指定整個控制單一的快捷菜單。 ?如果您需要多個行或多種細胞的快捷菜單,處理CellContextMenuStripNeeded或RowContextMenuStripNeeded事件。這些事件讓您管理自己的快捷菜單對象,讓您調整性能。 6.3使用自動調整大小高效 行,列和標題可以自動調整大小的單元格內容的變化,使細胞中的全部內容都沒有剪輯顯示。更改調整大小模式也可以調整行,列和標題。要確定正確的大小,DataGridView控件必須檢查每一個細胞,它必須適應值。當處理大量數據時,這種分析可以產生負面影響控制性能的自動調整大小時發生。為了避免性能下降,請遵循下列准則: ?避免使用帶有大量行集的DataGridView控制自動調整大小。如果你使用自動大小調整,只調整的基礎上所顯示的行。在虛擬模式下只使用所顯示的行以及。 對行和列?,使用DataGridViewAutoSizeRowsMo??de,DataGridViewAutoSizeColumnsMode和DataGridViewAutoSizeColumnMode枚舉的DisplayedCells或DisplayedCellsExceptHeaders領域。 ?對於行頭,使用該DataGridViewRowHeadersWidthSizeMode枚舉AutoSizeToDisplayedHeaders或AutoSizeToFirstHeader領域。 為了獲得最大的可擴展性?,關閉自動調整大小尺寸和使用方案。 6.4使用選定的單元格,行和列的集合高效 SelectedCells集合不執行效率大選擇。收藏的SelectedRows和SelectedColumns也可以是低效的,但在較小的程度,因為有許多比細胞中的行數少一個典型的DataGridView控件,比列行少得多。為了避免性能下降與這些藏品時,請遵循下列准則: ?要確定是否所有在DataGridView單元格已被選中,然后再訪問該SelectedCells集合的內容,檢查AreAllCellsSelected方法的返回值。請注意,但是,這種方法可能會導致行成為非共享。有關詳細信息,請參閱下一節。 ?避免使用的DataGridViewSelectedCellCollection Count屬性來確定所選細胞的數量。相反,使用GetCellCount()方法並傳入DataGridViewElementStates.Selected價值。同樣,使用DataGridViewRowCollection.GetRowCount()和DataGridViewColumnCollection.GetColumnCount()方法來確定所選元素,而不是訪問選定的行和列集合,數量。 ?避免細胞為基礎的選擇模式。相反,SelectionMode屬性設置為FullRowSelect或FullColumnSelect。 6.5使用共享行 實現有效的內存使用在通過共享行的DataGridView控制。作為行會分享他們的外觀和行為,盡可能通過DataGridViewRow類的共享實例的信息。 雖然共享行實例節省內存,很容易成為非共享行。例如,每當一個直接與用戶交互的一個單元,它的行成為非共享。因為這是無法避免,在這個主題中的准則是有用的,只有當工作與數據量非常大,只有當用戶將與每一個數據你的程序運行時間的一小部分。 阿行不能共享在未綁定的DataGridView控制,如果它的任何單元格包含值。當DataGridView控件綁定到外部數據源,或當您實現虛擬模式,並提供您自己的數據源,該單元格值存儲以外的控制,而不是在單元格對象,允許行被共享。 行對象只能共享,如果它的所有細胞的狀態可以從該行的狀態和細胞列載的狀態決定。如果您更改單元格的狀態,這樣它可以不再從它的行和列的狀態推斷,該行不能被共享。 例如,行不能共享在下列情形之一: ?該行包含一個選定的單元格是不是在選定的列。 ?該行包含一個與它的ToolTipText或ContextMenuStrip屬性設置單元。 ?該行包含其項目屬性的DataGridViewComboBoxCell集。 在綁定模式或虛擬模式,您可以通過處理CellToolTipTextNeeded提供CellContextMenuStripNeeded事件和個別細胞工具提示和快捷菜單。 DataGridView控件將自動嘗試使用共享每當行添加到DataGridViewRowCollection行。使用下面的指引,以確保行共享: ?避免調用Add(Object []的)的添加方法和插入(對象[])的插入的行的集合方法重載超載。這些重載自動創建非共享行。 ?確保在RowTemplate屬性指定的行可以在下列情況下,共享: ?當調用add()或Add方法添加或插入(智力,智力)的行的集合插入方法重載(智力)重載。 ?當增加RowCount屬性的值。 ?當設置DataSource屬性。 ?確保該行的indexSource參數指定當呼叫可以共享的行集合AddCopy,AddCopies,InsertCopy和InsertCopies方法。 ?請確定指定的行或列時,可以共享調用Add(的DataGridViewRow)Add方法的重載,AddRange方法,插入(Int32的,的DataGridViewRow)方法重載的插入,和Rows集合InsertRange方法。 要確定行是否是共享的,使用DataGridViewRowCollection.SharedRow(int)方法來檢索行對象,然后檢查對象的Index屬性。共享行總是為-1 Index屬性值。 6.6防止行成為非共享 共享成為非共享行可以作為一個代碼或用戶操作的結果。為了避免影響性能,你應該避免造成行成為非共享。在應用開發,你可以處理RowUnshared事件來確定行成為非共享。這是非常有用的調試行共享問題。 為了防止行成為非共享,請使用下列准則: ?避免索引中的行集或通過它迭代與foreach循環。你不會通常需要直接訪問行。 DataGridView的操作方法,對行,而不是采取行實例行索引參數。此外,對於行相關的事件處理程序接收行屬性,您可以用它來操作,而不會造成他們成為非共享行的事件參數對象。 ?如果您需要訪問的行對象,請使用DataGridViewRowCollection.SharedRow(int)方法並傳入行的實際索引。請注意,但是,修改一個共享行對象通過此方法檢索將修改所有行共享此對象。在新記錄行不共享,所以這是不會受到影響,當您修改任何其他行中的其他行。還要注意的是一個共享行代表不同的行可能有不同的快捷菜單。以檢索共享行實例的正確快捷菜單中,使用GetContextMenuStrip方法並傳入行的實際索引。如果您訪問共享行的ContextMenuStrip屬性,而是將使用-1共享行的索引,將不檢索正確的快捷菜單。 ?避免索引DataGridViewRow.Cells集合。訪問一個細胞將直接導致其父行成為非共享,實例化一個新的DataGridViewRow。為細胞相關的事件處理程序接收單元屬性,你可以用它來操作不會導致行成為非共享細胞事件參數對象。您也可以使用CurrentCellAddress屬性來檢索,而不用訪問細胞直接當前單元格的行和列索引。 ?避免細胞為基礎的選擇模式。這些模式導致行成為非共享。相反,將SelectionMode屬性設置DataGridViewSelectionMode.FullRowSelect或DataGridViewSelectionMode.FullColumnSelect。 ?不處理DataGridViewRowCollection.CollectionChanged或RowStateChanged事件。這些事件會導致行成為非共享。另外,不要叫DataGridViewRowCollection.OnCollectionChanged(CollectionChangeEventArgs)或OnRowStateChanged(智力,DataGridViewRowStateChangedEventArgs)方法,提高了這些事件。 ?不訪問SelectedCells集合時SelectionMode屬性值是FullColumnSelect,ColumnHeaderSelect,FullRowSelect或RowHeaderSelect。這會導致所有行成為非共享選擇。 ?不要調用AreAllCellsSelected(布爾)方法。這種方法可能會導致行成為非共享。 ?不要調用SelectAll方法當SelectionMode屬性值是CellSelect。這會導致所有行成為非共享。 ?不要設置只讀或選定的一對假時,在其列對應的屬性設置為true單元屬性。這會導致所有行成為非共享。 ?不訪問DataGridViewRowCollection.List財產。這會導致所有行成為非共享。 ?不要調用Sort方法的Sort(IComparer接口)超載。一個自定義比較排序會導致所有行成為非共享。 附錄 A – FAQ 該附錄包含的代碼示例和片段集中解答了前面散落的常見問題: 1. 如何使指定的單元格不可編輯? ReadOnly屬性決定了單元格中的數據是否可以編輯,可以設置單元格的ReadOnly 屬性,也可以設置DataGridViewRow.ReadOnly 或DataGridViewColumn.ReadOnly使得一行或一列所包含的單元格都是只讀的。 默認情況下,如果一行或一列是只讀的,那么其包含的單元格也會使只讀的。 不過你仍可以操作一個只讀的單元格,比如選中它,將其設置為當前單元格,但用戶不能修改單元格的內容。注意,即使單元格通過ReadOnly屬性設置為只讀,仍然可以通過編程的方式修改它,另外ReadOnly也不會影響用戶是否可以刪除行。 2. 如何讓一個單元格不可用(disable)? 單元格可以設置為只讀而不可編輯,但DataGridView卻沒提供使單元格不可用的支持。一般意義上,不可用意味着用戶不能進行操作,通常會帶有外觀的暗示,如灰色。沒有一種簡單的方法來創建那種不可操作的單元格,但提供一個暗示性的外觀告訴用戶某單元格不可用還是可行的。內置的單元格類型沒有進行不可用設置的屬性,下面的例子擴展了DataGridViewButtonCell ,參照常見控件的Enabled屬性,為其添加了Enabled屬性,如果該屬性設置為false,那么其外觀狀態將類似於普通按鈕的不可用狀態。 public class DataGridViewDisableButtonColumn : DataGridViewButtonColumn { public DataGridViewDisableButtonColumn() { this.CellTemplate = new DataGridViewDisableButtonCell(); } } public class DataGridViewDisableButtonCell : DataGridViewButtonCell { private bool enabledValue; public bool Enabled { get { return enabledValue; } set { enabledValue = value; } } // Override the Clone method so that the Enabled property is copied. public override object Clone() { DataGridViewDisableButtonCell cell = (DataGridViewDisableButtonCell)base.Clone(); cell.Enabled = this.Enabled; return cell; } // By default, enable the button cell. public DataGridViewDisableButtonCell() { this.enabledValue = true; } protected override void Paint(Graphics graphics, Rectangle clipBounds, Rectangle cellBounds, int rowIndex, DataGridViewElementStates elementState, object value, object formattedValue, string errorText, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle, DataGridViewPaintParts paintParts) { // The button cell is disabled, so paint the border, // background, and disabled button for the cell. if (!this.enabledValue) { // Draw the cell background, if specified. if ((paintParts & DataGridViewPaintParts.Background) == DataGridViewPaintParts.Background) { SolidBrush cellBackground = new SolidBrush(cellStyle.BackColor); graphics.FillRectangle(cellBackground, cellBounds); cellBackground.Dispose(); } // Draw the cell borders, if specified. if ((paintParts & DataGridViewPaintParts.Border) == DataGridViewPaintParts.Border) { PaintBorder(graphics, clipBounds, cellBounds, cellStyle, advancedBorderStyle); } // Calculate the area in which to draw the button. Rectangle buttonArea = cellBounds; Rectangle buttonAdjustment = this.BorderWidths(advancedBorderStyle); buttonArea.X += buttonAdjustment.X; buttonArea.Y += buttonAdjustment.Y; buttonArea.Height -= buttonAdjustment.Height; buttonArea.Width -= buttonAdjustment.Width; // Draw the disabled button. ButtonRenderer.DrawButton(graphics, buttonArea, PushButtonState.Disabled); // Draw the disabled button text. if (this.FormattedValue is String) { TextRenderer.DrawText(graphics, (string)this.FormattedValue, this.DataGridView.Font, buttonArea, SystemColors.GrayText); } } else { // The button cell is enabled, so let the base class // handle the painting. base.Paint(graphics, clipBounds, cellBounds, rowIndex, elementState, value, formattedValue, errorText, cellStyle, advancedBorderStyle, paintParts); } } } 3. 如何避免用戶將焦點設置到指定的單元格? 默認情況下DataGridView的操作(navigation)模型在限制用戶將焦點置於指定的單元格方面沒有提供任何支持。你可以實現自己的操作邏輯,這需要重寫合適的鍵盤、導航、鼠標方法,如DataGridView.OnKeyDown, DataGridView.ProcessDataGridViewKey, DataGridView.SetCurrentCellAddressCore, DataGridView.SetSelectedCellCore, DataGridView.OnMouseDown。 4. 如何使所有單元格總是顯示控件(不論它是否處於編輯狀態)? DataGridView 控件只支持在單元格處於編輯狀態時顯示真實的控件(如TextBox)。DataGridView 沒有被設計為顯示多控件或為每行重復顯示控件。DataGridView 在單元格不被編輯時為其繪制對應控件的外觀,該外觀可能是你想要的。例如,DataGridViewButtonCell 類型的單元格,不管它是否處於編輯狀態,總是表現為一個按鈕。 5. Why does the cell text show up with “square” characters where they should be new lines(TODO,未能實現該效果)? By default, text in a DataGridViewTextBoxCell does not wrap. This can be controlled via the WrapMode property on the cell style (e.g. DataGridView.DefaultCellStyle.WrapMode). Because text doesn’t wrap, new line characters in the text do not apply and so they are displayed as a “non-printable” character. This is similar to setting a TextBox’s Text property to the same text when the TextBox’s MultiLine property is false. 6. 如何在單元格內同時顯示圖標和文本? DataGridView控件沒有對在同一單元格內同時顯示圖標和文本提供支持。但通過實現自定義的繪制事件,如CellPaint 事件,你可以輕松實現這個效果。 下面這段代碼擴展了DataGridViewTextBoxColumn 和DataGridViewTextBoxCell類,將一個圖片顯示在文本旁邊。這個示例使用了DataGridViewCellStyle.Padding 屬性來調整文本的位置,重寫了Paint 方法來繪制圖片。該示例可以得到簡化,方法是處理CellPainting 事件,在這里實現類似的功能。 public class TextAndImageColumn:DataGridViewTextBoxColumn { private Image imageValue; private Size imageSize; public TextAndImageColumn() { this.CellTemplate = new TextAndImageCell(); } public override object Clone() { TextAndImageColumn c = base.Clone() as TextAndImageColumn; c.imageValue = this.imageValue; c.imageSize = this.imageSize; return c; } public Image Image { get { return this.imageValue; } set { if (this.Image != value) { this.imageValue = value; this.imageSize = value.Size; if (this.InheritedStyle != null) { Padding inheritedPadding = this.InheritedStyle.Padding; this.DefaultCellStyle.Padding = new Padding(imageSize.Width, inheritedPadding.Top, inheritedPadding.Right, inheritedPadding.Bottom); } } } } private TextAndImageCell TextAndImageCellTemplate { get { return this.CellTemplate as TextAndImageCell; } } internal Size ImageSize { get { return imageSize; } } } public class TextAndImageCell : DataGridViewTextBoxCell { private Image imageValue; private Size imageSize; public override object Clone() { TextAndImageCell c = base.Clone() as TextAndImageCell; c.imageValue= this.imageValue; c.imageSize = this.imageSize; return c; } public Image Image { get { if (this.OwningColumn == null || this.OwningTextAndImageColumn == null) { return imageValue; } else if (this.imageValue != null) { return this.imageValue; } else { return this.OwningTextAndImageColumn.Image; } } set { if (this.imageValue != value) { this.imageValue = value; this.imageSize = value.Size; Padding inheritedPadding = this.InheritedStyle.Padding; this.Style.Padding = new Padding(imageSize.Width, inheritedPadding.Top, inheritedPadding.Right, inheritedPadding.Bottom); } } } protected override void Paint(Graphics graphics, Rectangle clipBounds, Rectangle cellBounds, int rowIndex, DataGridViewElementStates cellState, object value, object formattedValue, string errorText, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle, DataGridViewPaintParts paintParts) { // Paint the base content base.Paint(graphics, clipBounds, cellBounds, rowIndex, cellState, value, formattedValue, errorText, cellStyle, advancedBorderStyle, paintParts); if (this.Image != null) { // Draw the image clipped to the cell. System.Drawing.Drawing2D.GraphicsContainer container = graphics.BeginContainer(); graphics.SetClip(cellBounds); graphics.DrawImageUnscaled(this.Image, cellBounds.Location); graphics.EndContainer(container); } } private TextAndImageColumn OwningTextAndImageColumn { get { return this.OwningColumn as TextAndImageColumn; } } } 7. 如何隱藏一列? 有時希望僅顯示DataGridView的部分列,將其它列隱藏。比如DataGridView含有一列包含員工薪水信息,你可能希望僅將這些信息顯示給具有一定信用級別的人,其他人則隱藏。 通過編程方式隱藏 DataGridViewColumn類的Visible 屬性決定了是否顯示該列。 通過設計器隱藏 1) 右擊DataGridView控件,選擇Edit Columns; 2) 在列列表中選擇一列; 3) 在列屬性網格中,將Visible屬性設置為false。 8. 如何避免用戶對列排序? 對於DataGridView 控件,默認情況下,TextBox類型的列會自動排序,而其它類型的列則不會自動排序。這種自動排序有時會把數據變得比較亂,這時你會想更改這些默認設置。 DataGridViewColumn的屬性SortMode決定了列的排序方式,將其設置為DataGridViewColumnSortMode.NotSortable就可以避免默認的排序行為。 9. 如何針對多個列排序? 默認情況下DataGridView不支持針對多列排序。下面針對是否將數據綁定到DataGridView來分別演示如何為其添加多列排序功能。 9.1 將數據綁定到DataGridView時 DataGridView進行數據綁定的時候,數據源(如DataView)可對多個列排序。DataGridView會保留這種排序,但只有第一個排序列會顯示排序符號(向上或向下的箭頭),此外SortedColumn屬性也只會返回第一個排序列。 一些數據源內置了對多列排序的支持。如果你的數據源實現了IBindingListView接口,提供了對Sort屬性的支持,那么該數據源就支持多列排序。為了明確指出DataGridView對多列排序,手動為已排序列設置正確的SortGlyphDirection屬性,指示該列已經排序。 下面這個示例使用DataTable作為數據源,使用其DefaultView的 Sort 屬性對第二列和第三列排序;該示例同時演示了如何設置列的SortGlyphDirection屬性。該示例假定在你的窗體上有一個DataGridView控件和一個BindingSource組件: DataTable dt = new DataTable(); dt.Columns.Add("C1", typeof(int)); dt.Columns.Add("C2", typeof(string)); dt.Columns.Add("C3", typeof(string)); dt.Rows.Add(1, "1", "Test1"); dt.Rows.Add(2, "2", "Test2"); dt.Rows.Add(2, "2", "Test1"); dt.Rows.Add(3, "3", "Test3"); dt.Rows.Add(4, "4", "Test4"); dt.Rows.Add(4, "4", "Test3"); DataView view = dt.DefaultView; view.Sort = "C2 ASC, C3 ASC"; bindingSource.DataSource = view; DataGridViewTextBoxColumn col0 = new DataGridViewTextBoxColumn(); col0.DataPropertyName = "C1"; dataGridView1.Columns.Add(col0); col0.SortMode = DataGridViewColumnSortMode.Programmatic; col0.HeaderCell.SortGlyphDirection = SortOrder.None; DataGridViewTextBoxColumn col1 = new DataGridViewTextBoxColumn(); col1.DataPropertyName = "C2"; dataGridView1.Columns.Add(col1); col1.SortMode = DataGridViewColumnSortMode.Programmatic; col1.HeaderCell.SortGlyphDirection = SortOrder.Ascending; DataGridViewTextBoxColumn col2 = new DataGridViewTextBoxColumn(); col2.DataPropertyName = "C3"; dataGridView1.Columns.Add(col2); col2.SortMode = DataGridViewColumnSortMode.Programmatic; col2.HeaderCell.SortGlyphDirection = SortOrder.Ascending; 9.2 Unbound DataGridView To provide support for sorting on multiple columns you can handle the SortCompare event or call the Sort(IComparer) overload of the Sort method for greater sorting flexibility. 9.2.1 Custom Sorting Using the SortCompare Event The following code example demonstrates custom sorting using a SortCompare event handler. The selected DataGridViewColumn is sorted and, if there are duplicate values in the column, the ID column is used to determine the final order. using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Windows.Forms; class Form1 : Form { private DataGridView dataGridView1 = new DataGridView(); // Establish the main entry point for the application. [STAThreadAttribute()] static void Main() { Application.EnableVisualStyles(); Application.Run(new Form1()); } public Form1() { // Initialize the form. // This code can be replaced with designer generated code. dataGridView1.AllowUserToAddRows = false; dataGridView1.Dock = DockStyle.Fill; dataGridView1.SortCompare += new DataGridViewSortCompareEventHandler( this.dataGridView1_SortCompare); Controls.Add(this.dataGridView1); this.Text = "DataGridView.SortCompare demo"; PopulateDataGridView(); } // Replace this with your own population code. public void PopulateDataGridView() { // Add columns to the DataGridView. dataGridView1.ColumnCount = 3; // Set the properties of the DataGridView columns. dataGridView1.Columns[0].Name = "ID"; dataGridView1.Columns[1].Name = "Name"; dataGridView1.Columns[2].Name = "City"; dataGridView1.Columns["ID"].HeaderText = "ID"; dataGridView1.Columns["Name"].HeaderText = "Name"; dataGridView1.Columns["City"].HeaderText = "City"; // Add rows of data to the DataGridView. dataGridView1.Rows.Add(new string[] { "1", "Parker", "Seattle" }); dataGridView1.Rows.Add(new string[] { "2", "Parker", "New York" }); dataGridView1.Rows.Add(new string[] { "3", "Watson", "Seattle" }); dataGridView1.Rows.Add(new string[] { "4", "Jameson", "New Jersey" }); dataGridView1.Rows.Add(new string[] { "5", "Brock", "New York" }); dataGridView1.Rows.Add(new string[] { "6", "Conner", "Portland" }); // Autosize the columns. dataGridView1.AutoResizeColumns(); } private void dataGridView1_SortCompare(object sender, DataGridViewSortCompareEventArgs e) { // Try to sort based on the cells in the current column. e.SortResult = System.String.Compare( e.CellValue1.ToString(), e.CellValue2.ToString()); // If the cells are equal, sort based on the ID column. if (e.SortResult == 0 && e.Column.Name != "ID") { e.SortResult = System.String.Compare( dataGridView1.Rows[e.RowIndex1].Cells["ID"].Value.ToString(), dataGridView1.Rows[e.RowIndex2].Cells["ID"].Value.ToString()); } e.Handled = true; } } 9.2.2 Custom Sorting Using the IComparer Interface The following code example demonstrates custom sorting using the Sort(IComparer) overload of the Sort method, which takes an implementation of the IComparer interface to perform a multiple-column sort. using System; using System.Drawing; using System.Windows.Forms; class Form1 : Form { private DataGridView DataGridView1 = new DataGridView(); private FlowLayoutPanel FlowLayoutPanel1 = new FlowLayoutPanel(); private Button Button1 = new Button(); private RadioButton RadioButton1 = new RadioButton(); private RadioButton RadioButton2 = new RadioButton(); // Establish the main entry point for the application. [STAThreadAttribute()] public static void Main() { Application.Run(new Form1()); } public Form1() { // Initialize the form. // This code can be replaced with designer generated code. AutoSize = true; Text = "DataGridView IComparer sort demo"; FlowLayoutPanel1.FlowDirection = FlowDirection.TopDown; FlowLayoutPanel1.Location = new System.Drawing.Point(304, 0); FlowLayoutPanel1.AutoSize = true; FlowLayoutPanel1.Controls.Add(RadioButton1); FlowLayoutPanel1.Controls.Add(RadioButton2); FlowLayoutPanel1.Controls.Add(Button1); Button1.Text = "Sort"; RadioButton1.Text = "Ascending"; RadioButton2.Text = "Descending"; RadioButton1.Checked = true; Controls.Add(FlowLayoutPanel1); Controls.Add(DataGridView1); } protected override void OnLoad(EventArgs e) { PopulateDataGridView(); Button1.Click += new EventHandler(Button1_Click); base.OnLoad(e); } // Replace this with your own code to populate the DataGridView. private void PopulateDataGridView() { DataGridView1.Size = new Size(300, 300); // Add columns to the DataGridView. DataGridView1.ColumnCount = 2; // Set the properties of the DataGridView columns. DataGridView1.Columns[0].Name = "First"; DataGridView1.Columns[1].Name = "Last"; DataGridView1.Columns["First"].HeaderText = "First Name"; DataGridView1.Columns["Last"].HeaderText = "Last Name"; DataGridView1.Columns["First"].SortMode = DataGridViewColumnSortMode.Programmatic; DataGridView1.Columns["Last"].SortMode = DataGridViewColumnSortMode.Programmatic; // Add rows of data to the DataGridView. DataGridView1.Rows.Add(new string[] { "Peter", "Parker" }); DataGridView1.Rows.Add(new string[] { "James", "Jameson" }); DataGridView1.Rows.Add(new string[] { "May", "Parker" }); DataGridView1.Rows.Add(new string[] { "Mary", "Watson" }); DataGridView1.Rows.Add(new string[] { "Eddie", "Brock" }); } private void Button1_Click(object sender, EventArgs e) { if (RadioButton1.Checked == true) { DataGridView1.Sort(new RowComparer(SortOrder.Ascending)); } else if (RadioButton2.Checked == true) { DataGridView1.Sort(new RowComparer(SortOrder.Descending)); } } private class RowComparer : System.Collections.IComparer { private static int sortOrderModifier = 1; public RowComparer(SortOrder sortOrder) { if (sortOrder == SortOrder.Descending) { sortOrderModifier = -1; } else if (sortOrder == SortOrder.Ascending) { sortOrderModifier = 1; } } public int Compare(object x, object y) { DataGridViewRow DataGridViewRow1 = (DataGridViewRow)x; DataGridViewRow DataGridViewRow2 = (DataGridViewRow)y; // Try to sort based on the Last Name column. int CompareResult = System.String.Compare( DataGridViewRow1.Cells[1].Value.ToString(), DataGridViewRow2.Cells[1].Value.ToString()); // If the Last Names are equal, sort based on the First Name. if (CompareResult == 0) { CompareResult = System.String.Compare( DataGridViewRow1.Cells[0].Value.ToString(), DataGridViewRow2.Cells[0].Value.ToString()); } return CompareResult * sortOrderModifier; } } } 10. 如何為編輯控件添加事件處理函數? 有時候你需要處理單元格包含的編輯控件的特定事件。你需要處理DataGridView.EditingControlShowing 事件,它的第二個參數的Control屬性能讓你訪問該單元格包含的編輯控件。如果你要處理的事件不屬於它的基類Control,還需要將該控件轉換為特定的控件(一般為ComboBox控件或TextBox控件)。 注意:如果類型相同,DataGridView會重用該編輯控件,因此,你應該確保不會添加已存在的事件處理函數,否則會調用相同的函數多次(可以在添加前先將其移除,請參考我的示例代碼)。 11. 應在何時移除編輯控件的事件處理函數? 如果你只是想臨時為編輯控件添加事件處理函數(可能是針對特定列的特定單元格),你可以在CellEndEdit事件中移除該處理函數。你也可以在添加之前移除任何已存在的事件處理函數。 12. 如何處理ComboBox列中控件的SelectIndexChanged事件? 有時知道用戶何時選擇了ComboBox編輯控件的項(item)會比較有用。對於窗體上的ComboBox 控件,你通常會處理它的SelectedIndexChanged事件,對於DataGridViewComboBox,通過處理DataGridView.EditingControlShowing事件你可以完成相同的事情。下面這段示例代碼演示了這一點。注意:它同時也演示了如何避免添加多個相同的事件處理函數(即在添加前先移除已存在的事件處理函數,可以參考問題11)。 private void dataGridView1_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e) { ComboBox cb = e.Control as ComboBox; if (cb != null) { // first remove event handler to keep from attaching multiple: cb.SelectedIndexChanged -= new EventHandler(cb_SelectedIndexChanged); // now attach the event handler cb.SelectedIndexChanged += new EventHandler(cb_SelectedIndexChanged); } } void cb_SelectedIndexChanged(object sender, EventArgs e) { MessageBox.Show("Selected index changed"); } 13. 如何通過拖放調整行的順序? 通過拖放調整行的順序不是DataGridView的內置功能,但使用標准的拖放處理代碼,你可以很容易的實現這個功能。下面這個代碼片斷演示了這個過程,假定你的窗體上有一個name為dataGridView1的DataGridView,它的AllowDrop屬性為true,還要為它添加必要的事件處理方法。(我試運行了這段代碼,如果通過數據綁定為DataGridView添加數據,那么下面的代碼將不會生效,因為它只能為非綁定方式添加的行排序,如果要以綁定方式添加數據,請參看我的示例程序) private Rectangle dragBoxFromMouseDown; private int rowIndexFromMouseDown; private int rowIndexOfItemUnderMouseToDrop; private void dataGridView1_MouseMove(object sender, MouseEventArgs e) { if ((e.Button & MouseButtons.Left) == MouseButtons.Left) { // If the mouse moves outside the rectangle, start the drag. if (dragBoxFromMouseDown != Rectangle.Empty && !dragBoxFromMouseDown.Contains(e.X, e.Y)) { // Proceed with the drag and drop, passing in the list item. DragDropEffects dropEffect = dataGridView1.DoDragDrop( dataGridView1.Rows[rowIndexFromMouseDown], DragDropEffects.Move); } } } private void dataGridView1_MouseDown(object sender, MouseEventArgs e) { // Get the index of the item the mouse is below. rowIndexFromMouseDown = dataGridView1.HitTest(e.X, e.Y).RowIndex; if (rowIndexFromMouseDown != -1) { // Remember the point where the mouse down occurred. // The DragSize indicates the size that the mouse can move // before a drag event should be started. Size dragSize = SystemInformation.DragSize; // Create a rectangle using the DragSize, with the mouse position being // at the center of the rectangle. dragBoxFromMouseDown = new Rectangle(new Point(e.X - (dragSize.Width / 2), e.Y - (dragSize.Height / 2)), dragSize); } else // Reset the rectangle if the mouse is not over an item in the ListBox. dragBoxFromMouseDown = Rectangle.Empty; } private void dataGridView1_DragOver(object sender, DragEventArgs e) { e.Effect = DragDropEffects.Move; } private void dataGridView1_DragDrop(object sender, DragEventArgs e) { // The mouse locations are relative to the screen, so they must be // converted to client coordinates. Point clientPoint = dataGridView1.PointToClient(new Point(e.X, e.Y)); // Get the row index of the item the mouse is below. rowIndexOfItemUnderMouseToDrop = dataGridView1.HitTest(clientPoint.X, clientPoint.Y).RowIndex; // If the drag operation was a move then remove and insert the row. if (e.Effect== DragDropEffects.Move) { DataGridViewRow rowToMove = e.Data.GetData( typeof(DataGridViewRow)) as DataGridViewRow; dataGridView1.Rows.RemoveAt(rowIndexFromMouseDown); dataGridView1.Rows.Insert(rowIndexOfItemUnderMouseToDrop, rowToMove); } } 14. 如何調整最后一列的寬度使其占據網格的剩余客戶區? 以默認方式填充DataGridView時,可能會發生因列的寬度不夠,而暴露出控件的灰色背景的情況,很不美觀。將最后一列的AutoSizeMode屬性設置為Fill會使該列調整大小來填充網格的剩余客戶區(client area)。作為一個可選的方式,你可以設置最后一列MinimumWidth屬性,以保持該列的寬度不至於太小。 15. 如何讓TextBox類型的單元格支持換行? 默認情況下,DataGridViewTextBoxCell不支持換行,這個可以由DataGridViewCellStyle的WrapMode屬性來控制。 (如DataGridView.DefaultCellStyle.WrapMode)。將WrapMode 屬性DataGridViewTriState枚舉的三個取值之一。 下面的代碼示例使用DataGridView.DefaultCellStyle屬性設置整個控件所包含的單元格的WrapMode屬性(即設置所有單元格的換行模式)。 this.dataGridView1.DefaultCellStyle.WrapMode = DataGridViewTriState.True; 16. 如何使Image列不顯示任何圖像(字段值為null時)? 默認情況下Image類型的列和單元格將null值轉換為標准的“X”圖像( ),將Image列的NullValue屬性設置為null可使該列不顯示任何圖像。下面這行代碼演示了如何設置Image列的NullValue屬性。 this.dataGridViewImageColumn1.DefaultCellStyle.NullValue = null; 17. 如何能夠在ComboBox類型的單元格中輸入數據? 默認情況下,DataGridViewComboBoxCell不接受用戶的輸入值。但有時確實有向ComboxBox輸入數據的需要。實現這個功能,你需要做兩件事。一是將ComboBox編輯控件的DropDownStyle屬性設置為DropDown,使用戶可以進行輸入(否則只能進行選擇);二是確保用戶輸入的值能夠添加到ComboBox的Items集合。這是因為ComboBoxCell的值必須在Items集合中,否則會觸發DataError事件(參看3.5.1節),而適合添加新值到Items集合的地方是CellValidating事件處理函數: private void dataGridView1_CellValidating(object sender, DataGridViewCellValidatingEventArgs e) { if (e.ColumnIndex == comboBoxColumn.DisplayIndex) { if (!this.comboBoxColumn.Items.Contains(e.FormattedValue)) { this.comboBoxColumn.Items.Add(e.FormattedValue); } } } private void dataGridView1_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e) { if (this.dataGridView1.CurrentCellAddress.X == comboBoxColumn.DisplayIndex) { ComboBox cb = e.Control as ComboBox; if (cb != null) { cb.DropDownStyle = ComboBoxStyle.DropDown; } } } 18. How do I have a combo box column display a sub set of data based upon the value of a different combo box column(TODO)? Sometimes data that you want to display in the DataGridView has a relationship between two tables such as a category and subcategory. You want to let the user select the category and then choose between a subcategory based upon the category. This is possible with the DataGridView by using two combo box columns. To enable this, two versions of the filtered list (subcategory) needs to be created. One list has no filter applied while the other one will be filtered only when the user is editing a subcategory cell. Two lists are required due to the requirement described in 3.5.1 section that a combo box cells value must be in the items collection or else a DataError event is raised. In this case, since all combo box cells in the column use the same datasource if you filter the datasource for one row then a combo box cell in another row might not have its value visible in the datasource, thus causing a DataError event. The below example uses the Northwind database to display related data from the Territory and Region tables (a territory is in a specific region.) Using the category and subcategory concept, the Region is the category and the Territory is the subcategory. private void Form1_Load(object sender, EventArgs e) { this.territoriesTableAdapter.Fill(this.northwindDataSet.Territories); this.regionTableAdapter.Fill(this.northwindDataSet.Region); // Setup BindingSource for filtered view. filteredTerritoriesBS = new BindingSource(); DataView dv = new DataView(northwindDataSet.Tables["Territories"]); filteredTerritoriesBS.DataSource = dv; } private void dataGridView1_CellBeginEdit(object sender, DataGridViewCellCancelEventArgs e) { if (e.ColumnIndex == territoryComboBoxColumn.Index) { // Set the combobox cell datasource to the filtered BindingSource DataGridViewComboBoxCell dgcb = (DataGridViewComboBoxCell)dataGridView1 [e.ColumnIndex, e.RowIndex]; dgcb.DataSource = filteredTerritoriesBS; // Filter the BindingSource based upon the region selected this.filteredTerritoriesBS.Filter = "RegionID = " + this.dataGridView1[e.ColumnIndex - 1, e.RowIndex].Value.ToString(); } } private void dataGridView1_CellEndEdit(object sender, DataGridViewCellEventArgs e) { if (e.ColumnIndex == this.territoryComboBoxColumn.Index) { // Reset combobox cell to the unfiltered BindingSource DataGridViewComboBoxCell dgcb = (DataGridViewComboBoxCell)dataGridView1 [e.ColumnIndex, e.RowIndex]; dgcb.DataSource = territoriesBindingSource; //unfiltered this.filteredTerritoriesBS.RemoveFilter(); } } 19. 如何在用戶編輯控件的時候(而不是在驗證時)就顯示錯誤圖標? 在使用錯誤文本和圖標時,有時你希望為用戶提供一個即時反饋,以提示當前的輸入不正確。默認情況下,即使設置了ErrorText屬性,如果單元格仍處於編輯模式下,那么錯誤圖標也不會顯示,比如TextBox和ComboBox。 下面的示例演示了如何在CellValidating事件中填充(padding)一個單元格為錯誤圖標提供空間。因為默認情況下填充行為會影響錯誤圖標的位置,該示例(TODO)。The below sample demonstrates how you can set a cell’s padding in the CellValidating event to provide spacing for the error icon. Since padding by default affects the location of the error icon the sample uses the CellPainting to move the position of the icon for painting. Lastly, the sample uses the tooltip control to display a custom tooltip when the mouse is over the cell to indicate what the problem is. This sample could also be written as a custom cell that overrides GetErrorIconBounds method to provide a location for the error icon that was independent of the padding. private ToolTip errorTooltip; private Point cellInError = new Point(-2, -2); public Form1() { InitializeComponent(); dataGridView1.ColumnCount = 3; dataGridView1.RowCount = 10; } private void dataGridView1_CellValidating(object sender, DataGridViewCellValidatingEventArgs e) { if (dataGridView1.IsCurrentCellDirty) { if (e.FormattedValue.ToString() == "BAD") { DataGridViewCell cell = dataGridView1[e.ColumnIndex, e.RowIndex]; cell.ErrorText = "Invalid data entered in cell"; // increase padding for icon. This moves the editing control if (cell.Tag == null) { cell.Tag = cell.Style.Padding; cell.Style.Padding = new Padding(0, 0, 18, 0); cellInError = new Point(e.ColumnIndex, e.RowIndex); } if (errorTooltip == null) { errorTooltip = new ToolTip(); errorTooltip.InitialDelay = 0; errorTooltip.ReshowDelay = 0; errorTooltip.Active = false; } e.Cancel = true; } } } private void dataGridView1_CellPainting(object sender, DataGridViewCellPaintingEventArgs e) { if (dataGridView1.IsCurrentCellDirty && !String.IsNullOrEmpty(e.ErrorText)) { // paint everything except error icon e.Paint(e.ClipBounds, DataGridViewPaintParts.All & ~(DataGridViewPaintParts.ErrorIcon)); // now move error icon over to fill in the padding space GraphicsContainer container = e.Graphics.BeginContainer(); e.Graphics.TranslateTransform(18, 0); e.Paint(this.ClientRectangle, DataGridViewPaintParts.ErrorIcon); e.Graphics.EndContainer(container); e.Handled = true; } } private void dataGridView1_CellEndEdit(object sender, DataGridViewCellEventArgs e) { if (dataGridView1[e.ColumnIndex, e.RowIndex].ErrorText != String.Empty) { DataGridViewCell cell = dataGridView1[e.ColumnIndex, e.RowIndex]; cell.ErrorText = String.Empty; cellInError = new Point(-2,-2); // restore padding for cell. This moves the editing control cell.Style.Padding = (Padding)cell.Tag; // hide and dispose tooltip if (errorTooltip != null) { errorTooltip.Hide(dataGridView1); errorTooltip.Dispose(); errorTooltip = null; } } } // show and hide the tooltip for error private void dataGridView1_CellMouseMove(object sender, DataGridViewCellMouseEventArgs e) { if (cellInError.X == e.ColumnIndex && cellInError.Y == e.RowIndex) { DataGridViewCell cell = dataGridView1[e.ColumnIndex, e.RowIndex]; if (cell.ErrorText != String.Empty) { if (!errorTooltip.Active) { errorTooltip.Show(cell.ErrorText, dataGridView1, 1000); } errorTooltip.Active = true; } } } private void dataGridView1_CellMouseLeave(object sender, DataGridViewCellEventArgs e) { if (cellInError.X == e.ColumnIndex && cellInError.Y == e.RowIndex) { if (errorTooltip.Active) { errorTooltip.Hide(dataGridView1); errorTooltip.Active = false; } } } 20. 如何同時顯示綁定數據和非綁定數據? The data you display in the DataGridView control will normally come from a data source of some kind, but you might want to display a column of data that does not come from the data source. This kind of column is called an unbound column. Unbound columns can take many forms. As discussed in the data section above, you can use virtual mode to display additional data along with bound data. The following code example demonstrates how to create an unbound column of check box cells to enable the user to select database records to process. The grid is put into virtual mode and responds to the necessary events. The selected records are kept by ID in a dictionary to allow the user to sort the content but not lose the checked rows. private System.Collections.Generic.Dictionary<int, bool> checkState; private void Form1_Load(object sender, EventArgs e) { dataGridView1.AutoGenerateColumns = false; dataGridView1.DataSource = customerOrdersBindingSource; // The check box column will be virtual. dataGridView1.VirtualMode = true; dataGridView1.Columns.Insert(0, new DataGridViewCheckBoxColumn()); // Initialize the dictionary that contains the boolean check state. checkState = new Dictionary<int, bool>(); } private void dataGridView1_CellValueChanged(object sender, DataGridViewCellEventArgs e) { // Update the status bar when the cell value changes. if (e.ColumnIndex == 0 && e.RowIndex != -1) { // Get the orderID from the OrderID column. int orderID = (int)dataGridView1.Rows[e.RowIndex].Cells["OrderID"].Value; checkState[orderID] = (bool)dataGridView1.Rows[e.RowIndex].Cells[0].Value; } private void dataGridView1_CellValueNeeded(object sender, DataGridViewCellValueEventArgs e) { // Handle the notification that the value for a cell in the virtual column // is needed. Get the value from the dictionary if the key exists. if (e.ColumnIndex == 0) { int orderID = (int)dataGridView1.Rows[e.RowIndex].Cells["OrderID"].Value; if (checkState.ContainsKey(orderID)) { e.Value = checkState[orderID]; } else e.Value = false; } } private void dataGridView1_CellValuePushed(object sender, DataGridViewCellValueEventArgs e) { // Handle the notification that the value for a cell in the virtual column // needs to be pushed back to the dictionary. if (e.ColumnIndex == 0) { // Get the orderID from the OrderID column. int orderID = (int)dataGridView1.Rows[e.RowIndex].Cells["OrderID"].Value; // Add or update the checked value to the dictionary depending on if the // key (orderID) already exists. if (!checkState.ContainsKey(orderID)) { checkState.Add(orderID, (bool)e.Value); } else checkState[orderID] = (bool)e.Value; } } 21. How do I show data that comes from two tables(TODO)? The DataGridView does not provide any new features apart from virtual mode to enable this. What you can do is use the JoinView class described in the following articlehttp://support.microsoft.com/default.aspx?scid=kb;en-us;325682. Using this class you can join two or more DataTables together. This JoinView can then be databound to the DataGridView. 22. 如何顯示主從表? 使用DataGridView時最常見的情況之一就是主從表單,這時要顯示具有主從關系的兩個數據表。在主表中選擇一行記錄,從表中也會隨之變化,顯示相應的記錄。 通過DataGridView控件和BindingSource 組件的交互作用來實現主從表單是非常簡單的。下面的示例演示的是SQL Server的范例數據庫Northwind 中的兩個表:Customers 和Orders。在主DataGridView中選擇一個顧客,那么該顧客的所有訂單會顯示在從DataGridView 中。 using System; using System.Data; using System.Data.SqlClient; using System.Windows.Forms; public class Form1 : System.Windows.Forms.Form { private DataGridView masterDataGridView = new DataGridView(); private BindingSource masterBindingSource = new BindingSource(); private DataGridView detailsDataGridView = new DataGridView(); private BindingSource detailsBindingSource = new BindingSource(); [STAThreadAttribute()] public static void Main() { Application.Run(new Form1()); } // Initializes the form. public Form1() { masterDataGridView.Dock = DockStyle.Fill; detailsDataGridView.Dock = DockStyle.Fill; SplitContainer splitContainer1 = new SplitContainer(); splitContainer1.Dock = DockStyle.Fill; splitContainer1.Orientation = Orientation.Horizontal; splitContainer1.Panel1.Controls.Add(masterDataGridView); splitContainer1.Panel2.Controls.Add(detailsDataGridView); this.Controls.Add(splitContainer1); this.Load += new System.EventHandler(Form1_Load); this.Text = "DataGridView master/detail demo"; } private void Form1_Load(object sender, System.EventArgs e) { // Bind the DataGridView controls to the BindingSource // components and load the data from the database. masterDataGridView.DataSource = masterBindingSource; detailsDataGridView.DataSource = detailsBindingSource; GetData(); // Resize the master DataGridView columns to fit the newly loaded data. masterDataGridView.AutoResizeColumns(); // Configure the details DataGridView so that its columns automatically // adjust their widths when the data changes. detailsDataGridView.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells; } private void GetData() { try { // Specify a connection string. Replace the given value with a // valid connection string for a Northwind SQL Server sample // database accessible to your system. String connectionString = "Integrated Security=SSPI;Persist Security Info=False;" + "Initial Catalog=Northwind;Data Source=localhost"; SqlConnection connection = new SqlConnection(connectionString); // Create a DataSet. DataSet data = new DataSet(); data.Locale = System.Globalization.CultureInfo.InvariantCulture; // Add data from the Customers table to the DataSet. SqlDataAdapter masterDataAdapter = new SqlDataAdapter("select * from Customers", connection); masterDataAdapter.Fill(data, "Customers"); // Add data from the Orders table to the DataSet. SqlDataAdapter detailsDataAdapter = new SqlDataAdapter("select * from Orders", connection); detailsDataAdapter.Fill(data, "Orders"); // Establish a relationship between the two tables. DataRelation relation = new DataRelation("CustomersOrders", data.Tables["Customers"].Columns["CustomerID"], data.Tables["Orders"].Columns["CustomerID"]); data.Relations.Add(relation); // Bind the master data connector to the Customers table. masterBindingSource.DataSource = data; masterBindingSource.DataMember = "Customers"; // Bind the details data connector to the master data connector, // using the DataRelation name to filter the information in the // details table based on the current row in the master table. detailsBindingSource.DataSource = masterBindingSource; detailsBindingSource.DataMember = "CustomersOrders"; } catch (SqlException) { MessageBox.Show("To run this example, replace the value of the " + "connectionString variable with a connection string that is " + "valid for your system."); } } } 23. 如何在同一DataGridView中顯示主從表? DataGridView 不支持在同一DataGridView 中顯示主從表。Windows Forms的先前版本中的DataGrid控件或許是你需要的一個解決方案。 24. 如何避免用戶對列排序? 對於DataGridView 控件,默認情況下,TextBox類型的列會自動排序,而其它類型的列則不會自動排序。這種自動排序有時會把數據變得比較亂,這時你會想更改這些默認設置。 DataGridViewColumn的屬性SortMode決定了列的排序方式,將其設置為DataGridViewColumnSortMode.NotSortable就可以避免默認的排序行為。 25. 如何在點擊工具欄按鈕的時候將數據提交到數據庫? 默認情況下,操作工具欄或菜單不會導致對控件的驗證。但對於綁定控件來說,提交數據前進行驗證是必要的。而一旦窗體和其中的所有控件得到驗證,當前編輯過的數據就需要提交。最后,數據適配器(如SqlDataAdapter)需要將數據的修改寫入數據庫。要達到這個效果,將下面三行代碼加到相應的事件處理函數(指工具欄按鈕或菜單項的事件)內: this.Validate(); this.customersBindingSource.EndEdit(); this.customersTableAdapter.Update(this.northwindDataSet.Customers); 26. 如何在用戶刪除記錄時顯示確認對話框? 當用戶選擇DataGridView的一行,按下Delete鍵時就會觸發UserDeletingRow 事件。你可以提示用戶是否確定要刪除該行記錄,建議僅在用戶要刪除已存在的記錄(而不是用戶添加的新行)時才進行這種提示。將下面這些代碼添加到UserDeletingRow事件的處理方法中就可以實現這種功能: if (!e.Row.IsNewRow) { DialogResult response = MessageBox.Show("Are you sure?", "Delete row?", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2); if (response == DialogResult.No) e.Cancel = true; } ------------- //針對某列的數據驗證 if ((e.ColumnIndex ==3) { if (dgvStandard.IsCurrentCellDirty) { int iValue = 0; if (!int.TryParse(e.FormattedValue.ToString(), out iValue)) { DataGridViewCell cell = dgvStandard[e.ColumnIndex, e.RowIndex]; cell.ErrorText = "請輸入數字"; // increase padding for icon. This moves the editing control if (cell.Tag == null) { cell.Tag = cell.Style.Padding; cell.Style.Padding = new Padding(0, 0, 18, 0); cellInError = new Point(e.ColumnIndex, e.RowIndex); } if (errorTooltip == null) { errorTooltip = new ToolTip(); errorTooltip.InitialDelay = 0; errorTooltip.ReshowDelay = 0; errorTooltip.Active = false; } e.Cancel = true; //取消更改 dgvStandard.CancelEdit(); } else { dgvStandard[e.ColumnIndex, e.RowIndex].ErrorText = ""; } } } -------數據異常時不拋出----- /// <summary> /// 數據異常時不拋出 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void dgvStandard_DataError(object sender, DataGridViewDataErrorEventArgs e) { e.ThrowException = false; }