你的位置:首页 > ASP.net教程

[ASP.net教程][Winform] DataGridView(FAQ)


Q1.  如何使单元格不可编辑?

A:设置ReadOnly属性,可以设置的对象包括DataGridViewRow(行)、DataGridViewColumn(列)、DataGridViewCell(单元格)以及自身DataGridView对象均可设置ReadOnly属性来限制单元格的编辑状态。

扩展:需要注意的是,当DataGridView通过DataSource绑定数据自动生成行列时,如果直接在Form的构造函数初始化界面InitializeComponent后直接设置ReadOnly属性,会造成一些意想不到的效果……

 1 public MainForm() 2 { 3   InitializeComponent() 4  5   Application.DoEvents() 6   dataGridView.DataSource = Person.GetPersons() 7   dataGridView[0, 0].ReadOnly = true 8   dataGridView.Rows[2].ReadOnly = true 9   dataGridView.Columns[1].ReadOnly = true 10 } 

此时对DataGridViewCell、DataGridViewRow的ReadOnly设置无效,而对DataGridViewColumn的ReadOnly设置有效。

另外,ReadOnly属性只是限制用户在界面上对单元格内容编辑的限制,并不影响在编程代码中对该单元格的编辑以及删除行列操作。

当然,你也可以在CellBeginEdit事件中对单元格进行判断,然后直接结束编辑即可,如下:

 1 dataGridView.CellBeginEdit += (sender, e) => 2 { 3   if (e.RowIndex == 0 && e.ColumnIndex == 0) 4   { 5     e.Cancel = true 6     // 或者 7     // dataGridView.EndEdit(); 8   } 9 }

Q2.  如何禁用单元格(Disable)?

A:DataGridView不支持设置单元格的不可用状态,所以采用折中办法,使该“禁用”单元格的选中状态和其背景颜色相同,给人暗示性的外观提示该单元格“禁用”。

有一种便捷的方式,就是将该“禁用”单元格的选中颜色设置成非选中颜色,即如果未选中前是白底黑字,则将该“禁用”单元格的选中状态也改成白底黑字即可,对单元格、行和列均适用,举例如下:

 1 dataGridView[2, 2].Style.SelectionBackColor = Color.White 2 dataGridView[2, 2].Style.SelectionForeColor = Color.Black 3  4 dataGridView.Rows[1].DefaultCellStyle.SelectionBackColor = Color.White 5 dataGridView.Rows[1].DefaultCellStyle.SelectionForeColor = Color.Black 6  7 dataGridView.Columns[0].DefaultCellStyle.SelectionBackColor = Color.White 8 dataGridView.Columns[0].DefaultCellStyle.SelectionForeColor = Color.Black

需要注意的是,同Q1中一样,在InitializeComponent方法后面直接操作,其中对单元格的设置无效,对行、列的设置有效!!

但是这种方法对文本内容的单元有效,对于DataGridViewButtonColumn、DataGridViewCheckBoxColumn、DataGridViewComboBoxColumn等其他特殊列就没效果了,毕竟对于特殊列,禁用单元格就是要禁用其中的特殊控件,这时候就需要重写其中的单元格模版了,以DataGridViewButtonColumn为例,代码如下:

public class DataGridViewButtonColumnExt : DataGridViewButtonColum{  public DataGridViewButtonColumnExt()  {    this.CellTemplate = new DataGridViewButtonCellExt()  }}public class DataGridViewButtonCellExt : DataGridViewButtonCell{  private bool _Enabled;// 设置该单元格是否可用  /// <summary>  /// 单元格是否可用  /// </summary>  public bool Enabled  {    get    {      return _Enabled    }    set    {      _Enabled = value    }  }  public override object Clone()  {    DataGridViewButtonCellExt cell =(DataGridViewButtonCellExt)base.Clone()    cell.Enabled = this.Enabled    return cell  }  public DataGridViewButtonCellExt()  {    this._Enabled = true  }  protected override void Paint(Graphics graphics, Rectangle clipBounds, Rectangle cellBounds,    int rowIndex, DataGridViewElementStates elementState, object value,    object formattedValue, string errorText, DataGridViewCellStyle cellStyle,    DataGridViewAdvancedBorderStyle advancedBorderStyle, DataGridViewPaintParts paintParts)  {    if (!this._Enabled)    {      // 绘制背景      if ((paintParts & DataGridViewPaintParts.Background) == DataGridViewPaintParts.Background)      {        SolidBrush cellBackground = new SolidBrush(cellStyle.BackColor)        graphics.FillRectangle(cellBackground, cellBounds)        cellBackground.Dispose()      }      // 绘制边框      if ((paintParts & DataGridViewPaintParts.Border) == DataGridViewPaintParts.Border)      {        PaintBorder(graphics, clipBounds, cellBounds, cellStyle, advancedBorderStyle)      }      Rectangle buttonArea = cellBound      Rectangle buttonAdjustment = this.BorderWidths(advancedBorderStyle)      buttonArea.X += buttonAdjustment.X      buttonArea.Y += buttonAdjustment.Y      buttonArea.Height -= buttonAdjustment.Height      buttonArea.Width -= buttonAdjustment.Width            // 绘制按钮控件      ButtonRenderer.DrawButton(graphics, buttonArea, PushButtonState.Disabled)      // 绘制文本内容      if (this.FormattedValue is String)      {        TextRenderer.DrawText(graphics, (string)this.FormattedValue,          this.DataGridView.Font, buttonArea, SystemColors.GrayText)      }    }    else    {      base.Paint(graphics, clipBounds, cellBounds, rowIndex, elementState, value, formattedValue,        errorText, cellStyle, advancedBorderStyle, paintParts)    }  }}

View Code

下面是CheckBox列的重写例子,因为复选框有两种禁用效果,一种选中时禁用,一种是未选中时禁用,所以加了一个IsChecked属性:

public class DataGridViewCheckBoxColumnExt : DataGridViewCheckBoxColum{	public DataGridViewCheckBoxColumnExt()	{		this.CellTemplate = new DataGridViewCheckBoxCellExt()	}}public class DataGridViewCheckBoxCellExt : DataGridViewCheckBoxCell{	private bool _Enable	private bool _IsChecked		/// <summary>	/// 是否选中	/// </summary>	public bool IsChecked	{		get		{			return _IsChecked		}		set		{			_IsChecked = value		}	}	/// <summary>	/// 是否可用	/// </summary>	public bool Enable	{		get		{			return _Enable		}		set		{			_Enable = value		}	}	public DataGridViewCheckBoxCellExt()	{		_Enable = true		_IsChecked = false	}	public override object Clone()	{		DataGridViewCheckBoxCellExt dgvcce = (DataGridViewCheckBoxCellExt)base.Clone()		dgvcce.Enable = this.Enable		dgvcce.IsChecked = this.IsChecked		return dgvcce	}	protected override void Paint(Graphics graphics, Rectangle clipBounds, Rectangle cellBounds,		int rowIndex, DataGridViewElementStates elementState, object value, object formattedValue,		string errorText, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle,		DataGridViewPaintParts paintParts)	{		if (!_Enable)		{			// 绘制背景			if ((paintParts & DataGridViewPaintParts.Background) == DataGridViewPaintParts.Background)			{				SolidBrush cellBackground = new SolidBrush(cellStyle.BackColor)				graphics.FillRectangle(cellBackground, cellBounds)				cellBackground.Dispose()			}			// 绘制边框			if ((paintParts & DataGridViewPaintParts.Border) == DataGridViewPaintParts.Border)			{				PaintBorder(graphics, clipBounds, cellBounds, cellStyle, advancedBorderStyle)			}			Point pos = cellBounds.Locatio			// 调整位置居中			pos.X = pos.X + cellBounds.Width / 2 - 7			pos.Y = pos.Y + cellBounds.Height / 2 - 7			// 绘制按钮控件			CheckBoxRenderer.DrawCheckBox(graphics, pos,				IsChecked ? CheckBoxState.CheckedDisabled : CheckBoxState.UncheckedDisabled)		}		else		{			base.Paint(graphics, clipBounds, cellBounds, rowIndex, elementState, value,				formattedValue, errorText, cellStyle, advancedBorderStyle, paintParts)		}	}}

View Code

Q3.  如何在单元格内同时显示图片和文字?

A:参考网上查到的资料,将文本列(DataGridViewTextBoxColum)中的文本向右偏移,然后在前面置入图片;

public class TextAndImageColumn : DataGridViewTextBoxColum{  private Image m_ImageValue  private Size m_ImageSize  public TextAndImageColumn()  {    this.CellTemplate = new TextAndImageCell()  }  public override object Clone()  {    TextAndImageColumn c = base.Clone() as TextAndImageColum    c.m_ImageValue = this.m_ImageValue    c.m_ImageSize = this.m_ImageSize    return c  }  public Image Image  {    get    {      return this.m_ImageValue    }    set    {      if (this.Image != value)      {        this.m_ImageValue = value        this.m_ImageSize = value.Size        if (this.InheritedStyle != null)        {          Padding inheritedPadding = this.InheritedStyle.Padding          this.DefaultCellStyle.Padding = new Padding(m_ImageSize.Width,            inheritedPadding.Top, inheritedPadding.Right,            inheritedPadding.Bottom)        }      }    }  }  public Size ImageSize  {    get    {      return m_ImageSize    }    set    {      m_ImageSize = value    }  }  private TextAndImageCell TextAndImageCellTemplate  {    get    {      return this.CellTemplate as TextAndImageCell    }  }}public class TextAndImageCell : DataGridViewTextBoxCell{  private Image m_ImageValue  private Size m_ImageSize  public override object Clone()  {    TextAndImageCell c = base.Clone() as TextAndImageCell    c.m_ImageValue = this.m_ImageValue    c.m_ImageSize = this.m_ImageSize    return c  }  public Image Image  {    get    {      if (this.OwningColumn == null || this.OwningTextAndImageColumn == null)      {        return m_ImageValue      }      else if (this.m_ImageValue != null)      {        return this.m_ImageValue      }      else      {        return this.OwningTextAndImageColumn.Image      }    }    set    {      if (this.m_ImageValue != value)      {        this.m_ImageValue = value        this.m_ImageSize = value.Size        Padding inheritedPadding = this.InheritedStyle.Padding        this.Style.Padding = new Padding(m_ImageSize.Width,          inheritedPadding.Top, inheritedPadding.Right,          inheritedPadding.Bottom)      }    }  }  protected override void Paint(Graphics graphics, Rectangle clipBounds,    Rectangle cellBounds, int rowIndex, DataGridViewElementStates cellState,    object value, object formattedValue, string errorText,    DataGridViewCellStyle cellStyle,    DataGridViewAdvancedBorderStyle advancedBorderStyle,    DataGridViewPaintParts paintParts)  {    // Paint the base content    base.Paint(graphics, clipBounds, cellBounds, rowIndex, cellState,     value, formattedValue, errorText, cellStyle,     advancedBorderStyle, paintParts)    if (this.Image != null)    {      // Draw the image clipped to the cell.      System.Drawing.Drawing2D.GraphicsContainer container = graphics.BeginContainer()      graphics.SetClip(cellBounds)      graphics.DrawImageUnscaled(this.Image, cellBounds.Location)      graphics.EndContainer(container)    }  }  private TextAndImageColumn OwningTextAndImageColum  {    get    {      return this.OwningColumn as TextAndImageColum    }  }}

View Code

还有一种实现方式是在CellPainting事件中对指定单元格进行图文绘制,参考百度文库 

Q4.  如何让DataGridViewComboBox可编辑?

A:见 DataGridView中DataGridViewComboBox的可编辑 。

还有一种根据参考资料1提供的方法,更简捷,就是在CellValidating事件中将新编辑的内容添加到Items集合中,在EditingControlShowing事件中将下拉框类型DropDownStyle设置成ComboBoxStyle.DropDown,使用户可以进入编辑状态,代码如下:

 1 private void dgv4_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e) 2 { 3   try 4   { 5     if (dgv4.CurrentCellAddress.X == 4) 6     { 7       ComboBox cb = e.Control as ComboBox; 8       if (cb != null) 9       { 10         cb.DropDownStyle = ComboBoxStyle.DropDown; 11       } 12     } 13   } 14   catch (Exception ex) 15   { 16     MessageBox.Show(ex.Message); 17   } 18 } 19  20 private void dgv4_CellValidating(object sender, DataGridViewCellValidatingEventArgs e) 21 { 22   try 23   { 24     if (e.ColumnIndex == 4) 25     { 26       DataGridViewComboBoxColumn dgvcbc = (DataGridViewComboBoxColumn)dgv4.Columns[4]; 27       if (!dgvcbc.Items.Contains(e.FormattedValue)) 28       { 29         dgvcbc.Items.Add(e.FormattedValue); 30       } 31     } 32   } 33   catch (Exception ex) 34   { 35     MessageBox.Show(ex.Message); 36   } 37 }

Q5.  如何多列排序?

A:分绑定数据和非绑定数据两种情况处理。

1、绑定数据

如果绑定的数据源(如DataView)支持多列排序,则DataGridView在绑定数据后会保留数据源排序后的结果,但是只有第一个进行排序的列会在DataGridView的显示排序标记。而且,DataGridView的SortedColumn属性也只返回第一个排序列。

如果数据源实现了IBindingListView接口,并提供了对Sort属性的支持,那么该数据源就可以支持多列排序。为了弥补上面提到的只标记了第一排序列的缺陷,可以手动对进行排序的列设置SortGlyphDirection属性来标记。

 1 BindingSource bindingSource = new BindingSource(); 2 dgv4.AutoGenerateColumns = false; 3 dgv4.DataSource = bindingSource; 4  5 DataTable dt = new DataTable(); 6 dt.Columns.Add("C1", typeof(int)); 7 dt.Columns.Add("C2", typeof(string)); 8 dt.Columns.Add("C3", typeof(string)); 9  10 dt.Rows.Add(1, "1", "Test1"); 11 dt.Rows.Add(2, "2", "Test2"); 12 dt.Rows.Add(2, "2", "Test1"); 13 dt.Rows.Add(3, "3", "Test3"); 14 dt.Rows.Add(4, "4", "Test4"); 15 dt.Rows.Add(4, "4", "Test3"); 16  17 DataView view = dt.DefaultView; 18 view.Sort = "C2 ASC,C3 DESC"; 19 bindingSource.DataSource = view; 20  21 DataGridViewTextBoxColumn col0 = new DataGridViewTextBoxColumn(); 22 col0.DataPropertyName = "C1"; 23 dgv4.Columns.Add(col0); 24 col0.SortMode = DataGridViewColumnSortMode.Programmatic; 25 col0.HeaderCell.SortGlyphDirection = SortOrder.None; 26  27 DataGridViewTextBoxColumn col1 = new DataGridViewTextBoxColumn(); 28 col1.DataPropertyName = "C2"; 29 dgv4.Columns.Add(col1); 30 col1.SortMode = DataGridViewColumnSortMode.Programmatic; 31 col1.HeaderCell.SortGlyphDirection = SortOrder.Ascending; 32  33 DataGridViewTextBoxColumn col2 = new DataGridViewTextBoxColumn(); 34 col2.DataPropertyName = "C3"; 35 dgv4.Columns.Add(col2); 36 col2.SortMode = DataGridViewColumnSortMode.Programmatic; 37 col2.HeaderCell.SortGlyphDirection = SortOrder.Descending;

需要注意的是,对SortGlyphDirection属性的设置要在DataGridView绑定DataSource后面操作,否则会不生效。image

上面代码来自资料参考2的,可以简化成:

DataTable dt = new DataTable();dt.Columns.Add("C1", typeof(int));dt.Columns.Add("C2", typeof(string));dt.Columns.Add("C3", typeof(string));dt.Rows.Add(1, "1", "Test1");dt.Rows.Add(2, "2", "Test2");dt.Rows.Add(2, "2", "Test1");dt.Rows.Add(3, "3", "Test3");dt.Rows.Add(4, "4", "Test4");dt.Rows.Add(4, "4", "Test3");DataView view = dt.DefaultView;view.Sort = "C2 ASC,C3 DESC";dgv4.DataSource = dt;dgv4.Columns[1].SortMode = DataGridViewColumnSortMode.Programmatic;dgv4.Columns[1].HeaderCell.SortGlyphDirection = SortOrder.Ascending;dgv4.Columns[2].SortMode = DataGridViewColumnSortMode.Programmatic;dgv4.Columns[2].HeaderCell.SortGlyphDirection = SortOrder.Descending;

View Code

2、非绑定数据

为了提供对多个列排序的支持,可以通过处理SortCompare事件,或者调用重载的Sort ( IComparer ) 方法以更灵活的方式进行排序。

2.1、SortCompare事件

private void dgv4_SortCompare(object sender, DataGridViewSortCompareEventArgs e){  try  {    e.SortResult = String.Compare(e.CellValue1.ToString(), e.CellValue2.ToString());    if (e.SortResult == 0 && e.Column.Name != "ID")    {      e.SortResult = string.Compare(        dgv4.Rows[e.RowIndex1].Cells["ID"].Value.ToString(),        dgv4.Rows[e.RowIndex2].Cells["ID"].Value.ToString());    }    e.Handled = true;  }  catch (Exception ex)  {    MessageBox.Show(ex.Message);  }}

上面的例子表示当你点击列头对某一列进行排序时,如果值相同时,则会按照对应ID值进行排序;所以如果要支持多列排序,在该事件里处理即可。

2.2、IComparer 接口

楼主扩展了资料里提供的参考例子,改成通用的多列排序接口实现,

 1 public class RowComparer : IComparer 2 { 3   private Dictionary<string, int> m_SortList; 4  5   /// <summary> 6   /// 排序字符串,格式:(ColName1 AES,ColName2 DESC,ColName3 AES,...) 7   /// </summary> 8   public string Sort 9   { 10     get; 11     set; 12   } 13  14   /// <summary> 15   /// 构造函数,初始化排序条件 16   /// </summary> 17   public RowComparer(string sort) 18   { 19     Sort = sort; 20     try 21     { 22       string[] tmp = Sort.Split(','); 23       m_SortList = new Dictionary<string, int>(); 24       for (int i = 0; i < tmp.Length; i++) 25       { 26         string[] tmp2 = tmp[i].Split(new char[] { ' ' }, 27           StringSplitOptions.RemoveEmptyEntries); 28         string colName = tmp2[0].ToLower(); 29         int sortType = tmp2[1].ToLower().Equals("AES") ? 1 : -1; 30         if (m_SortList.ContainsKey(colName)) 31         { 32           m_SortList[colName] = sortType; 33         } 34         else 35         { 36           m_SortList.Add(colName, sortType); 37         } 38       } 39     } 40     catch (Exception ex) 41     { 42       throw new Exception(ex.Message); 43     } 44   } 45  46   #region IComparer 成员 47  48   public int Compare(object x, object y) 49   { 50     int compareResult = 0;// 比较结果 51     int sortMode = 0;// 排序方式 52     try 53     { 54       DataGridViewRow dgvr1 = (DataGridViewRow)x; 55       DataGridViewRow dgvr2 = (DataGridViewRow)y; 56       foreach (string colName in m_SortList.Keys) 57       { 58         compareResult = string.Compare(dgvr1.Cells[colName].Value.ToString(), 59           dgvr2.Cells[colName].Value.ToString()); 60         sortMode = m_SortList[colName]; 61         if (compareResult != 0) 62         { 63           break; 64         } 65       } 66     } 67     catch (Exception ex) 68     { 69       MessageBox.Show(ex.Message); 70     } 71  72     return compareResult * sortMode; 73   } 74  75   #endregion 76 }

View Code

Sort属性采用DataView的Sort属性设置,然后在RowComparer构造函数对排序字符串进行处理,最后在接口方法Compare中依先后顺序逐级排序;

Q6.  绑定List时,如何使DataGridView和List数据源同步修改?(3)

A:当DataGridView绑定List数据源时,对List进行操作后并不会实时更新到DataGridView上,这时候采用BindingList就可以很好的解决问题。BindingList类可以用作创建双向数据绑定机制的基类。BindingList提供IBindingList接口的具体泛型实现,这样就不必实现完整的IBindingList接口了。

BindingList还可以通过扩展的AddNew方法支持工厂创建的示例;通过EndNewCancelNew方法实现新项的事务性提交和回滚。

Q7.  如何在用户删除记录时显示确认对话框?

A:用户选中一行后按Delete键会触发UserDeletingRow事件(当然,前提是要设置DataGridView的AllowUserToDeleteRows属性为True才可以),在该事件里提示用户是否删除当前选中记录即可。

private void dgv4_UserDeletingRow(object sender, DataGridViewRowCancelEventArgs e){  try  {    if (!e.Row.IsNewRow)    {      if (MessageBox.Show("确定删除当前选中数据?", "删除",        MessageBoxButtons.YesNo, MessageBoxIcon.Question,        MessageBoxDefaultButton.Button2)        == System.Windows.Forms.DialogResult.No)      {        e.Cancel = true;      }    }  }  catch (Exception ex)  {    MessageBox.Show(ex.Message);  }}

Q8.  如何为单元格内的控件添加事件处理函数?

A:诸如DataGridViewButtonColumn列里的Button,DataGridViewCheckBoxColumn列里的CheckBox等等,要给Button或CheckBox控件添加事件处理函数,则可以通过实现DataGridView的EditingControlShowing事件,该事件是在单元格进入编辑模式时触发,可以处理执行该编辑控件的自定义初始化。它的第二个参数DataGridViewEditingControlShowingEventArgs类型,其Control属性就是该单元格内的编辑控件了。拿DataGridViewComboBoxColumn列里的ComboBox事件举个例子:

private void dgv4_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e){  try  {    ComboBox cb = e.Control as ComboBox;    if (cb != null)    {      cb.SelectedIndexChanged -= new EventHandler(cb_SelectedIndexChanged);      cb.SelectedIndexChanged += cb_SelectedIndexChanged;    }  }  catch (Exception ex)  {    MessageBox.Show(ex.Message);  }}private void cb_SelectedIndexChanged(object sender, EventArgs e){  MessageBox.Show("Selected Index Changed!");}

View Code
需要注意的是,在EditingControlShowing事件里对编辑控件进行事件绑定时,要防止其添加多个相同的事件处理函数,所以在绑定事件前可以先移除相应的事件处理函数。

Q9.  如何让数据显示区域(列)填充整个DataGridView大小?

A:可以设置DataGridView的AutoSizeColumnsMode属性,设置为DataGridViewAutoSizeColumnsMode.Fill,此时列宽会自动调整到使所有列宽精确填充控件的显示区域。当然,你可以通过设置每一列的DataGridViewColumn.FillWeight属性来设置每一列的相对宽度。

如果只是想把最后一列填充剩下的空间,而前面那些列都是固定大小的,那可以直接设置最后一列的DataGridViewColumn.AutoSizeMode的属性为 DataGridViewAutoSizeColumnMode.Fill 即可。

Q10.  如何使单元格内文本内容(TextBox)自动换行?

A:设置DataGridViewColumn.DefaultCellStyle.WrapMode = DataGridViewTriState.True;

Q11.  如何设置Image列中当值为NULL时不显示图像(默认显示“X”图像)?

A:设置Image列的 dataGridView.DefaultCellStyle.NullValue = null;

 

资料参考

1、http://www.cnblogs.com/xiaofengfeng/archive/2011/04/16/2018504.html (主要参考)

2、http://blogs.msdn.com/b/jfoscoding/archive/2005/11/17/494012.aspx

3、https://msdn.microsoft.com/zh-cn/library/ms132679.aspx#Mtps_DropDownFilterTextBindingList

 

 

未完待续……