ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

C#进销存管理系统实战:WinForms工程拆解与SQL Server数据库对接

C#进销存管理系统实战:WinForms工程拆解与SQL Server数据库对接 简介《C# 进销存管理系统》压缩包是一份面向C#初、中级学习者及中小型企业管理软件开发者的完整项目资源覆盖采购、销售、库存、报表与财务等核心业务的编码实现。资源共109个文件压缩后约5.18MB其中以49个.cs源码文件为主包含窗体逻辑与业务处理代码17个.resx和16个.resources文件用于界面资源8个.ico图标及若干图片完善界面视觉同时附带SQL Server数据库文件.mdf/.ldf、可执行程序.exe和项目工程文件.sln/.csproj便于直接运行、调试与二次开发。资源对WinForms布局、实体框架或SQL操作、数据验证及异常处理等常见技术点有直观呈现适合作为课程设计或毕业设计的参考模板。目前已有60人学习下载对于希望深入理解进销存业务流程和C#桌面应用开发模式的读者这份资源能帮助快速建立起从数据库设计到界面交互的完整认知。1. C# 进销存管理系统先搞清楚 ZIP 里装的是什么再决定要不要跑「C# 进销存管理系统」这个资源包不是文档是个能打开的 WinForms 工程。压缩包里是一堆 .Designer.cs 窗体文件进货、销售、退货、库存、员工、公司六个业务窗体加上 frmMain 主窗体。它解决小企业最现实的问题进了多少货、卖了多少、库存还剩多少。对做课程设计的学生它是现成的窗体布局和数据库交互参考对刚碰 .NET 的开发人员DataGridView 绑定、SQL 参数化、主从表事务这些高频写法都能找到对应物。它适合照改不适合照抄。如果你只想要注册登录 demo这套系统偏重了要的是采购-销售-库存闭环它够用。下面按「跑起来、拆窗体、对账、避坑」过一遍。2. 把 ZIP 变成能 F5 的工程环境、数据库还原与连接字符串三关2.1 环境三件套Visual Studio、.NET Framework 与 SQL Server 怎么对齐解压后别急着双击 .sln先确认三样东西Visual Studio 版本、.NET Framework 目标版本、SQL Server 实例。这套工程的 .csproj 是传统格式不是 SDK-Style界面用的是 WinForms 而不是 WPF正常情况下 VS2015 到 VS2022 都能打开。但 VS2022 得装好「使用 .NET Framework 4 开发」这个工作负载否则打开后设计器会白屏。用记事本打开 .csproj找TargetFrameworkVersionv4.x/TargetFrameworkVersion这一行就能看到目标版本。如果写的是 v4.0 或 v4.5机器上装了 4.8 也兼容向下兼容不用慌。数据库方面SQL Server 2008R2、2012、2016、2019 都能跑装 Express 版就够学习用关键是实例名要记对——默认实例是.命名实例是.\SQLEXPRESS连接串里写错一个点就连不上。注意这类课程设计项目里版本报错九成不是代码问题是 VS 组件没装全。先补工作负载再考虑改代码。2.2 解开 ZIP 先分层csproj、Designer.cs、GenerateResource.Cache 各是什么角色压缩包里一眼看过去全是 .Designer.cs容易吓到人。拆开看其实就是几类角色.sln和.csproj是工程入口双击打开用*.Designer.cs是 Visual Studio 设计器自动生成的布局代码负责告诉程序按钮在哪个坐标、TextBox 挂了什么事件每个窗体还对应一个普通.cs业务代码文件和 Designer.cs 成对出现按钮点击逻辑、数据库操作都写在这里。至于CHEXC.csproj.GenerateResource.Cache这是个编译缓存不是源码随手删掉都不影响最终编译。还有一张Background.bmp多半是 frmMain 或登录窗体的背景图。为什么先分层因为经常有人把 Designer.cs 当普通代码手动改改坏之后整个窗体在设计器里打不开——能用设计器改的就别手写声明。打开工程前先清一次缓存省得撞上文件占用del /s /q *.GenerateResource.Cache这条命令会把当前目录及子目录里的生成缓存全清掉。删完再开 .sln能避开很多莫名其妙的加载错误。2.3 数据库还原与表结构从窗体名反推核心表进销存系统的命根子是数据库。从窗体名能反推出核心表结构这套工程的对应关系大致如下业务窗体主表明细/关联表frmJhGoodsInfoPurchaseOrder 采购单PurchaseDetail、SupplierfrmSellGoodsSalesOrder 销售单SalesDetail、CustomerfrmThGoodsInfoReturnOrder 退货单ReturnDetail、SalesOrderfrmKcGoodsInventory 库存表Product、WarehousefrmEmpInfoEmployee 员工表账号密码字段frmCompanyInfoCompany 公司表单条记录数据库还原一般有两种形式压缩包里带.bak的用 SSMS 右键「数据库」→「还原数据库」选文件带.sql脚本的直接执行脚本建库。假如两种都没有那就得按窗体字段反推建表工作量不小建议先在包里翻一遍有没有 SQL 目录。连接字符串通常在App.config里也可能写死在某个DbHelper类中。典型写法长这样connectionStrings add nameCHEXC_CONN connectionStringData Source.;Initial CatalogCHEXC;User IDsa;Passwordyour_password;Persist Security InfoTrue providerNameSystem.Data.SqlClient / /connectionStrings各参数含义Data Source是服务器实例名Initial Catalog是数据库名User ID/Password是 SQL Server 登录账号如果改成Integrated SecurityTrue则走 Windows 身份验证。课程设计代码大多写死 sa 账号你本机要是没给 sa 设密码或没开混合验证就会卡在这一步。2.4 第一次 F5从登录到主界面的完整启动链路环境就绪后按 F5入口在Program.cs的Main方法[STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); // frm 在这里是启动窗体, 通常是登录窗体 Application.Run(new frm()); }启动流程一般是Main里Application.Run一个启动窗体登录窗体里输入员工账号密码去 Employee 表验证通过后把当前窗体 hide 掉再 new 一个 frmMain 出来 Show。第一次 F5 时常见的结局不是看到登录框而是先弹一个 SqlException——这其实是好事说明代码已经跑到数据库访问那一步了只是连接没通。按 2.3 的连接串逐项核对绝大多数能解决。3. 拆窗体改业务采购、销售、退货、库存六张窗体的实现套路与改法3.1 进货单 frmJhGoodsInfo供应商下拉、明细 DataGridView 与入库事务frmJhGoodsInfo 进货单是这套系统里最标准的「主从表」窗体。上半部分是头信息供应商下拉框、进货日期、单据编号、经办人下半部分是一个 DataGridView每一行选商品、填数量、填单价金额自动算。保存时要干两件事把头部信息插入采购主表再循环明细行插入采购明细表同时把每件商品的库存数量加上本次入库量。这里有个常见误用为了图省事把明细循环和主表插入拆成两个独立方法、各开各的连接一旦中途报错就会出现「有主单没明细」或「明细存了库存没加」的脏数据。正确做法是把整个保存包进一个事务任何一个环节失败就全部回滚。采购成本这块也要注意进货单的单价就是入库成本价报表里的毛利 销售金额 - 对应进货成本。如果进货价有波动课程设计一般按最新进价或平均成本算别混用否则对账时永远差几分钱。3.2 销售单 frmSellGoods客户选择、价格计算与库存扣减时机销售单的套路和进货单几乎一样但多了两个关键点价格计算和库存扣减。价格通常在 DataGridView 的CellEndEdit事件里按商品单价自动带出数量一改金额实时刷新库存扣减则必须放在保存动作里不能在单元格编辑时就扣——用户改了几行又取消库存已经被白扣了。扣库存前还要先查一遍现存量数量不足直接弹窗拒绝别等 UPDATE 完才发现负库存。核心保存结构如下using (SqlConnection conn new SqlConnection(connStr)) { conn.Open(); SqlTransaction tran conn.BeginTransaction(); try { // 1. 插入销售主表, 拿到新单号 SqlCommand cmdHead new SqlCommand( INSERT INTO SalesOrder(OrderNo, CustomerID, OrderDate, EmpID) VALUES(no, cid, date, eid); SELECT SCOPE_IDENTITY();, conn, tran); cmdHead.Parameters.AddWithValue(no, GenerateOrderNo()); cmdHead.Parameters.AddWithValue(cid, cmbCustomer.SelectedValue); cmdHead.Parameters.AddWithValue(date, dtpDate.Value); cmdHead.Parameters.AddWithValue(eid, currentEmpId); int orderId Convert.ToInt32(cmdHead.ExecuteScalar()); // 2. 逐行检查库存、插入明细、扣减现存量 foreach (DataGridViewRow row in dgvDetail.Rows) { if (row.IsNewRow) continue; int productId Convert.ToInt32(row.Cells[ProductID].Value); int qty Convert.ToInt32(row.Cells[Qty].Value); SqlCommand cmdCheck new SqlCommand( SELECT Qty FROM Inventory WHERE ProductIDpid, conn, tran); cmdCheck.Parameters.AddWithValue(pid, productId); if (Convert.ToInt32(cmdCheck.ExecuteScalar()) qty) throw new Exception(商品库存不足); // 插入明细和扣库存的 Command 均传入同一个 tran // cmdDetail / cmdStock 与 cmdHead 写法一致, 参数用 oid/pid/qty tran.Commit(); } } catch (Exception ex) { tran.Rollback(); MessageBox.Show(保存失败: ex.Message); } }注意三点SqlTransaction必须和开连接的SqlConnection配对所有SqlCommand都要传入同一个tran对象Commit放在 try 的末尾Rollback放在 catch 里。如果你在源码里看到每个 Command 单独 new 一个 Connection那就是一份需要改造的隐患代码。另外AddWithValue在课程设计里很常见但生产环境建议用类型化参数避免 decimal、datetime 隐式转换带来的精度问题。3.3 退货单 frmThGoodsInfo红冲回补与负数单据的设计退货单是进销存里最容易写乱的地方。常见实现有两种一种是在原销售单上做「红冲」把原单状态改成已退货、库存回补另一种是做一个独立的 ReturnOrder 退货单明细里记录商品、数量和退货原因同时把数量回补到库存。课程设计大多用第二种数据结构清晰报表也好统计。无论是哪种要守住一条原则退货必须让库存增加、让销售净额减少。如果退货单做成普通负数单据报表统计时就要把「销售金额 - 退货金额」当成净销售而不是直接在销售表里存负数——后者在联表查询时很容易把负数行漏掉对账那天你会感谢自己当初分了两张表。3.4 库存查询 frmKcGoods实时数量、预警颜色与盘点对账frmKcGoods 库存查询窗体通常是一张只读的 DataGridView展示商品编码、名称、规格、现存量、安全库存、存放仓库。实时数量来自库存表而不是在查询时用 SUM 去现场算——进销存里库存表是独立维护的累计结果现场算那是报表干的事放查询里又慢又容易错。预警功能一般通过CellFormatting事件实现当前量小于安全库存时把该行背景刷成浅黄或浅红。这段代码不复杂却是整套系统里最直观体现业务规则的地方。盘点对账时要先冻结库存表导出一份账面数量实盘数录入后做差异调整调整记录最好带上操作人姓名和时间不然出了问题找不到责任人。3.5 员工与公司信息基础资料 CRUD 的通用模板frmEmpInfo 是典型的基础资料维护窗体新增、编辑、删除、搜索再加一个保存按钮。员工表通常带账号密码字段登录功能就是从这张表读数据的。这里有个安全细节容易忽略如果源码里密码是明文存储我建议至少改成 MD5 或 SHA256 哈希再存别让真实密码直接落库。frmCompanyInfo 更简单往往只有一行记录界面就是几个 TextBox 加一个保存按钮。这两个窗体是整套系统里最值得复制的模板——以后要加仓库表、客户表、供应商表照着 frmEmpInfo 复制改造半天就能多出一组 CRUD 页面这就是「一份工程吃透十张表通用」的意思。4. 让数字闭环报表统计、财务结算与多线程加载的落地细节4.1 销售报表与库存报表统计口径与时间段参数报表是本系统的价值出口。销售报表常见口径按时间段、按商品、按客户、按业务员四种维度切。SQL 写法万变不离其宗核心是聚合加分组-- 销售汇总报表: 按商品统计某时段销量与销售额 SELECT p.ProductName, SUM(sd.Quantity) AS TotalQty, SUM(sd.Quantity * sd.Price) AS TotalAmount FROM SalesOrder so JOIN SalesDetail sd ON so.OrderID sd.OrderID JOIN Product p ON sd.ProductID p.ProductID WHERE so.OrderDate BETWEEN start AND end GROUP BY p.ProductName ORDER BY TotalAmount DESC;start和end是 DateTime 类型来自界面上的两个 DateTimePickerBETWEEN是闭区间想用开区间可以写和。分组列必须出现在 GROUP BY 里否则 SQL 直接报错。如果只看语法网上 c#高级编程 这类书讲得更系统但报表怎么落到业务字段上还是得看真实工程的 SQL。库存报表则反过来以 Inventory 表为驱动外联商品和仓库再加一个「低于安全库存」的过滤条件。口径定下来之后后端只要把 WHERE 条件做成参数化查询改起来就很快。4.2 财务结算逻辑采购付款、销售回款与应收应付财务这块在课程设计里通常不会做成完整凭证而是用「应收应付」两张账体现。销售单保存后客户的应收账款增加收到货款时做一笔收款登记把 SalesOrder 的 PaidAmount 累加应收余额 SUM(订单金额) - SUM(已收金额)。采购侧对称是应付账款。这里最翻车的点是折扣和运费。如果你把折扣直接减到明细单价里单价就不是原价退货按原单价回补时会差钱如果你把运费直接加在总金额上又没单独字段记录后期对账永远差一笔。我一般会建议在订单主表上补两个字段DiscountAmount、FreightAmount让报表能分开统计别全糊在 Amount 里。4.3 多线程与数据验证大批量单据处理时怎么保住响应速度数据量一上来直接在 UI 线程里跑 SQL 聚合界面会卡到像死机。用 BackgroundWorker 或 Task 把查询丢到后台加载完成再回主线程绑定private void btnLoadReport_Click(object sender, EventArgs e) { var worker new BackgroundWorker(); worker.DoWork (s, args) { // 后台线程执行查询, 不碰任何界面控件 args.Result ReportHelper.GetSalesSummary(txtStart.Text, txtEnd.Text); }; worker.RunWorkerCompleted (s, args) { dgvReport.DataSource args.Result; // 回主线程再绑定 lblStatus.Text 加载完成; }; worker.RunWorkerAsync(); }ReportHelper.GetSalesSummary的内部就是 4.1 那段 SQL。后台线程不能直接访问 TextBox、DataGrid 这类控件参数通过RunWorkerAsync传进去结果通过e.Result回传这是 WinForms 多线程的入门写法也是很多 c#教程 里反复强调的点。数据验证放两个时机单元格级别的CellValidating校验数量必须是正数、单价不能为负保存按钮里再做一次整体校验比如明细不能为空、供应商必须选择。两层都过才允许写库这是避免脏数据的最后一道闸。5. 避坑跑这套 C# 进销存最容易翻车的 5 个地方每条都有解法5.1 连不上数据库报「用户 sa 登录失败」或「无法打开数据库 CHEXC」现象启动即弹 SqlException错误提示Cannot open database CHEXC requested by login或Login failed for user sa根本看不到登录窗体。原因连接串里的Initial Catalog在目标 SQL Server 上不存在也就是库没还原或者 SQL Server 只开了 Windows 身份验证连接串却用 sa再或者实例名写错实际是.\SQLEXPRESS却写成了.。解决先用 SSMS 确认实例名、登录方式和库是否存在。最快的改法是切到 Windows 身份验证Data Source.\SQLEXPRESS;Initial CatalogCHEXC;Integrated SecurityTrue把 User ID 和 Password 删掉即可。如果你手头完全没有 SQL Server想转到 Accessc#与access 的老套路或 MySQL网上 mysql80 zip 配置教程 很全先给你提个醒换库不是改连接串就完事SqlParameter、SqlConnection得换成 OleDb 或 MySql 全家桶工作量不小。想省事还是装个 SQL Server Express 或 LocalDB 更贴近原工程。5.2 一编译就报 GenerateResource.Cache 相关错误或窗体设计器打不开现象打开 .sln 后 VS 报项目加载失败或提示GenerateResource.Cache文件被占用无法生成双击某个窗体进设计器时报「未将对象引用设置到对象的实例」。原因压缩包里带着编译中间产物如果文件被标记只读或者被杀毒软件锁住VS 重新生成资源时会撞车另一个高频原因是工程缺了 .NET Framework 4 开发组件设计器直接白屏。解决先清缓存再开工程del /s /q *.GenerateResource.Cache rmdir /s /q bin obj然后重新打开 .sln。如果设计器还打不开去 VS Installer 的「单个组件」里勾选 .NET Framework 4.x targeting pack。另外别把 Designer.cs 当成普通代码手动删控件声明丢了布局再找回来很痛苦最好用 git 做一次基线备份。5.3 中文乱码或控件字体错位复制到新机器后界面变样现象按钮标题出现方框或问号DataGridView 列头中文正常但窗体标题乱码同一套代码在另一台机器上布局错位、控件重叠。原因多数是项目文件编码被破坏或者窗体里写了Font new Font(宋体, 9F)而本机没有宋体且 DPI 缩放和原机器不一致。解决把整个工程的字体统一替换成 Microsoft YaHei在 frmMain 构造函数里加一句this.Font new Font(Microsoft YaHei, 9F)然后检查每个窗体的AutoScaleMode如果是None就改成Font缩放适配会好很多。乱码如果只出现在个别硬编码字符串上把该文件另存为 UTF-8 with BOM 再编译。5.4 登录成功但点业务菜单就报「未将对象引用设置到对象的实例」现象输对账号密码进了主窗体点「销售管理」或「库存查询」直接弹 NullReferenceException调用栈停在某个SelectedValue或 DataSet 的表访问上。原因登录窗体只负责验证账号基础资料商品、供应商、客户是各业务窗体 Load 时才加载。如果某张表是空的比如 Inventory 一行都没有库存查询里拿空 DataTable 绑定还没问题但代码如果先去访问dt.Rows[0]就炸了。解决顺着异常堆栈找到第一个空对象最稳妥的改法是给业务窗体加一个统一的LoadBaseData方法先判断每张表count 0再绑定下拉框访问SelectedValue之前先确认Items.Count 0否则赋默认值 -1。这套系统里报表也要注意DataSet 里表名和界面绑定的DataMember差一个空格或大小写不同就会绑了个寂寞。5.5 报表查出来是空表或金额对不上统计口径没定住现象界面合计是 3200报表打出来是 2680或者报表页明明该有数据DataGridView 一片空白。原因前者多半是把退货单也加进了销售金额或者折扣字段没做减法后者通常是 DataSet 里表名和界面绑定的 DataMember 对不上绑定失败。日期参数没传进去也常见——SQL 里写死 BETWEEN界面上的时间根本没生效。解决把统计口径固定成注释写在 SQL 里明明白白写「销售金额不含退货」日期参数必须用start/end传进查询别拼字符串绑定前先打断点看DataSet.Tables.Count和TableName是否匹配。再给报表加一个空数据提示标签别让用户看着空白表格以为程序死了。6. 进阶改造与验证这套进销存的一线技巧让它更像生产工具6.1 先验证再动手三笔单据把账对上系统才算跑通拿到这套 C# 进销存第一件事不是改代码而是把业务链路完整跑一遍新建一笔进货 10 件单价 5 元保存后看库存变成 10再新建一笔销售 4 件单价 8 元保存后看库存变成 6然后做一笔退货 1 件库存应该回到 7销售净额应该是 4 件 × 8 元 - 1 件 × 8 元 24 元。这一步跑通说明库存增减和金额核算是一致的后面改业务才有底。对账可以写一条查询收口-- 对账查询: 某时间段销售净额 SELECT so.OrderID, SUM(sd.Quantity * sd.Price) AS GrossAmount, ISNULL(r.ReturnAmount, 0) AS ReturnAmount, SUM(sd.Quantity * sd.Price) - ISNULL(r.ReturnAmount, 0) AS NetAmount FROM SalesOrder so JOIN SalesDetail sd ON so.OrderID sd.OrderID LEFT JOIN ( SELECT OrderID, SUM(Quantity * Price) AS ReturnAmount FROM ReturnDetail GROUP BY OrderID ) r ON so.OrderID r.OrderID WHERE so.OrderDate BETWEEN start AND end GROUP BY so.OrderID, r.ReturnAmount;这个查询把退货单独聚合后左联净额 毛额 - 退货。界面合计和这条 SQL 结果对得上系统才算真正闭环。6.2 改造第一刀抽数据访问层、单文件部署与导出验证跑通之后想往生产方向推第一刀应该是抽数据访问层。把 DbHelper、连接串、通用增删改查收进一个独立的 ClassLibrary 工程UI 工程引用它这就是 c# 类库的使用最典型的落地场景。操作不复杂解决方案里右键「添加」→「新建项目」→「类库(.NET Framework)」把公共方法剪切过去再在 UI 工程里 Add Reference。抽完之后你会发现每个窗体的重复代码少了一半。部署方面有两个实用技巧一是用 Costura.Fody 把数据访问程序集合并进主 EXE配合 .config 一起拷到目标机器就完事省得一堆 DLL 拖泥带水二是如果老板要求在没数据库的机器上也能导数据可以仿照 c# listview项保存到txt文件 的做法把 DataGridView 内容按行写到 UTF-8 的 TXT 或 CSV再加一个「从 Excel 粘贴」的导入入口门店盘点会很顺手。连接串也可以放到外置 JSON 配置里现场改路径不用重编译。我从第一次拿到这套源码到现在每次碰到进销存、仓库、点餐这类课程设计工程都强制自己走三遍先删缓存清 bin/obj然后对齐连接字符串还原数据库最后手工录采购、销售、退货各一笔把账对上。这套 C# 进销存管理系统我照这个顺序跑了确认数据闭环才动业务代码。希望帮到你。本文还有配套的精品资源点击获取
返回列表