写在前面
在日常使用DataGridView时,大多数情况下用起来都特别简单,但偶尔遇到一些坑时也会折磨人很久。现在编写此篇博客, 以记录在平时工作时遇到的一些坑。
场景1:DataGridView的CellFormatting事件
因为业务需要,我的DataGridView绑定的数据源中,有个列不在DataSource绑定的对象里,这时就需要增加自定义列,同时也需要自己编写展示自定义列的数据的处理,于是我就对这个列增加了CellFormatting事件。最初的时候,我编写的代码如下,结果发现我原本DataSource绑定的对象中的任意一个列,一旦有null值,就会报DataFormatted错误,我百思不得其解,最后才发现,我把这个e.FormattingApplied写在了CellFormatting事件方法的最外面,如下代码所示:
// 错误示例
private void DGV_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
if (e.ColumnIndex == dgv.Columns["attr1"].Index)
{
// 业务逻辑
if(xxx){
e.Value = xxx.ToString();
}
else{
e.Value = string.Empty;
}
}
e.FormattingApplied = true; // 致命错误
}
正确的应该是这么写:
// 改正之后
private void DGV_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
if (e.ColumnIndex == dgv.Columns["attr1"].Index)
{
// 业务逻辑
if(xxx){
e.Value = xxx.ToString();
}
else{
e.Value = string.Empty;
}
e.FormattingApplied = true; // 此行代码如果放至最外层,会对DataGridView的每一个列都进行格式化
}
}
原因:把e.FormattingApplied=true写在最外层,就会导致数据源所有的列都会生效,这时候一旦你数据源中的某个列有null值,就会报异常。而放在if内部,则只会对这单个列生效。(还是不能过于省代码)
场景2:DataGridView的数据源中含有嵌套对象如何显示列
这个场景简单介绍一下:就是我的DataGridView的DataSource绑定到了一个List<MyParentObj>,然后,MyParentObj内部,还定义了一个子对象MySubObj,现在,我需要将MySubObj的字段展示到DataGridView,而默认绑定的情况下,DataGridView只能识别到MyParentObj中个各种基本类型的列,比如MyParentObj中有个string类型的steelName,那么当你通过myDataGridView.DataSource = myParentObjs的方式进行绑定后,可以直接通过myDataGridView.Columns["steelName"].XXX的方式,直接对该属性进行展示列的相关设置。但是,却无法通过这种方式来操作MySubObj的任一属性。后面我在网上找了几种办法,然后发现了一种比较简单但是不优雅的方式,那就是在MyParentObj中,再定义几个字段,这些字段就是用来访问MySubObj中的各个元素。举个例子,我们需要在DataGridView中展示MySubObj中的furName属性,在MyParentObj中定义一个属性mySubObj,该属性的get方法即返回MySubObj的furName,默认的类定义代码如下:
public class MyParentObj {
public string steelName
{
get;
set;
}
public MySubObj mySubObj;
{
get;
set;
}
}
public class MySubObj
{
public string furName
{
get;
set;
}
}
增加访问子类furName之后的类定义如下:
public class MyParentObj {
public string steelName
{
get;
set;
}
public MySubObj mySubObj;
{
get;
set;
}
// 新增的mySubObjFurName属性用于访问子类的成员
public string mySubObjFurName
{
get
{
if (mySubObj != null && mySubObj.furName != null)
{
return mySubObj.furName;
}
return "";
}
set
{
mySubObj.furName = value;
}
}
}
public class MySubObj
{
public string furName
{
get;
set;
}
}
对类增加对应的属性后,就可以直接使用myDataGridView.Columns["mySubObjFurName"].XXX的方式,来对furName属性进行访问了。(当然了,不到万不得已,尽量不要使用这种方式)
场景3:基于场景1对列进行了CellFormatting事件之后,通过dataGridView.Rows[i].Cells[j].Value的方式来获取该单元格的值,结果为null
在通过场景1的方式,将自定义的列展示出来之后,我后面需要再其他地方来获取这些自定义列的值,最初是通过调用dataGridView.Rows[i].Cells[j].Value的方式来获取,在调试时我发现结果均为null(注意是结果为null,不是默认值),通过查阅相关资料均为发现问题原因,后面通过询问ChatGPT,给我一个可能的答案,居然还真是的!然后我就把场景1的CellFormatting事件代码做了如下修改:
// 改正之后
private void DGV_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
if (e.ColumnIndex == dgv.Columns["attr1"].Index)
{
// 业务逻辑
if(xxx){
e.Value = xxx.ToString();
row.Cells[e.ColumnIndex].Value = e.Value; // 增加该行
}
else{
e.Value = string.Empty;
}
e.FormattingApplied = true; // 此行代码如果放至最外层,会对DataGridView的每一个列都进行格式化
}
}
相较于场景1,只增加了一句:row.Cells[e.ColumnIndex].Value = e.Value; 这一行表示是对该单元格赋值,而e.Value = xxx.ToString(); 仅仅只是展示数据,并不会对该行赋值!
其他场景等后续更新