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 article http://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;
}
————————————————
版权声明:本文为CSDN博主「yudehui」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/yudehui/article/details/6290622