☰
C# Winform停车场系统实战:数据库+界面+计费全链路
2026/10/19 6:25:33 网站建设 项目流程

简介:这是一套面向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 解压即用:三步打开主界面(不写代码、不改配置)

我们跳过所有“新建项目”“添加引用”“配置连接字符串”的步骤,直接用现成资源启动:

  1. 解压到纯英文路径:把停车场收费系统.zip解压到一个全英文、无空格、无中文的文件夹,例如D:\parking_system\(千万别解压到“我的文档”或桌面,路径含中文或空格会导致数据库文件加载失败);
  2. 双击打开解决方案:进入解压文件夹,找到Parking.sln文件,双击它——这会自动用Visual Studio打开整个项目;
  3. 按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 info
    如果返回类似MSSQLLocalDB的实例名,说明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后加一行:
    MessageBox.Show($"共查到{dt.Rows.Count}条记录");
    如果弹出“0”,说明SQL没查到数据,去检查SQL语句和数据库里是否有测试数据。

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",而要用系统提供的加密方法:
    string pwdHash = FormsAuthentication.HashPasswordForStoringInConfigFile("123", "MD5"); // 然后把 pwdHash 存入数据库
    或者,更简单:先用系统自带的“管理员登录”功能,用默认账号(如admin/123)登录,再通过界面“修改密码”功能来设新密码——系统会自动加密。

提示:这5个坑,每一个我都亲眼见过同学卡住超过1小时。它们不涉及高深技术,但直指新手最脆弱的环节:对“对象生命周期”“事件绑定机制”“数据流向”“加密常识”的模糊认知。遇到报错,先对照这5条,比百度错误码快得多。


6. 下一步学什么?从“能跑”到“能改”,一条不绕弯的实战路径

你现在能双击运行系统、能点按钮查记录、能改一行代码让时间自动填入——这已经超越了90%只学语法的新手。但别停在这儿。真正的成长,是从“看懂别人写的”走向“自己能动手扩功能”。下面这条路径,是我带A同学(零基础,3个月后独立开发公司内部工具)走通的,每一步都踩过坑、验证过效果,不画饼、不堆概念。

6.1 第一周:把“车辆入场”模块彻底吃透(目标:能独立加一个“车辆颜色”字段)

别急着碰收费算法或数据库优化。先选一个最小闭环:入场。目标很具体——在界面上加一个“车辆颜色”下拉框,选项为“红、蓝、黑、白”,选中后,连同车牌一起存进数据库。

  • 怎么做:
    1. 在CarInfo表里用SSMS新增一列Color nvarchar(10);
    2. 在FrmInPark.cs设计页拖一个ComboBox,Name=cbColor,DropDownStyle=DropDownList;
    3. 在FrmInPark_Load里加三行:
      cbColor.Items.AddRange(new string[] { "红", "蓝", "黑", "白" }); cbColor.SelectedIndex = 0; // 默认选第一个
    4. 在保存逻辑里(找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”。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询