Q1. 如何使單元格不可編輯?
A:設置 ReadOnly 屬性,可以設置的對象包括 DataGridViewRow(行)、DataGridViewColumn(列)、DataGridViewCell(單元格)以及自身 DataGridView 對象均可設置 ReadOnly 屬性來限制單元格的編輯狀態。
擴展:需要注意的是,當 DataGridView 通過 DataSource 綁定數據自動生成行列時,如果直接在 Form 的構造函數初始化界面 InitializeComponent 后直接設置 ReadOnly 屬性,會造成一些意想不到的效果……
1 public MainForm() 2 { 3 InitializeComponent() 4 5 Application.DoEvents() 6 dataGridView.DataSource = Person.GetPersons() 7 dataGridView[0, 0].ReadOnly = true 8 dataGridView.Rows[2].ReadOnly = true 9 dataGridView.Columns[1].ReadOnly = true 10 }
此時對 DataGridViewCell、DataGridViewRow的ReadOnly 設置無效,而對 DataGridViewColumn 的 ReadOnly 設置有效。
另外,ReadOnly 屬性只是限制用戶在界面上對單元格內容編輯的限制,並不影響在編程代碼中對該單元格的編輯以及刪除行列操作。
當然,你也可以在 CellBeginEdit 事件中對單元格進行判斷,然后直接結束編輯即可,如下:
1 dataGridView.CellBeginEdit += (sender, e) => 2 { 3 if (e.RowIndex == 0 && e.ColumnIndex == 0) 4 { 5 e.Cancel = true 6 // 或者 7 // dataGridView.EndEdit(); 8 } 9 }
Q2. 如何禁用單元格(Disable)?
A:DataGridView 不支持設置單元格的不可用狀態,所以采用折中辦法,使該“禁用”單元格的選中狀態和其背景顏色相同,給人暗示性的外觀提示該單元格“禁用”。
有一種便捷的方式,就是將該“禁用”單元格的選中顏色設置成非選中顏色,即如果未選中前是白底黑字,則將該“禁用”單元格的選中狀態也改成白底黑字即可,對單元格、行和列均適用,舉例如下:
1 dataGridView[2, 2].Style.SelectionBackColor = Color.White 2 dataGridView[2, 2].Style.SelectionForeColor = Color.Black 3 4 dataGridView.Rows[1].DefaultCellStyle.SelectionBackColor = Color.White 5 dataGridView.Rows[1].DefaultCellStyle.SelectionForeColor = Color.Black 6 7 dataGridView.Columns[0].DefaultCellStyle.SelectionBackColor = Color.White 8 dataGridView.Columns[0].DefaultCellStyle.SelectionForeColor = Color.Black
需要注意的是,同 Q1 中一樣,在 InitializeComponent 方法后面直接操作,其中對單元格的設置無效,對行、列的設置有效!!
但是這種方法對文本內容的單元有效,對於 DataGridViewButtonColumn、DataGridViewCheckBoxColumn、DataGridViewComboBoxColumn 等其他特殊列就沒效果了,畢竟對於特殊列,禁用單元格就是要禁用其中的特殊控件,這時候就需要重寫其中的單元格模版了,以 DataGridViewButtonColumn 為例,代碼如下:

public class DataGridViewButtonColumnExt : DataGridViewButtonColum { public DataGridViewButtonColumnExt() { this.CellTemplate = new DataGridViewButtonCellExt() } } public class DataGridViewButtonCellExt : DataGridViewButtonCell { private bool _Enabled;// 設置該單元格是否可用 /// <summary> /// 單元格是否可用 /// </summary> public bool Enabled { get { return _Enabled } set { _Enabled = value } } public override object Clone() { DataGridViewButtonCellExt cell =(DataGridViewButtonCellExt)base.Clone() cell.Enabled = this.Enabled return cell } public DataGridViewButtonCellExt() { this._Enabled = 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) { if (!this._Enabled) { // 繪制背景 if ((paintParts & DataGridViewPaintParts.Background) == DataGridViewPaintParts.Background) { SolidBrush cellBackground = new SolidBrush(cellStyle.BackColor) graphics.FillRectangle(cellBackground, cellBounds) cellBackground.Dispose() } // 繪制邊框 if ((paintParts & DataGridViewPaintParts.Border) == DataGridViewPaintParts.Border) { PaintBorder(graphics, clipBounds, cellBounds, cellStyle, advancedBorderStyle) } Rectangle buttonArea = cellBound Rectangle buttonAdjustment = this.BorderWidths(advancedBorderStyle) buttonArea.X += buttonAdjustment.X buttonArea.Y += buttonAdjustment.Y buttonArea.Height -= buttonAdjustment.Height buttonArea.Width -= buttonAdjustment.Width // 繪制按鈕控件 ButtonRenderer.DrawButton(graphics, buttonArea, PushButtonState.Disabled) // 繪制文本內容 if (this.FormattedValue is String) { TextRenderer.DrawText(graphics, (string)this.FormattedValue, this.DataGridView.Font, buttonArea, SystemColors.GrayText) } } else { base.Paint(graphics, clipBounds, cellBounds, rowIndex, elementState, value, formattedValue, errorText, cellStyle, advancedBorderStyle, paintParts) } } }
下面是 CheckBox 列的重寫例子,因為復選框有兩種禁用效果,一種選中時禁用,一種是未選中時禁用,所以加了一個 IsChecked 屬性:

public class DataGridViewCheckBoxColumnExt : DataGridViewCheckBoxColum { public DataGridViewCheckBoxColumnExt() { this.CellTemplate = new DataGridViewCheckBoxCellExt() } } public class DataGridViewCheckBoxCellExt : DataGridViewCheckBoxCell { private bool _Enable private bool _IsChecked /// <summary> /// 是否選中 /// </summary> public bool IsChecked { get { return _IsChecked } set { _IsChecked = value } } /// <summary> /// 是否可用 /// </summary> public bool Enable { get { return _Enable } set { _Enable = value } } public DataGridViewCheckBoxCellExt() { _Enable = true _IsChecked = false } public override object Clone() { DataGridViewCheckBoxCellExt dgvcce = (DataGridViewCheckBoxCellExt)base.Clone() dgvcce.Enable = this.Enable dgvcce.IsChecked = this.IsChecked return dgvcce } 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) { if (!_Enable) { // 繪制背景 if ((paintParts & DataGridViewPaintParts.Background) == DataGridViewPaintParts.Background) { SolidBrush cellBackground = new SolidBrush(cellStyle.BackColor) graphics.FillRectangle(cellBackground, cellBounds) cellBackground.Dispose() } // 繪制邊框 if ((paintParts & DataGridViewPaintParts.Border) == DataGridViewPaintParts.Border) { PaintBorder(graphics, clipBounds, cellBounds, cellStyle, advancedBorderStyle) } Point pos = cellBounds.Locatio // 調整位置居中 pos.X = pos.X + cellBounds.Width / 2 - 7 pos.Y = pos.Y + cellBounds.Height / 2 - 7 // 繪制按鈕控件 CheckBoxRenderer.DrawCheckBox(graphics, pos, IsChecked ? CheckBoxState.CheckedDisabled : CheckBoxState.UncheckedDisabled) } else { base.Paint(graphics, clipBounds, cellBounds, rowIndex, elementState, value, formattedValue, errorText, cellStyle, advancedBorderStyle, paintParts) } } }
Q3. 如何在單元格內同時顯示圖片和文字?
A:參考網上查到的資料,將文本列(DataGridViewTextBoxColum)中的文本向右偏移,然后在前面置入圖片;

public class TextAndImageColumn : DataGridViewTextBoxColum { private Image m_ImageValue private Size m_ImageSize public TextAndImageColumn() { this.CellTemplate = new TextAndImageCell() } public override object Clone() { TextAndImageColumn c = base.Clone() as TextAndImageColum c.m_ImageValue = this.m_ImageValue c.m_ImageSize = this.m_ImageSize return c } public Image Image { get { return this.m_ImageValue } set { if (this.Image != value) { this.m_ImageValue = value this.m_ImageSize = value.Size if (this.InheritedStyle != null) { Padding inheritedPadding = this.InheritedStyle.Padding this.DefaultCellStyle.Padding = new Padding(m_ImageSize.Width, inheritedPadding.Top, inheritedPadding.Right, inheritedPadding.Bottom) } } } } public Size ImageSize { get { return m_ImageSize } set { m_ImageSize = value } } private TextAndImageCell TextAndImageCellTemplate { get { return this.CellTemplate as TextAndImageCell } } } public class TextAndImageCell : DataGridViewTextBoxCell { private Image m_ImageValue private Size m_ImageSize public override object Clone() { TextAndImageCell c = base.Clone() as TextAndImageCell c.m_ImageValue = this.m_ImageValue c.m_ImageSize = this.m_ImageSize return c } public Image Image { get { if (this.OwningColumn == null || this.OwningTextAndImageColumn == null) { return m_ImageValue } else if (this.m_ImageValue != null) { return this.m_ImageValue } else { return this.OwningTextAndImageColumn.Image } } set { if (this.m_ImageValue != value) { this.m_ImageValue = value this.m_ImageSize = value.Size Padding inheritedPadding = this.InheritedStyle.Padding this.Style.Padding = new Padding(m_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 OwningTextAndImageColum { get { return this.OwningColumn as TextAndImageColum } } }
還有一種實現方式是在 CellPainting 事件中對指定單元格進行圖文繪制,參考百度文庫
Q4. 如何讓 DataGridViewComboBox 可編輯?
A:見 DataGridView 中 DataGridViewComboBox 的可編輯 。
還有一種根據 參考資料1 提供的方法,更簡捷,就是在 CellValidating 事件中將新編輯的內容添加到 Items 集合中,在 EditingControlShowing 事件中將下拉框類型 DropDownStyle 設置成 ComboBoxStyle.DropDown,使用戶可以進入編輯狀態,代碼如下:
1 private void dgv4_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e) 2 { 3 try 4 { 5 if (dgv4.CurrentCellAddress.X == 4) 6 { 7 ComboBox cb = e.Control as ComboBox; 8 if (cb != null) 9 { 10 cb.DropDownStyle = ComboBoxStyle.DropDown; 11 } 12 } 13 } 14 catch (Exception ex) 15 { 16 MessageBox.Show(ex.Message); 17 } 18 } 19 20 private void dgv4_CellValidating(object sender, DataGridViewCellValidatingEventArgs e) 21 { 22 try 23 { 24 if (e.ColumnIndex == 4) 25 { 26 DataGridViewComboBoxColumn dgvcbc = (DataGridViewComboBoxColumn)dgv4.Columns[4]; 27 if (!dgvcbc.Items.Contains(e.FormattedValue)) 28 { 29 dgvcbc.Items.Add(e.FormattedValue); 30 } 31 } 32 } 33 catch (Exception ex) 34 { 35 MessageBox.Show(ex.Message); 36 } 37 }
Q5. 如何多列排序?
A:分綁定數據和非綁定數據兩種情況處理。
1、綁定數據
如果綁定的數據源(如 DataView)支持多列排序,則 DataGridView 在綁定數據后會保留數據源排序后的結果,但是只有第一個進行排序的列會在 DataGridView 的顯示排序標記。而且,DataGridView 的 SortedColumn 屬性也只返回第一個排序列。
如果數據源實現了 IBindingListView 接口,並提供了對 Sort 屬性的支持,那么該數據源就可以支持多列排序。為了彌補上面提到的只標記了第一排序列的缺陷,可以手動對進行排序的列設置 SortGlyphDirection 屬性來標記。
1 BindingSource bindingSource = new BindingSource(); 2 dgv4.AutoGenerateColumns = false; 3 dgv4.DataSource = bindingSource; 4 5 DataTable dt = new DataTable(); 6 dt.Columns.Add("C1", typeof(int)); 7 dt.Columns.Add("C2", typeof(string)); 8 dt.Columns.Add("C3", typeof(string)); 9 10 dt.Rows.Add(1, "1", "Test1"); 11 dt.Rows.Add(2, "2", "Test2"); 12 dt.Rows.Add(2, "2", "Test1"); 13 dt.Rows.Add(3, "3", "Test3"); 14 dt.Rows.Add(4, "4", "Test4"); 15 dt.Rows.Add(4, "4", "Test3"); 16 17 DataView view = dt.DefaultView; 18 view.Sort = "C2 ASC,C3 DESC"; 19 bindingSource.DataSource = view; 20 21 DataGridViewTextBoxColumn col0 = new DataGridViewTextBoxColumn(); 22 col0.DataPropertyName = "C1"; 23 dgv4.Columns.Add(col0); 24 col0.SortMode = DataGridViewColumnSortMode.Programmatic; 25 col0.HeaderCell.SortGlyphDirection = SortOrder.None; 26 27 DataGridViewTextBoxColumn col1 = new DataGridViewTextBoxColumn(); 28 col1.DataPropertyName = "C2"; 29 dgv4.Columns.Add(col1); 30 col1.SortMode = DataGridViewColumnSortMode.Programmatic; 31 col1.HeaderCell.SortGlyphDirection = SortOrder.Ascending; 32 33 DataGridViewTextBoxColumn col2 = new DataGridViewTextBoxColumn(); 34 col2.DataPropertyName = "C3"; 35 dgv4.Columns.Add(col2); 36 col2.SortMode = DataGridViewColumnSortMode.Programmatic; 37 col2.HeaderCell.SortGlyphDirection = SortOrder.Descending;
需要注意的是,對 SortGlyphDirection 屬性的設置要在 DataGridView 綁定 DataSource 后面操作,否則會不生效。
上面代碼來自資料參考2的,可以簡化成:

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 DESC"; dgv4.DataSource = dt; dgv4.Columns[1].SortMode = DataGridViewColumnSortMode.Programmatic; dgv4.Columns[1].HeaderCell.SortGlyphDirection = SortOrder.Ascending; dgv4.Columns[2].SortMode = DataGridViewColumnSortMode.Programmatic; dgv4.Columns[2].HeaderCell.SortGlyphDirection = SortOrder.Descending;
2、非綁定數據
為了提供對多個列排序的支持,可以通過處理 SortCompare 事件,或者調用重載的 Sort ( IComparer ) 方法以更靈活的方式進行排序。
2.1、SortCompare 事件
private void dgv4_SortCompare(object sender, DataGridViewSortCompareEventArgs e) { try { e.SortResult = String.Compare(e.CellValue1.ToString(), e.CellValue2.ToString()); if (e.SortResult == 0 && e.Column.Name != "ID") { e.SortResult = string.Compare( dgv4.Rows[e.RowIndex1].Cells["ID"].Value.ToString(), dgv4.Rows[e.RowIndex2].Cells["ID"].Value.ToString()); } e.Handled = true; } catch (Exception ex) { MessageBox.Show(ex.Message); } }
上面的例子表示當你點擊列頭對某一列進行排序時,如果值相同時,則會按照對應 ID 值進行排序;所以如果要支持多列排序,在該事件里處理即可。
2.2、IComparer 接口
樓主擴展了資料里提供的參考例子,改成通用的多列排序接口實現,

1 public class RowComparer : IComparer 2 { 3 private Dictionary<string, int> m_SortList; 4 5 /// <summary> 6 /// 排序字符串,格式:(ColName1 AES,ColName2 DESC,ColName3 AES,...) 7 /// </summary> 8 public string Sort 9 { 10 get; 11 set; 12 } 13 14 /// <summary> 15 /// 構造函數,初始化排序條件 16 /// </summary> 17 public RowComparer(string sort) 18 { 19 Sort = sort; 20 try 21 { 22 string[] tmp = Sort.Split(','); 23 m_SortList = new Dictionary<string, int>(); 24 for (int i = 0; i < tmp.Length; i++) 25 { 26 string[] tmp2 = tmp[i].Split(new char[] { ' ' }, 27 StringSplitOptions.RemoveEmptyEntries); 28 string colName = tmp2[0].ToLower(); 29 int sortType = tmp2[1].ToLower().Equals("AES") ? 1 : -1; 30 if (m_SortList.ContainsKey(colName)) 31 { 32 m_SortList[colName] = sortType; 33 } 34 else 35 { 36 m_SortList.Add(colName, sortType); 37 } 38 } 39 } 40 catch (Exception ex) 41 { 42 throw new Exception(ex.Message); 43 } 44 } 45 46 #region IComparer 成員 47 48 public int Compare(object x, object y) 49 { 50 int compareResult = 0;// 比較結果 51 int sortMode = 0;// 排序方式 52 try 53 { 54 DataGridViewRow dgvr1 = (DataGridViewRow)x; 55 DataGridViewRow dgvr2 = (DataGridViewRow)y; 56 foreach (string colName in m_SortList.Keys) 57 { 58 compareResult = string.Compare(dgvr1.Cells[colName].Value.ToString(), 59 dgvr2.Cells[colName].Value.ToString()); 60 sortMode = m_SortList[colName]; 61 if (compareResult != 0) 62 { 63 break; 64 } 65 } 66 } 67 catch (Exception ex) 68 { 69 MessageBox.Show(ex.Message); 70 } 71 72 return compareResult * sortMode; 73 } 74 75 #endregion 76 }
Sort 屬性采用 DataView 的 Sort 屬性設置,然后在 RowComparer 構造函數對排序字符串進行處理,最后在接口方法 Compare 中依先后順序逐級排序;
Q6. 綁定 List 時,如何使 DataGridView 和 List 數據源同步修改?(3)
A:當 DataGridView 綁定 List 數據源時,對 List 進行操作后並不會實時更新到 DataGridView 上,這時候采用 BindingList 就可以很好的解決問題。BindingList 類可以用作創建雙向數據綁定機制的基類。BindingList 提供 IBindingList 接口的具體泛型實現,這樣就不必實現完整的 IBindingList 接口了。
BindingList 還可以通過擴展的 AddNew 方法支持工廠創建的示例;通過 EndNew 和 CancelNew 方法實現新項的事務性提交和回滾。
Q7. 如何在用戶刪除記錄時顯示確認對話框?
A:用戶選中一行后按 Delete 鍵會觸發 UserDeletingRow 事件(當然,前提是要設置 DataGridView 的 AllowUserToDeleteRows 屬性為 True 才可以),在該事件里提示用戶是否刪除當前選中記錄即可。
private void dgv4_UserDeletingRow(object sender, DataGridViewRowCancelEventArgs e) { try { if (!e.Row.IsNewRow) { if (MessageBox.Show("確定刪除當前選中數據?", "刪除", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) == System.Windows.Forms.DialogResult.No) { e.Cancel = true; } } } catch (Exception ex) { MessageBox.Show(ex.Message); } }
Q8. 如何為單元格內的控件添加事件處理函數?
A:諸如 DataGridViewButtonColumn 列里的 Button,DataGridViewCheckBoxColumn 列里的 CheckBox 等等,要給 Button 或 CheckBox 控件添加事件處理函數,則可以通過實現 DataGridView 的 EditingControlShowing 事件,該事件是在單元格進入編輯模式時觸發,可以處理執行該編輯控件的自定義初始化。它的第二個參數 DataGridViewEditingControlShowingEventArgs 類型,其 Control 屬性就是該單元格內的編輯控件了。拿 DataGridViewComboBoxColumn 列里的 ComboBox 事件舉個例子:

private void dgv4_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e) { try { ComboBox cb = e.Control as ComboBox; if (cb != null) { cb.SelectedIndexChanged -= new EventHandler(cb_SelectedIndexChanged); cb.SelectedIndexChanged += cb_SelectedIndexChanged; } } catch (Exception ex) { MessageBox.Show(ex.Message); } } private void cb_SelectedIndexChanged(object sender, EventArgs e) { MessageBox.Show("Selected Index Changed!"); }
需要注意的是,在 EditingControlShowing 事件里對編輯控件進行事件綁定時,要防止其添加多個相同的事件處理函數,所以在綁定事件前可以先移除相應的事件處理函數。
Q9. 如何讓數據顯示區域(列)填充整個 DataGridView 大小?
A:可以設置 DataGridView 的 AutoSizeColumnsMode 屬性,設置為 DataGridViewAutoSizeColumnsMode.Fill,此時列寬會自動調整到使所有列寬精確填充控件的顯示區域。當然,你可以通過設置每一列的 DataGridViewColumn.FillWeight 屬性來設置每一列的相對寬度。
如果只是想把最后一列填充剩下的空間,而前面那些列都是固定大小的,那可以直接設置最后一列的 DataGridViewColumn.AutoSizeMode 的屬性為 DataGridViewAutoSizeColumnMode.Fill 即可。
Q10. 如何設置 Image 列中當值為 NULL 時不顯示圖像(默認顯示“X”圖像)?
A:設置 Image 列的 dataGridView.DefaultCellStyle.NullValue = null;
Q11. 如何設置單元格內容自動換行?
A:設置自動換行:
datagridview.DefaultCellStyle.WrapMode = DataGridViewTriState.True;
然后設置自動調整高度,設置
datagridview.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.DisplayedCellsExceptHeaders;
或者 datagridview.AutoResizeRows(DataGridViewAutoSizeRowsMode.DisplayedCellsExceptHeaders);
需要注意的是,該設置對中文有效;如果是英文,一個單詞的寬度超過單元格的寬度,則是不會自動處理換行的,但是英文單詞與單詞之間有空格的話,也會處理自動換行。
Q12. 如何禁用列的自動排序?
A:設置 DataGridViewColumn 的 SortMode 屬性,該屬性默認值為 DataGridViewColumnSortMode.Automatic,即自動排序,改成 DataGridViewColumnSortMode.NotSortable 則可避免默認的排序;或者設置成 DataGridViewColumnSortMode.Programmatic,僅允許通過編程的方式對列進行排序。
Q13. 如何拖動調整行順序?
A:設置 DataGridView 的 AllowDrop 屬性為 true,並實現 MouseDown、MouseMove、DragOver、DragDrop 四個事件,參考代碼如下:

1 private Rectangle _dragBox; 2 private int _rowIndexFrom;// 鼠標按下位置的行索引 3 private int _rowIndexTo;// 將要拖動到的行索引位置 4 private void dgv4_MouseDown(object sender, MouseEventArgs e) 5 { 6 try 7 { 8 _rowIndexFrom = dgv4.HitTest(e.X, e.Y).RowIndex; 9 if (_rowIndexFrom != -1) 10 { 11 Size size = SystemInformation.DragSize; 12 _dragBox = new Rectangle(new Point(e.X - (size.Width / 2), e.Y - (size.Height / 2)), size); 13 } 14 else 15 { 16 _dragBox = Rectangle.Empty; 17 } 18 } 19 catch (Exception ex) 20 { 21 MessageBox.Show(ex.Message); 22 } 23 } 24 private void dgv4_MouseMove(object sender, MouseEventArgs e) 25 { 26 try 27 { 28 if ((e.Button & MouseButtons.Left) == MouseButtons.Left) 29 { 30 if (_dragBox != Rectangle.Empty && !_dragBox.Contains(e.X, e.Y)) 31 { 32 DragDropEffects dropEffect = dgv4.DoDragDrop(dgv4.Rows[_rowIndexFrom], DragDropEffects.Move); 33 } 34 } 35 } 36 catch (Exception ex) 37 { 38 MessageBox.Show(ex.Message); 39 } 40 } 41 private void dgv4_DragDrop(object sender, DragEventArgs e) 42 { 43 try 44 { 45 // 鼠標坐標是相對於屏幕的,轉換成工作區 DataGridView 上的坐標 46 Point clientPoint = dgv4.PointToClient(new Point(e.X, e.Y)); 47 // 獲取鼠標下面的所在行號 48 _rowIndexTo = dgv4.HitTest(clientPoint.X, clientPoint.Y).RowIndex; 49 if (e.Effect == DragDropEffects.Move) 50 { 51 DataGridViewRow rowToMove = e.Data.GetData(typeof(DataGridViewRow)) as DataGridViewRow; 52 dgv4.Rows.RemoveAt(_rowIndexFrom); 53 dgv4.Rows.Insert(_rowIndexTo, rowToMove); 54 } 55 } 56 catch (Exception ex) 57 { 58 MessageBox.Show(ex.Message); 59 } 60 } 61 private void dgv4_DragOver(object sender, DragEventArgs e) 62 { 63 e.Effect = DragDropEffects.Move;// 設置拖動效果 64 }
Q14. 如何顯示主從表?
A:主從表的意思就是通過兩個 DataGridView 控件顯示具有主從關系的兩個數據表,在主表中選擇一行記錄,則從表中也會隨之改變,顯示對應的記錄數據。
1、下面代碼是 DataGridView 直接綁定 DataSet 數據集:
DataTable masterDT = Person.GetPersons2();// 主表,主鍵"ID" DataTable detailDT = Person.GetPersons3();// 從表,外鍵"ID" DataSet dataDS = new DataSet(); dataDS.Tables.Add(masterDT); dataDS.Tables.Add(detailDT); // 建立主從表關系 dataDS.Relations.Add("Custom", masterDT.Columns["ID"], detailDT.Columns["ID"]); // 數據綁定 dgv5_1.DataSource = dataDS; dgv5_1.DataMember = dataDS.Tables[0].TableName; dgv5_2.DataSource = dataDS; dgv5_2.DataMember = dataDS.Tables[0].TableName + ".Custom"; dgv5_1.MultiSelect = false;
代碼中主表 masterDT,從表 detailDT,通過 DataSet 的 Relations 建立主從關系,注意其中 Add 方法的第一個參數是關系名稱,在后面的數據綁定中要用到。
負責顯示主表數據的 DataGridView(dgv5_1),負責顯示從表數據的 DataGridView(dgv5_2),其 DataSource 均綁定同一個 DataSet,DataMember 注意區別,主表的 dgv5_1 直接綁定主表名稱即可,從表的 dgv5_2 的 DataMember 屬性則要綁定"主表名稱.關系名稱"。
2、下面代碼是 DataGridView 綁定 BindingSource:
DataTable masterDT = Person.GetPersons2();// 主表,主鍵"ID" DataTable detailDT = Person.GetPersons3();// 從表,外鍵"ID" DataSet dataDS = new DataSet(); dataDS.Tables.Add(masterDT); dataDS.Tables.Add(detailDT); dataDS.Relations.Add("Custom", masterDT.Columns["ID"], detailDT.Columns["ID"]); BindingSource masterBS = new BindingSource(); masterBS.DataSource = dataDS; masterBS.DataMember = masterDT.TableName; dgv5_1.DataSource = masterBS; BindingSource detailBS = new BindingSource(); detailBS.DataSource = masterBS; detailBS.DataMember = "Custom"; dgv5_2.DataSource = detailBS; dgv5_1.MultiSelect = false;
注意如果 DataGridView 通過 BindingSource 綁定數據,並且在 BindingSource 中設置 DataSource 和 DataMember 時,此時的從表對應的 BindingSource 設置的 DataMember 應該直接用關系名稱即可,與上面的"主表名稱.關系名稱"區別開來!
Q15. 如何同時顯示綁定數據和非綁定數據?
A:樓主就以資料1中例子來講解,下面代碼是樓主親敲親測的。該例子是說如何創建一列復選框列,用來負責選擇數據。並且通過字典緩存(_CheckState)以及相應的事件,保證在用戶排序時不會丟失已有的選擇狀態。
private Dictionary<int, bool> _CheckState;// 存儲非綁定列數據 public MainForm() { InitializeComponent(); _CheckState = new Dictionary<int, bool>(); dgv4.AutoGenerateColumns = true; dgv4.ReadOnly = false; dgv4.DataSource = Person.GetPersons2(); dgv4.VirtualMode = true; dgv4.Columns.Insert(0, new DataGridViewCheckBoxColumn()); dgv4.CellValueChanged += (s, e) => { if (e.ColumnIndex == 0 && e.RowIndex != -1) { int id = Convert.ToInt32(dgv4.Rows[e.RowIndex].Cells["ID"].Value); _CheckState[id] = Convert.ToBoolean(dgv4.Rows[e.RowIndex].Cells[0].Value); } }; dgv4.CellValueNeeded += (s, e) => { if (e.ColumnIndex == 0) { int id = Convert.ToInt32(dgv4.Rows[e.RowIndex].Cells["ID"].Value); if (_CheckState.ContainsKey(id)) { e.Value = _CheckState[id]; } else { e.Value = false; } } }; dgv4.CellValuePushed += (s, e) => { if (e.ColumnIndex == 0) { int id = Convert.ToInt32(dgv4.Rows[e.RowIndex].Cells["ID"].Value); if (!_CheckState.ContainsKey(id)) { _CheckState.Add(id, Convert.ToBoolean(e.Value)); } else { _CheckState[id] = Convert.ToBoolean(e.Value); } } }; }
1、復選框值改變時通過 CellValueChanged 事件緩存到 _ChekState 字典中;2、VirtualMode 為 true,並且單元格有值時需要顯示和格式化的處理,是通過 CellValueNeeded 事件;3、VirtualMode 屬性為 true,並且單元格值已更改並需要存儲在基礎數據源中時通過 CellValuePushed 事件處理。
Q16. 列頭右鍵菜單控制列的顯隱[4]
A:需求是在列頭位置處增加右鍵菜單,控制 DataGridView 所有列的顯示和隱藏。
首先在 DataGridView 列的增刪時對菜單選項進行更新,然后在菜單點擊事件中修改列的可見性以及菜單欄該選項的 IsChecked 屬性,最后在右鍵單擊處顯示菜單即可。
1 // 列頭位置顯示右鍵菜單 2 dataGridView.ColumnHeaderMouseClick += (sender, e) => 3 { 4 if (e.Button == MouseButtons.Right) 5 { 6 contextMenuStrip.Show(MousePosition.X, MousePosition.Y); 7 } 8 }; 9 // 增加列時 10 dataGridView.ColumnAdded += (sender, e) => 11 { 12 ToolStripMenuItem item = new ToolStripMenuItem(e.Column.Name); 13 contextMenuStrip.Items.Add(item); 14 item.Checked = e.Column.Visible; 15 }; 16 // 刪除列時 17 dataGridView.ColumnRemoved += (sender, e) => 18 { 19 int count = contextMenuStrip.Items.Count; 20 for (int i = 0; i < count; i++) 21 { 22 ToolStripMenuItem tsm = (ToolStripMenuItem)contextMenuStrip.Items[i]; 23 if (tsm.Text.Equals(e.Column.Name)) 24 { 25 contextMenuStrip.Items.Remove(tsm); 26 break; 27 } 28 } 29 }; 30 // 菜單欄控制列的顯示和隱藏 31 contextMenuStrip.ItemClicked += (sender, e) => 32 { 33 string colName = e.ClickedItem.Text; 34 bool isChecked = ((ToolStripMenuItem)e.ClickedItem).Checked; 35 dataGridView.Columns[colName].Visible = !isChecked; 36 ((ToolStripMenuItem)e.ClickedItem).Checked = !isChecked; 37 };
資料參考
1、http://www.cnblogs.com/xiaofengfeng/archive/2011/04/16/2018504.html (主要參考)
2、http://blogs.msdn.com/b/jfoscoding/archive/2005/11/17/494012.aspx
3、https://msdn.microsoft.com/zh-cn/library/ms132679.aspx#Mtps_DropDownFilterTextBindingList
4、http://www.cnblogs.com/over140/archive/2012/04/16/2451200.html