目录
一、前端界面
添加的datagridview应该如下设置:
1.只设置启用编辑
2.设置好每列的名称、控件类型、提示中文(页眉文本),选择【可见】等选项(建议TextBox选可见+只读,Checkbox选可见即可,Button选可见即可。选择只读后,不能被编辑或选中。)
3.如果不需要默认列,可以设置:RowHeadersVisibed = False
二、模块代码
【实体列表】
1.初始化列表代码
【备注】1.建立列表是模拟查数据库。2.“如果学生名为"小苏",则移除查看详情按钮”是一个题目要求,实际中不需要该功能可注释掉该代码。3.数据实体绑定于Tag上,Tag能识别哪行被点击或选中。
/// <summary>
/// 初始化填充数据
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Form1_Load(object sender, EventArgs e)
{
#region 建立列表
Student student1 = new Student { ID = 1, Name = "小苏", Remarks = "小苏的备注" };
Student student2 = new Student { ID = 2, Name = "小明", Remarks = "小明的备注" };
Student student3 = new Student { ID = 3, Name = "小花", Remarks = "小花的备注" };
var list = new List<Student>();
list.Add(student1);
list.Add(student2);
list.Add(student3);
#endregion
// 清空现有数据
dataGridView1.Rows.Clear();
//添加数据
foreach (var item in list)
{
int rowIndex = dataGridView1.Rows.Add();
dataGridView1.Rows[rowIndex].Cells["ID"].Value = item.ID;//字段
dataGridView1.Rows[rowIndex].Cells["StuName"].Value = item.Name;//字段
dataGridView1.Rows[rowIndex].Cells["Details"].Value = "查看详情";//按钮名称
dataGridView1.Rows[rowIndex].Tag = item;
// 如果学生名为"小苏",则移除查看详情按钮
if (item.Name == "小苏")
{
dataGridView1.Rows[rowIndex].Cells["Details"] = new DataGridViewTextBoxCell();//重新初始化
dataGridView1.Rows[rowIndex].Cells["Details"].ReadOnly = true; // 设置为只读
}
}
}
【扩展】也可以直接选择绑定而不写上述代码:(灵活性降低)
2.点击单元格按钮事件(datagridview双击进入)
作用:点击查看详情跳出备注信息
/// <summary>
/// 点击查看详情
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void dataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
if (e.ColumnIndex == dataGridView1.Columns["Details"].Index && e.RowIndex >= 0)//若点击了标签为【Details】列的按钮
{
// 获取当前行对应的实体对象【注意修改此处Student类】,此处能获取到Remarks字段(虽然没有显示在界面上,但也绑定到Tag了)
var item = dataGridView1.Rows[e.RowIndex].Tag as Student;
//将list实体中的Remarks展示出来(item包含的字段:ID,Name,Remarks)
MessageBox.Show(item.Remarks, "内容详情");
//【补充一:获取按钮上的文字】
}
}
dataGridView1_CellClick比dataGridView1_CellContentClick更加灵敏!
前者是点击单元格触发后者是点击单元格的文字才触发!
【补充一:获取按钮上的文字】
DataGridViewButtonCell buttonCell = dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex] as DataGridViewButtonCell;//获取点击的按钮
if (buttonCell != null && buttonCell.Value != null)
{
if (buttonCell.Value.ToString() == "借出")//按钮字样为借出
{
;// 执行借出操作
}
else if (buttonCell.Value.ToString() == "归还")//按钮字样为归还
{
;// 执行归还操作
}
}
3.一键选中(checkbox)双击进入
作用:点击一键选中按钮,选中列表所有的checkbox
/// <summary>
/// 一键选中
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
if(checkBox1.Checked)
{
foreach (DataGridViewRow row in dataGridView1.Rows)
{
DataGridViewCheckBoxCell checkBox = row.Cells["ManySelect"] as DataGridViewCheckBoxCell;//获取【ManySelect】的checkbox列
if (checkBox != null)
{
checkBox.Value = true;
}
}
}
}
4.查看选中(button)双击进入
作用:点击查看选中按钮,获得一个选中的列表
方法一:
/// <summary>
/// 点击查看选中
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button1_Click(object sender, EventArgs e)//点击触发
{
//获取【DataGridView1】的checkbox选中的实体
var selectlist = new List<Student>();//将【Student】类存放入列表
//遍历获取数据
foreach (DataGridViewRow row in dataGridView1.Rows)
{
DataGridViewCheckBoxCell checkBox = row.Cells["ManySelect"] as DataGridViewCheckBoxCell;
if (checkBox != null && checkBox.Value != null && (bool)checkBox.Value)
{
// 获取与选中CheckBox相关联的实体,假设实体类型为Student
var entity = row.Tag as Student; //【Student】实体
if (entity != null)
{
selectlist.Add(entity);
}
}
}
//后续可对selectlist操作
}
方法二:(获取单一内容)
private void button3_Click(object sender, EventArgs e)
{
// 创建一个列表来存储选中的内容
List<string> selectedContents = new List<string>();
// 遍历所有行
foreach (DataGridViewRow row in dataGridView1.Rows)
{
// 检查该行是否被选中
if (Convert.ToBoolean(row.Cells["Select"].Value) == true)
{
// 获取 "Content" 列的值,并添加到列表中
string contentValue = row.Cells["Content"].Value.ToString();
selectedContents.Add(contentValue);
}
}
// 显示选中的内容(可以根据需要进行其他处理)
MessageBox.Show(string.Join(", ", selectedContents));
}
5.点击按钮获取选中的一行实体
前提条件:需要默认列,可以设置 RowHeadersVisibed = True ,即获取点击单元行头的实体
private void button3_Click(object sender, EventArgs e)
{
if (dataGridView2.SelectedRows.Count == 1)//当选中了一行时
{
// 获取选中的那行,转化为【StuMessage】实体
var selectedEntity = dataGridView2.Rows[dataGridView2.SelectedRows[0].Index].Tag as StuMessage;
;
}
}
6.点击单元格获取实体
private void dataGridView2_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
var stumodel = dataGridView2.Rows[e.RowIndex].Tag as StuMessage;
}
//如果单纯获取点击的内容:
string clickedContent = dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex].Value.ToString();
7.点击单元行头获取实体
private void dataGridView2_RowHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)
{
var stumodel = dataGridView2.Rows[e.RowIndex].Tag as StuMessage;
}
8.获取数据列表
封装方法
/// <summary>
/// 获取指定datagridview的列表,并转化为T实体
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="dataGridView"></param>
/// <returns></returns>
private List<T> GetDataGridList<T>(DataGridView dataGridView) where T : class
{
List<T> list = new List<T>();
foreach (DataGridViewRow row in dataGridView.Rows)
{
var item = row.Tag as T;
list.Add(item);
}
return list;
}
调用方法:请将Notice替换为您的实体
var list = GetDataGridList<Notice>(dataGridView1);
9.列头居中对齐的方法
三、整体代码
【注意】此代码为Demo代码,并不符合编写规范,旨在帮助开发者找到灵感。
using System.Collections.Generic;
namespace WinFormsApp2
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
class Student
{
public int ID { get; set; }
public string Name { get; set; }
public string Remarks { get; set; }
}
/// <summary>
/// 初始化填充数据
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Form1_Load(object sender, EventArgs e)
{
#region 建立列表
Student student1 = new Student { ID = 1, Name = "小苏", Remarks = "小苏的备注" };
Student student2 = new Student { ID = 2, Name = "小明", Remarks = "小明的备注" };
Student student3 = new Student { ID = 3, Name = "小花", Remarks = "小花的备注" };
var list = new List<Student>();
list.Add(student1);
list.Add(student2);
list.Add(student3);
#endregion
// 清空现有数据
dataGridView1.Rows.Clear();
//添加数据
foreach (var item in list)
{
int rowIndex = dataGridView1.Rows.Add();
dataGridView1.Rows[rowIndex].Cells["ID"].Value = item.ID;//字段
dataGridView1.Rows[rowIndex].Cells["StuName"].Value = item.Name;//字段
dataGridView1.Rows[rowIndex].Cells["Details"].Value = "查看详情";//按钮名称
dataGridView1.Rows[rowIndex].Tag = item;
// 如果学生名为"小苏",则移除查看详情按钮
if (item.Name == "小苏")
{
dataGridView1.Rows[rowIndex].Cells["Details"] = new DataGridViewTextBoxCell();//重新初始化
dataGridView1.Rows[rowIndex].Cells["Details"].ReadOnly = true; // 设置为只读
}
}
}
/// <summary>
/// 点击查看选中
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button1_Click(object sender, EventArgs e)//点击触发
{
//获取【DataGridView1】的checkbox选中的实体
var selectlist = new List<Student>();//将【Student】类存放入列表
//遍历获取数据
foreach (DataGridViewRow row in dataGridView1.Rows)
{
DataGridViewCheckBoxCell checkBox = row.Cells["ManySelect"] as DataGridViewCheckBoxCell;
if (checkBox != null && checkBox.Value != null && (bool)checkBox.Value)
{
// 获取与选中CheckBox相关联的实体,假设实体类型为Student
var entity = row.Tag as Student; //【Student】实体
if (entity != null)
{
selectlist.Add(entity);
}
}
}
//后续可对selectlist操作
}
/// <summary>
/// 点击查看详情
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void dataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
if (e.ColumnIndex == dataGridView1.Columns["Details"].Index && e.RowIndex >= 0)//若点击了标签为【Details】列的按钮
{
// 获取当前行对应的实体对象【注意修改此处Student类】,此处能获取到Remarks字段(虽然没有显示在界面上,但也绑定到Tag了)
var item = dataGridView1.Rows[e.RowIndex].Tag as Student;
//将list实体中的Remarks展示出来(item包含的对象:ID,Name,Remarks)
MessageBox.Show(item.Remarks, "内容详情");
}
}
/// <summary>
/// 一键选中
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
if(checkBox1.Checked)
{
foreach (DataGridViewRow row in dataGridView1.Rows)
{
DataGridViewCheckBoxCell checkBox = row.Cells["ManySelect"] as DataGridViewCheckBoxCell;//获取【ManySelect】多选框列
if (checkBox != null)
{
checkBox.Value = true;
}
}
}
}
}
}
四、测试结果
1.通过debug发现选中有效(测试通过)
2.发现小苏对应按钮被删除(测试通过)
3.发现一键多选奏效(测试通过,图略)
4.发现查看详情有效(测试通过)
五、样式
修改某行的字体颜色:
if (item.Name == "小明")
{
dataGridView1.Rows[rowIndex].DefaultCellStyle.ForeColor = Color.Red;//文字颜色
dataGridView1.Rows[rowIndex].DefaultCellStyle.BackColor = Color.Yellow;//背景颜色
}
六、高度封装的数据填充方法
【封装方法】
/// <summary>
/// 获取DataGridView,需传入控件、数据列表、标题列表
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="dataGridView"></param>
/// <param name="list"></param>
/// <param name="headtext"></param>
/// <exception cref="Exception"></exception>
private void GetDataGridView<T>(DataGridView dataGridView,List<T> list,List<string> headtext) where T : class
{
//设置表头样式和属性
dataGridView.AllowUserToAddRows = false;//不允许添加、删除
dataGridView.AllowUserToDeleteRows = false;
dataGridView.ReadOnly = true;//设置可读
dataGridView.RowHeadersVisible = false;//隐藏最左边的空白栏
dataGridView.ColumnHeadersDefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;//表头居中对齐
dataGridView.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;//自适应宽度
//dataGridView.RowTemplate.Height = 80;//设置行高
//获取字段列表
var field = typeof(T).GetProperties();
//判断输入标题
if (headtext.Count != field.Length)
{
throw new Exception("表头的标题个数要和字段长度一致!");
}
// 设置表头样式
dataGridView.ColumnHeadersDefaultCellStyle = new DataGridViewCellStyle
{
Alignment = DataGridViewContentAlignment.MiddleCenter, // 中间对齐
BackColor = Color.LightGray, // 表头背景色
ForeColor = Color.Black, // 表头文字颜色
Font = new Font("微软雅黑", 12F, FontStyle.Bold), // 表头字体
};
//设置表格内容(按实体顺序依次设置名字)
dataGridView.Columns.Clear();
foreach (var item in headtext)
{
dataGridView.Columns.Add(new DataGridViewTextBoxColumn //增加文字列
{
DefaultCellStyle = new DataGridViewCellStyle { Alignment = DataGridViewContentAlignment.MiddleCenter },
HeaderText = item,//中文标题
MinimumWidth = 6,
Name = field[headtext.FindIndex(x=>x==item)].Name,//字段的名字 例如ID Name
ReadOnly = true,
SortMode = DataGridViewColumnSortMode.NotSortable,
Width = 110
});
}
//dataGridView.Columns.Add(new DataGridViewButtonColumn //增加按钮
//{
// DefaultCellStyle = new DataGridViewCellStyle { Alignment = DataGridViewContentAlignment.MiddleCenter },
// HeaderText = "操作",//中文标题
// MinimumWidth = 6,
// Name = "btn1",//字段的名字 例如ID Name
// ReadOnly = true,
// SortMode = DataGridViewColumnSortMode.NotSortable,
// Width = 110
//});
//dataGridView.Columns[0].Width = 200; // 手动调节宽度,注意需要注释掉前面的【AutoSizeColumnsMode 自适应宽度】
//dataGridView.Columns[1].Width = 200; // 手动调节宽度
// dataGridView.Columns[2].Width = 80; // 手动调节宽度
// dataGridView.Columns[3].Width = 80; // 手动调节宽度
// dataGridView.Columns[4].Width = 80; // 手动调节宽度
// dataGridView.Columns[5].Width = 80; // 手动调节宽度
// dataGridView.Columns[6].Width = 300; // 手动调节宽度
// 清空现有数据
dataGridView.Rows.Clear();
//添加数据
foreach (var item in list)
{
int rowIndex = dataGridView.Rows.Add();
foreach (var jtem in field)
{
dataGridView.Rows[rowIndex].Cells[jtem.Name.ToString()].Value = jtem.GetValue(item);//字段
dataGridView.Rows[rowIndex].DefaultCellStyle.ForeColor = Color.Black;
//if (jtem.Name.ToString().Equals("time"))//对特定的字段处理
//{
//dataGridView.Rows[rowIndex].Cells[jtem.Name.ToString()].Value = ((DateTime)(jtem.GetValue(item))).ToString("yyyy年MM月dd日");
//}
//dataGridView.Rows[rowIndex].Cells["btn1"].Value = "点击我";//按钮名称
//移除按钮(两步)
//if (false)
//{
// dataGridView.Rows[rowIndex].Cells["btn1"] = new DataGridViewTextBoxCell();//重新初始化
// dataGridView.Rows[rowIndex].Cells["btn1"].ReadOnly = true; // 设置为只读
//}
}
dataGridView.Rows[rowIndex].Tag = item;//绑定到Tag上方便后续调用
}
}
【调用示例】
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
string sql = @"SELECT types,time,objects FROM BuyTable ";
var entity = new Notice();
//从数据库中获取列表存入list
var list = entity.QueryBySQL(sql);
//设置标题
var headtext = new List<string>() { "购买类型", "购买时间","购买物品" };
//获取dataGridView
GetDataGridView(dataGridView10,list, headtext);
}
七、高度封装的分页方法
/// <summary>
/// 查询数据
/// </summary>
private void GetData()
{
var list = GetList().ToList();//假设这里是查表操作
list = GetPageList(list, label7, label6, button2, button3);//【步骤一】传入相关控件实现分页
List<string> title = new List<string> { { "开奖期数" }, { "开奖时间" }, { "平均值" }, { "最大值" }, { "最小值" }, { "跨度值" }, { "开奖号码" } };
GetDataGridView(dataGridView1, list, title);//渲染到DataGridView中
}
private int currentIndex = 1; // 当前页索引
private int totalPages = 0; // 总页数
private List<T> GetPageList<T>(List<T> list, Label total, Label Pagedetails, Button lastpage, Button nextpage, int pagesize = 7)
{
lastpage.Enabled = false;
nextpage.Enabled = false;
var count = list.Count;
totalPages = (count + pagesize - 1) / pagesize; // 计算总页数
if (Pagedetails.Text.Equals("第1页/共x页")) // 【 步骤二 winform默认设置好Pagedetails="第1页/共x页"】,此处代表第一次进入
{
currentIndex = 1; // 初始化当前页
}
else
{
// 创建正则表达式对象
Regex regex = new Regex(@"第(\d+)页/共(\d+)页");
// 尝试匹配字符串
Match match = regex.Match(Pagedetails.Text);
currentIndex = Convert.ToInt32(match.Groups[1].Value);
}
// 更新页数显示
Pagedetails.Text = $"第{currentIndex}页/共{totalPages}页";
total.Text = $"共{count}条数据";
// 更新按钮状态
lastpage.Enabled = currentIndex > 1; // 如果不是第一页,启用上一页按钮
nextpage.Enabled = currentIndex < totalPages; // 如果不是最后一页,启用下一页按钮
return list.Skip((currentIndex - 1) * pagesize).Take(pagesize).ToList(); // 获取当前页的数据
}
//上一页(双击按钮订阅此事件)
private void button2_Click(object sender, EventArgs e)
{
if (currentIndex > 1) // 检查是否可以向前翻页
{
currentIndex--; // 当前页减一
label6.Text = $"第{currentIndex}页/共{totalPages}页";//【步骤三】更新 Pagedetails 标签
GetData();
}
}
//下一页(双击按钮订阅此事件)
private void button3_Click(object sender, EventArgs e)
{
if (currentIndex < totalPages) // 检查是否可以向后翻页
{
currentIndex++; // 当前页加一
label6.Text = $"第{currentIndex}页/共{totalPages}页";//【步骤三】更新 Pagedetails 标签
GetData();
}
}
八、高度封装的单实体修改方法
数据展示封装:
/// <summary>
/// 实体,datagridview,渲染字典=[字段:标题内容]
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="entity"></param>
/// <param name="dataGridView1"></param>
/// <param name="dict"></param>
private void GetEntityUpdateList<T>(T entity, DataGridView dataGridView1, Dictionary<string, string> dict)
{
// 清空现有的数据
dataGridView1.Rows.Clear();
dataGridView1.Columns.Clear();
var keys = dict.Keys.ToList();
var values = dict.Values.ToList();
var feildlist = typeof(T).GetProperties().Where(x => keys.Contains(x.Name)).ToList();
// 添加列
dataGridView1.Columns.Add("Feild", "字段");
dataGridView1.Columns.Add("Content", "内容");
// 添加行
int index = 0;
Dictionary<string, string> tagdict = new Dictionary<string, string>();
Type type = typeof(T);
object instance = Activator.CreateInstance(type);
foreach (var item in keys)
{
var feild = feildlist.FirstOrDefault(x => x.Name == item);
dataGridView1.Rows.Add(values[index], feild?.GetValue(entity)?.ToString());
type.GetProperty(keys[index])?.SetValue(instance, feild?.GetValue(entity));
index++;
}
dataGridView1.Tag = instance;
// 设置 DataGridView 为可编辑模式
dataGridView1.EditMode = DataGridViewEditMode.EditOnEnter;
// 禁止新增行
dataGridView1.AllowUserToAddRows = false;
// 禁止删除行
dataGridView1.AllowUserToDeleteRows = false;
// 自动调整列宽
dataGridView1.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells;
}
使用:
(1)渲染datagridview(数据展示)
var dict = new Dictionary<string, string>() { { "TitleHead", "标题头" }, { "TitleEnd", "标题" }, { "Href", "文章链接" }, { "Rank", "排序序号" } };
GetEntityUpdateList(article,dataGridView1, dict);//article是一个实体,有TitleHead,TitleEnd等字段,且有内容
(2)读取datagridview
var dict = new Dictionary<string, string>() { { "TitleHead", "标题头" }, { "TitleEnd", "标题" }, { "Href", "文章链接" }, { "Rank", "排序序号" } };
var entity = GetInputEntityByDataGridView<Article>(dataGridView1, dict);//获取输入的实体
/// <summary>
/// T=要转化的实体,dataGridView控件,之前传进来的渲染字典
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="dataGridView1"></param>
/// <param name="dict"></param>
/// <returns></returns>
private T GetInputEntityByDataGridView<T>(DataGridView dataGridView1, Dictionary<string, string> dict) where T:class
{
Dictionary<string, object> new_dict = new Dictionary<string, object>();//存放[字段:输入内容]的字典
//填充new_dict(新字典)
foreach (DataGridViewRow row in dataGridView1.Rows)
{
var feild_chinese = row.Cells[0].Value;
var feild_input = dict.Where(x=>x.Value.Equals(feild_chinese)).FirstOrDefault().Key;
var value = row.Cells[1].Value;
new_dict.Add(feild_input, value);
}
//新建一个T,反射+新字典 依次给各个字段赋值
var type = typeof(T);
var instance = Activator.CreateInstance(type);
foreach (var item in dict.Keys.ToList())
{
var cstype = type.GetProperty(item)?.PropertyType;
type.GetProperty(item).SetValue(instance, Convert.ChangeType(new_dict[item], cstype));
}
return instance as T;//强制转化为T
}
九、小结
1.增: 建议用textbox、combobox等工具增,而不是直接datagridview新增,一来代码编写麻烦,二来输入工具不能多样化(比如说时间控件、多选框控件)。
2.删:建议每条数据加一个删除按钮,方便操作
3.改:建议选中某条数据然后将数据信息转移到textbox上,label显示“您已选中xxx数据”,然后点击button去修改相应信息(选中数据和点击按钮都能获取到对应实体)
4.查:同第2条
5.如果单纯用datagridview作增删查改,虽然能实现,但是代码复杂难以维护,而且输入条件单一,容易操作失误,不建议这么做。