简介:本资源是一套面向编程初学者与教育信息化实践者的《学生信息管理系统》完整开发包,聚焦小程序级数据库应用开发,适用于高校计算机基础课程设计、教务管理工具原型开发及Python+SQLite入门实战。压缩包共4个文件,含2份Word文档(系统使用说明与配置指南)、1个可执行程序(studentsystem.exe)和1个核心Python源码文件(studentsystem.py),总大小5.96MB,结构精简,便于快速部署与代码研读。已有142人学习下载,适合通过真实项目理解CRUD操作、SQL语句编写(INSERT/UPDATE/SELECT/COUNT)、基础排序与搜索算法实现等关键知识点。读者可直接运行exe体验全流程功能,对照py源码学习数据库连接、异常处理与控制台交互逻辑,并借助文档掌握配置要点与使用边界,是理论结合实践的典型教学级小系统范例。
1. 这不是又一个“课程设计交差版”:一个能真跑起来、带完整数据库交互、支持增删改查闭环的学生信息管理系统源码包
你是不是也见过太多标着“学生信息管理系统”的压缩包,双击解压后只有三个文件:main.c、README.txt(写着“运行前请配置数据库”)、还有一张手绘ER图截图?这次不一样——这个《学生信息管理系统》程序使用包,是某高校软件工程实训课沉淀下来的可部署、可调试、可扩展的完整实现,含完整 Visual Studio 2022 工程(C# WinForms)、SQL Server 数据库脚本(含建库、建表、初始化示例数据)、独立安装包(MSI)、以及配套的用户操作手册PDF。它不依赖任何在线服务或云后台,所有逻辑在本地完成;数据库连接字符串明文可查、可改;关键业务逻辑(如学号唯一性校验、成绩范围约束、班级-学生级联删除)全部落在代码层而非仅靠数据库约束。适合两类人:一是大二大三正在啃数据库原理+GUI开发的学生,拿来当课程设计基线,三天内就能跑通、改界面、加字段;二是刚转岗做教务系统维护的一线工程师,需要快速理解这类管理系统的典型分层结构(UI层→业务逻辑层→数据访问层)和常见陷阱。它解决的不是“能不能显示表格”,而是“改完学号后历史成绩记录会不会断链”“导出Excel时中文列名乱码怎么根治”这种真实血泪问题。
2. 从零启动:环境准备、数据库初始化与工程加载全流程
2.1 环境依赖清单:只装这三样,拒绝“缺XX.dll”玄学报错
该系统对运行环境有明确边界,不追求兼容老旧系统,但杜绝模糊依赖。经实测,以下组合为最小可行环境:
| 组件 | 版本要求 | 获取方式 | 备注 |
|---|---|---|---|
| .NET Framework | 4.7.2 或更高 | Windows 自带(Win10 1809+ 默认含)或 Microsoft 官方下载页 | 必须安装,WinForms 项目无法降级到 4.5 |
| SQL Server Express | 2019 或 2022 版本 | SQL Server Express 免费下载 | 选“带工具”的完整版,否则 SSMS 缺失 |
| Visual Studio | 2022 社区版(免费) | Visual Studio 官网 | 安装时勾选“.NET 桌面开发”和“SQL Server Data Tools”工作负载 |
提示:不要试图用 VS Code 打开
.sln文件——它无法加载 WinForms 设计器,双击FormMain.cs [Design]将直接报错。必须用 Visual Studio。
2.2 数据库初始化:四步执行脚本,比手动建表快五倍
源码包中/Database/目录下包含三个核心 SQL 脚本,顺序执行即可完成全量初始化:
-- 1. 创建数据库(执行一次) CREATE DATABASE StudentDB ON PRIMARY ( NAME = 'StudentDB_Data', FILENAME = 'C:\Program Files\Microsoft SQL Server\MSSQL15.SQLEXPRESS\MSSQL\DATA\StudentDB.mdf' ) LOG ON ( NAME = 'StudentDB_Log', FILENAME = 'C:\Program Files\Microsoft SQL Server\MSSQL15.SQLEXPRESS\MSSQL\DATA\StudentDB_log.ldf' ); GO-- 2. 建表与约束(执行一次) USE StudentDB; GO CREATE TABLE Classes ( ClassID INT IDENTITY(1,1) PRIMARY KEY, ClassName NVARCHAR(50) NOT NULL UNIQUE ); CREATE TABLE Students ( StudentID INT IDENTITY(1,1) PRIMARY KEY, StudentNo CHAR(10) NOT NULL UNIQUE, -- 学号固定10位,强制唯一 Name NVARCHAR(30) NOT NULL, Gender CHAR(2) CHECK (Gender IN ('男','女')), ClassID INT NOT NULL, EnrollmentDate DATE DEFAULT GETDATE(), CONSTRAINT FK_Students_ClassID FOREIGN KEY (ClassID) REFERENCES Classes(ClassID) ); CREATE TABLE Scores ( ScoreID INT IDENTITY(1,1) PRIMARY KEY, StudentID INT NOT NULL, Subject NVARCHAR(20) NOT NULL, Score DECIMAL(5,2) CHECK (Score BETWEEN 0 AND 100), CONSTRAINT FK_Scores_StudentID FOREIGN KEY (StudentID) REFERENCES Students(StudentID) ON DELETE CASCADE ); GO-- 3. 插入示例数据(执行一次,用于验证功能) USE StudentDB; GO INSERT INTO Classes (ClassName) VALUES ('计算机2101'), ('软件2102'), ('网络2103'); INSERT INTO Students (StudentNo, Name, Gender, ClassID) VALUES ('2021000001', '张三', '男', 1), ('2021000002', '李四', '女', 1), ('2021000003', '王五', '男', 2); INSERT INTO Scores (StudentID, Subject, Score) VALUES (1, 'C#编程', 89.5), (1, '数据库原理', 92.0), (2, 'Web前端', 85.0); GO逻辑说明:
- 第一步创建独立数据库
StudentDB,避免污染默认master库;路径按你本地 SQL Server 实际安装路径调整(常见路径见注释); - 第二步建表时已嵌入关键业务约束:
StudentNo强制UNIQUE、Gender限定值域、Scores表对Students启用ON DELETE CASCADE—— 删除学生时自动清理其所有成绩,防止孤儿记录; - 第三步插入三条班级、三条学生、三条成绩,构成最小闭环数据集,后续启动程序即可看到真实列表。
2.3 工程加载与首次编译:确认三个关键路径无硬编码
解压源码包后,进入/Source/StudentManagementSystem/目录,双击StudentManagementSystem.sln。VS 2022 加载后,需重点检查并修正以下三处路径(它们在源码中以字符串形式存在,非配置文件):
数据库连接字符串位置:打开
DAL/DatabaseHelper.cs,查找connectionString变量:public static string connectionString = @"Data Source=DESKTOP-ABC123\SQLEXPRESS;Initial Catalog=StudentDB;Integrated Security=True;";将
DESKTOP-ABC123\SQLEXPRESS替换为你本机 SQL Server 实例名(可在 SSMS 连接时左上角看到,常见为localhost\SQLEXPRESS或.\SQLEXPRESS)。Excel 导出模板路径:打开
BLL/ExportService.cs,查找templatePath:private const string templatePath = @"C:\Templates\StudentReport.xlsx";此路径仅用于读取 Excel 样式模板。若不存在,程序会跳过样式应用,但不会崩溃。建议新建
C:\Templates\文件夹,并放入一个空白.xlsx文件(命名一致),确保导出时列宽、标题居中等样式生效。日志文件写入路径:打开
Common/Logger.cs,查找logFilePath:private static readonly string logFilePath = @"C:\Logs\StudentSystem.log";确保
C:\Logs\目录存在且当前用户有写入权限(右键目录 → 属性 → 安全 → 编辑 → 添加当前用户 → 勾选“写入”)。
完成以上三处修改后,按Ctrl + Shift + B编译整个解决方案。若出现CS0234: 命名空间中找不到类型或命名空间错误,大概率是未安装“SQL Server Data Tools”工作负载,请返回 VS 安装器补全。
3. 核心功能拆解:增删改查背后的三层架构与关键参数控制
3.1 UI层:WinForms 界面布局逻辑与事件绑定机制
主窗体FormMain.cs采用典型的 TabControl 分页设计,共四页签:
- 学生管理:DataGridView 显示学生列表,上方 Panel 包含学号、姓名、性别、班级下拉框、入学日期 DateTimePicker;
- 班级管理:仅一个 DataGridView + “新增班级”按钮;
- 成绩录入:左侧学生树形选择(TreeView),右侧科目+成绩输入框+“保存”按钮;
- 数据报表:含“导出Excel”、“打印预览”两个按钮,调用封装好的导出服务。
关键事件绑定示例(学生新增):
点击“添加学生”按钮触发btnAddStudent_Click事件,其核心逻辑如下:
private void btnAddStudent_Click(object sender, EventArgs e) { // 1. 前端校验:学号长度、是否为空、是否重复(此处为客户端轻量校验) if (string.IsNullOrWhiteSpace(txtStudentNo.Text) || txtStudentNo.Text.Length != 10) { MessageBox.Show("学号必须为10位字符!", "输入错误", MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } // 2. 调用业务逻辑层方法,传入实体对象 var student = new Student { StudentNo = txtStudentNo.Text.Trim(), Name = txtName.Text.Trim(), Gender = cmbGender.SelectedItem?.ToString() ?? "男", ClassID = (int)cmbClass.SelectedValue, EnrollmentDate = dtpEnrollment.Value.Date }; bool success = StudentService.AddStudent(student); // 3. 根据返回结果刷新界面 if (success) { MessageBox.Show("学生添加成功!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); LoadStudentGrid(); // 重新查询并绑定DataGridView ClearStudentForm(); // 清空输入框 } else { MessageBox.Show("添加失败:学号可能已存在,请检查。", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); } }参数说明:
txtStudentNo.Text.Length != 10:硬性限制学号为10位,规避后期数据库CHAR(10)字段截断风险;cmbClass.SelectedValue:班级下拉框绑定的是Classes表的ClassID(int),而非ClassName(string),确保外键关联准确;dtpEnrollment.Value.Date:DateTimePicker 返回DateTime类型,.Date截取日期部分,避免时间戳干扰;StudentService.AddStudent(student):将 UI 层数据封装为Student实体,交由 BLL 层处理,实现关注点分离。
3.2 BLL层:业务规则集中地与事务边界定义
BLL/StudentService.cs是业务逻辑中枢,AddStudent方法体现典型事务控制:
public static bool AddStudent(Student student) { using (var connection = new SqlConnection(DatabaseHelper.connectionString)) { connection.Open(); using (var transaction = connection.BeginTransaction()) { try { // 步骤1:插入学生主表 string insertSql = "INSERT INTO Students (StudentNo, Name, Gender, ClassID, EnrollmentDate) VALUES (@no, @name, @gender, @classid, @date); SELECT SCOPE_IDENTITY();"; var cmd = new SqlCommand(insertSql, connection, transaction); cmd.Parameters.AddWithValue("@no", student.StudentNo); cmd.Parameters.AddWithValue("@name", student.Name); cmd.Parameters.AddWithValue("@gender", student.Gender); cmd.Parameters.AddWithValue("@classid", student.ClassID); cmd.Parameters.AddWithValue("@date", student.EnrollmentDate); object result = cmd.ExecuteScalar(); if (result == null || !(result is decimal)) throw new Exception("学生插入失败,未获取到新ID"); int newStudentID = Convert.ToInt32(result); // 步骤2:为新学生初始化默认成绩(可选,此处注释掉,实际项目中常需) // InsertDefaultScores(connection, transaction, newStudentID); transaction.Commit(); return true; } catch (Exception ex) { transaction.Rollback(); Logger.LogError($"AddStudent failed: {ex.Message}"); return false; } } } }逻辑说明:
- 使用
SqlConnection+SqlTransaction显式开启事务,确保“插入学生”与“后续关联操作”(如初始化成绩)原子性; SCOPE_IDENTITY()获取本次插入生成的自增主键StudentID,为后续可能的级联操作提供依据;cmd.Parameters.AddWithValue防止 SQL 注入,所有用户输入均走参数化;Logger.LogError记录异常堆栈到日志文件,便于定位transaction.Rollback()触发原因。
3.3 DAL层:数据访问抽象与SQL执行细节
DAL/StudentDAO.cs封装具体 SQL 执行,GetAllStudents方法展示如何安全查询并映射:
public static List<Student> GetAllStudents() { var list = new List<Student>(); string sql = @" SELECT s.StudentID, s.StudentNo, s.Name, s.Gender, s.EnrollmentDate, c.ClassName, s.ClassID FROM Students s INNER JOIN Classes c ON s.ClassID = c.ClassID ORDER BY s.EnrollmentDate DESC"; using (var connection = new SqlConnection(DatabaseHelper.connectionString)) { connection.Open(); using (var cmd = new SqlCommand(sql, connection)) { using (var reader = cmd.ExecuteReader()) { while (reader.Read()) { list.Add(new Student { StudentID = Convert.ToInt32(reader["StudentID"]), StudentNo = reader["StudentNo"].ToString(), Name = reader["Name"].ToString(), Gender = reader["Gender"].ToString(), EnrollmentDate = Convert.ToDateTime(reader["EnrollmentDate"]), ClassName = reader["ClassName"].ToString(), // 视图字段,非实体原始属性 ClassID = Convert.ToInt32(reader["ClassID"]) }); } } } } return list; }参数说明:
INNER JOIN确保只返回班级存在的学生,避免ClassID为 NULL 的脏数据;ORDER BY s.EnrollmentDate DESC按入学时间倒序,最新学生排最前,符合教务查看习惯;reader["ClassName"]是 JOIN 后的别名列,虽不在Student实体定义中,但为 UI 显示所需,故在构造时直接赋值给ClassName属性(该属性在Student类中已声明为public string ClassName { get; set; });Convert.ToDateTime对reader["EnrollmentDate"]做强转,若数据库中该字段为 NULL,会抛出异常——因此生产环境应改用reader.IsDBNull("EnrollmentDate") ? (DateTime?)null : Convert.ToDateTime(...),但本包为教学简化,暂未处理。
4. 避坑指南:五个真实翻车现场与对应解法(附错误日志定位)
4.1 现象:启动程序后 DataGridView 显示“System.Data.DataRowView”,而不是学生姓名
原因:DataGridView的DataSource被错误绑定到DataTable或DataSet的DefaultView,但未设置AutoGenerateColumns = true,且未手动定义DataPropertyName。
解决:检查FormMain_Load中绑定代码,确保使用List<Student>直接绑定,并启用自动生成列:
dataGridViewStudents.AutoGenerateColumns = true; // 关键! dataGridViewStudents.DataSource = StudentService.GetAllStudents();若手动定义列,则必须为每列设置DataPropertyName与Student类属性名完全一致(如"Name",不能是"studentName")。
4.2 现象:点击“删除学生”后,成绩表里对应记录未被清除,出现孤立成绩
原因:SQL Server 中Scores表的外键约束ON DELETE CASCADE未生效,通常因建表时未显式指定或数据库兼容级别过低。
解决:在 SSMS 中执行以下语句验证约束状态:
SELECT fk.name AS ForeignKeyName, t1.name AS ReferencingTable, t2.name AS ReferencedTable, fk.delete_referential_action_desc AS DeleteAction FROM sys.foreign_keys fk INNER JOIN sys.tables t1 ON fk.parent_object_id = t1.object_id INNER JOIN sys.tables t2 ON fk.referenced_object_id = t2.object_id WHERE t1.name = 'Scores' AND t2.name = 'Students';若DeleteAction显示NO_ACTION,则需重建外键:
ALTER TABLE Scores DROP CONSTRAINT FK_Scores_StudentID; ALTER TABLE Scores ADD CONSTRAINT FK_Scores_StudentID FOREIGN KEY (StudentID) REFERENCES Students(StudentID) ON DELETE CASCADE;4.3 现象:导出 Excel 时弹出“未找到 Microsoft.Office.Interop.Excel 程序集”
原因:项目引用了Microsoft.Office.Interop.Excel(位于BLL/ExportService.cs),但目标机器未安装 Microsoft Excel 桌面版。
解决:推荐方案——改用开源库ClosedXML(已包含在源码包/Libraries/ClosedXML.dll中)。替换ExportService.cs中的导出逻辑:
// 删除原 Interop 引用,添加 using ClosedXML.Excel; public static bool ExportToExcel(List<Student> students, string filePath) { try { using (var workbook = new XLWorkbook()) { var ws = workbook.Worksheets.Add("学生信息"); ws.Cell(1, 1).Value = "学号"; ws.Cell(1, 2).Value = "姓名"; ws.Cell(1, 3).Value = "性别"; ws.Cell(1, 4).Value = "班级"; ws.Cell(1, 5).Value = "入学日期"; int row = 2; foreach (var s in students) { ws.Cell(row, 1).Value = s.StudentNo; ws.Cell(row, 2).Value = s.Name; ws.Cell(row, 3).Value = s.Gender; ws.Cell(row, 4).Value = s.ClassName; ws.Cell(row, 5).Value = s.EnrollmentDate.ToString("yyyy-MM-dd"); row++; } workbook.SaveAs(filePath); } return true; } catch (Exception ex) { Logger.LogError($"ExportToExcel failed: {ex.Message}"); return false; } }注意:
ClosedXML不依赖 Office 安装,纯托管代码,部署更轻量。
4.4 现象:修改学生姓名后,点击“保存”无反应,日志中出现SqlException: String or binary data would be truncated
原因:Students.Name字段在数据库中定义为NVARCHAR(20),但用户输入了超过20个汉字(每个汉字占2字节,20个汉字即40字节,超出NVARCHAR(20)的20字符上限)。
解决:在StudentService.UpdateStudent方法中增加长度校验:
if (student.Name.Length > 20) { MessageBox.Show($"姓名不能超过20个字符,当前输入{student.Name.Length}个", "输入超限"); return false; }同时,在数据库层面扩大字段:
ALTER TABLE Students ALTER COLUMN Name NVARCHAR(50);4.5 现象:安装 MSI 包后,程序启动报错“无法加载 DLL 'SQLite.Interop.dll'”
原因:源码包中混入了 SQLite 版本的测试分支文件(/Source/StudentManagementSystem_Sqlite/),而主工程误引用了 SQLite 相关 DLL。
解决:在 VS 解决方案资源管理器中,右键点击项目 → “引用” → 查找System.Data.SQLite和SQLite.Interop,右键卸载。确认DAL/DatabaseHelper.cs中连接字符串为 SQL Server 格式(含Data Source=),而非 SQLite 的Data Source=xxx.db。
5. 进阶技巧:让系统支撑真实教务场景的四个关键改造点
5.1 支持批量导入:从 Excel 读取学生数据并智能去重
教务老师常需一次性导入上百条新生数据。原系统仅支持单条录入,我们通过ClosedXML快速增强:
- 在
FormMain中添加“导入Excel”按钮及OpenFileDialog; - 实现导入逻辑(关键代码):
private void btnImportExcel_Click(object sender, EventArgs e) { using (var dialog = new OpenFileDialog()) { dialog.Filter = "Excel files (*.xlsx)|*.xlsx|All files (*.*)|*.*"; if (dialog.ShowDialog() != DialogResult.OK) return; try { var importedCount = 0; using (var workbook = new XLWorkbook(dialog.FileName)) { var ws = workbook.Worksheet(1); // 假设Excel第一行为标题:学号,姓名,性别,班级名称,入学日期 for (int row = 2; row <= ws.LastRowUsed().RowNumber(); row++) { var studentNo = ws.Cell(row, 1).Value.ToString().Trim(); var name = ws.Cell(row, 2).Value.ToString().Trim(); var gender = ws.Cell(row, 3).Value.ToString().Trim(); var className = ws.Cell(row, 4).Value.ToString().Trim(); var enrollDateStr = ws.Cell(row, 5).Value.ToString().Trim(); if (string.IsNullOrEmpty(studentNo) || string.IsNullOrEmpty(name)) continue; // 查询班级ID int classID = ClassService.GetClassIDByName(className); if (classID == -1) // 未找到班级,自动创建 { classID = ClassService.AddClass(className); } DateTime enrollDate = DateTime.TryParse(enrollDateStr, out var date) ? date : DateTime.Today; var student = new Student { StudentNo = studentNo, Name = name, Gender = gender == "女" ? "女" : "男", ClassID = classID, EnrollmentDate = enrollDate }; if (StudentService.AddStudent(student)) importedCount++; } } MessageBox.Show($"成功导入 {importedCount} 条学生记录", "导入完成"); } catch (Exception ex) { MessageBox.Show($"导入失败:{ex.Message}", "错误"); } } }此改造点价值:
- 零数据库侵入:复用现有
ClassService和StudentService,不改动表结构; - 容错性强:跳过空行、自动创建不存在的班级、日期解析失败则用当天日期;
- 性能可控:逐行处理,内存占用低,万级数据无压力。
5.2 成绩分析视图:用 Chart 控件绘制班级平均分雷达图
原系统仅有成绩录入,缺乏分析能力。我们利用 WinForms 自带System.Windows.Forms.DataVisualization.Charting添加可视化:
- 在
FormMain中拖入Chart控件,命名为chartClassAvg; - 在“数据报表”页签中添加“生成分析图”按钮;
- 点击事件中填充数据(示例:各科平均分):
private void btnGenerateChart_Click(object sender, EventArgs e) { var subjects = new[] { "C#编程", "数据库原理", "Web前端", "算法设计", "英语" }; var avgScores = new double[subjects.Length]; for (int i = 0; i < subjects.Length; i++) { avgScores[i] = ScoreService.GetAverageScoreBySubject(subjects[i]); } chartClassAvg.Series.Clear(); var series = chartClassAvg.Series.Add("班级平均分"); series.ChartType = SeriesChartType.Radar; // 雷达图 series.IsValueShownAsLabel = true; for (int i = 0; i < subjects.Length; i++) { series.Points.AddXY(subjects[i], avgScores[i]); } chartClassAvg.ChartAreas[0].AxisX.MajorGrid.Enabled = false; chartClassAvg.ChartAreas[0].AxisY.MajorGrid.Enabled = false; }效果:生成五边形雷达图,直观对比各科教学效果。此功能无需额外 NuGet 包,.NET Framework 4.7.2+ 原生支持。
5.3 权限分级:为管理员与普通教师分配不同操作菜单
当前系统无权限控制,所有用户功能一致。我们通过简单角色标记实现基础分级:
- 在
StudentDB中新增Users表:
CREATE TABLE Users ( UserID INT IDENTITY(1,1) PRIMARY KEY, Username NVARCHAR(20) NOT NULL UNIQUE, PasswordHash NVARCHAR(128) NOT NULL, -- 存储SHA256哈希值 Role NVARCHAR(10) CHECK (Role IN ('Admin', 'Teacher')) -- 角色字段 );- 登录窗体
LoginForm.cs验证后,将Role存入静态变量:
public static string CurrentUserRole { get; set; } // 登录成功后赋值 CurrentUserRole = reader["Role"].ToString();- 主窗体
FormMain_Load中动态隐藏菜单项:
if (LoginForm.CurrentUserRole != "Admin") { tabControl1.TabPages.Remove(tabPageClass); // 移除班级管理页签 tabControl1.TabPages.Remove(tabPageSystem); // 移除系统设置页签(假设有) }此改造点意义:
- 最小改动:不引入复杂权限框架,用数据库字段+条件判断实现核心隔离;
- 可审计:所有用户操作日志中可追加
CurrentUserRole字段,满足基本合规要求; - 易扩展:后续增加“教务员”角色,只需改
CHECK约束和条件判断即可。
5.4 打印功能增强:生成带页眉页脚、页码、学校Logo的正式成绩单
原“打印预览”仅调用PrintPreviewDialog显示 DataGridView,格式简陋。我们用PrintDocument类定制:
- 新建
PrintStudentReport.cs类,继承PrintDocument; - 重写
OnPrintPage方法(关键渲染逻辑):
protected override void OnPrintPage(PrintPageEventArgs e) { base.OnPrintPage(e); var fontTitle = new Font("微软雅黑", 16, FontStyle.Bold); var fontHeader = new Font("微软雅黑", 10); var fontContent = new Font("微软雅黑", 9); // 页眉:学校Logo + 标题 if (e.PageSettings.PrinterResolution.X > 0) { var logoRect = new Rectangle(50, 30, 80, 40); e.Graphics.DrawImage(Properties.Resources.SchoolLogo, logoRect); // 需提前添加Logo资源 } e.Graphics.DrawString("XX大学学生成绩单", fontTitle, Brushes.Black, 150, 30); // 表格内容(此处简化,实际需循环绘制) var y = 100; e.Graphics.DrawString($"学号:{currentStudent.StudentNo}", fontContent, Brushes.Black, 50, y); e.Graphics.DrawString($"姓名:{currentStudent.Name}", fontContent, Brushes.Black, 50, y + 20); e.Graphics.DrawString($"班级:{currentStudent.ClassName}", fontContent, Brushes.Black, 50, y + 40); // 页脚:页码 var pageNum = e.PageSettings.LowerMargin + e.PageSettings.BottomMargin - 20; e.Graphics.DrawString($"第 {e.PageSettings.FromPage} 页,共 {e.PageSettings.ToPage} 页", fontHeader, Brushes.Gray, 300, pageNum); e.HasMorePages = false; // 单页打印 }此技巧价值:
- 脱离Word依赖:无需调用 Office COM,纯 GDI+ 绘制,部署即用;
- 品牌统一:Logo、字体、间距均可代码控制,输出符合学校VI规范;
- 可复用:同一套
PrintDocument逻辑稍作修改,即可用于“班级花名册”“成绩汇总表”等多类文档。
从那以后我每次交付教务类桌面系统,都强制走一遍这四个改造:先加批量导入防老师熬夜复制粘贴,再塞个雷达图让领导一眼看懂教学短板,接着用角色字段卡住非授权操作,最后用PrintDocument把成绩单印得像教务处红头文件——不是为了炫技,是让系统真正长在业务流程里,而不是浮在课程设计作业表上。希望帮到你。
本文还有配套的精品资源,点击获取