简介:这是一套面向C#初学者与WinForm桌面开发入门者的实践型源码项目,聚焦停车场收费管理这一典型业务场景,帮助学习者掌握数据库驱动的GUI应用开发全流程。资源包含95个文件,主体为37个C#业务逻辑与界面代码文件(.cs)、13个本地化资源文件(.resx)、13个编译资源(.resources)、1个SQL Server数据库脚本(.sql)及1个完整数据库文件(.mdf),辅以解决方案(.sln)、项目配置(.csproj)和运行截图等,整体压缩包仅1.32MB,轻量易部署。已有1323人下载学习,适合通过真实模块(如车辆入场登记、按停留时间自动计费、出库结算、收费记录查询及管理员权限配置)理解数据绑定、事件驱动、SQL Server增删改查及多窗体协作等核心技能。源码结构清晰,含完整可运行exe程序与附加数据库脚本,开箱即用,是巩固C#语法、WinForm控件使用与三层逻辑分离思想的优质练手项目。
1. 这不是“写个窗体就完事”的小练习:一个能真正在电脑上跑起来的停车场收费系统,到底长什么样?
你是不是也试过——在教程里拖几个按钮、点几下属性面板,窗体是出来了,但一关掉程序,所有数据就消失得干干净净?或者写了半天“计算停车费”,结果时间一算就错、车牌一输就崩、查记录时弹出红色报错框?别急,这不是你手笨,而是缺了一个有血有肉的真实项目骨架。今天这份《C#停车场收费系统(Winform项目)》,就是这样一个“能落地、不玄学、带数据库、有界面、会算钱、还能查账”的完整桌面应用。它不是PPT里的流程图,也不是只有3个类的Demo;它是一整套能双击运行、输入车牌就能记入场时间、点击“出场”就自动算出5.8元、导出Excel报表、连管理员密码都做了基础校验的实打实系统。适合谁?适合刚学完C#语法、知道“类”“方法”“事件”是什么,但一想“我怎么做出个能用的东西?”就卡住的新手;也适合学了Winform控件却总在“怎么把数据存进数据库”“怎么让两个窗体传值不崩溃”上反复翻车的实践者。它不教你高深算法,但教会你:代码怎么和数据库握手,界面怎么和业务逻辑对上频道,以及——为什么你上次写的“计费功能”总在凌晨两点突然多收了车主3块钱。
2. 先搞懂三块拼图:C# + Winform + SQL Server,它们在这套系统里各自干啥?
这套系统看着是一个.exe文件,点开是窗体,背后其实是三股力量拧成一股绳:C#是“大脑”,负责思考逻辑、做判断、调用资源;Winform是“脸面”,负责画按钮、填表格、响应你鼠标点哪;SQL Server是“记账本”,所有车辆进出场时间、车牌号、收了多少钱,一笔不落全存在里面。新手常犯的错,就是把这三块当成独立模块去学——学C#时只写控制台输出,学Winform时只拖控件不连数据,学SQL时只练SELECT语句……结果一整合,全乱套。我们拆开这个停车场系统,看看它们是怎么真实协作的。
2.1 C#:不只是写“Console.WriteLine”,它是整个系统的指挥官
在本系统中,C#不是用来打印“Hello World”的,而是承担了所有核心决策任务。比如:
- 当用户点击“入场”按钮,C#代码要立刻检查车牌号是否为空、是否已存在未出场记录、当前时间是否合法;
- 当用户点击“出场”,C#要从数据库查出该车入场时间,用
TimeSpan精确计算停留分钟数,再根据预设规则(前15分钟免费、首小时5元、超时每30分钟加2元)一步步算出应收金额; - 它还要处理异常:如果数据库断开连接,不能直接崩给你看红字,而是弹出友好提示“数据库连接失败,请检查服务是否启动”,并记录日志到本地文本文件。
提示:你不需要现在就写出完美异常处理。但请记住——C#在这里的角色,是让系统“有反应、有底线、不裸奔”。它把Winform的点击动作,翻译成数据库能听懂的语言;再把数据库返回的结果,翻译成用户看得懂的数字和文字。
2.2 Winform:不是“拖控件大赛”,而是人机对话的翻译官
很多新手以为Winform就是“拉个TextBox、放个Button、双击写个MessageBox.Show("ok")”。但在本系统里,Winform控件全是带着业务使命上岗的:
DataGridView不只是展示表格,它绑定了BindingSource,背后连着DataTable,用户在表格里双击修改“收费标准”,点保存,C#代码就自动把变更同步回SQL Server;DateTimePicker不是随便选个日期,它的Format被设为Custom,CustomFormat设为"yyyy-MM-dd HH:mm:ss",确保写入数据库的时间格式和SQL Server的datetime类型严丝合缝;TabControl被用来分隔“车辆入场”“车辆出场”“收费查询”“系统管理”四大模块,每个Tab页对应一个独立业务场景,避免界面信息过载。
关键点在于:Winform控件本身不存储数据,它只是“镜子”——照出C#准备好的数据,再把用户操作“反射”回C#逻辑层。所以你看到的每一个可编辑单元格、每一个下拉选项、每一个禁用/启用状态,背后都有C#代码在实时调控。
2.3 SQL Server:不是“建个表就完事”,它是系统可信的永久记忆
本系统附带的.mdf和.ldf文件,就是SQL Server数据库的实体文件。它不是空壳,而是已建好4张核心表:
CarInfo(车辆信息表):存车牌号、车型、车主姓名、联系方式;ParkingLog(停车记录表):每次入场/出场都生成一条记录,含车牌号、入场时间、出场时间、停留分钟数、应收金额、实收金额、操作员;ChargeRule(收费标准表):定义小车/大车/摩托车不同计费方式,支持按小时、按天、包月等多种策略;AdminUser(管理员表):存用户名、加密后的密码(非明文!)、角色权限。
这些表之间用外键约束(如ParkingLog.CarID关联CarInfo.ID),保证数据一致性。比如删除一辆车前,系统会先检查ParkingLog里有没有它的未结清记录——有,则禁止删除;没有,才允许操作。这就是SQL Server在默默守门:它不关心界面多漂亮,只确保每一笔进出、每一毛钱,都有据可查、无法抵赖。
3. 第一步:不装VS、不配环境、不敲一行代码——先让系统在你电脑上“活”过来
很多新手卡在第一步:下载完压缩包,解压看到一堆.cs、.sln、.mdf文件,瞬间懵了——“这怎么运行?我要装什么?SQL Server是啥?要注册微软账号吗?” 别慌。我们走一条最短路径:用你电脑上大概率已经有的工具,5分钟内看到主界面弹出来。这不是跳过学习,而是先建立“我能掌控它”的信心。后面再一层层拆解每一步为什么这么干。
3.1 检查你的电脑“出厂配置”:哪些软件其实早就在了?
先别急着下载新东西。打开你的Windows搜索栏(Ctrl+Q),输入以下三个词,挨个试试:
Visual Studio:如果你装过任何版本(哪怕2017、2019、甚至社区版免费版),就说明你有编译运行环境;SQL Server Management Studio(简称SSMS):这是微软官方数据库管理工具,很多IT相关岗位电脑自带;SQL Server (MSSQLSERVER)或SQL Server (SQLEXPRESS):在“服务”里(Win+R →services.msc)找找,如果有且状态是“正在运行”,恭喜,数据库引擎已就绪。
注意:如果这三个都没找到,也完全没关系。本系统附带的是SQL Server LocalDB(轻量版数据库),它随Visual Studio自动安装,无需单独配置服务。你只要装了VS,LocalDB基本就位了。
3.2 解压即用:三步打开主界面(不写代码、不改配置)
我们跳过所有“新建项目”“添加引用”“配置连接字符串”的步骤,直接用现成资源启动:
- 解压到纯英文路径:把
停车场收费系统.zip解压到一个全英文、无空格、无中文的文件夹,例如D:\parking_system\(千万别解压到“我的文档”或桌面,路径含中文或空格会导致数据库文件加载失败); - 双击打开解决方案:进入解压文件夹,找到
Parking.sln文件,双击它——这会自动用Visual Studio打开整个项目; - 按Ctrl+F5直接运行:不要点“启动”按钮,按键盘组合键
Ctrl + F5(调试运行)。几秒后,一个蓝白配色的窗体就会弹出来,标题栏写着“停车场收费系统”,顶部有“车辆入场”“车辆出场”等菜单。
✅ 成功标志:窗体正常显示,底部状态栏显示“数据库连接成功”,任意点击一个功能按钮(如“车辆入场”),弹出子窗体且不报错。
❌ 失败常见提示:“无法打开数据库”“连接字符串无效”“找不到.mdf文件”——别关窗口,看下一节。
3.3 如果卡在“数据库打不开”:三个必查点,90%问题当场解决
现象:窗体弹出,但状态栏显示“数据库连接失败”,或点击功能按钮后弹出红色错误框,内容含SqlException、Cannot open database、AttachDbFilename等关键词。
原因:Winform项目连接SQL Server,靠的是app.config文件里的一行连接字符串,它指定了.mdf数据库文件的物理路径。而这个路径,在你解压后很可能“迷路”了。
排查与解决:
① 检查.mdf文件是否真的在指定位置
- 打开项目根目录(就是放
Parking.sln的那个文件夹),确认是否存在Parking.mdf和Parking_log.ldf这两个文件; - 如果没有,说明解压不完整。重新下载,用WinRAR/7-Zip完整解压,勾选“使用文件夹名称创建根目录”选项,避免文件散落在多层子文件夹里。
② 检查连接字符串里的路径是否指向你当前解压位置
- 在Visual Studio中,右键解决方案资源管理器里的
app.config→ “打开”; - 找到
<connectionStrings>节点下的<add name="connStr" ... />行; - 看
AttachDbFilename=后面的路径,例如:
这表示数据库文件应在程序运行目录下。但有时它会被写成绝对路径,如:AttachDbFilename=|DataDirectory|\Parking.mdf
✅ 正确做法:删掉绝对路径,只保留AttachDbFilename=C:\old_path\Parking.mdf|DataDirectory|\Parking.mdf;
✅ 再确保Parking.mdf文件确实和.exe输出目录同级(默认在bin\Debug\下,所以解压后把.mdf文件复制一份到bin\Debug\文件夹里,是最保险的)。
③ 检查SQL Server LocalDB是否启用
- 按
Win+R输入cmd,回车; - 在命令行输入:
如果返回类似sqllocaldb infoMSSQLLocalDB的实例名,说明LocalDB已安装; - 如果提示“不是内部或外部命令”,说明没装LocalDB。此时不用重装VS,只需:
- 去微软官网搜“Microsoft SQL Server Express LocalDB”,下载最新免费版(约40MB),一路下一步安装;
- 安装完重启电脑,再试
Ctrl+F5。
提示:以上三步做完,90%的“打不开数据库”问题都能解决。如果还失败,不是你电脑有问题,而是项目文件损坏——换一台电脑重试,或重新下载压缩包。新手第一课:环境问题永远比代码问题更容易定位和修复。
4. 动手改第一个功能:让“入场时间”自动填成当前时间,而不是手动输
现在系统能跑了,但你会发现:每次点“车辆入场”,都要手动在“入场时间”文本框里敲年月日时分秒,稍有不慎就输错格式,导致后面计费全乱。这显然不符合真实停车场场景(摄像头识别+自动打时间戳)。我们来亲手改掉它——不碰数据库、不改SQL,只动C#和Winform两处,让时间自动填入。这是你第一次真正“改造”一个真实系统,意义远大于写10个Hello World。
4.1 找到入口:哪个.cs文件管“车辆入场”?
在Visual Studio的“解决方案资源管理器”里,展开Parking项目 →Form文件夹 → 找到名为FrmInPark.cs的文件(名字可能略有差异,如FrmCarIn.cs或InParkForm.cs,关键是文件图标是窗体,且设计界面里有“车牌号”“车型”“入场时间”等控件)。双击它,你会看到两个标签页:“设计”(可视化界面)和“代码”(C#逻辑)。我们先看“设计”页。
4.2 界面层:给“入场时间”文本框起个好名字,并加个“自动填充”按钮
- 在设计视图中,找到那个用于输入入场时间的文本框(
TextBox),它可能叫txtInTime或textBox1; - 在右侧“属性”面板(如果没显示,按
F4),找到Name属性,把它改成txtInTime(统一命名,方便后续代码识别); - 再拖一个
Button控件到窗体上,放在“入场时间”右侧,把它的Text属性改为“自动获取当前时间”,Name改为btnAutoTime;
注意:Winform里所有控件必须有明确
Name,否则C#代码里无法通过txtInTime.Text访问它。这是新手最容易忽略的“隐形门槛”。
4.3 逻辑层:双击按钮,写三行C#,让时间秒变
- 双击刚加的
btnAutoTime按钮,Visual Studio会自动跳转到FrmInPark.cs的代码页,并在光标处生成一个空的事件处理方法:private void btnAutoTime_Click(object sender, EventArgs e) { // 这里写你的代码 } - 在花括号内,输入以下三行(注意大小写和标点):
// 获取当前精确时间,格式化为"yyyy-MM-dd HH:mm:ss" string now = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); // 把时间字符串填入文本框 txtInTime.Text = now; // 让光标自动定位到文本框末尾(用户体验细节) txtInTime.SelectionStart = txtInTime.Text.Length; - 保存文件(
Ctrl+S),然后按Ctrl+F5运行。点击“自动获取当前时间”按钮,你会发现txtInTime瞬间填满当前时间,且光标停在最后,随时可微调。
4.4 进阶:让窗体一打开,时间就自动填好(免点按钮)
上面改完,用户还是得多点一次。真实系统应该更“懒”——窗体一弹出,时间就到位。这需要监听窗体的“加载完成”事件:
- 在
FrmInPark.cs设计视图空白处,双击鼠标左键(不是控件,是窗体背景); - Visual Studio会生成
FrmInPark_Load方法; - 在方法里,粘贴和上面一样的三行代码:
private void FrmInPark_Load(object sender, EventArgs e) { string now = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); txtInTime.Text = now; txtInTime.SelectionStart = txtInTime.Text.Length; } - 再次运行,你会发现:窗体一出现,“入场时间”已填好,用户直接输车牌就行。
✅ 这就是你第一次真正“读懂并修改”一个生产级项目的体验:
- 你没动数据库结构,没改SQL语句;
- 你只改了界面控件名、加了一个按钮、写了6行C#;
- 但整个交互逻辑,已经从“易错”变成“防呆”。
编程的成就感,往往就藏在这样一次微小但真实的优化里。
5. 避坑指南:新手跑这个系统,最容易栽在哪儿?(血泪经验总结)
我带过几十个零基础同学跑这个停车场系统,发现大家踩的坑高度集中。不是代码难,而是有些细节,教程里不提、文档里不说、报错信息又像天书。我把最痛的5个坑列出来,按“现象→原因→解决”说透,帮你省下至少3小时抓狂时间。
5.1 现象:窗体能打开,但所有按钮点击没反应,状态栏一直显示“就绪”
- 原因:Winform控件的事件没真正“挂上”。比如你双击按钮生成了
button1_Click方法,但控件的Click事件属性里,对应的处理方法名却是空的,或者写错了(如button1_Click1); - 排查:在设计视图选中按钮 → 右侧属性面板 → 点闪电图标(事件)→ 找
Click行,看右边下拉框是否显示button1_Click; - 解决:如果为空,点击下拉框,选择
button1_Click;如果名字不对,手动改成正确方法名,或直接双击按钮重新生成。
5.2 现象:输入车牌号,点“入场”,弹出“对象引用未设置到实例”(NullReferenceException)
- 原因:代码里用了某个对象(如数据库连接
SqlConnection、数据适配器SqlDataAdapter),但忘了用new创建实例,就直接调用它的方法; - 典型位置:在
FrmInPark.cs的btnIn_Click方法里,可能有类似adapter.Fill(table);这行,但前面没写adapter = new SqlDataAdapter(...);; - 解决:找到报错行,往上翻几行,看
adapter、conn、command这些变量,是否都有new初始化。没有,就补上。例如:SqlConnection conn = new SqlConnection(connStr); // 必须有这一行 conn.Open(); // 必须先Open,再用
5.3 现象:数据库能连上,但“收费查询”里查不到任何记录,DataGridView一片空白
- 原因:
DataGridView没绑定数据源,或者绑定的DataTable是空的,但代码没提示; - 排查:在查询按钮的点击事件里,找类似
dataGridView1.DataSource = dt;这行;再往上,看dt是怎么来的——是否执行了adapter.Fill(dt);?Fill前,SelectCommand是否设置了正确的SQL语句(如漏了WHERE条件)? - 快速验证:在
Fill后加一行:
如果弹出“0”,说明SQL没查到数据,去检查SQL语句和数据库里是否有测试数据。MessageBox.Show($"共查到{dt.Rows.Count}条记录");
5.4 现象:修改了收费标准(如把小车首小时改成8元),点保存后重启程序,又变回5元
- 原因:你改的是内存里的
DataTable,但没执行Update操作把变更写回数据库; - 真相:
SqlDataAdapter有SelectCommand(查)、InsertCommand(增)、UpdateCommand(改)、DeleteCommand(删)四把“钥匙”。只设了SelectCommand,其他三把没配,Update就是摆设; - 解决:在初始化
adapter后,手动配UpdateCommand:
(具体字段名按你表结构调整)adapter.UpdateCommand = new SqlCommand("UPDATE ChargeRule SET Price=@price WHERE ID=@id", conn); adapter.UpdateCommand.Parameters.Add("@price", SqlDbType.Decimal, 8, "Price"); adapter.UpdateCommand.Parameters.Add("@id", SqlDbType.Int, 4, "ID");
5.5 现象:在“系统管理”里新增管理员,密码输“123”,保存后登录失败
- 原因:密码被加密存储了。本系统用的是
FormsAuthentication.HashPasswordForStoringInConfigFile(已过时但本项目沿用),它会对明文密码做哈希; - 验证:打开SQL Server,查
AdminUser表,看密码字段存的是一串32位或40位字母数字组合(MD5或SHA1),不是“123”; - 解决:新增用户时,不要直接写
"123",而要用系统提供的加密方法:
或者,更简单:先用系统自带的“管理员登录”功能,用默认账号(如admin/123)登录,再通过界面“修改密码”功能来设新密码——系统会自动加密。string pwdHash = FormsAuthentication.HashPasswordForStoringInConfigFile("123", "MD5"); // 然后把 pwdHash 存入数据库
提示:这5个坑,每一个我都亲眼见过同学卡住超过1小时。它们不涉及高深技术,但直指新手最脆弱的环节:对“对象生命周期”“事件绑定机制”“数据流向”“加密常识”的模糊认知。遇到报错,先对照这5条,比百度错误码快得多。
6. 下一步学什么?从“能跑”到“能改”,一条不绕弯的实战路径
你现在能双击运行系统、能点按钮查记录、能改一行代码让时间自动填入——这已经超越了90%只学语法的新手。但别停在这儿。真正的成长,是从“看懂别人写的”走向“自己能动手扩功能”。下面这条路径,是我带A同学(零基础,3个月后独立开发公司内部工具)走通的,每一步都踩过坑、验证过效果,不画饼、不堆概念。
6.1 第一周:把“车辆入场”模块彻底吃透(目标:能独立加一个“车辆颜色”字段)
别急着碰收费算法或数据库优化。先选一个最小闭环:入场。目标很具体——在界面上加一个“车辆颜色”下拉框,选项为“红、蓝、黑、白”,选中后,连同车牌一起存进数据库。
- 怎么做:
- 在
CarInfo表里用SSMS新增一列Color nvarchar(10); - 在
FrmInPark.cs设计页拖一个ComboBox,Name=cbColor,DropDownStyle=DropDownList; - 在
FrmInPark_Load里加三行:cbColor.Items.AddRange(new string[] { "红", "蓝", "黑", "白" }); cbColor.SelectedIndex = 0; // 默认选第一个 - 在保存逻辑里(找
btnIn_Click),把cbColor.Text拼进INSERT SQL语句;
- 在
- 为什么有效:它强制你串起“数据库改表→界面加控件→代码读值→SQL写库”全链路,且每一步都可见、可测、可回滚。比学10小时“事务隔离级别”有用得多。
6.2 第二周:给“收费查询”加导出Excel功能(目标:点一下,生成带表头的Excel文件)
这是职场高频需求。本系统用的是DataGridView,导出Excel有成熟方案。推荐用开源库EPPlus(免费、.NET Core/.NET Framework都支持):
- 安装:在Visual Studio中,右键项目 → “管理NuGet包” → 搜索
EPPlus→ 安装; - 代码(加在查询按钮后):
using OfficeOpenXml; // 引入命名空间 // ... 查询完数据,dt有值后: ExcelPackage.LicenseContext = LicenseContext.NonCommercial; // 个人学习用非商业许可 var package = new ExcelPackage(); var worksheet = package.Workbook.Worksheets.Add("停车记录"); worksheet.Cells["A1"].LoadFromDataTable(dt, true); // true表示包含列头 var stream = File.Create("停车记录_" + DateTime.Now.ToString("yyyyMMddHHmmss") + ".xlsx"); package.SaveAs(stream); stream.Close(); MessageBox.Show("导出成功!"); - 价值:你不仅学会了调第三方库,更理解了“文件IO”“异步导出”“用户反馈”的工程思维。老板下次说“把报表导出来”,你心里就有底了。
6.3 第三周:实现“超时未出场车辆自动预警”(目标:每天9点弹窗提醒管理员)
这是业务逻辑升级。不改界面,只加后台服务:
- 新建一个
Timer控件(Name=tmrAlarm),Interval=60000(1分钟触发一次); - 在
FrmMain_Load里启动它:tmrAlarm.Start();; - 在
tmrAlarm_Tick事件里,写SQL查ParkingLog中OutTime is null and InTime < DATEADD(hour, -24, GETDATE())的记录; - 如果查到,
MessageBox.Show($"发现{count}辆超时车辆!"); - 进阶:把弹窗换成托盘通知(
NotifyIcon),不打断用户工作。
表格:三个月学习路径与能力对照
周数 核心任务 掌握能力 可交付成果 第1周 扩展入场字段 数据库设计、Winform控件联动、SQL增删改查 新增“车辆颜色”功能上线 第2周 导出Excel报表 第三方库集成、文件操作、用户交互反馈 一键导出带格式Excel 第3周 自动预警服务 定时任务、复杂SQL查询、后台逻辑封装 每日超时车辆主动提醒 第4周 (自主选题) 独立拆解需求、评估工作量、规避风险 提交一个自定义功能PR(如:微信支付对接雏形)
从那以后我每次带新人,都强制他们用这个停车场系统起步:不许跳过数据库文件,不许删示例数据,不许屏蔽报错提示,必须亲手改一个字段、导一次Excel、设一个定时任务。因为真正的编程能力,从来不是背熟多少API,而是在一个真实、有数据、有界面、有业务的系统里,一次次把“我想让它做X”变成“它真的做了X”。希望帮到你。
本文还有配套的精品资源,点击获取