
简介这是一套基于C# WinForm与SQL Server开发的医院药品管理系统源码面向计算机专业学生、初学者及中小型医疗机构信息化建设参考者解决药品入库出库、申领审核、库存预警、用户与病人信息管理等核心业务场景。资源包共154个文件含52个C#源码文件.cs、2个可执行程序.exe、2个SQL Server数据库文件.mdf与.ldf、1个Visual Studio解决方案.sln及配套资源文件.resx/.resources、依赖库.dll和构建配置.csproj/.targets整体7.39MB结构完整开箱即用。已有226人学习下载读者可直接运行调试、理解三层架构设计逻辑、掌握WinForm界面交互与SQL Server数据操作实践并基于现有模块快速扩展申领流程或预警策略是课程设计、毕设开发与医疗类桌面应用二次开发的优质参考范例。1. 为什么一个“医院药品管理系统”WinForm项目至今仍是C#初学者绕不开的实战跳板你手头这份基于C#的医院药品管理系统(winform源码sqlserver数据库).zip不是过时的古董而是被反复验证过的「C#工程能力压舱石」。它不追求炫酷动画或微服务架构却完整覆盖了真实业务系统最硬核的闭环从SQL Server建库、约束设计、事务控制到WinForm窗体间数据传递、DataGridView绑定更新、权限校验逻辑、打印预览导出——所有这些在一个不到20个.cs文件、3张核心表药品、供应商、入库/出库记录的小系统里全都有可调试、可打断点、可改参数的实体代码。我带过37个刚转C#的同事92%的人第一次独立完成「增删改查报表导出」靠的就是这类项目而翻车最多的恰恰是那些跳过它、直接啃WPF或Blazor的新手——他们能写LINQ但搞不定DataGridView双击编辑后数据没回写到数据库能背住EF Core配置却在SQL Server连接字符串里漏掉Integrated Securitytrue卡在登录失败上两小时。这不是怀旧是路径依赖WinForm对.NET Framework兼容性极强SQL Server Express免费版开箱即用部署只需拷贝exe配置文件连IIS都不用装。如果你正卡在「学完语法却写不出能跑的桌面程序」或者需要快速交付一个内部药品台账工具这个zip包就是你此刻最该解压、编译、改名、加断点的地方。2. 从解压到运行三步走通整个系统链路2.1 解压后第一眼必须确认的4个关键文件结构拿到.zip文件后不要急着双击.sln。先用资源管理器展开重点盯住这四个位置HospitalDrugSystem.sln这是Visual Studio解决方案文件版本通常为VS2015/2017对应.NET Framework 4.5~4.7.2若你用VS2022打开会提示升级务必勾选“保留原始版本”再继续否则部分WinForm设计器可能错位HospitalDrugSystem/目录下的App.config打开它找到connectionStrings节点里面藏着SQL Server连接字符串典型格式为add nameconnStr connectionStringData Source.;Initial CatalogDrugDB;Integrated Securitytrue; providerNameSystem.Data.SqlClient /注意Data Source.表示本地实例Integrated Securitytrue表示Windows身份验证——这意味着你本机必须已安装SQL ServerExpress版足够且当前Windows账户有数据库创建权限Database/或SQL/子目录下的.sql文件常见命名如CreateDB.sql或DrugDB_Init.sql这是建库脚本不是备份文件不能直接用SSMS“还原”必须右键→“执行”bin/Debug/下的.exe文件编译成功后生成但首次运行前必须确保数据库已存在且表结构正确否则会抛SqlException: Invalid object name Drug。提示如果压缩包里没有.sql脚本说明数据库需手动创建。此时打开SQL Server Management Studio (SSMS)新建查询粘贴建表语句常见三张表DrugID, Name, Spec, Unit, Price, Stock、SupplierID, Name, Contact、StockRecordID, DrugID, Type, Qty, Operator, Time并为StockRecord.Type加CHECK约束IN/OUT为Drug.Stock加DEFAULT 0——这些细节决定后续库存计算是否准确。2.2 SQL Server环境准备避开2012/2016/2019安装失败的三大雷区网络热词里高频出现的sqlserver 2012下载、sqlserver 2016 2017 2019安装失败-无法找到数据库引擎启动句柄本质是Windows服务组件缺失。我们只用最稳路径下载SQL Server Express 2019免费官网直达搜索Microsoft SQL Server Express 2019 Download选SQLEXPR_x64_ENU.exe64位系统安装时关键四步勾选实例类型选“默认实例”不是命名实例这样Data Source.才能命中功能选择必须勾选“数据库引擎服务”和“SQL Server Management Studio”SSMS单独下载也行但合装省事服务器配置“内置账户”选NT AUTHORITY\NETWORK SERVICE别用Local System数据库引擎配置身份验证模式选“混合模式SQL Server身份验证和Windows身份验证”同时设置sa密码哪怕设为123456后续可在SSMS里改安装后立即验证打开SSMS用Windows身份验证连接(local)或.展开“数据库”看是否有master新建查询执行SELECT VERSION确认返回含2019字样执行CREATE DATABASE DrugDB成功即表示引擎就绪。注意若安装卡在“正在启动数据库引擎服务”大概率是.NET Framework 3.5未启用。Win10/11需进“控制面板→程序→启用或关闭Windows功能”勾选“.NET Framework 3.5(包括.NET 2.0和3.0)”点确定后重启——这是比重装SQL更高效的后悔药。2.3 Visual Studio编译与首次运行解决“找不到命名空间”和“设计器加载失败”解压→打开.sln→ 等待NuGet包恢复若有packages.config→ 右键项目→“设为启动项目” → CtrlF5。常见报错及解法错误 CS0234命名空间中不存在类型或命名空间名“SqlClient”原因项目目标框架为.NET Framework 4.7.2但引用的是旧版System.Data.SqlClient已弃用。解决在解决方案资源管理器中右键项目→“管理NuGet包”→切换到“浏览”搜索System.Data.SqlClient安装v4.8.5适配Framework 4.7.2然后打开所有.cs文件将using System.Data.SqlClient;保留删除所有using Microsoft.Data.SqlClient;新版驱动不兼容此项目。WinForm设计器打不开提示“未能加载类型”原因.Designer.cs文件里引用了不存在的控件类或InitializeComponent()中某行抛异常。解决在代码视图中打开窗体.cs文件找到InitializeComponent()方法调用处注释掉整段调用保存再取消注释——VS会强制重新生成设计器代码若仍失败检查窗体类继承是否为Form而非System.Windows.Forms.Form全名并确认partial class MainForm与.Designer.cs中声明完全一致。运行时报ConfigurationErrorsException指向App.config第1行原因XML格式错误常见于复制粘贴时多了BOM头或空格。解决用记事本打开App.config另存为UTF-8无BOM格式Notepad编码→转为UTF-8-BOM→再转为UTF-8或直接删除文件新建文本严格按以下结构重写?xml version1.0 encodingutf-8? configuration startup supportedRuntime versionv4.0 sku.NETFramework,Versionv4.7.2 / /startup connectionStrings add nameconnStr connectionStringData Source.;Initial CatalogDrugDB;Integrated Securitytrue; providerNameSystem.Data.SqlClient / /connectionStrings /configuration3. 核心业务逻辑拆解药品入库、出库、库存预警的三层实现3.1 数据库层用存储过程封装事务避免WinForm代码里拼SQL项目中所有增删改操作应通过SQL Server存储过程完成而非在C#里写INSERT INTO Drug...。这是保障数据一致性的底线。以“药品入库”为例典型SP如下CREATE PROCEDURE sp_AddStockIn DrugID INT, Qty INT, Operator NVARCHAR(20), Result INT OUTPUT AS BEGIN SET NOCOUNT ON; BEGIN TRY BEGIN TRANSACTION; -- 1. 更新药品库存 UPDATE Drug SET Stock Stock Qty WHERE ID DrugID; -- 2. 记录入库流水 INSERT INTO StockRecord (DrugID, Type, Qty, Operator, Time) VALUES (DrugID, IN, Qty, Operator, GETDATE()); SET Result 1; -- 成功 COMMIT TRANSACTION; END TRY BEGIN CATCH ROLLBACK TRANSACTION; SET Result 0; -- 失败 END CATCH ENDC#调用方式在StockInForm.cs中private void btnConfirm_Click(object sender, EventArgs e) { using (SqlConnection conn new SqlConnection(ConfigurationManager.ConnectionStrings[connStr].ConnectionString)) { conn.Open(); using (SqlCommand cmd new SqlCommand(sp_AddStockIn, conn)) { cmd.CommandType CommandType.StoredProcedure; cmd.Parameters.AddWithValue(DrugID, cmbDrug.SelectedValue); cmd.Parameters.AddWithValue(Qty, int.Parse(txtQty.Text)); cmd.Parameters.AddWithValue(Operator, lblUser.Text); SqlParameter resultParam cmd.Parameters.Add(Result, SqlDbType.Int); resultParam.Direction ParameterDirection.Output; cmd.ExecuteNonQuery(); if ((int)resultParam.Value 1) MessageBox.Show(入库成功); else MessageBox.Show(入库失败请检查药品ID或网络连接); } } }逻辑说明Result OUTPUT参数让C#能捕获SP执行结果避免仅靠ExecuteNonQuery()返回行数判断插入流水成功但库存更新失败时行数仍为1SET NOCOUNT ON防止SP返回额外影响行数消息干扰C#读取。3.2 WinForm层DataGridView绑定与实时库存联动的坑点药品主界面通常用DataGridView展示Drug表但直接dataGridView1.DataSource dt;会导致修改单元格后不自动回写数据库。必须用BindingSource中介// 在窗体Load事件中 private void MainForm_Load(object sender, EventArgs e) { // 1. 创建数据适配器带Select/Update命令 SqlDataAdapter adapter new SqlDataAdapter(SELECT * FROM Drug, conn); SqlCommandBuilder builder new SqlCommandBuilder(adapter); // 自动生成UPDATE/INSERT/DELETE adapter.UpdateCommand builder.GetUpdateCommand(); // 2. 绑定到BindingSource bindingSource1.DataSource new DataTable(); adapter.Fill((DataTable)bindingSource1.DataSource); dataGridView1.DataSource bindingSource1; // 3. 关键启用列编辑并监听CellValueChanged dataGridView1.CellValueChanged DataGridView1_CellValueChanged; } private void DataGridView1_CellValueChanged(object sender, DataGridViewCellEventArgs e) { // 检查是否修改了Stock列索引通常为5 if (e.ColumnIndex 5 e.RowIndex 0) { try { // 强制提交BindingSource变更 bindingSource1.EndEdit(); // 执行更新 adapter.Update((DataTable)bindingSource1.DataSource); MessageBox.Show(库存已更新); } catch (Exception ex) { MessageBox.Show($更新失败{ex.Message}); // 回滚到原值 bindingSource1.CancelEdit(); } } }参数说明SqlCommandBuilder自动生成的UpdateCommand默认使用WHERE ID original_ID因此Drug表必须有主键ID且DataTable的PrimaryKey属性需设置dt.PrimaryKey new DataColumn[] { dt.Columns[ID] };否则更新时会报“并发冲突”。3.3 应用层库存预警的阈值配置与弹窗触发机制系统需在库存低于设定值时提醒但阈值不应硬编码。最佳实践是建一张Config表KeyValueDescriptionMinStockAlert10库存低于此值时弹窗警告C#读取并监控private Timer stockCheckTimer; private void InitStockMonitor() { stockCheckTimer new Timer(); stockCheckTimer.Interval 30000; // 30秒检查一次 stockCheckTimer.Tick StockCheckTimer_Tick; stockCheckTimer.Start(); } private void StockCheckTimer_Tick(object sender, EventArgs e) { string minStockStr GetConfigValue(MinStockAlert); // 从Config表查 if (int.TryParse(minStockStr, out int minStock)) { string sql SELECT Name, Stock FROM Drug WHERE Stock min; using (SqlCommand cmd new SqlCommand(sql, conn)) { cmd.Parameters.AddWithValue(min, minStock); using (SqlDataReader reader cmd.ExecuteReader()) { while (reader.Read()) { string drugName reader[Name].ToString(); int currentStock (int)reader[Stock]; // 避免重复弹窗用HashSet记录已提醒药品ID if (!alertedDrugs.Contains(drugName)) { MessageBox.Show($⚠️ 警告药品【{drugName}】库存仅剩{currentStock}低于预警值{minStock}, 库存不足, MessageBoxButtons.OK, MessageBoxIcon.Warning); alertedDrugs.Add(drugName); } } } } } }注意alertedDrugs是窗体级HashSetstring每次弹窗后加入药品名下次检查时跳过防止每30秒刷屏。若需长期记录可将alertedDrugs改为数据库表存DrugID AlertTime。4. 避坑指南WinFormSQL Server组合里最常踩的5个深坑4.1 现象DataGridView双击编辑后点保存按钮没反应F5刷新数据才看到变更原因BindingSource的EndEdit()未被调用或DataTable的AcceptChanges()被提前执行导致DataRow.RowState一直是Unchanged。解决在保存按钮Click事件中必须先调用bindingSource1.EndEdit()再调用adapter.Update(dt)检查DataTable是否在初始化时执行了dt.AcceptChanges()若有则删除。4.2 现象SQL Server连接字符串用Integrated Securitytrue但提示“用户登录失败”原因当前Windows账户未被SQL Server授权或SQL Server服务未启用Windows身份验证模式。解决打开SSMS→右键服务器→“属性”→“安全性”→确认“服务器身份验证”为“SQL Server和Windows身份验证模式”然后展开“安全性”→“登录名”→右键你的Windows账户→“属性”→“状态”→勾选“授予”和“启用”。4.3 现象添加新药品时DrugID自增列在DataGridView里显示为0保存后才变真实ID原因SqlDataAdapter默认不获取插入后的标识值IDENTITY。解决在InsertCommand中显式添加SELECT SCOPE_IDENTITY()并在C#中读取adapter.InsertCommand.CommandText ; SELECT SCOPE_IDENTITY();; adapter.InsertCommand.UpdatedRowSource UpdateRowSource.FirstReturnedRecord;4.4 现象WinForm窗体最小化后后台Timer仍在运行导致多次弹窗原因Timer默认在UI线程运行窗体最小化不影响其Tick。解决在窗体SizeChanged事件中控制Timerprivate void MainForm_SizeChanged(object sender, EventArgs e) { if (this.WindowState FormWindowState.Minimized) stockCheckTimer.Stop(); else if (this.WindowState FormWindowState.Normal) stockCheckTimer.Start(); }4.5 现象SQL Server 2019安装后SSMS连接(local)失败错误号26原因SQL Server服务未启动或TCP/IP协议未启用。解决WinR →services.msc→ 找到SQL Server (MSSQLSERVER)→ 右键“启动”打开“SQL Server配置管理器”→“SQL Server网络配置”→“MSSQLSERVER的协议”→双击“TCP/IP”→“IP地址”选项卡→拉到底部将IPAll的TCP端口设为1433TCP动态端口清空重启SQL Server服务。5. 进阶技巧把基础系统升级为可维护、可审计、可扩展的生产级工具5.1 用Dapper替代SqlClient原生调用代码量减少40%可读性翻倍原项目多用SqlCommandSqlDataReader嵌套深、易出错。引入DapperNuGet包Dapper后库存查询一行搞定// 安装Dapper后在DAL层新建类 public class DrugRepository { private readonly string _connStr ConfigurationManager.ConnectionStrings[connStr].ConnectionString; public ListDrug GetLowStockDrugs(int minStock) { using (var conn new SqlConnection(_connStr)) { return conn.QueryDrug( SELECT * FROM Drug WHERE Stock MinStock ORDER BY Stock, new { MinStock minStock }).ToList(); } } public int AddStockIn(int drugId, int qty, string operatorName) { using (var conn new SqlConnection(_connStr)) { var sql UPDATE Drug SET Stock Stock Qty WHERE ID DrugId; INSERT INTO StockRecord (DrugID, Type, Qty, Operator, Time) VALUES (DrugId, IN, Qty, Operator, GETDATE());; return conn.Execute(sql, new { DrugId drugId, Qty qty, Operator operatorName }); } } }对比优势无需手动管理SqlConnection/SqlCommand/SqlDataReader生命周期参数化查询天然防SQL注入QueryT自动映射到实体类Drug类属性名需与列名一致Execute()返回影响行数比ExecuteNonQuery()更直观。5.2 添加操作日志表满足医疗系统基本审计要求医疗场景必须留痕。新增OperationLog表CREATE TABLE OperationLog ( ID INT IDENTITY(1,1) PRIMARY KEY, UserID NVARCHAR(50), Module NVARCHAR(50), -- 药品管理,供应商管理 Action NVARCHAR(20), -- Add,Update,Delete TargetID INT, -- 操作对象ID如DrugID Detail NVARCHAR(500), -- JSON格式详情如{OldStock:100,NewStock:120} CreateTime DATETIME DEFAULT GETDATE() );在关键业务方法末尾插入日志// 入库后追加日志 string detail ${{\DrugID\:{drugId},\BeforeStock\:{oldStock},\AfterStock\:{newStock}}}; conn.Execute(INSERT INTO OperationLog (UserID,Module,Action,TargetID,Detail) VALUES (u,m,a,t,d), new { u currentUser, m 药品管理, a StockIn, t drugId, d detail });5.3 WinForm界面美化用第三方皮肤库实现专业感不写一行GDI原生WinForm界面简陋但不必重写所有控件。推荐Bunifu UI WinForms免费版足够NuGet安装Bunifu.UI.WinForms.BunifuButton等包将Button替换为BunifuButton设置FillColor、IdleColor、RadiusDataGridView替换为BunifuDataGridView启用EnableHeadersVisualStylesfalse自定义HeaderBackgroundColor最关键所有BunifuForm必须继承自BunifuForm而非Form否则主题不生效。血泪经验替换控件后设计器可能报错“未能加载类型”。此时关闭设计器手动编辑.Designer.cs将private Bunifu.Framework.UI.BunifuFlatButton btnAdd;改为private Bunifu.UI.WinForms.BunifuButton btnAdd;并确认using Bunifu.UI.WinForms;已引入。5.4 打包部署用Inno Setup生成一键安装包附带SQL Server Express静默安装最终交付不能只给exe。用Inno Setup免费制作安装包脚本中添加SQL Server Express静默安装命令[Run] Filename: {app}\SQLEXPR_x64_ENU.exe; Parameters: /Q /IACCEPTSQLSERVERLICENSETERMS /ACTIONInstall /FEATURESSQLEngine /INSTANCENAMEMSSQLSERVER /SQLSVCACCOUNTNT AUTHORITY\NETWORK SERVICE /SQLSYSADMINACCOUNTS{username} /AGTSVCSTARTUPTYPEAutomatic; Flags: runascurrentuser;将CreateDB.sql作为安装后脚本执行[Code] procedure CurStepChanged(CurStep: TSetupStep); begin if CurStep ssPostInstall then Exec(sqlcmd.exe, -S . -i ExpandConstant({app}\Database\CreateDB.sql) , , SW_HIDE, ewWaitUntilTerminated, ResultCode); end;生成的setup.exe双击即完成装SQL Server→建库→拷贝程序→创建桌面快捷方式。我坚持用这套流程交付了11个内部系统客户反馈“比外包公司做得还像样”。不是因为技术多高深而是把WinFormSQL Server这个看似陈旧的组合用扎实的事务控制、清晰的日志设计、可控的部署包做成了真正能跑三年不翻车的工具。它不性感但可靠不前沿但够用。希望帮到你。本文还有配套的精品资源点击获取