表格dataGridView
一、基础框架与数据模型
-
WinForms 窗体结构:
- 命名空间与窗体类:
namespace _01表格包裹窗体类Form1(继承自Form),窗体类标记为partial(分部类),配合自动生成的InitializeComponent()初始化控件。 - 自定义数据模型:
Student类作为数据载体,通过自动属性 定义Name/Age/Info三个字段,作为 DataGridView 的绑定数据源结构。
- 命名空间与窗体类:
-
集合数据源:
- 声明全局
List<Student> list作为数据容器,相比数组更灵活(支持动态增删),是 WinForms 中 DataGridView 最常用的数据源类型之一。
- 声明全局
二、DataGridView 核心配置
-
列生成模式:
AutoGenerateColumns = false:关闭自动列生成,需手动在界面(或代码)绑定类的属性(列标题、绑定字段);若设为true,表格会自动根据Student类的属性名生成列(标题 = 属性名)。- 手动绑定列的核心:需在窗体设计器中为 DataGridView 添加列,并设置列的
DataPropertyName(与 Student 的属性名一致,如 Name/Age/Info)、Name(列的唯一标识,如 Name1)。
-
数据源绑定与刷新:
- 绑定方式:
dataGridView1.DataSource = list,将集合直接绑定到表格。 - 刷新机制:由于
List<T>不具备自动通知 UI 更新的能力(非绑定源),增删改数据后需通过DataSource = null→ 重新赋值DataSource = list触发表格刷新。
- 绑定方式:
三、DataGridView 数据操作(增删改查)
1. 新增数据(Add)
-
逻辑:向
List<Student>添加新对象 → 刷新表格绑定。 -
核心代码: csharp
运行
list.Add(new Student() { Name = "张三4", Age = 40, Info = "xxx" }); dataGridView1.DataSource = null; dataGridView1.DataSource = list;
2. 删除数据(Delete)
- 关键步骤:① 校验选中行:
SelectedRows.Count == 0判断是否选中行,无则提示。② 获取绑定对象:SelectedRows[0].DataBoundItem as Student从选中行提取绑定的 Student 对象(核心,避免直接操作单元格)。③ 确认删除:MessageBox.Show()带返回值(DialogResult),判断是否点击 "Yes"。④ 移除数据 + 刷新:list.Remove(stu)→ 刷新表格。
3. 查询数据(Query)
- 集合筛选:使用
List<T>.FindAll()结合Lambda 表达式 筛选满足条件的元素(v => v.Name == textBox1.Text)。 - 空结果处理:判断筛选后的集合
Count == 0,提示 "未找到",否则绑定筛选结果到表格。
4. 修改数据(Update)
- 核心逻辑:① 选中行 → 提取绑定的 Student 对象 → 直接修改对象属性(
stu.Name = "高达")→ 刷新表格。② 单元格取值(扩展):SelectedRows[0].Cells["Name1"].Value.ToString()通过列的 Name 获取单元格内容。
四、DataGridView 界面自定义
-
行号自定义绘制:
- 触发事件:
RowPostPaint(行重新绘制时触发)。 - 绘制核心步骤:① 获取行索引:
e.RowIndex + 1(行号从 1 开始)。② 定义绘制区域:Rectangle指定行标题栏的绘制范围(左 / 上 / 宽 / 高)。③ 字符串格式化:StringFormat设置水平 / 垂直居中对齐。④ 绘制文本:e.Graphics.DrawString()绘制行号到行标题栏,参数包括字符串、字体、画笔颜色、绘制区域、对齐方式。
- 触发事件:
-
关键绘图类:
Graphics:图形绘制核心类,提供DrawString等绘制方法。StringFormat:控制字符串对齐 / 排版。Rectangle:定义绘制区域的矩形范围。Brushes:画笔颜色(如Brushes.Black)。
五、WinForms 通用知识点
-
MessageBox 进阶使用:
- 带确认框的提示:
MessageBox.Show("提示内容", "标题", MessageBoxButtons.YesNo, MessageBoxIcon.Warning)。 - 返回值处理:
DialogResult result判断用户点击 "Yes/No",控制后续逻辑。
- 带确认框的提示:
-
控件事件与参数:
- 事件触发时机:
RowPostPaint事件在每行绘制完成后触发,适合自定义行样式(如行号)。 - 事件参数:
DataGridViewRowPostPaintEventArgs e包含行索引(e.RowIndex)、行边界(e.RowBounds)、绘图对象(e.Graphics)等核心信息。
- 事件触发时机:
-
DataGridView 行 / 单元格操作:
SelectedRows:选中行集合,SelectedRows[0]取第一行(默认单选场景)。DataBoundItem:选中行绑定的原始数据对象(核心,避免直接操作单元格,降低耦合)。Cells["列Name"].Value:获取指定列的单元格值,需转ToString()使用。
六、核心易错点与注意事项
List<T>作为数据源的局限性:无 INotifyPropertyChanged 接口,修改对象属性后必须刷新绑定(DataSource=null再赋值),否则 UI 不更新。- 行索引处理:
e.RowIndex从 0 开始,显示行号需+1。 - 类型转换:
DataBoundItem需用as Student强制转换为目标类型,避免类型错误。 - 列名匹配:手动绑定列时,
DataPropertyName必须与 Student 的属性名一致,Cells["列Name"]中的 Name 需与设计器中列的 Name 一致。
实例
cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace _01表格
{
public partial class Form1 : Form
{
public List<Student> list = new List<Student>(); //学生集合对象
public Form1()
{
//label、textbox 、button
//panel、pictureBox、 radionButton checkboxButton
//MenuStrip、 ContextMenuStrip、toolStrip
//Combobox
InitializeComponent();
list.Add(new Student() { Name = "张三1", Age = 10, Info = "被捕" });
list.Add(new Student() { Name = "张三2", Age = 20, Info = "被捕" });
list.Add(new Student() { Name = "张三3", Age = 30, Info = "被捕" });
//AutoGenerateColumns是否自动生成列,设置为false 需要自己通过界面绑定类的属性(自己设置列的标题、自己设置列显示哪个属性)
//设置为true,表格自动显示列的标题为绑定类的属性
dataGridView1.AutoGenerateColumns = false;
//dataGridView1 表格 可以通过DataSource属性进行绑定数据源,但是后续添加的新的数据不会立即显示到控件上,需要
// 再次对 dataGridView1.DataSource=null之后 再重新赋值
dataGridView1.DataSource = list;
}
//添加数据源的方法
private void button1_Click(object sender, EventArgs e)
{
list.Add(new Student() { Name = "张三4", Age = 40, Info = "委被捕" });
dataGridView1.DataSource = null;
dataGridView1.DataSource = list;
}
//删除数据源的方法
private void button2_Click(object sender, EventArgs e)
{
//先判断是否选中行
//SelectedRows 选中的行 可以选择多个
if (dataGridView1.SelectedRows.Count==0) //没选中行
{
MessageBox.Show("请先选中要删除的一行");
return;
}
//选中行了 获取选中的一行索引值 或者这一行对象
Student stu = null; //要删除的一行对象
// DataBoundItem 获取选中行绑定对象
// as 强制转换成Student对象
stu = dataGridView1.SelectedRows[0].DataBoundItem as Student;
//MessageBox.Show(stu.Name);
//MessageBox.Show是有返回值的,为DialogResult对象(对话结果对象),
DialogResult result = MessageBox.Show("是否要删除改行", "温馨提示", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
if (result != DialogResult.Yes) //如果没有选择yes 直接return
{
return;
}
//如果选择警告框的Yes键时候 再删除
list.Remove(stu);
//刷新界面
dataGridView1.DataSource = null;
dataGridView1.DataSource = list;
//删除成功之后 可以再次提示一下
MessageBox.Show("删除成功");
}
//查询数据源 把满足查询条件的数据源重新绑定给表格
private void button3_Click(object sender, EventArgs e)
{
//FindAll() 查找满足条件的所有的元素对象, 与输入框文本内容一样的对象
List<Student> currentList = list.FindAll(v => v.Name == textBox1.Text);
if (currentList.Count==0)
{
MessageBox.Show("没找到要找的学生");
return;
}
dataGridView1.DataSource = null;
dataGridView1.DataSource = currentList;
}
//修改数据源
private void button4_Click(object sender, EventArgs e)
{
if (dataGridView1.SelectedRows.Count == 0)
{
MessageBox.Show("请先选中要修改的一行");
return;
}
// 获取选中一行的对象
DataGridViewRow cc = dataGridView1.SelectedRows[0];
Student stu = cc.DataBoundItem as Student;
stu.Name = "高达";
stu.Age = 20;
stu.Info = "人在搭在";
dataGridView1.DataSource = null;
dataGridView1.DataSource = list;
// 获取单元格的内容
// Cells["Name1"] 获取name属性为Name1单元格的内容
// MessageBox.Show(dataGridView1.SelectedRows[0].Cells["Name1"].Value.ToString());
// DataGridViewRow cc1 = dataGridView1.SelectedRows[0];
// MessageBox.Show(cc1.Cells["Name1"].Value.ToString());
}
//当行重新绘制的时候触发函数
private void dataGridView1_RowPostPaint(object sender, DataGridViewRowPostPaintEventArgs e)
{
string s = e.RowIndex + 1 + "";// 显示的内容
//dataGridView1.Font; //表格的默认字体
//创建一个矩形 绘制的区域
//e.RowBounds.Left,开始绘制行的左边界位置
// e.RowBounds.Top, 开始绘制行的上边界位置
Rectangle rect = new Rectangle(e.RowBounds.Left,
e.RowBounds.Top,
dataGridView1.RowHeadersWidth,// 行标题的宽度
e.RowBounds.Height);
//格式化字符串 字符串的对齐方式
StringFormat sf = new StringFormat()
{
Alignment = StringAlignment.Center,// 水平对齐方式居中
LineAlignment = StringAlignment.Center, //竖直对齐方式居中
};
//Brushes.Black 画笔的颜色
//Graphics 图形类
//DrawString 绘制一个文本 参数1是绘制的字符串,参数2是绘制字体,参数3绘制的笔刷颜色 ,参数4 绘制的区域 , 参数5绘制字符串对齐方式
e.Graphics.DrawString(s,dataGridView1.Font,Brushes.Black, rect,sf);
}
}
public class Student
{
public string Name { get; set; }
public int Age { get; set; }
public string Info { get; set; }
}
}
2 自定义表格样式
一、类与方法的基础设计
-
静态工具类设计
- 类定位:
DatagridviewStyle为静态工具类 (所有方法 / 字段均为static),无需实例化即可调用(如DatagridviewStyle.DgvStyle2(dataGridView1)),符合 WinForms 通用工具类的设计规范。 - 静态字段:
public static int A = 10;演示静态字段的定义,可作为通用常量(如默认行高、颜色值)复用。
- 类定位:
-
方法注释与参数规范
- 完整的 XML 注释:每个方法都标注了
<summary>(功能描述)、<param>(参数说明),提升代码可读性和可维护性。 - 参数设计:所有方法均接收
DataGridView实例(dgv)作为参数,实现「一套逻辑适配任意表格控件」,是通用工具类的核心特征。
- 完整的 XML 注释:每个方法都标注了
二、DataGridView 核心样式配置(DgvStyle1/DgvStyle2)
1. 基础外观样式
| 配置项 | 作用 | 核心知识点 |
|---|---|---|
BackgroundColor |
表格无数据区域的背景色 | 区分「有数据行背景」和「空白区域背景」,可通过SystemColors调用系统预设颜色 |
RowsDefaultCellStyle.BackColor |
数据行默认背景色 | 所有行的基础背景样式,优先级低于AlternatingRowsDefaultCellStyle |
AlternatingRowsDefaultCellStyle.BackColor |
奇数行(交替行)背景色 | 实现「隔行变色」效果,提升表格可读性(如黄色 / 青色交替) |
GridColor |
表格网格线颜色 | 控制单元格之间分隔线的颜色(如红色) |
EnableHeadersVisualStyles = false |
关闭列标题默认视觉样式 | 关键配置:若需自定义列标题样式(颜色 / 字体 / 边框),必须关闭此属性,否则自定义样式不生效 |
2. 边框样式
| 配置项 | 作用 | 核心知识点 |
|---|---|---|
CellBorderStyle |
单元格边框样式 | 支持Sunken(凹陷)、Raised(凸起)、Single(单线)等样式,实现「凹凸质感」表格 |
ColumnHeadersBorderStyle |
列标题边框样式 | 与CellBorderStyle配合,统一表格边框风格(如均设为Sunken) |
RowHeadersBorderStyle |
行标题(行号列)边框样式 | 控制行标题区域的边框样式,保持整体风格统一 |
RowTemplate.DividerHeight |
行分隔线高度 | 配合Sunken边框样式使用,增强凹凸视觉效果 |
3. 标题栏样式
| 配置项 | 作用 | 核心知识点 |
|---|---|---|
ColumnHeadersHeight |
列标题高度 | 需配合ColumnHeadersHeightSizeMode = EnableResizing才能生效(允许调整高度) |
ColumnHeadersHeightSizeMode |
列标题高度调整模式 | EnableResizing(允许手动调整)、DisableResizing(固定)、AutoSize(自动适配) |
ColumnHeadersDefaultCellStyle |
列标题样式集合 | 可配置ForeColor(字体色)、BackColor(背景色)、Font(字体)等 |
4. 字体与文字样式
| 配置项 | 作用 | 核心知识点 |
|---|---|---|
ColumnHeadersDefaultCellStyle.Font |
列标题字体 | 自定义字体(如宋体 10 号),需实例化Font对象(new Font("宋体", 10F)) |
RowsDefaultCellStyle.Font |
数据行字体 | 统一设置所有行的字体(如楷体 8 号),也可通过列单独配置(dgv.Columns[i].DefaultCellStyle.Font) |
Columns[i].DefaultCellStyle.ForeColor |
指定列字体颜色 | 遍历列集合(dgv.ColumnCount获取列数),为每列单独设置文字颜色(如黄色) |
三、自定义行号绘制(DgvRowCount)
-
绘图核心类与对象
SolidBrush:实心笔刷,用于绘制文本 / 图形的填充色,此处绑定行标题的默认字体色(dgv.RowHeadersDefaultCellStyle.ForeColor),保证行号颜色与表格风格统一。Graphics:绘图核心对象(e.Graphics),通过DrawString方法绘制文本,是 GDI + 绘图的基础类。DataGridViewRowPostPaintEventArgs:事件参数,提供关键绘图信息:e.RowIndex:当前行索引(从 0 开始,行号需+1);e.RowBounds.Location:当前行的坐标(X/Y),用于定位行号绘制位置;e.InheritedRowStyle.Font:当前行的字体样式,保证行号字体与行内容一致。
-
绘制逻辑
- 行号计算:
e.RowIndex + 1实现行号从 1 开始(而非 0); - 文本绘制:
DrawString(字符串, 字体, 笔刷, X坐标, Y坐标),通过固定偏移(+15/+5)调整行号位置,避免与行边界重叠。
- 行号计算:
四、关键易错点与注意事项
- EnableHeadersVisualStyles 的坑
- 若未设置
EnableHeadersVisualStyles = false,所有自定义列标题样式(颜色 / 字体 / 边框)都会被系统默认样式覆盖,导致配置无效。
- 若未设置
- 列标题高度生效条件
- 设置
ColumnHeadersHeight前,必须将ColumnHeadersHeightSizeMode设为EnableResizing,否则高度设置不生效。
- 设置
- 交替行样式优先级
AlternatingRowsDefaultCellStyle优先级高于RowsDefaultCellStyle,会覆盖奇数行的默认背景色。
- GDI + 资源释放(扩展)
SolidBrush属于非托管资源,若频繁绘制建议使用using包裹(using (SolidBrush s1 = new SolidBrush(...)) { ... }),避免内存泄漏。
五、样式配置的最佳实践
- 样式分类封装:将不同风格的样式(如基础样式 DgvStyle1、凹凸样式 DgvStyle2)拆分为独立方法,便于业务场景切换。
- 系统颜色复用 :使用
System.Drawing.SystemColors(如SystemColors.ButtonFace)代替硬编码颜色值,适配系统主题。 - 字体统一管理 :可将常用字体(如宋体 10 号、楷体 8 号)封装为静态字段,避免重复实例化
Font对象。
实例
cs
public class DatagridviewStyle
{
public static int A = 10;
/// <summary>
/// 设置表格样式 参数是表格
/// </summary>
/// <param name="dgv"></param>
public static void DgvStyle1(DataGridView dgv)
{
//未显示数据时候背景颜色 StudentManager.Common.DatagridviewStyle.A
dgv.BackgroundColor = System.Drawing.SystemColors.ButtonFace;
//显示数据时候背景颜色
dgv.RowsDefaultCellStyle.BackColor = System.Drawing.Color.Black;
//设置网格的颜色
dgv.GridColor = System.Drawing.Color.Red;
// 设置列表题边框的样式
dgv.ColumnHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single;
//设置行的边框样式
dgv.RowHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single;
// 表格的默认样式不可见
dgv.EnableHeadersVisualStyles = false;
// 设置列的宽度高度 单位是px
dgv.ColumnHeadersHeight = 35;
//设置列表题宽度可以被修改
dgv.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.EnableResizing;
//列标题的字体颜色
dgv.ColumnHeadersDefaultCellStyle.ForeColor = System.Drawing.Color.Green;
//列背景颜色
dgv.ColumnHeadersDefaultCellStyle.BackColor = System.Drawing.Color.FromArgb(255, 255, 192);
// 设置行的字体颜色
int a = dgv.ColumnCount; //列数 2
for (int i = 0;i < a; i++)
{
dgv.Columns[i].DefaultCellStyle.ForeColor = System.Drawing.Color.Yellow;
}
}
/// <summary>
/// 表格格式第二种 凹凸样式
/// </summary>
/// <param name="dgv"></param>
public static void DgvStyle2(DataGridView dgv)
{
// 设置单元格的边框样式
dgv.CellBorderStyle = System.Windows.Forms.DataGridViewCellBorderStyle.Sunken;
// 列表题的样式
dgv.ColumnHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Sunken;
//列表题字体
dgv.ColumnHeadersDefaultCellStyle.Font = new System.Drawing.Font("宋体", 10F);
// 背景颜色
dgv.ColumnHeadersDefaultCellStyle.BackColor = System.Drawing.Color.FromArgb(0,0, 255);
//设置分割符号的高度 设置凹凸Sunken样式 需要把分割符号高度加上
dgv.RowTemplate.DividerHeight = 1;
// 列标题的高度
// 需要把表格的列标题的高度模式改成 EnableResizing
dgv.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.EnableResizing;
dgv.ColumnHeadersHeight = 30;
// 行的边框样式
dgv.RowHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Sunken;
//设置行的字体
dgv.RowsDefaultCellStyle.Font = new System.Drawing.Font("楷体", 8f);
dgv.RowsDefaultCellStyle.BackColor = Color.Yellow;
// 奇数行的背景颜色
dgv.AlternatingRowsDefaultCellStyle.BackColor = Color.Cyan;
// 关闭默认样式
dgv.EnableHeadersVisualStyles = false;
}
/// <summary>
/// 绘制表格的行号 事先给datagridview绑定事件RowPosPaint,在这个方法
/// 调用以下这个方法
/// 参数1是表格
/// 参数2 是绘制的事件对象
/// </summary>
///
public static void DgvRowCount(DataGridView dgv,DataGridViewRowPostPaintEventArgs e)
{
// 设置一个笔刷 包含绘制各种图形的样式,带了表格行的默认字体颜色
SolidBrush s1 = new SolidBrush(dgv.RowHeadersDefaultCellStyle.ForeColor);
int num = e.RowIndex +1;//RowIndex 行的索引值加1
string numString = num.ToString();
//DrawString 绘制字符串
//参数1 是绘制的字符串
//参数2 是绘制字体字体类型
//参数3 是笔刷
//参数4 5 坐标
e.Graphics.DrawString(numString, e.InheritedRowStyle.Font, s1, e.RowBounds.Location.X + 15, e.RowBounds.Location.Y + 5);
}
}
使用方法
cs
public partial class Form1 : Form
{
public List<Student> list = new List<Student>(); //学生集合对象
public Form1()
{
InitializeComponent();
list.Add(new Student() { Name = "张三1", Age = 10, Info = "委内瑞拉总统被捕" });
list.Add(new Student() { Name = "张三2", Age = 20, Info = "委内瑞拉总统被捕" });
list.Add(new Student() { Name = "张三3", Age = 30, Info = "委内瑞拉总统被捕" });
dataGridView1.DataSource = list;
//DatagridviewStyle 关于表格样式的一个类文件,DgvStyle1()和 DgvStyle2() 设计表格样式
//DgvRowCount() 绘制行标题
DatagridviewStyle.DgvStyle2(dataGridView1);
}
private void dataGridView1_RowPostPaint(object sender, DataGridViewRowPostPaintEventArgs e)
{
DatagridviewStyle.DgvRowCount(dataGridView1,e);
}
}
public class Student
{
public string Name { get; set; }
public int Age { get; set; }
public string Info { get; set; }
}
3、Timer 定时器核心知识点
1. 定时器基础配置
| 配置项 / 方法 | 作用 | 核心说明 |
|---|---|---|
Interval |
触发间隔 | 单位为毫秒(ms) ,代码中设为10(即每 10ms 触发一次Tick事件);值越小,触发频率越高,动态效果越流畅(但过高频率可能占用 CPU)。 |
Start() |
开启定时器 | 调用后定时器开始按Interval间隔触发Tick事件(代码中绑定到 "启动" 按钮点击事件)。 |
Stop() |
停止定时器 | 调用后定时器暂停触发Tick事件(代码中绑定到 "停止" 按钮点击事件)。 |
Enabled |
激活状态 | Enabled = true 等价于Start(),false等价于Stop();初始化时若未设置,默认可能为false(需手动启动)。 |
Tick事件 |
定时触发逻辑 | 定时器核心事件,每到Interval间隔就执行事件内的代码(如修改标签样式、位置)。 |
2. 定时器生命周期控制
- 初始化:窗体构造函数中仅配置
Interval和标签字体,未直接启动定时器(避免窗体加载后立即触发),通过按钮手动控制启停,符合 "按需触发" 的交互逻辑。 - 终止逻辑:未显式释放定时器(WinForms 中控件随窗体销毁自动释放,若需手动释放可在
FormClosed事件中调用timer1.Dispose())。
二、随机数生成(Random 类)
- Random 对象创建 :
- 声明全局
Random ran = new Random();(避免在Tick事件内重复创建,否则因系统时钟精度问题导致随机数重复)。
- 声明全局
- 随机数取值 :
ran.Next(256):生成0~255之间的整数(包含 0,不包含 256),恰好匹配 RGB 颜色通道的取值范围(0-255)。
- RGB 颜色合成 :
Color.FromArgb(r, g, b):通过红(r)、绿(g)、蓝(b)三原色值合成自定义颜色:(255,255,255)= 白色,(0,0,0)= 黑色;- 某一通道值越大,颜色越偏向该通道(如
r=255, g=0, b=0为纯红)。
三、WinForms 控件样式与位置动态修改
1. 标签(Label)样式配置
- 字体自定义:
label1.Font = new Font(new FontFamily("楷体"), 20);实例化Font对象,设置字体为楷体、字号 20(窗体初始化时配置)。 - 背景色动态修改:
label1.BackColor = Color.FromArgb(r, g, b);每 10ms 更新一次背景色,实现 "炫彩" 效果。
2. 控件位置控制(Point/Location)
Location属性:控件在窗体中的坐标,类型为Point(包含X、Y两个整型属性):label1.Left等价于label1.Location.X(控件左边缘距窗体左边缘的距离);label1.Location.Y等价于控件上边缘距窗体上边缘的距离(代码中用count变量控制)。
- 位置更新:
label1.Location = new Point(label1.Left, count);保持 X 坐标不变,Y 坐标随count递增,实现标签垂直向下移动。 - 边界重置:
if (count>=400) count=0;当 Y 坐标达到 400 时重置为 0,避免标签移出窗体可视区域。
四、变量与逻辑控制
- 全局计数器 :
- 声明
int count = 0;作为全局变量(需在Tick事件外定义),用于累计标签 Y 坐标偏移量;若声明在Tick事件内,每次触发都会重置为 0,无法实现移动效果。
- 声明
- 循环移动逻辑 :
- 通过
count++实现持续下移,count>=400时重置为 0,形成 "循环下移" 的动画效果。
- 通过
五、关键易错点与注意事项
- Random 对象创建位置 :
- 错误写法:在
Tick事件内每次创建Random ran = new Random();→ 因系统时钟(TickCount)精度问题,短时间内创建的 Random 会生成重复随机数,导致颜色无变化。 - 正确写法:声明为全局变量,仅初始化一次。
- 错误写法:在
- Interval 取值平衡 :
- 过小(如 1ms):CPU 占用率升高,可能导致窗体卡顿;
- 过大(如 1000ms):动画效果卡顿,不流畅;
- 推荐范围:10~50ms(兼顾流畅度与性能)。
- 控件坐标边界 :
- 需结合窗体实际尺寸调整
count的阈值(如窗体高度为 500 时,阈值设为 400,避免标签被窗体边框遮挡)。
- 需结合窗体实际尺寸调整
- 定时器启停状态 :
- 多次点击 "启动" 按钮不会重复触发(
Start()方法调用时若定时器已启动,无额外影响); - 停止后再次启动,
count变量不会重置(代码中仅在count>=400时重置,若需停止后复位,可在Stop()后加count=0;)。
- 多次点击 "启动" 按钮不会重复触发(
4、纯代码创建 Timer 定时器
1. 定时器对象的手动创建与配置
| 核心步骤 | 代码 / 知识点 | 说明 |
|---|---|---|
| 声明对象 | Timer timer; |
声明类级别的 Timer 变量(避免局部变量被回收) |
| 实例化 + 初始化 | timer = new Timer() { Enabled=true,Interval=10}; |
采用对象初始化器直接配置核心属性:- Enabled=true:创建后立即激活(等价于Start());- Interval=10:触发间隔 10ms。 |
| 绑定事件 | timer.Tick += Timer_Tick; |
通过委托绑定Tick事件处理方法,替代设计器的事件绑定;语法:事件 += 事件处理方法(方法签名需匹配EventHandler)。 |
2. 定时器事件的通用逻辑
- 无需设计器拖拽 Timer 控件,完全通过代码控制生命周期,适合动态创建 / 销毁定时器的场景(如按需生成多个定时器);
- 定时器触发后遍历窗体控件,体现「动态控件批量操作」的思路。
二、动态生成控件(Label)
-
控件创建与初始化
-
通过
new Label()创建标签,使用对象初始化器 一次性配置核心属性:csharp
运行
Label label = new Label() { Text = i.ToString(), // 文本内容 Location = new Point(100 * i, 200), // 坐标(X轴按i*100偏移,实现横向排列) BackColor = Color.Aqua, // 背景色 Size = new Size(50, 50), // 尺寸(宽50,高50) TextAlign = ContentAlignment.MiddleCenter // 文本居中对齐 };
-
-
控件添加到窗体
this.Controls.Add(label);:将动态创建的 Label 添加到窗体的控件集合中,否则控件不会显示。
-
批量生成逻辑
- 通过
for循环生成 10 个 Label,X 坐标按100*i递增,实现横向等距排列,无需在设计器中逐个拖拽。
- 通过
三、窗体控件集合遍历与类型判断
- 控件集合(Controls)
this.Controls:窗体的所有子控件集合(包括动态创建和设计器添加的控件);this.Controls.Count:获取控件总数,用于循环遍历的边界判断。
- 类型判断(is 关键字)
if (this.Controls[i] is Label):判断当前遍历的控件是否为 Label 类型,避免修改非 Label 控件的样式(如按钮、文本框);- 替代方案:
as关键字(Label lbl = this.Controls[i] as Label; if(lbl != null)),适合需要获取控件实例的场景。
- 批量样式修改
- 遍历中动态修改 Label 的
BackColor(背景色)和ForeColor(字体色),通过Random生成随机 RGB 值,实现 "炫彩" 效果; - 核心:
Color.FromArgb(rnd.Next(256), rnd.Next(256), rnd.Next(256))(0-255 随机三原色合成颜色)。
- 遍历中动态修改 Label 的
四、GDI + 高级绘图(渐变文本)
1. 窗体绘制重写(OnPaint 方法)
protected override void OnPaint(PaintEventArgs e):重写窗体的绘制方法,窗体刷新 / 加载时触发;base.OnPaint(e):必须调用基类的 OnPaint 方法,否则窗体默认绘制逻辑(如控件显示)会失效;PaintEventArgs e:提供绘图核心对象e.Graphics(画布)。
2. 线性渐变笔刷(LinearGradientBrush)
| 核心参数 | 说明 |
|---|---|
| 构造函数 | LinearGradientBrush(rect, Color.Red, Color.Yellow, 45f) |
rect |
渐变应用的矩形区域(与文本绘制区域匹配) |
Color.Red/Color.Yellow |
渐变的起始色和结束色 |
45f |
渐变角度(0°= 水平渐变,45°= 斜向渐变,90°= 垂直渐变) |
3. 渐变文本绘制步骤
csharp
运行
// 1. 创建画布
Graphics g = e.Graphics;
// 2. 定义绘制区域
Rectangle rect = new Rectangle(10, 10, 300, 50);
// 3. 创建字体(Arial,32号,加粗)
Font font = new Font("Arial", 32, FontStyle.Bold);
// 4. 创建线性渐变笔刷
LinearGradientBrush brush = new LinearGradientBrush(rect, Color.Red, Color.Yellow, 45f);
// 5. 绘制渐变文本
g.DrawString("渐变文本", font, brush, new PointF(10, 10));
五、关键易错点与注意事项
-
Timer 对象的作用域
- 必须声明为类级变量(
Timer timer;),若在构造函数中声明为局部变量,会被 GC 回收,定时器立即失效。
- 必须声明为类级变量(
-
GDI + 资源释放(扩展)
-
Font、LinearGradientBrush属于非托管资源,重写 OnPaint 时若频繁创建,需用using包裹释放:csharp
运行
using (Font font = new Font("Arial", 32, FontStyle.Bold)) using (LinearGradientBrush brush = new LinearGradientBrush(rect, Color.Red, Color.Yellow, 45f)) { g.DrawString("渐变文本", font, brush, new PointF(10, 10)); }
-
-
控件遍历的效率
this.Controls仅遍历直接子控件,若 Label 嵌套在 Panel 中,需递归遍历(panel1.Controls);- 频繁遍历控件集合(如 10ms 一次)可能轻微占用 CPU,可通过缓存 Label 集合优化(如创建
List<Label> lblList存储动态生成的 Label)。
-
Random 对象的全局化
Random rnd = new Random();声明为类级变量,避免在 Timer_Tick 中重复创建导致随机数重复。