一、单元格内容的操作 // 取得当前单元格内容 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; }