1.csv和表格的转换
DataTable dt = new DataTable();
dataGridView1.DataSource = dt;
1 datagridview 控件的常用属性
dataGridView1.Columns 表格控件的所有列
dataGridView1.Columns .Count 表格控件的所有列的个数
dataGridView1.Rows 表格控件的所有行
dataGridView1[0,0] 根据几行几列取出表格数据
dataGridView1.Columns[i].HeaderTex 表格控件的列的文本内容
2 dataTable常用的属性和方法
DataTable dt = new DataTable(); 创建dataTable类型
DataRow row = dt.NewRow(); 创建一行
dt.Rows.Add(row); 向dataTable添加一行
dt.Columns.Add(); 向dataTable添加一列
dt.Columns datatable所有的列
dt.Rows datatable所有的行
dt.Columns[i].ColumnName;列标题
cs
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
//dataGridView1.DataSource = 集合
//DataTable 表格数据 分为列和行,需要给列添加数据,也需要给行添加数据
//DataTable dt = new DataTable();
//dt.Columns; // 列的集合
//dt.NewRow();//创建一个新行
//dataGridView1.DataSource = dt;
}
//读取csv
private void button1_Click(object sender, EventArgs e)
{
//CSVAPI c = new CSVAPI();
//c.DuQu(dataGridView1);
DataTable dt = new DataTable();// 创建一个表格数据
OpenFileDialog ofd = new OpenFileDialog();
if (ofd.ShowDialog() == DialogResult.OK)
{
//获取文件路径进行读取 ofd.FileName
FileStream fs = new FileStream(ofd.FileName, FileMode.Open, FileAccess.Read);
StreamReader sr = new StreamReader(fs, Encoding.Default);
string data; // 读取的数据
string[] lines; // 使用逗号分割之后的数组
bool isHead = true;//是否是表头数据
//data=sr.ReadLine() 一行一行去读取
//sr.ReadToEnd(); 读取完
while ((data = sr.ReadLine()) != null) // 证明有数据
{
//MessageBox.Show(data); //姓名,年龄,性别
lines = data.Split(',');//["姓名","年龄","性别"]
if (isHead)//如果是表头的
{
for (int i = 0; i < lines.Length; i++)
{
dt.Columns.Add(lines[i]); //给列添加数据
}
isHead = false; // 处理完表头之后把isHead=false
}
else //不是表头数据 ["张三",10,"男"]
{
DataRow dataRow = dt.NewRow();//创建行 多个单元格 给每一个单元格赋值
for (int i = 0; i < lines.Length; i++)
{
// dataRow[i] 给每一个单元格赋值
dataRow[i] = lines[i];
}
dt.Rows.Add(dataRow); //添加到dt的Rows属性
}
}
//DataTable类型的数据 需要Columns 、Rows 赋值
dataGridView1.DataSource = dt; //绑定数据源
}
}
//写入csv
private void button2_Click(object sender, EventArgs e)
{
//把dataGridView1表格展示的数据保存到csv
//怎么从dataGridView1控件把数据取出来 赋值给DataTable ,再把DataTable 写入csv
DataTable dt = new DataTable(); // 需要Columns 、Rows 赋值
//从dataGridView1.Columns所有的列取出列表题
for (int i = 0; i < dataGridView1.Columns.Count; i++)
{
dt.Columns.Add(dataGridView1.Columns[i].HeaderText);// 把控件的列的标题取出来赋值给datatable的列中
}
// MessageBox.Show(string.Join(",", dt.Columns[1])+""); Columns 已经存储了列表题
//MessageBox.Show(dataGridView1.Rows.Count + "");
//给dt的Rows赋值 add(DataRow)
for (int i = 0; i < dataGridView1.Rows.Count; i++) //遍历控件的行数
{
DataRow dr = dt.NewRow(); //dr 每一个单元格赋值,取出每一个控件单元格值
for (int j = 0; j < dataGridView1.Columns.Count; j++)
{
////dataGridView1[j, i].Value 访问第j列 第i行的单元格
dr[j] = dataGridView1[j, i].Value;
// MessageBox.Show(dataGridView1[j, i].Value.ToString());
}
dt.Rows.Add(dr);
}
//以上完成给DatatTable 类型填充数据了 开始写入csv
SaveFileDialog dialog = new SaveFileDialog(); //保存对话框
dialog.Filter = "CSV文件(*.csv)|*.csv|CSV文件(*.txt) |*.txt"; //过滤属性
if (dialog.ShowDialog()==DialogResult.OK)
{
FileStream fs = new FileStream(dialog.FileName, FileMode.Create, FileAccess.Write);
StreamWriter sr = new StreamWriter(fs, Encoding.Default);
string data = "";//写入字符串
//分表头写入
for (int i = 0; i < dt.Columns.Count; i++)
{
data += dt.Columns[i].ColumnName;
if (i< dt.Columns.Count -1) //需要拼接逗号
{
data += ",";
}
}
sr.WriteLine(data);
data = null;// 防止重复写入
for (int i = 0; i < dt.Rows.Count-1; i++) //从DataTable数据里面去取单元格数据
{
for (int j = 0; j < dt.Columns.Count; j++)
{
data += dt.Rows[i][j];//dt.Rows[i] 第几行 ,dt.Rows[i][j] 第几行d第几个格子
if (j< dt.Columns.Count-1)
{
data += ",";
}
}
sr.WriteLine(data);
data = null;
}
sr.Close();
fs.Close();
}
}
}
2. 二进制序列化 / 反序列化的核心概念
- 序列化 :将内存中的对象 转换为二进制数据流(字节序列),便于存储到文件、网络传输等;
- 反序列化 :将二进制数据流还原为内存中的对象,恢复对象的属性和状态;
- 与 JSON 序列化的区别:JSON 序列化是对象↔字符串(可读),二进制序列化是对象↔二进制(不可读、体积更小、效率更高)。
2. 关键类与特性
| 元素 | 作用 |
|---|---|
[Serializable] |
标记类允许被序列化 ,缺少该特性会抛出SerializationException异常; |
BinaryFormatter |
.NET 提供的二进制序列化工具,核心方法:- Serialize(流, 对象):序列化对象到流;- Deserialize(流):从流反序列化,返回object需强转; |
FileStream |
文件流,用于读写本地文件,是序列化的 "载体"(序列化需依托流完成); |
3. 代码关键细节
csharp
运行
// 序列化核心代码
FileStream fs = new FileStream("1.txt", FileMode.Create); // 创建/覆盖文件流
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(fs,i); // 把Info对象i序列化到文件流中
fs.Close(); // 必须关闭流,释放文件占用
// 反序列化核心代码
FileStream fs = new FileStream("1.txt", FileMode.Open); // 打开文件流
Info i1 = binaryFormatter.Deserialize(fs) as Info; // 反序列化后强转为Info类型
- 反序列化返回值是
object类型,需通过as或强制类型转换((Info))转为目标类型; - 流操作后必须调用
Close()(或用using自动释放),否则会导致文件被占用无法修改 / 删除。
4. 优化建议(新手必知)
原代码未处理异常且未优雅释放流,建议优化为:
csharp
运行
// 序列化优化版(using自动释放流,try-catch处理异常)
private void button1_Click(object sender, EventArgs e)
{
try
{
Info i = new Info() { Name = "中东小霸王", Description = "以色列三村一镇" };
// using语句:代码块结束自动释放FileStream,无需手动Close
using (FileStream fs = new FileStream("1.txt", FileMode.Create))
{
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(fs, i);
}
MessageBox.Show("序列化成功!");
}
catch (Exception ex)
{
MessageBox.Show("序列化失败:" + ex.Message);
}
}
// 反序列化优化版
private void button2_Click(object sender, EventArgs e)
{
try
{
if (!File.Exists("1.txt")) // 先判断文件是否存在
{
MessageBox.Show("文件不存在!");
return;
}
using (FileStream fs = new FileStream("1.txt", FileMode.Open))
{
BinaryFormatter binaryFormatter = new BinaryFormatter();
Info i1 = binaryFormatter.Deserialize(fs) as Info;
if (i1 != null) // 防止反序列化失败导致null
{
MessageBox.Show(i1.Name + "----" + i1.Description);
}
}
}
catch (Exception ex)
{
MessageBox.Show("反序列化失败:" + ex.Message);
}
}
cs
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
// json序列化和反序列化 : 把对象和字符串之间转换过程
// 二进制序列化和反序列化: 把对象和二进制字符串之间转换过程
}
//写入 把对象转成二进制字符 再写入本地
private void button1_Click(object sender, EventArgs e)
{
Info i = new Info()
{
Name = "中东小霸王",
Description = "以色列三村一镇"
};
FileStream fs = new FileStream("1.txt", FileMode.Create);
//二进制格式化工具
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(fs,i);//二进制序列化 参数1:文件流 ,参数2序列化的对象
fs.Close(); //关闭数据流
}
//读取
private void button2_Click(object sender, EventArgs e)
{
FileStream fs = new FileStream("1.txt", FileMode.Open);
BinaryFormatter binaryFormatter = new BinaryFormatter();
Info i1 = binaryFormatter.Deserialize(fs) as Info;// 反序列化
MessageBox.Show(i1.Name + "----" + i1.Description);
fs.Close();
}
}
// 标记一个类 可以被序列化
[Serializable]
public class Info
{
public string Name { get; set; }
public string Description { get; set; } //Description 描述
}
3. 窗体跳转传值
创建并显示另一个窗体的核心语法:
csharp
运行
// 1. 创建目标窗体对象(可传参)
Form2 f2 = new Form2(/* 可选:构造函数参数 */);
// 2. 显示窗体(两种方式)
f2.Show(); // 非模态显示
f2.ShowDialog(); // 模态显示
2. 两种窗体显示方式的核心区别
| 显示方式 | 特点 | 适用场景 |
|---|---|---|
Show() |
非模态窗口,不阻塞 UI 线程;Form1 和 Form2 可同时操作;Form1 代码会继续向下执行; | 需同时操作多个窗体(如主窗体 + 子窗体) |
ShowDialog() |
模态窗口,阻塞 UI 线程;必须关闭 Form2 后,Form1 的代码才会继续执行;Form2 置顶,无法操作 Form1; | 需用户完成子窗体操作后再继续(如弹窗、确认框) |
3. 窗体间传值的两种基础方式(重点)
方式 1:通过构造函数传参(推荐,数据初始化更规范)
实现步骤(需配合 Form2 的代码):
-
Step1:在 Form2 中定义带参数的构造函数,接收 Form1 传递的值: csharp
运行
// Form2中的代码 public partial class Form2 : Form { private string _receiveInfo; // 存储接收的值 // 自定义构造函数,接收Form1传递的参数 public Form2(string info) { InitializeComponent(); _receiveInfo = info; // 保存传递过来的值 } } -
Step2:Form1 中创建 Form2 时传参: csharp
运行
// Form1中的代码 Form2 f2 = new Form2(info); // 把Form1的info传给Form2的构造函数 f2.Show();
方式 2:通过公共属性赋值(灵活,可随时赋值)
实现步骤(需配合 Form2 的代码):
-
Step1:在 Form2 中定义公共属性,用于接收值: csharp
运行
// Form2中的代码 public partial class Form2 : Form { // 定义公共属性,供Form1赋值 public string s2 { get; set; } private void Form2_Load(object sender, EventArgs e) { // 窗体加载时,可使用接收的s2值(如显示在控件上) MessageBox.Show("接收的值:" + s2); } } -
Step2:Form1 中创建 Form2 后,给属性赋值: csharp
运行
// Form1中的代码 Form2 f2 = new Form2(info); // 先传构造函数参数 f2.s2 = s1; // 再给Form2的公共属性s2赋值 f2.Show();
4. 代码注意点(新手易踩坑)
- 原代码中
Form2 f2 = new Form2(info);要求 Form2 必须有带 string 参数的构造函数,否则会编译报错; - 公共属性传值需保证 Form2 的属性是
public(如public string s2 { get; set; }),private 属性无法被 Form1 赋值; - 若使用
ShowDialog(),Form2 关闭前,Form1 的后续代码不会执行(比如赋值、其他操作); - 窗体传值是单向的(Form1→Form2),若需 Form2 回传值给 Form1,需额外处理(如 Form2 定义事件、回调函数)。
三、核心知识点总结
- 窗体显示 :
Show()是非模态(不阻塞),ShowDialog()是模态(阻塞),根据业务场景选择; - 传值方式 :
- 构造函数传参:适合初始化必传值,代码更规范;
- 公共属性赋值:适合可选值、多值传递,更灵活;
From1代码
cs
_05跳转传值
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public string info = "催缴物业费"; // 演示构造函数传参数
public string s1 = "code change world";
private void button1_Click(object sender, EventArgs e)
{
//方法1 通过构造函数传参数进行传递
// Form2 f2 = new Form2(info); //创建form的窗体对象
//f2.Show();// 展示form2窗体 不会阻塞UI线程的
//f2.ShowDialog(); // 以对话框的方式进行展示,阻塞UI界面, 必须用户关闭窗体之后才会往下执行
//方法2: 通过在Form2定义一个公共属性接收传递的变量,在form2的Form2_Load事件可以接收传递的
Form2 f2 = new Form2(info);
f2.s2 = s1; //把s1传递给form2的s2属性
f2.Show();
}
}
Form2代码
cs
public partial class Form2 : Form
{
public string s2 { get; set; }
public Form2(string info)
{
InitializeComponent();
MessageBox.Show(info + "------"+ s2);
}
//窗体的加载事件,当窗体里面控件了 资源加载完毕之后执行
private void Form2_Load(object sender, EventArgs e)
{
MessageBox.Show( "+++++++" + s2);
}
}
2.通过方法传值
一. 预定义委托(Func/Action/Predicate)------ 核心重点
C# 提供了一系列预定义委托,无需手动定义委托类型就能直接使用,避免重复编写委托声明,代码更简洁。
| 委托类型 | 核心特征 | 示例绑定的方法 |
|---|---|---|
Action |
无返回值,可重载(如Action<int>带 1 个 int 参数、Action<int,string>带 2 个参数) |
Action f2 = T2;(T2 无参无返回) |
Func<TResult> |
有返回值(最后一个泛型参数是返回值类型),可重载(如Func<int,string>:int 参数,string 返回) |
Func<string> f1 = T1;(无参,返回 string)Func<bool> f3 = T3;(无参,返回 bool) |
Predicate<T> |
专用委托:只有 1 个参数 ,返回值固定为bool(等价于Func<T,bool>) |
Predicate<int> f4 = T4;(int 参 |
4. 新手易踩坑点
- 委托变量未赋值时调用会抛出
NullReferenceException,因此调用前需判空(if (f1 != null)); - Form2 中接收委托 / 对象的变量必须是
public,否则 Form1 无法访问赋值; Predicate<T>只能绑定 "单参数、返回 bool" 的方法,不能绑定无参或多参数方法(如Predicate<int> f4 = T3;会编译报错,因为 T3 无参)。
二、核心知识点总结
- 预定义委托 :
Action(无返回值)、Func<>(有返回值)、Predicate<T>(单参数 + bool 返回)是 C# 内置委托,无需手动定义即可绑定对应特征的方法; - 委托本质:是方法的 "引用",可将方法赋值给委托变量,实现方法的传递(如窗体间传方法);
- 窗体传值拓展 :除了传递普通数据,还可通过委托传递方法、通过
this传递窗体实例,实现窗体间的方法调用 / 对象交互。
From1
cs
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Func<string> f1 = T1; //接收一个有返回值的方法
Action f2 = T2; //接收一个无返回值的方法
Func<bool> f3 = T3;//接收一个返回值为true的方法
Predicate<int> f4 = T4; //接收一个返回值为true的方法
}
bool T4(int a)
{
return true;
}
bool T3()
{
return true;
}
void T2()
{
}
public string T1()
{
return "Hello world";
}
private void button1_Click(object sender, EventArgs e)
{
Form2 f2 = new Form2();
f2.f1 = T1;//把T1方法传递给form2窗体的f1变量
f2.ffff = this; // 把窗体1this对象传递给form2变量
f2.Show();
}
}
From2
cs
public partial class Form2 : Form
{
public Func<string> f1;
public Form1 ffff; // 接收窗体1的对象
public Form2()
{
InitializeComponent();
}
// Form2怎么找Form1对象
private void Form2_Load(object sender, EventArgs e)
{
// f1?.Invoke()
// f1? 表示f1是否有值
//.Invoke()再去调用f1方法
MessageBox.Show( f1?.Invoke()+"----" + ffff.T1());
}
}