目录
一、概述
二、DataTable 的用法
1.创建表和列
2.添加行
3.取值和赋值
4.删除行
5.遍历 DataTable
6.判断 DataTable 列中是否存在某个值
7.设置主键
8.获取 DataRow 所在的行号
9.DataTable 转换为 List
10.将 List 转 DataTable
三、DataGridView 的用法
1.绑定数据
2.获取绑定的数据源
3.获取 / 设置 选中单元格的数据
4.设置单元格的宽高
结束
一、概述
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;
在 Winform 中的运行效果
此时表格的行还没有数据,下面看看怎么添加行数据吧
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 都是可以的,这里就不一一展示了。
2.获取绑定的数据源
可以将绑定的数据转换为绑定之前的类型,如下
DataTable dataTable = (dataGridView1.DataSource as DataTable);
目前测试 DataTable 类型是没有问题的
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