目录
一、概述
datagridview 控件提供用于显示数据的可自定义表。 类 datagridview 允许通过使用属性(如 defaultcellstyle、 columnheadersdefaultcellstyle、 cellborderstyle和)自定义单元格、行、列和 gridcolor边框。 有关详细信息,请参阅微软 datagridview 控件中的基本格式设置和样式设置。
可以使用 datagridview 控件在基础数据源中或不使用基础数据源显示数据。 如果不指定数据源,可以创建包含数据的列和行,并将其直接添加到 datagridview using rows and columns properties。 还可以使用 rows 集合访问 datagridviewrow 对象和 datagridviewrow.cells 属性来直接读取或写入单元格值。 索引 item[] 器还提供对单元格的直接访问。
作为手动填充控件的替代方法,可以设置 datasource 属性 datamember 以绑定到 datagridview 数据源并自动填充数据。 有关详细信息,请参阅微软 datagridview 控件中显示数据。
处理大量数据时,可以将属性设置为 virtualmodetrue 显示可用数据的子集。 虚拟模式需要实现在其中填充控件的数据缓存 datagridview 。
二、datatable 的用法
1.创建表和列
使用 new datatable() 来创建一个 datatable 表,创建表可以加上表名,也可以不加
datatable dt = new datatable();
datatable dt1 = new datatable("datas");
datatable 表 和我们常用的 excel 是一样的,如下图,列就是 a,b,c,d..... 行就是 1,2,3,4.....
只是 datatable 在创建后,是空的,既没有自动创建的列,也没有自动创建的行,这些都是要我们通过代码来实现的。
创建列通过 实例化 datacolumn 类,并 datacolumn 类 添加到 datatable.columns 中来实现添加一列。
datatable dt = new datatable("datas");
datacolumn dc1 = new datacolumn("商品编号");
datacolumn dc2 = new datacolumn("商品名称");
datacolumn dc3 = new datacolumn("商品重量");
datacolumn dc4 = new datacolumn("商品价格");
datacolumn dc5 = new datacolumn("购买数量");
dt.columns.add(dc1);
dt.columns.add(dc2);
dt.columns.add(dc3);
dt.columns.add(dc4);
dt.columns.add(dc5);
在添加列的同时,可以给列添加一些设置
datatable dt = new datatable("datas");
datacolumn dc1 = new datacolumn("商品编号");
datacolumn dc2 = new datacolumn("商品名称");
datacolumn dc3 = new datacolumn("商品重量");
datacolumn dc4 = new datacolumn("商品价格");
datacolumn dc5 = new datacolumn("购买数量");
dc1.autoincrement = true;//自动增加
dc1.autoincrementseed = 1;//起始为1
dc1.autoincrementstep = 1;//步长为1
dc1.allowdbnull = false;//是否允许空值
dt.columns.add(dc1);
dt.columns.add(dc2);
dt.columns.add(dc3);
dt.columns.add(dc4);
dt.columns.add(dc5);
只是这么做比较繁琐,因此不推荐。
推荐用下面方式去添加列
datatable dt = new datatable("datas");
datacolumn dc = new datacolumn();
dc.autoincrement = true;//自动增加
dc.autoincrementseed = 1;//起始为1
dc.autoincrementstep = 1;//步长为1
dc.allowdbnull = false;//是否允许空值
//添加列
dt.columns.add("姓名", typeof(string));
dt.columns.add("年龄", typeof(int));
dt.columns.add("身高", typeof(float));
dt.columns.add("体重", typeof(float));
datagridview1.datasource = dt;
效果:
可以看到,datagridview 在绑定 datatable 后会默认出现一个空白的行,你可能会有疑问,是不是这句代码导致的,如下:
dc.autoincrement = true;//自动增加
dc.autoincrementseed = 1;//起始为1
dc.autoincrementstep = 1;//步长为1
dc.allowdbnull = false;//是否允许空值
开始测试:
private void form1_load(object sender, eventargs e)
{
datatable dt = new datatable("datas");
//添加列
dt.columns.add("姓名", typeof(string));
dt.columns.add("年龄", typeof(int));
dt.columns.add("身高", typeof(float));
dt.columns.add("体重", typeof(float));
dt.rows.add("2号", 12, 220, 300);
datagridview1.datasource = dt;
}
效果:
所以实验证明,用 datatable 绑定数据,是会默认添加一个空白行的,如果不想表格中有空白行,可以使用 list<类> 这种方式去绑定,参考下面代码:
using system;
using system.collections.generic;
using system.windows.forms;
namespace datetable的使用
{
public partial class form1 : form
{
public form1()
{
initializecomponent();
}
list<dog> doglist = new list<dog>();
private void form1_load(object sender, eventargs e)
{
doglist.add(new dog() { name = "dog1", age = 1, color = "bule", gender = 1 });
doglist.add(new dog() { name = "dog2", age = 2, color = "green", gender = 0 });
doglist.add(new dog() { name = "dog3", age = 3, color = "red", gender = 1 });
datagridview1.datasource = doglist;
}
}
}
public class dog
{
public string name { get; set; }
public int age { get; set; }
public string color { get; set; }
public int gender { get; set; }
}
效果:
2.添加行
通过添加 datarow 类来添加行,如下:
datatable dt = new datatable("datas");
datacolumn dc = new datacolumn();
dc.autoincrement = true;//自动增加
dc.autoincrementseed = 1;//起始为1
dc.autoincrementstep = 1;//步长为1
dc.allowdbnull = false;//是否允许空值
//添加列
dt.columns.add("姓名", typeof(string));
dt.columns.add("年龄", typeof(int));
dt.columns.add("身高", typeof(float));
dt.columns.add("体重", typeof(float));
//添加行
datarow newrow = dt.newrow();
newrow["姓名"] = "1号";
newrow["年龄"] = "17";
newrow["身高"] = "155";
newrow["体重"] = "220";
dt.rows.add(newrow);
datagridview1.datasource = dt;
运行:
这么写虽然可以实现添加行,但用起来很繁琐, 当然也有简洁的写法
datatable dt = new datatable("datas");
datacolumn dc = new datacolumn();
dc.autoincrement = true;//自动增加
dc.autoincrementseed = 1;//起始为1
dc.autoincrementstep = 1;//步长为1
dc.allowdbnull = false;//是否允许空值
//添加列
dt.columns.add("姓名", typeof(string));
dt.columns.add("年龄", typeof(int));
dt.columns.add("身高", typeof(float));
dt.columns.add("体重", typeof(float));
//添加行
dt.rows.add("2号", 12, 220, 300);
datagridview1.datasource = dt;
运行:
这里添加行的写法,也可以换成下面的写法,效果是一样的
dt.rows.add(new object[] { "2号", 12, 220, 300 });
3.取值和赋值
在获取和设置这些数据之前,先添加一些数据
datatable dt = new datatable("datas");
datacolumn dc = new datacolumn();
dc.autoincrement = true;//自动增加
dc.autoincrementseed = 1;//起始为1
dc.autoincrementstep = 1;//步长为1
dc.allowdbnull = false;//是否允许空值
//添加列
dc = dt.columns.add("姓名", typeof(string));
dc = dt.columns.add("年龄", typeof(int));
dc = dt.columns.add("身高", typeof(float));
dc = dt.columns.add("体重", typeof(float));
//添加行
dt.rows.add("1号", 17, 155, 220);
dt.rows.add("2号", 12, 220, 300);
dt.rows.add("3号", 45, 170, 132);
datagridview1.datasource = dt;
运行:
1)赋值
通过索引号赋值
datatable dt = new datatable("datas");
datacolumn dc = new datacolumn();
dc.autoincrement = true;//自动增加
dc.autoincrementseed = 1;//起始为1
dc.autoincrementstep = 1;//步长为1
dc.allowdbnull = false;//是否允许空值
//添加列
dt.columns.add("姓名", typeof(string));
dt.columns.add("年龄", typeof(int));
dt.columns.add("身高", typeof(float));
dt.columns.add("体重", typeof(float));
//添加行
dt.rows.add("1号", 17, 155, 220);
dt.rows.add("2号", 12, 220, 300);
dt.rows.add("3号", 45, 170, 132);
datagridview1.datasource = dt;
//1)赋值
dt.rows[0][0] = "张三";
运行:
注意这里,dt.rows[0] 是指第几行, 第二个 0 才是指第几列,如果将 dt.rows[0] 改为 dt.rows[1] 效果如下:
通过列名赋值
dt.rows[0] 里面的行号的定义只能用数字去定义,但是后面的列可以用列名去获取
dt.rows[0]["姓名"] = "张三";
效果:
2)取值
取值和获取值用法和变量的用法差不多,只是返回来的类型是 object 类型,做一下转换就好了
datatable dt = new datatable("datas");
datacolumn dc = new datacolumn();
dc.autoincrement = true;//自动增加
dc.autoincrementseed = 1;//起始为1
dc.autoincrementstep = 1;//步长为1
dc.allowdbnull = false;//是否允许空值
//添加列
dt.columns.add("姓名", typeof(string));
dt.columns.add("年龄", typeof(int));
dt.columns.add("身高", typeof(float));
dt.columns.add("体重", typeof(float));
//添加行
dt.rows.add("1号", 17, 155, 220);
dt.rows.add("2号", 12, 220, 300);
dt.rows.add("3号", 45, 170, 132);
datagridview1.datasource = dt;
//2)取值
object name = dt.rows[0]["姓名"];
object age = dt.rows[0][1];
console.writeline(name);
console.writeline(age);
运行:
4.删除行
删除行有多中写法,可以使用 dt.rows.remove(dt.rows[0]);
datatable dt = new datatable("datas");
datacolumn dc = new datacolumn();
dc.autoincrement = true;//自动增加
dc.autoincrementseed = 1;//起始为1
dc.autoincrementstep = 1;//步长为1
dc.allowdbnull = false;//是否允许空值
//添加列
dt.columns.add("姓名", typeof(string));
dt.columns.add("年龄", typeof(int));
dt.columns.add("身高", typeof(float));
dt.columns.add("体重", typeof(float));
//添加行
dt.rows.add("1号", 17, 155, 220);
dt.rows.add("2号", 12, 220, 300);
dt.rows.add("3号", 45, 170, 132);
//删除行
dt.rows.remove(dt.rows[0]);
datagridview1.datasource = dt;
也可以使用 dt.rows.removeat(0) 效果是一样的
dt.rows.removeat(0);
运行:
5.遍历 datatable
列名,和表格的内容遍历是分开的
datatable dt = new datatable("datas");
datacolumn dc = new datacolumn();
dc.autoincrement = true;//自动增加
dc.autoincrementseed = 1;//起始为1
dc.autoincrementstep = 1;//步长为1
dc.allowdbnull = false;//是否允许空值
//添加列
dt.columns.add("姓名", typeof(string));
dt.columns.add("年龄", typeof(int));
dt.columns.add("身高", typeof(float));
dt.columns.add("体重", typeof(float));
//添加行
dt.rows.add("1号", 17, 155, 220);
dt.rows.add("2号", 12, 220, 300);
dt.rows.add("3号", 45, 170, 132);
//打印所有列名
string columnname = string.empty;
for (int i = 0; i < dt.columns.count; i++)
{
columnname += string.format("{0}({1}) | ", dt.columns[i].columnname, i);
}
console.writeline(columnname);
//打印每一行的数据
foreach (datarow row in dt.rows)
{
string columnstr = string.empty;
foreach (datacolumn column in dt.columns)
{
columnstr += row[column] + " | ";
}
console.writeline(columnstr);
}
datagridview1.datasource = dt;
打印每一行的数据,不想用 foreach ,使用 for 循环也是可以的。
for (int i = 0; i < dt.rows.count; i++)
{
string columnstr = string.empty;
for (int j = 0; j < dt.columns.count; j++)
{
columnstr += dt.rows[i][j] + " | ";
}
console.writeline(columnstr);
}
运行效果,winform 和 控制台:
6.判断 datatable 列中是否存在某个值
使用 datatable.select 来查询表格中的数据
datatable dt = new datatable("datas");
datacolumn dc = new datacolumn();
dc.autoincrement = true;//自动增加
dc.autoincrementseed = 1;//起始为1
dc.autoincrementstep = 1;//步长为1
dc.allowdbnull = false;//是否允许空值
//添加列
dt.columns.add("姓名", typeof(string));
dt.columns.add("年龄", typeof(int));
dt.columns.add("身高", typeof(float));
dt.columns.add("体重", typeof(float));
//添加行
dt.rows.add("1号", 17, 155, 220);
dt.rows.add("2号", 12, 220, 300);
dt.rows.add("3号", 45, 170, 132);
//判断 datatable 列中是否存在某个值
datarow[] seleres = dt.select(string.format("{0}='{1}'", "姓名", "2号"));
console.writeline("寻找结果:{0}", seleres.length > 0);
运行:
将代码作一下更改
datarow[] seleres = dt.select(string.format("{0}='{1}'", "姓名", "2"));
console.writeline("寻找结果:{0}", seleres.length > 0);
运行:
7.设置主键
表的主键必须是唯一的,才能标识表中的记录。 还可以使用由两列或更多列组成的主键的表。 当单个列不能包含足够的唯一值时,将发生这种情况。 例如,两列主键可能包含“ordernumber”和“productid”列。 由于主键可以由多个列组成,因此 primarykey 该属性由对象数组 datacolumn 组成。
datatable dt = new datatable("datas");
datacolumn dc = new datacolumn();
dc.autoincrement = true;//自动增加
dc.autoincrementseed = 1;//起始为1
dc.autoincrementstep = 1;//步长为1
dc.allowdbnull = false;//是否允许空值
//添加列
dt.columns.add("姓名", typeof(string));
dt.columns.add("年龄", typeof(int));
dt.columns.add("身高", typeof(float));
dt.columns.add("体重", typeof(float));
//添加行
dt.rows.add("1号", 17, 155, 220);
dt.rows.add("2号", 12, 220, 300);
dt.rows.add("3号", 45, 170, 132);
//设置主键
datacolumn[] primarykeycolumns = new datacolumn[2];
//添加主键,必须是已经在datatable里有的列名
primarykeycolumns[0] = dt.columns["姓名"];
primarykeycolumns[1] = dt.columns["年龄"];
//配置主键
dt.primarykey = primarykeycolumns;
8.获取 datarow 所在的行号
一个主键的获取方法
datatable dt = new datatable("datas");
datacolumn dc = new datacolumn();
dc.autoincrement = true;//自动增加
dc.autoincrementseed = 1;//起始为1
dc.autoincrementstep = 1;//步长为1
dc.allowdbnull = false;//是否允许空值
//添加列
dt.columns.add("姓名", typeof(string));
dt.columns.add("年龄", typeof(int));
dt.columns.add("身高", typeof(float));
dt.columns.add("体重", typeof(float));
//添加行
dt.rows.add("1号", 17, 155, 220);
dt.rows.add("2号", 12, 220, 300);
dt.rows.add("3号", 45, 170, 132);
//设置主键
datacolumn[] primarykeycolumns = new datacolumn[1];
//添加主键,必须是已经在datatable里有的列名
primarykeycolumns[0] = dt.columns["姓名"];
//配置主键
dt.primarykey = primarykeycolumns;
//获取行号
datarow datarow = dt.rows.find("3号");
int index = datarow.table.rows.indexof(datarow);
console.writeline("行号:{0}", index);
运行:
如果是两个主键,要这么写
//设置主键
datacolumn[] primarykeycolumns = new datacolumn[2];
//添加主键,必须是已经在datatable里有的列名
primarykeycolumns[0] = dt.columns["姓名"];
primarykeycolumns[1] = dt.columns["年龄"];
//配置主键
dt.primarykey = primarykeycolumns;
//获取行号
datarow datarow = dt.rows.find(new object[] { "3号", 45 });
int index = datarow.table.rows.indexof(datarow);
console.writeline("行号:{0}", index);
运行结果依然是:2
注意,这里的姓名和年龄必须匹配,如果写错了,比如,将 45 改为 46,代码也会报错
9.datatable 转换为 list<t>
代码
using system;
using system.collections.generic;
using system.componentmodel;
using system.data;
using system.reflection;
/// <summary>
/// 将datatable数据源转换成实体类
/// </summary>
public static class converttool
{
/// <summary>
/// datatable转换成实体类
/// </summary>
/// <typeparam name="t"></typeparam>
/// <param name="dt"></param>
/// <returns></returns>
public static list<t> tabletoentity<t>(datatable dt) where t : class, new()
{
list<t> list = new list<t>();
try
{
foreach (datarow row in dt.rows)
{
t entity = new t();
propertyinfo[] parray = entity.gettype().getproperties();
foreach (propertyinfo p in parray)
{
if (dt.columns.contains(p.name))
{
if (!p.canwrite) continue;
var value = row[p.name];
if (value != dbnull.value)
{
type targettype = p.propertytype;
type converttype = targettype;
if (targettype.isgenerictype && targettype.getgenerictypedefinition().equals(typeof(nullable<>)))
{
//可空类型
nullableconverter nullableconverter = new nullableconverter(targettype);
converttype = nullableconverter.underlyingtype;
}
if (!string.isnullorempty(converttype.fullname) && !string.isnullorempty(value.tostring()))
{
value = convert.changetype(value, converttype);
}
switch (converttype.fullname)
{
case "system.decimal":
p.setvalue(entity, convert.todecimal(value), null);
break;
case "system.string":
p.setvalue(entity, convert.tostring(value), null);
break;
case "system.int32":
p.setvalue(entity, convert.toint32(value), null);
break;
case "system.int64":
p.setvalue(entity, convert.toint64(value), null);
break;
case "system.int16":
p.setvalue(entity, convert.toint16(value), null);
break;
case "system.double":
p.setvalue(entity, convert.todouble(value), null);
break;
case "system.datetime":
p.setvalue(entity, convert.todatetime(value), null);
break;
default:
p.setvalue(entity, value, null);
break;
}
}
}
}
list.add(entity);
}
}
catch (exception ex)
{
console.writeline(ex.message);
}
if(list.count > 0)
return list;
else
return null;
}
}
10.将 list<t> 转 datatable
这里分不转换标题和转换标题,先看看 list<t> 直接转换为 datatable,如果 t 的实体类字段为英文,那么 datatable 的列名也将全是英文显示
/// <summary>
/// 将 list 转换成 datatable (不转换标题)
/// </summary>
/// <typeparam name="t"></typeparam>
/// <param name="data"></param>
/// <returns></returns>
public static datatable todatatable<t>(list<t> data)
{
if(data == null || data.count == 0) return null;
propertydescriptorcollection properties = typedescriptor.getproperties(typeof(t));
datatable dt = new datatable();
for (int i = 0; i < properties.count; i++)
{
propertydescriptor property = properties[i];
dt.columns.add(property.name, property.propertytype);
}
object[] values = new object[properties.count];
foreach (t item in data)
{
for (int i = 0; i < values.length; i++)
{
values[i] = properties[i].getvalue(item);
}
dt.rows.add(values);
}
return dt;
}
将英文的字段转换为中文列名,这需要做一些转换,我建立了一个自定义特性 userattribute,如下
using system;
//自定义特性类
[attributeusage(attributetargets.property)]
internal class userattribute : attribute
{
public string chinesename { get; set; }
public userattribute(string chinesename)
{
chinesename = chinesename;
}
}
将要把转换为中文的字段,在特性中添加中文名
internal class userinfo
{
[user("用户名")]
public string username { get; set; }
[user("地址")]
public string address { get; set; }
[user("年龄")]
public int age { get; set; }
[user("重量")]
public int weight { get; set; }
}
转换方法:
/// <summary>
/// 将 list 转换为 datatable (转换标题)
/// </summary>
/// <param name="list">数据实体</param>
/// <returns></returns>
public static datatable listtodatatable<t>(list<t> list)
{
if (list == null || list.count == 0) return null;
//创建一个名为"tablename"的空表
datatable dt = new datatable("tablename");
//key 中文名, value 英文名
dictionary<string, string> dic = new dictionary<string, string>();
//创建传入对象名称的列
foreach (var item in list.firstordefault().gettype().getproperties())
{
object[] attrs = item.getcustomattributes(true);
if (attrs.length > 0 && attrs[0] is userattribute)
{
userattribute user = attrs[0] as userattribute;
dt.columns.add(user.chinesename);
dic.add(user.chinesename, item.name);
}
else
dt.columns.add(item.name);
}
//循环存储
foreach (var item in list)
{
//新加行
datarow value = dt.newrow();
//根据datatable中的值,进行对应的赋值
foreach (datacolumn dtcolumn in dt.columns)
{
int i = dt.columns.indexof(dtcolumn);
string cloumnname = dtcolumn.columnname;
if (dic.containskey(cloumnname))
{
cloumnname = dic[cloumnname];
}
//基元元素,直接复制,对象类型等,进行序列化
if (value.gettype().isprimitive)
{
value[i] = item.gettype().getproperty(cloumnname).getvalue(item);
}
else
{
value[i] = jsonconvert.serializeobject(item.gettype().getproperty(cloumnname).getvalue(item));
}
}
dt.rows.add(value);
}
return dt;
}
三、datagridview 的用法
1.绑定数据
绑定数据在上面的案例中用了多次,就一句,将控件的 datasource 属性绑定 datatable 即可。
datagridview1.datasource = dt;
这里能绑定的不只是 datatable 类型,dataset,dataview,arraylist,dictionary,list 都是可以的,这里更推荐使用 list<类> 去绑定数据,可以参考我的另一个帖子,里面会介绍 datagridview 绑定数据可能出现的问题
c# winform datagridview 数据刷新问题_c# datagridview 更新数据-csdn博客
2.获取绑定的数据源
可以将绑定的数据转换为绑定之前的类型,如下
datatable datatable = (datagridview1.datasource as datatable);
目前测试这样获取的 datatable 是没有问题的,注意:如果 datasource 绑定的是 list,那么就 list 进行转换,用什么类型的绑定的,就用什么类型转换,否则转换的结果是 null。
3.获取 / 设置 选中单元格的数据
先给 datagridview1 控件添加一个事件 cellclick
代码
private void datagridview1_cellclick(object sender, datagridviewcelleventargs e)
{
//当前的行数
int selectrowindex = datagridview1.currentrow.index;
//当前的列数
int selectcolumnindex = datagridview1.currentcell.columnindex;
datatable datatable = (datagridview1.datasource as datatable);
int rowlen = datatable.rows.count;
if (selectrowindex >= rowlen) return;
console.writeline("当前选中行:{0},选中的列:{1}", selectrowindex, selectcolumnindex);
}
运行后,点击最后一行,最后一列
控制台输出
由于绑定数据后,会自动添加一行,这里点击空白行,就会超出 datatable 的行数,不加判断的话就会报错,这里需要注意下。
有了行和列的索引,获取值就比较简单了,直接获取 datatable 的值就好了
object obj = datatable.rows[selectrowindex][selectcolumnindex];
console.writeline("value:{0}", obj);
设置它的值也是通用的道理。
4.设置单元格的宽高
宽高的自适应
//根据数据内容自动调整列宽
datagridview1.autosizecolumnsmode = datagridviewautosizecolumnsmode.allcellsexceptheader;
//根据数据内容自动调整行高
datagridview1.autosizerowsmode = datagridviewautosizerowsmode.allcellsexceptheaders;
根据第二章的例子,效果
系统自动去掉了空白的部分,这看起来特别拥挤,还不如手动设置宽度。
手动设置列宽的写法,这里的0,1,2,3 是列的索引
datagridview1.columns[0].width = 100;
datagridview1.columns[1].width = 100;
datagridview1.columns[2].width = 200;
datagridview1.columns[3].width = 150;
结束
如果这个帖子对你有所帮助,欢迎 关注 + 点赞 + 留言,谢谢!
end
发表评论