C#入门学习周记3

发布时间:2026/11/2 6:35:51
C#入门学习周记3 本周做项目智能仓库管理系统Smart Warehouse Management System, SWMS项目搭建本项目是使用三层架构搭建项目。三层架构简单说就是把程序分成 UI界面/ BLL业务逻 辑/ DAL数据访问 三层通常再加一个 Model实体 作为各层之间传递数据的载体。项目结构示例三层架构的优缺点1. 职责分离清晰 可选工具类、常量、配置UI 层只负责界面显示、用户输入、事件响应。BLL 层负责业务规则、校验、计算、事务。DAL 层只负责和数据库打交道。2. 可维护性高业务逻辑集中在 BLL数据访问集中在 DAL。出问题时定位快界面问题找 UI规则问题找 BLL数据库问题找 DAL。3. 可替换性强换数据库SQL Server → MySQL只改 DAL。换界面 WinForm → WPF / WebBLL 和 DAL 可复用。换 ORMADO.NET → EF / Dapper也只影响 DAL。4. 便于团队分工前端/界面人员写 UI业务人员写 BLL数据库人员写 DAL5. 可测试性更好BLL 不依赖界面可以单独做单元测试。如果 DAL 再抽象成接口还能用 Mock 替换数据库。6. 适合中大型项目项目越大、逻辑越复杂三层架构的优势越明显能避免“窗体里写 SQL”的混乱。二、缺点1. 开发成本变高要多写 Model、BLL、DAL 多套代码。简单功能也要层层传递代码量比“窗体直接连数据库”大很多。2. 小型项目显得臃肿 如果只是个小工具、几个窗体、几张表三层架构会显得过度设计开发反而更慢。3. 性能上多一层调用 UI → BLL → DAL → 数据库比 UI 直接访问数据库多一层。不过对大多数 WinForm 项目来说这点开销可以忽略。项目创建第一步: 创建一个SWMS的解决方案, 项目名为UI , 项目模版: Windows窗体应用(.Net Framework)第二步: 在SWMS的解决方案,右键创建项目, 模型选择:类库(.Net Framework)依次创建DAL, Models,Common第三步: 为相同添加其他项目的依赖:UI 依赖 BLL 和 Models和CommonBLL 依赖 DAL 和 ModelsDAL 依赖 Models引入第三方UI库WinForm 虽然是一个成熟的桌面开发框架但原生控件样式较为传统。以下是几种流行的第三方 UI 库可以显著提升 WinForm 应用程序的外观和用户体验通过 Nuget 包管理器搜索 SunnyUI 安装业务功能开发使用 SunnyUI 来搭建一个无边框的登录界面,UI效果如下:创建一个窗体,名为 FrmLoginUI相关的设计不在这儿写了主要解释逻辑部分private void uiButton1_Click(object sender, EventArgs e) { //测试一下 //MessageBox.Show(测试一下); //弹一个对话框 //步骤: //1.获取用户名,密码 对应的UserBLL类的代码: string username tbUsername.Text; string password tbPwd.Text; //2.进行非空校验 if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password)) { UIMessageBox.Show(用户名,密码不能为空!); return; } //3.调用业务类的登录方法进行登录 bool rs userBLL.Login(username,password); //4.如果登录成功,跳转到主页窗体 if (rs) { //UIMessageBox.ShowAsk(登录成功!); //设置窗体的返回结果为OK this.DialogResult DialogResult.OK; //把当前登录的用户名保存到用户上下文UserContext中 UserContext.LoginName username; //把登录窗体关闭 Close(); return; } //5.如果登录失败,弹出错误提示,并把文本框清空 UIMessageBox.Show(用户名或者密码错误); tbUsername.Text ; tbPwd.Text ; } }对应的UserBLL类的代码/// summary 注意: 上面代码没有去连接数据库, 我们先把业务跑通, 后期我们可以把数据库和ADO引入 为了实现把登录用户名显示到首页窗体,我们使用了UserContext保存用户信息 /// 用户业务类 /// /summary public class UserBLL { private UserDAL userDAL new UserDAL(); /// summary /// 登录 /// /summary /// param nameusername用户名/param /// param namepassword密码/param /// returnstrue: 登录成功, false: 登录失败/returns public bool Login(string username, string password) { ////1.调用UserDAL类方法,根据用户名到数据库查询记录 //UserModel userModel userDAL.GetUserByUsername(username); ////2.判断是否存在,如果存在,判断密码是否一样 //if (userModel ! null) //{ // //3.如果密码一样, 登录成功 // if (userModel.Password password) // { // return true; //登录成功 // } //} if (username password) { return true; } // 否则登录失败 return false; } }3.2主页窗体的实现主窗体FrmMain为无边框 科技/工业风设计整体用 Dock 布局上标题栏 topPanel→ 中内容区 mainPanel→ 下状态与导航栏 bottomPanel。标题栏有日期/时间/最小化/最大化/关闭底部导航栏用 UITableLayoutPanel(8列)放 6 个 UISymbolButton 导航按钮。【例子】主页窗体效果图 UI设计FrmMain.Designer.cs起始FrmMain.cs后台逻辑OnLoad 里显示登录名/日期/时间、启动定时器、记录窗体正常大小位置、给所有导航按钮统一绑定点击事件并默认跳转环境监控页。导航用开关 NavBtn_Click 根据 Tag 跳转对应窗体。FrmMain.cs——OnLoad NavBtn_Click导航// FrmMain.cs 核心 private void FrmMain_Load(object sender, EventArgs e) { ShowLoginName(); ShowNowDate(); ShowTime(); timer1.Start(); _normalBounds this.Bounds; // 记录正常大小位置 // 给底部所有导航按钮统一绑定事件 foreach (var c in buttonLayoutPanel.Controls) if (c is UISymbolButton btn) btn.Click NavBtn_Click; NavigateUtil.NavigateToFrmEnvMonitor(mainPanel); // 默认显示环境监控 uiSymbolButton1.FillColor Color.SkyBlue; } // 导航按钮先全部还原背景色再高亮点击的按钮按 Tag 跳转 private void NavBtn_Click(object sender, EventArgs e) { foreach (var c in buttonLayoutPanel.Controls) if (c is UISymbolButton b1) b1.FillColor Color.FromArgb(49, 96, 133); UISymbolButton btn sender as UISymbolButton; btn.FillColor Color.SkyBlue; string tag btn.Tag?.ToString(); switch (tag) { case envMonitor: NavigateUtil.NavigateToFrmEnvMonitor(mainPanel); break; case inbound: NavigateUtil.NavigateToFrmInbound(mainPanel); break; case outbound: NavigateUtil.NavigateToFrmOutbound(mainPanel); break; case robotControl:NavigateUtil.NavigateToFrmRobotControl(mainPanel);break; case inventory: NavigateUtil.NavigateToFrmInventory(mainPanel); break; case report: NavigateUtil.NavigateToFrmReport(mainPanel); break; } }时间显示用定时器每秒更新最大化/还原用 _normalBounds 记忆原大小关闭前用 UIMessageBox.ShowAsk 确认。导航工具类NavigateUtil核心把独立的 UIForm 窗体“伪装”成普通控件嵌入到主窗体 UIPanel实现多页面切换并保持页面状态。关键childForm.TopLevel false使窗体可作为控件添加进容器。NavigateUtil.cs完整代码 TopLevelfalse原理解释// UI.Utils/NavigateUtil.cs public static void NavigateToF(UIPanel panel) where F : UIForm, new() { panel.Visible true; panel.BringToFront(); bool isExist false; foreach (Control c in panel.Controls) { Form form c as Form; if (form is F) { form.Show(); isExist true; break; } else form.Hide(); // 其它窗体隐藏只显示一个 } if (!isExist) { F childForm new F(); childForm.TopLevel false; // 核心变为非顶级窗体可作控件 childForm.ShowTitle false; // 去掉边框 childForm.Dock DockStyle.Fill; childForm.WindowState FormWindowState.Maximized; panel.Controls.Add(childForm); childForm.Show(); } }3.3环境监控业务实现3.3.1 UI布局设计布局用 Panel Dock顶部标题栏(pnlTitle,高45,深色#2B579A)、上方数据卡片区(pnlTop,高130,4个圆角卡片)、中间图表区(pnlMiddle左侧 pnlChart 折线图 右侧 pnlDevice 设备列表)、底部报警表格区(pnlBottom,高220)。3.3.2后台逻辑OnLoad 中拆分为 5 个独立 Init 方法单一职责InitTopCards顶部卡片代码动态创建for 循环 字典缓存便于扩展成更多卡片、InitChart双 Y 轴曲线左轴温度红色、右Y2轴湿度蓝色、InitDeviceList设备状态列表 UILight 指示灯、InitAlarmGrid报警表格 状态高亮、InitTimer1 秒定时模拟数据。后台逻辑——窗体基础设置生命周期顶部卡片动态创建// FrmEnvMonitor.cs 核心 protected override void OnLoad(EventArgs e) { base.OnLoad(e); InitTopCards(); InitChart(); InitDeviceList(); InitAlarmGrid(); InitTimer(); AddAlarmRecord(温度, 32.5°C, 未确认); // 示例报警 }双Y轴曲线配置设备状态指示灯逻辑// 双 Y 轴折线温度/湿度 private void InitChart() { _lineOption new UILineOption { Title { Text 温湿度实时曲线 }, XAxisType UIAxisType.DateTime, XAxis { Name 时间 }, YAxis { Name 温度(℃) }, Y2Axis { Name 湿度(%) } }; _tempSeries new UILineSeries(温度, Color.FromArgb(231,76,60)) { Smoothtrue, Width2 }; _humiditySeries new UILineSeries(湿度, Color.FromArgb(52,152,219), true) { Smoothtrue, Width2 }; _tempSeries.SetMaxCount(100); _humiditySeries.SetMaxCount(100); // 防内存溢出 _lineOption.AddSeries(_tempSeries); _lineOption.AddSeries(_humiditySeries); _lineChart.SetOption(_lineOption); }【报警表格数据绑定 CellFormatting高亮定时刷新引擎// 定时刷新模拟真实工业数据流 private void RefreshTimer_Tick(object sender, EventArgs e) { double temperature 22 _random.NextDouble() * 8; double humidity 45 _random.NextDouble() * 20; int airQuality _random.Next(30, 80); UpdateCardValue(温度, temperature.ToString(F1) °C); UpdateCardValue(湿度, humidity.ToString(F0) %); UpdateCardValue(空气质量, airQuality AQI); AddChartPoint(DateTime.Now, temperature, humidity); UpdateDeviceStatus(温湿度传感器, true); // 约 5% 概率触发报警 if (_random.Next(0, 100) 5) if (temperature 28.5) AddAlarmRecord(温度过高, temperature.ToString(F1)°C, 未确认); else if (humidity 62) AddAlarmRecord(湿度过高, humidity.ToString(F0)%, 未确认); UpdateAlarmCardDisplay(); } protected override void OnFormClosing(FormClosingEventArgs e) { _refreshTimer?.Stop(); _refreshTimer?.Dispose(); base.OnFormClosing(e); }3.4入库管理3.4.1 UI布局设计入库管理窗体同样用 Panel Dock顶部标题栏(pnlTitle)、查询过滤区(pnlFilter,高100单号、日期范围、状态、查询/重置/新增按钮)、底部数据表格区(pnlGrid)。3.4.2后台逻辑OnLoad 拆四步InitGrid手动 AddColumn 建列 操作按钮列、InitDatePickers日期默认值、LoadMockData模拟数据、RefreshGrid绑定 DataSource。数据过滤用 LINQ Where 实现“单号模糊 状态精确 日期范围”三重条件。// FrmInbound.cs 核心 private UIDataGridView _grid; private ListInboundOrder _allOrders new ListInboundOrder(); private ListInboundOrder _displayOrders new ListInboundOrder(); protected override void OnLoad(EventArgs e) { base.OnLoad(e); InitGrid(); InitDatePickers(); LoadMockData(); RefreshGrid(); } private void InitGrid() { _grid new UIDataGridView { Locationnew Point(15,15), AnchorAnchorStyles.Top|AnchorStyles.Left|AnchorStyles.Right|AnchorStyles.Bottom, AllowUserToAddRowsfalse, ReadOnlytrue, AutoSizeColumnsModeDataGridViewAutoSizeColumnsMode.Fill, SelectionModeDataGridViewSelectionMode.FullRowSelect }; _grid.Init(); _grid.AddColumn(入库单号,OrderNo); _grid.AddColumn(物料名称,MaterialName); _grid.AddColumn(数量,Quantity); _grid.AddColumn(供应商,Supplier); _grid.AddColumn(状态,Status); _grid.AddColumn(创建时间,CreateTime); // 操作按钮列点“详情”弹窗 DataGridViewButtonColumn btnCol new DataGridViewButtonColumn { HeaderText操作, NameOperation, Text详情, UseColumnTextForButtonValuetrue, FlatStyleFlatStyle.Flat }; _grid.Columns.Add(btnCol); _grid.CellFormatting Grid_CellFormatting; // 状态颜色 _grid.CellContentClick Grid_CellContentClick; // 按钮点击 pnlGrid.Controls.Add(_grid); } // 三重过滤单号模糊 状态精确 日期范围 private void ApplyFilter() { string orderNo txtOrderNo.Text.Trim(); string status cboStatus.Text; DateTime start dtpStart.Value.Date, end dtpEnd.Value.Date; if (start end) { UIMessageBox.ShowWarning(开始日期不能晚于结束日期); return; } _displayOrders _allOrders.Where(o { bool matchNo string.IsNullOrEmpty(orderNo) || o.OrderNo.Contains(orderNo); bool matchStatus status 全部 || o.Status status; if (DateTime.TryParse(o.CreateTime, out DateTime createTime)) { bool matchDate createTime.Date start createTime.Date end; return matchNo matchStatus matchDate; } return false; }).ToList(); _grid.DataSource null; _grid.DataSource _displayOrders; } // 行内“详情”按钮 private void Grid_CellContentClick(object sender, DataGridViewCellEventArgs e) { if (e.RowIndex 0) return; if (_grid.Columns[e.ColumnIndex].Name Operation) { InboundOrder order _displayOrders[e.RowIndex]; OpenDetailDialog(order); } }模型类 InboundOrderOrderNo 入库单号、MaterialName 物料名称、Quantity 数量、Supplier 供应商、Status 状态、CreateTime 创建时间、LocationCode 库位编码。3.5入库单操作窗体设计3.5.1 UI设计入库单操作窗体 FrmInboundDetail 比较简单直接使用绝对定位非 Dock。包含标题、物料名称、数量(UIIntegerUpDown)、供应商、库位分配(下拉框)、流程步骤区(pnlSteps①确认验货 ②执行上架 ③完成入库)、以及保存/取消按钮。窗体 MaximizeBoxfalse、MinimizeBoxfalse、StartPositionCenterParent。3.5.2后台逻辑实现双构造函数模式无参构造新建 Create实例化全新 InboundOrder 并自动生成流水号有参构造编辑 Update接收外部订单赋值给 _currentOrder 并回填界面。// FrmInboundDetail.cs 双构造函数 public InboundOrder ResultOrder { get; private set; } private InboundOrder _currentOrder; private bool _isNew; // 新建 public FrmInboundDetail() { InitializeComponent(); this.Style UIStyle.Blue; _isNew true; ResultOrder new InboundOrder(); InitLocationCombo(); GenerateOrderNo(); } // 修改 public FrmInboundDetail(InboundOrder order) { InitializeComponent(); this.Style UIStyle.Blue; _isNew false; _currentOrder order; ResultOrder new InboundOrder { OrderNoorder.OrderNo, MaterialNameorder.MaterialName, Quantityorder.Quantity, Supplierorder.Supplier, Statusorder.Status, CreateTimeorder.CreateTime, LocationCodeorder.LocationCode }; InitLocationCombo(); LoadOrderData(); }业务状态机与按钮联动企业级 WMS 中订单不能随意跳转。UpdateStepButtons() 按四种状态动态控制步骤文字颜色进行中橙色/完成绿色/未开始灰色与按钮启用当前步骤激活、其余禁用。流转待验货 →(只能验货)→ 已验货 →(只能上架)→ 已上架 →(只能完成)→ 已完成(全部禁用)。// 状态机 UpdateStepButtons private void UpdateStepButtons() { string status ResultOrder.Status; lblStep1.ForeColor lblStep2.ForeColor lblStep3.ForeColor Color.Gray; switch (status) { case 待验货: // 橙色高亮第1步激活验货按钮 lblStep1.ForeColor Color.FromArgb(255,140,66); btnVerify.Enabled true; btnShelve.Enabled false; btnComplete.Enabled false; break; case 已验货: lblStep1.ForeColor Color.FromArgb(39,174,96); lblStep2.ForeColor Color.FromArgb(255,140,66); btnVerify.Enabled false; btnShelve.Enabled true; btnComplete.Enabled false; break; case 已上架: lblStep1.ForeColor lblStep2.ForeColor Color.FromArgb(39,174,96); lblStep3.ForeColor Color.FromArgb(255,140,66); btnVerify.Enabled false; btnShelve.Enabled false; btnComplete.Enabled true; break; case 已完成: lblStep1.ForeColor lblStep2.ForeColor lblStep3.ForeColor Color.FromArgb(39,174,96); btnVerify.Enabled false; btnShelve.Enabled false; btnComplete.Enabled false; break; } }核心业务流转验货→上架→完成// 验货 private void btnVerify_Click(object sender, EventArgs e) { if (string.IsNullOrWhiteSpace(txtMaterial.Text) || string.IsNullOrWhiteSpace(txtSupplier.Text)) { UIMessageBox.ShowWarning(请填写完整物料信息); return; } ResultOrder.MaterialName txtMaterial.Text.Trim(); ResultOrder.Quantity numQuantity.Value; ResultOrder.Supplier txtSupplier.Text.Trim(); ResultOrder.Status 已验货; UpdateStepButtons(); UIMessageBox.Show(验货完成可进行上架操作, 提示, UIStyle.Green); } // 上架需先选库位 private void btnShelve_Click(object sender, EventArgs e) { if (cboLocation.SelectedItem null) { UIMessageBox.ShowWarning(请选择库位); return; } ResultOrder.LocationCode cboLocation.SelectedValue.ToString(); ResultOrder.Status 已上架; UpdateStepButtons(); UIMessageBox.Show(执行上架完成, 提示, UIStyle.Green); } // 完成生命周期结束全部锁定 private void btnComplete_Click(object sender, EventArgs e) { ResultOrder.Status 已完成; UpdateStepButtons(); UIMessageBox.Show(入库已完成, 提示, UIStyle.Green); }窗体关闭生命周期与拦截用户有 4 种关闭姿势点右上角 ×、点“取消”按钮、点“保存”按钮、按 AltF4。为防止误点 × 丢失数据重写 OnFormClosing当是用户关闭且未设为 OK 时弹窗询问是否保存。// 覆盖关闭行为 protected override void OnFormClosing(FormClosingEventArgs e) { if (e.CloseReason CloseReason.UserClosing this.DialogResult ! DialogResult.OK) { if (UIMessageBox.ShowAsk(是否保存当前数据)) btnSave_Click(null, null); // 模拟点击保存 else this.DialogResult DialogResult.Cancel; } base.OnFormClosing(e); } // 保存 private void btnSave_Click(object sender, EventArgs e) { if (_isNew) { if (string.IsNullOrWhiteSpace(txtMaterial.Text)) { UIMessageBox.ShowWarning(请输入物料名称); return; } ResultOrder.MaterialName txtMaterial.Text.Trim(); ResultOrder.Quantity numQuantity.Value; ResultOrder.Supplier txtSupplier.Text.Trim(); if (cboLocation.SelectedItem ! null) ResultOrder.LocationCode cboLocation.SelectedValue.ToString(); } this.DialogResult DialogResult.OK; // 通知主窗体保存成功 this.Close(); }辅助方法InitLocationCombo / GenerateOrderNo / LoadOrderData// 初始化库位下拉框模拟库位数据Code 与 Display 分离 private void InitLocationCombo() { var locations new ListLocationModel{ new LocationModel{ CodeA-01-01, DisplayA区-01架-01层-01位 }, new LocationModel{ CodeA-01-02, DisplayA区-01架-01层-02位 }, new LocationModel{ CodeB-02-03-01, DisplayB区-02架-03层-01位 }, new LocationModel{ CodeC-03-01-04, DisplayC区-03架-01层-04位 } }; cboLocation.DataSource locations; cboLocation.DisplayMember Display; cboLocation.ValueMember Code; } // 自动生成流水号与创建时间 private void GenerateOrderNo() { ResultOrder.OrderNo IN DateTime.Now.ToString(yyyyMMddHHmmss); ResultOrder.CreateTime DateTime.Now.ToString(yyyy-MM-dd HH:mm:ss); ResultOrder.Status 待验货; } // 编辑时回填界面并锁定输入 private void LoadOrderData() { txtMaterial.Text _currentOrder.MaterialName; numQuantity.Value _currentOrder.Quantity; txtSupplier.Text _currentOrder.Supplier; txtMaterial.Enabled false; numQuantity.Enabled false; txtSupplier.Enabled false; if (!string.IsNullOrEmpty(_currentOrder.LocationCode)) cboLocation.SelectedValue _currentOrder.LocationCode; UpdateStepButtons();

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询