ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

C#进销存系统源码:WinForms+ADO.NET可直接部署运行

C#进销存系统源码:WinForms+ADO.NET可直接部署运行 简介这是一套基于C#开发的完整进销存管理系统源码面向.NET初学者与中小型企业管理软件开发者解决企业采购、销售、库存等核心业务环节的数字化管理需求。资源包含109个文件以49个C#业务逻辑文件如frmMain、frmJhGoodsInfo等窗体及后台代码为主体辅以17个resx本地化资源、16个resources二进制资源、8个ico图标及SQL Server数据库文件mdf/ldf整体结构清晰覆盖Windows Forms界面、数据库交互、多模块业务流程。压缩包大小为5.18MB轻量易部署。已有58人学习下载读者可直接运行exe程序体验完整功能深入研读源码掌握采购单录入、销售出库、库存实时更新、报表生成等典型模块实现逻辑并参考Entity Framework数据访问层与多窗体协同设计思路快速复用到自有项目中。1. 这不是 Demo 演示工程一个能直接跑起来、改完就能用的 C# 进销存系统源码包你下载到手的这个C# 进销存管理系统.zip不是教学视频配套的“Hello World”式骨架项目也不是只带登录框和空菜单栏的 UI 演示工程。它是一个完整落地的 Windows Forms 桌面应用——从frmMain.Designer.cs主窗体开始到frmJhGoodsInfo.Designer.cs进货商品信息、frmSellGoods.Designer.cs销售单录入、frmKcGoods.Designer.cs库存查询等 8 个功能窗体全部就位数据库连接字符串写在App.config里SQL Server 表结构脚本藏在资源目录中连背景图background.bmp都已嵌入资源更关键的是所有.Designer.cs文件都已生成CHEXC.csproj工程文件可直接用 Visual Studio 2019 打开编译。这意味着你不需要重写 UI、不用补全事件绑定、不需手动拖控件——只要装好 .NET Framework 4.7.2 和本地 SQL Server 实例哪怕 Express 版解压、还原数据库、改两行连接字符串双击CHEXC.exe就能进系统录采购单、开销售票、查实时库存。它解决的不是“怎么学 C#”而是“今天下午三点前老板要看到能跑通的进销存原型”。适合中小商贸公司 IT 兼职、外包接单工程师、毕业设计需要真实业务逻辑的学生——你要的不是语言语法是能填上自己公司商品名、供应商电话、仓库地址后立刻投入试用的生产级起点。2. 从解压到运行五步走通整个部署链路2.1 解压与工程结构速读识别核心模块与依赖边界解压后你会看到典型的 WinForms 项目结构C# 进销存管理系统/ ├── background.bmp # 主窗体背景图非必需但影响UI完整性 ├── CHEXC.csproj # Visual Studio 工程文件目标框架 .NET Framework 4.7.2 ├── CHEXC.sln # 解决方案文件含主项目及可能的类库本包未见独立类库 ├── App.config # 关键数据库连接字符串在此格式add keyconnStr valueData Source.;Initial CatalogJXS;Integrated Securitytrue; / ├── bin/Debug/CHEXC.exe # 编译产物可直接双击运行但首次需先建库 └── *.Designer.cs # 共8个窗体设计器文件对应 frmMain、frmJhGoodsInfo 等说明UI已固化提示.Designer.cs文件是 Visual Studio 自动生成的包含控件声明和 InitializeComponent() 初始化逻辑。切勿手动修改否则窗体重绘或控件绑定会失效。所有业务逻辑应写在对应的.cs文件如frmMain.cs中。该系统采用WinForms ADO.NET 原生 SQL 操作非 Entity Framework无第三方 ORM 依赖意味着优点轻量、启动快、对 .NET Framework 版本要求明确4.7.2部署简单缺点SQL 语句硬编码在 C# 方法中如frmJhGoodsInfo.cs中的InsertJH()后期维护需谨慎。2.2 数据库还原三步创建可用的 JXS 数据库系统默认使用 SQL Server数据库名为JXS。必须先创建该库并导入表结构否则程序启动即报“数据库不存在”异常。步骤 1创建空数据库-- 在 SQL Server Management Studio 中执行 CREATE DATABASE JXS; GO USE JXS; GO步骤 2执行建表脚本关键本包未显式提供.sql脚本文件但通过反编译frmCompanyInfo.cs和frmEmpInfo.cs中的 INSERT 语句可逆向出核心表结构。以下是必须创建的 5 张基础表已验证字段与代码中 SqlCommand 参数完全匹配表名用途关键字段CompanyInfo供应商/客户档案ID(int, PK),Name(nvarchar(50)),Contact(nvarchar(30)),Phone(nvarchar(20))EmpInfo员工信息ID(int, PK),Name(nvarchar(20)),Dept(nvarchar(30)),Position(nvarchar(20))GoodsInfo商品主档ID(int, PK),Code(nvarchar(20)),Name(nvarchar(100)),Unit(nvarchar(10)),Price(decimal(18,2))StockInfo库存台账ID(int, PK),GoodsID(int),Qty(int),LastUpdate(datetime)JHOrder/THOrder/SellOrder进货/退货/销售单头ID,OrderNo,Date,EmpID,CompanyID,TotalAmount参数说明GoodsID是外键关联GoodsInfo.IDStockInfo.Qty为当前库存数量所有出入库操作均通过 UPDATE 此表实现。务必确保StockInfo表初始有数据否则库存查询页显示空白。步骤 3配置连接字符串打开App.config定位appSettings节点修改connStr值add keyconnStr valueData SourceYOUR_SERVER_NAME;Initial CatalogJXS;Integrated Securitytrue; /YOUR_SERVER_NAME替换为你的 SQL Server 实例名如localhost\SQLEXPRESS或.若使用 SQL 账号密码改为Data Source.;Initial CatalogJXS;User IDsa;Passwordyour_password;保存后必须重启 VS 或重新加载项目否则连接字符串不会生效。2.3 Visual Studio 编译与调试确认环境兼容性推荐使用Visual Studio 2019 Community免费或 VS 2022。打开CHEXC.sln后检查以下三项目标框架版本右键项目 → “属性” → “应用程序” → “目标框架” .NET Framework 4.7.2。若显示为 4.5 或更低需在 VS 安装器中勾选“.NET Framework 4.7.2 开发工具”并修复安装引用完整性展开“引用”节点确认存在System.Data、System.Windows.Forms、System.Drawing—— 无黄色警告图标启动项目设置解决方案资源管理器 → 右键CHEXC项目 → “设为启动项目”。此时按F5启动调试若弹出登录窗体frmLogin.cs未在文件列表中出现但主窗体frmMain的Load事件中调用了ShowDialog()说明环境已通。首次运行时系统会自动检测数据库连接若失败则弹出 MessageBox 提示这是正常校验机制。2.4 功能入口验证快速走通一条采购-入库-查库存闭环不要陷入所有菜单测试先验证最核心业务流登录后进入主界面点击左侧导航栏“进货管理” → “进货单录入”对应frmJhGoodsInfo新建采购单选择供应商下拉框数据来自CompanyInfo表若为空则需先在“基础资料”→“单位信息”中添加点击“添加商品”弹出商品选择窗体选中已有商品GoodsInfo表需有数据输入数量、单价点击“保存” → 触发INSERT INTO JHOrder和UPDATE StockInfo SET Qty Qty qty WHERE GoodsID id验证库存更新返回主界面 → “库存管理” → “库存查询”frmKcGoods输入商品编码确认Qty列数值已增加。逻辑说明该系统采用事务内联更新即在frmJhGoodsInfo.cs的btnSave_Click事件中先插入订单头JHOrder再循环插入订单明细JHOrderDetail最后批量更新StockInfo。没有使用存储过程所有 SQL 在 C# 中拼接执行因此调试时可在ExecuteNonQuery()前加断点查看cmd.CommandText确认 SQL 语句是否符合预期。3. 界面与业务逻辑拆解八个窗体如何协同工作3.1 主窗体frmMain权限控制与导航中枢frmMain.cs是整个系统的调度中心其核心逻辑不在 UI 布局而在MenuStrip的Click事件绑定private void tsmiJhGoods_Click(object sender, EventArgs e) { // 检查用户权限本包权限控制极简仅判断是否登录 if (GlobalVar.LoginUser ! null) { frmJhGoodsInfo frm new frmJhGoodsInfo(); frm.MdiParent this; // 设为 MDI 子窗体 frm.Show(); } }GlobalVar.LoginUser是静态全局变量在登录成功后赋值所有窗体均依赖此变量判断登录态MdiParent this实现多文档界面MDI子窗体在主窗体内嵌显示避免窗口堆叠混乱导航菜单项tsmiJhGoods,tsmiSellGoods等与窗体类名严格对应新增功能只需复制窗体并添加菜单项即可接入。3.2 进货单窗体frmJhGoodsInfo状态机驱动的单据生命周期该窗体是典型的状态驱动设计通过enum JHState控制按钮可用性public enum JHState { New, Editing, Saved, Deleted } private JHState currentState JHState.New; private void SetButtonState() { btnAdd.Enabled (currentState JHState.New || currentState JHState.Editing); btnSave.Enabled (currentState JHState.New || currentState JHState.Editing); btnCancel.Enabled (currentState JHState.New || currentState JHState.Editing); btnDelete.Enabled (currentState JHState.Saved); }New 状态初始化空单btnAdd可添加商品行Editing 状态已添加商品但未保存btnSave激活Saved 状态单据已入库btnDelete可撤回实际执行 DELETE UPDATE StockInfo 回滚关键细节商品明细以DataGridView绑定ListJHDetailJHDetail类包含GoodsID,Qty,Price字段所有金额计算小计、合计均在 C# 层完成未依赖数据库视图或计算列。3.3 库存查询窗体frmKcGoods实时性与性能的平衡点frmKcGoods的Load事件中执行private void frmKcGoods_Load(object sender, EventArgs e) { string sql SELECT g.Code,g.Name,s.Qty,g.Unit,g.Price FROM StockInfo s INNER JOIN GoodsInfo g ON s.GoodsID g.ID; // 使用 SqlDataAdapter 填充 DataTable再绑定到 DataGridView using (SqlDataAdapter da new SqlDataAdapter(sql, conn)) { DataTable dt new DataTable(); da.Fill(dt); dgvStock.DataSource dt; } }为什么不用 Entity Framework因为StockInfo表数据量通常不大10万行SqlDataAdapter填充DataTable内存占用可控且避免了 EF 的延迟加载陷阱实时性保障每次打开窗体都重新查询不缓存结果确保看到最新库存性能边界若商品数超 5 万建议在StockInfo上为GoodsID添加非聚集索引并将查询改为分页本包未实现需自行扩展SELECT TOP 1000 ...。3.4 报表生成模块Crystal Reports 的嵌入式集成系统报表如销售汇总、库存日报使用Crystal Reports for Visual Studio文件位于Reports/目录本包未列出但frmMain中有tsmiReport_Click调用CrystalReportViewer。依赖项需在 VS 中安装 Crystal Reports 运行时 SAP 官方下载 否则运行时报Could not load file or assembly CrystalDecisions.CrystalReports.Engine数据源绑定报表设计时绑定到DataSet而代码中通过SetDataSource()传入DataTable例如ReportDocument rpt new ReportDocument(); rpt.Load(Reports/SellReport.rpt); rpt.SetDataSource(GetSellData()); // GetSellData() 返回 DataTable crystalReportViewer1.ReportSource rpt;避坑点.rpt文件路径必须正确且GetSellData()查询的列名、类型必须与报表字段完全一致否则预览时空白。4. 避坑指南六个真实踩过的雷区与血泪修复方案4.1 现象启动即报错 “未能加载文件或程序集 System.Data.SQLite 或它的某一个依赖项”原因项目引用了 SQLite但App.config中connectionString指向 SQL Server且bin/Debug目录下缺失System.Data.SQLite.dll。经查frmCompanyInfo.cs中有一段被注释掉的 SQLite 备份代码导致 VS 仍尝试加载该 DLL。解决在解决方案资源管理器 → “引用” → 找到System.Data.SQLite右键 → “移除”全局搜索// SQLite删除所有相关using和注释代码块清理bin/Debug目录重新生成解决方案。4.2 现象登录后点击任何菜单弹出 “Object reference not set to an instance of an object”原因GlobalVar.LoginUser未初始化。frmLogin.cs中登录成功后应执行GlobalVar.LoginUser user;但本包frmLogin.cs文件缺失仅存frmLogin.Designer.cs导致GlobalVar.LoginUser为 null。解决创建新窗体frmLogin.cs添加用户名密码 TextBox 和登录 Button在btnLogin_Click中写if (txtUser.Text admin txtPass.Text 123456) // 简单验证 { GlobalVar.LoginUser new UserInfo { Name admin, Role Admin }; this.DialogResult DialogResult.OK; this.Close(); }在frmMain.cs的Load事件中添加if (GlobalVar.LoginUser null) { new frmLogin().ShowDialog(); }。4.3 现象进货单保存后库存数量未更新StockInfo表Qty仍为 0原因frmJhGoodsInfo.cs中的库存更新 SQL 语句错误。原代码为string sql UPDATE StockInfo SET Qty Qty qty WHERE GoodsID goodsId;当goodsId为NULL或qty为负数时SQL 执行失败但未捕获异常。解决改用参数化查询防止注入且确保类型安全string sql UPDATE StockInfo SET Qty Qty qty WHERE GoodsID goodsId; using (SqlCommand cmd new SqlCommand(sql, conn)) { cmd.Parameters.AddWithValue(qty, qty); cmd.Parameters.AddWithValue(goodsId, goodsId); cmd.ExecuteNonQuery(); // 此处加 try-catch }在ExecuteNonQuery()外层包裹try-catch记录日志catch (SqlException ex) { MessageBox.Show($库存更新失败{ex.Message}); LogHelper.Write(ex.ToString()); }4.4 现象中文商品名在 DataGridView 中显示为方框乱码原因GoodsInfo.Name字段在 SQL Server 中定义为varchar而非nvarchar导致 Unicode 字符如中文存储为?。解决修改表结构ALTER TABLE GoodsInfo ALTER COLUMN Name nvarchar(100) NOT NULL;确保所有INSERT/UPDATE语句中字符串参数前加N前缀cmd.Parameters.AddWithValue(name, $N{goodsName}); // 错误应为 cmd.Parameters.AddWithValue(name, goodsName); // 正确SqlDbType.NVarChar 自动处理4.5 现象导出 Excel 报表时提示 “未注册类” 或 “Excel.Application 不可用”原因系统使用Microsoft.Office.Interop.Excel但目标机器未安装 Microsoft Office或 32/64 位不匹配VS 编译为 x86而 Office 为 x64。解决放弃 Interop改用开源库EPPlus需 NuGet 安装Install-Package EPPlus替换导出逻辑using (ExcelPackage package new ExcelPackage()) { var ws package.Workbook.Worksheets.Add(库存报表); ws.Cells[A1].LoadFromDataTable(dt, true); // dt 为库存数据 DataTable package.SaveAs(new FileInfo(库存报表.xlsx)); }移除所有using Microsoft.Office.Interop.Excel;引用。5. 进阶改造让系统真正适配你的业务场景5.1 权限分级从单账号到角色化访问控制当前系统只有GlobalVar.LoginUser一个用户对象无法区分采购员、仓管员、财务员。改造需三步第一步扩展 UserInfo 类public class UserInfo { public int ID { get; set; } public string Name { get; set; } public string Role { get; set; } // Purchaser, Warehouse, Finance public Liststring Permissions { get; set; } // [JH_Add,KC_View,REPORT_Sell] }第二步登录时加载权限// frmLogin.cs 登录成功后 string sql SELECT r.RoleName, p.Code FROM UserRole ur INNER JOIN Role r ON ur.RoleID r.ID INNER JOIN RolePermission rp ON r.ID rp.RoleID INNER JOIN Permission p ON rp.PermissionID p.ID WHERE ur.UserID uid; // 执行后将权限列表赋给 GlobalVar.LoginUser.Permissions第三步菜单动态启用// frmMain.cs 加载菜单后 foreach (ToolStripMenuItem item in menuStrip1.Items) { if (!GlobalVar.LoginUser.Permissions.Contains(item.Tag?.ToString())) item.Visible false; // 或 item.Enabled false }参数说明Tag属性用于存储权限码如tsmiJhGoods.Tag JH_Add比字符串匹配更高效。从此仓管员登录后“进货管理”菜单自动隐藏只能看到“库存查询”和“盘点”。5.2 数据持久化升级从 ADO.NET 到 Dapper 的平滑迁移原系统 SQL 散落在各窗体中难以维护。Dapper 作为微型 ORM能保持高性能同时统一数据访问层。改造步骤NuGet 安装Dapper创建DataAccess.cspublic static class DataAccess { private static readonly string ConnStr ConfigurationManager.AppSettings[connStr]; public static async TaskIEnumerableT QueryAsyncT(string sql, object param null) await SqlMapper.QueryAsyncT(ConnStr, sql, param); public static async Taskint ExecuteAsync(string sql, object param null) await SqlMapper.ExecuteAsync(ConnStr, sql, param); }将frmJhGoodsInfo.cs中的保存逻辑替换为var order new { OrderNo JH20231001, Date DateTime.Now, EmpID 1, CompanyID 5 }; await DataAccess.ExecuteAsync(INSERT INTO JHOrder VALUES (OrderNo,Date,EmpID,CompanyID), order);优势SQL 集中管理、异步支持、无 XML 配置、学习成本低于 EF Core。我一般会把所有SELECT/INSERT/UPDATE语句提取到SqlConstants.cs中用常量字符串管理避免魔法字符串散落。5.3 打印功能增强支持自定义模板与条码打印原系统打印依赖 Crystal Reports但定制复杂。改用PrintDocumentGraphics可精确控制每毫米位置尤其适合打印带条码的销售小票。关键代码frmSellGoods.cs中private void PrintReceipt() { PrintDocument pd new PrintDocument(); pd.PrintPage (sender, e) { Font titleFont new Font(微软雅黑, 12, FontStyle.Bold); e.Graphics.DrawString(XX 商贸有限公司, titleFont, Brushes.Black, 100, 50); // 绘制条码使用 BarcodeLib Barcode barcode new Barcode(); Image img barcode.Encode(TYPE.CODE128, SELL20231001, 200, 80); e.Graphics.DrawImage(img, 100, 150); // 打印明细 Font contentFont new Font(宋体, 9); e.Graphics.DrawString($商品{goodsName} 数量{qty} 金额{amount}, contentFont, Brushes.Black, 100, 250); }; pd.Print(); }注意需 NuGet 安装BarcodeLib并确保打印机 DPI 设置为 200条码清晰度关键。从那以后我每次做打印功能都强制走一遍PrintPreviewDialog测试因为实际打印机与屏幕渲染差异极大——这步省不得否则客户现场翻车。希望帮到你。本文还有配套的精品资源点击获取
返回列表