简介:针对毕业设计场景的SPC产品质量在线分析系统C#源码包,面向计算机、自动化等相关专业学生,可用于课程设计、大作业或毕设参考。项目完整实现了用户登录、员工/产品/车间/工序/设备信息管理、分类搜索、控制图判异准则设置、测量数据备注,以及Xbar-R、Xmedian-R、X-Rs等多种控制图,并绘制直方图与Cpk图,覆盖质量数据在线分析的主要环节。
资源包为ZIP格式,约3.02MB,共165个文件。其中36个.cs源码文件承载核心业务逻辑,79个.png图片直观展示界面运行效果,另有resx/resources资源文件、配置文件与可执行程序等,目录结构清晰,便于按模块学习。压缩包内还包含解决方案与项目文件,使用Visual Studio 2013与SQL Server 2012即可构建运行。目前已有202人学习/下载,具备较好的参考热度。
毕设源码评审分达95分,调试运行正常,安装SQL Server 2012后即可直接运行。基础较强的学习者可在此基础上调整扩展,掌握WinForm+SQL Server的开发方式,并深入理解SPC控制图及Cpk等质量指标的实现细节。
1. 基于 SPC 的产品质量在线分析系统:这份 C# 毕设源码我替你先跑了一遍
先给结论:这套基于 SPC 的产品质量在线分析系统,是一份能直接跑起来的 C# WinForms 毕业设计源码,数据库用的是 SQL Server 2012,开发环境是 Visual Studio 2013。SPC(统计过程控制)在制造业里是用来监控生产过程中产品质量波动的,它的核心价值不只是把样本数据画成几张图,而是通过控制图、Cpk 这些统计工具判断产线到底稳不稳定。很多初学者拿到这套系统会先去看控制图画得漂不漂亮,实际上真正值得研究的是它背后的判异准则触发逻辑和数据流组织方式。
这套系统的功能面很完整:用户登录、员工/产品/车间/工序/设备信息维护、分类搜索、测量数据录入与备注、Xbar-R / Xmedian-R / X-Rs 控制图、直方图、Cpk 图,还带判异准则设置和失控记录处理流程。登录账号 admin、密码 admin,附加数据库后就能直接体验完整链路。适合两类人:一类是正在做毕业设计、需要一份功能闭环的参考实现的学生;另一类是刚进工厂做质量工程师、想搞清楚控制图到底怎么落地到代码里的从业者。它不教你 SPC 理论,它直接给你看一套能跑通的实现是怎么组织数据和绘制图形的。
2. 系统架构与数据层:先搞清 WinForms 项目是怎么把数据串起来的
2.1 项目文件构成:从缓存文件反推出工程的组织方式
拿到压缩包解压后,不要急着打开 MainForm.cs 就开读。我习惯先看一眼文件清单,因为工程结构能说明设计思路。这套系统里除了 onlineSPC.csproj 工程文件和一个 MainForm.cs 主窗体,还出现了 onlineSPCDataSet.Designer.cs,这说明项目用的是强类型 DataSet 而不是手写 SQL 拼接,数据访问层是通过数据集设计器自动生成的。这对毕设来说是很加分的做法——代码量比手写 SqlCommand 少,而且不容易出现 SQL 语法错误。
app.config 和 onlineSPC.exe.config 成对出现,说明连接字符串写在配置文件里。设计期生成的那几个 ResolveAssemblyReference.cache 和 GenerateResource.Cache 是 Visual Studio 编译过程的临时产物,不影响运行。真正的数据访问关系是:MainForm.cs 负责界面交互,onlineSPCDataSet.Designer.cs 负责把数据库表映射成内存中的强类型数据对象,界面上的 DataGridView 控件通过 BindingSource 绑定到 DataSet 的对应表。
顺手提一个容易被忽略的细节:用强类型 DataSet 的项目,当你改了数据库结构(比如给数据表加了一个字段),必须在设计器里同步更新 DataSet,否则运行时会报「列名无效」。后面避坑章节我会专门展开讲这个翻车场景。
2.2 登录模块:从 admin 账号看用户校验的实现方式
这套系统的登录逻辑并不复杂,但结构值得模仿。用户名 admin、密码 admin 存在数据库的用户表里,登录窗体接收输入后走一条校验流程,典型做法是先按用户名查出记录,再对密码做比对。常见的实现方式如下:
private bool ValidateUser(string username, string password) { // 通过强类型 DataSet 的 TableAdapter 查询匹配用户名的记录 DataSet1.UserDataTable userTable = this.userTableAdapter.GetDataByUsername(username); if (userTable.Rows.Count == 0) { MessageBox.Show("用户不存在"); return false; } DataRow row = userTable.Rows[0]; // 这里用字符串直接比对,生产环境建议改为哈希比对 if (row["Password"].ToString() == password) { return true; } MessageBox.Show("密码错误"); return false; }这段逻辑的要点在两点:第一,通过 TableAdapter 的带参查询 GetDataByUsername 去查用户,避免手写拼接 SQL 带来的注入风险;第二,密码比对用的是普通字符串相等,对毕设来说没问题,但如果想升级成安全的做法,应该在数据库存哈希值,比对时把输入密码做同样的哈希再比对。登录成功后,系统主窗体接收一个用户标识参数,方便后面在测量数据备注里记录是谁录入的。这里也能看到这套系统的边界:它没有做角色权限区分,所有登录用户看到的界面和操作权限是一样的,这点在论文里可以作为「未来改进方向」写进去。
2.3 五个基础信息维护页面:员工、产品、车间、工序、设备的增删改查
系统的信息维护模块包含五个维度:员工、产品、车间、工序、设备。这五个维度对应 SPC 分析里的分层需求——同一个产品可能由不同车间、不同设备生产,控制图必须能按这些维度去分组,否则混在一起统计会得出完全错误的结论。
实现上每个页面都是同一套路:窗体上放一个 DataGridView 展示列表,旁边放几个 TextBox 和 ComboBox 用于输入条件,按钮触发 TableAdapter 的插入、更新、删除方法。以添加产品信息为例,核心代码通常是这样的:
private void btnAddProduct_Click(object sender, EventArgs e) { try { // 先校验必填项,避免向数据库写入空值 if (string.IsNullOrEmpty(txtProductName.Text)) { MessageBox.Show("产品名称不能为空"); return; } // 调用 TableAdapter 的插入方法,参数顺序与数据库表字段一一对应 this.productTableAdapter.Insert( txtProductCode.Text.Trim(), txtProductName.Text.Trim(), txtSpecification.Text.Trim(), Convert.ToDecimal(txtTargetValue.Text.Trim()), Convert.ToDecimal(txtUpperLimit.Text.Trim()), Convert.ToDecimal(txtLowerLimit.Text.Trim()) ); // 刷新列表 this.productTableAdapter.Fill(this.onlineSPCDataSet.Product); } catch (Exception ex) { MessageBox.Show("添加失败:" + ex.Message); } }注意这里的 Insert 方法参数:productCode、productName、specification、targetValue、upperLimit、lowerLimit。其中规格上限和下限是为后面算 Cpk 服务的——Cpk 计算公式里必须用到 USL(规格上限)和 LSL(规格下限)。这套系统在数据结构层面就提前做了关联设计,不是每个页面孤立存在的。新建这些基础信息页面时,核心操作顺序是:先在设计器里拖 TableAdapter 并配置 Insert 语句,再写按钮事件调用对应方法。如果改动了数据库表结构,Insert 方法的参数类型可能和实际字段不匹配,运行时报错信息会直接指出「参数 @p2 无法转换」,处理方法是重新运行设计器的配置向导。
2.4 测量数据录入与备注:在线分析系统的数据入口设计
在线分析系统的「在线」二字,落到代码层面其实就是测量数据录入后能即时触发控制图刷新。这个模块的核心是一个数据录入窗体,字段包含产品编号、工序编号、设备编号、测量值、测量时间、备注信息。备注字段是这套系统一个很有心的设计——当某个点失控时,工程师可以在备注里写明当时发生了什么(换料、刀具磨损、操作员调整等),后续回溯失控原因时就有据可查。
数据保存后,系统主界面上的控制图和数据列表是按绑定关系自动刷新的。关键代码是保存后重新执行 Fill 方法:
private void btnSaveMeasurement_Click(object sender, EventArgs e) { // 检查重复录入:同一产品、同一工序、同一测量时间不应出现两条数据 if (IsDuplicateRecord(txtProductCode.Text, dtpMeasureTime.Value)) { MessageBox.Show("该时间点已存在测量记录"); return; } this.measurementTableAdapter.Insert( txtProductCode.Text.Trim(), cboProcess.SelectedValue.ToString(), cboEquipment.SelectedValue.ToString(), Convert.ToDecimal(txtMeasureValue.Text.Trim()), dtpMeasureTime.Value, txtRemark.Text.Trim() ); // 保存后立即刷新主界面数据列表 this.measurementTableAdapter.Fill(this.onlineSPCDataSet.Measurement); MessageBox.Show("保存成功"); }这里值得注意的参数是 cboProcess.SelectedValue——Combobox 绑定的是工序表,SelectedValue 拿到的是工序主键编号,而不是界面上显示的名称。初学 C# 的人经常在这里出错:SelectedItem 拿的是显示文本,SelectedValue 拿的是绑定值字段。如果数据表的外键关联没配对,保存后列表显示的是数字而不是名称,排查方向就是检查窗体的 Combobox 的 DisplayMember 和 ValueMember 属性是否分别指向了名称字段和 ID 字段。
3. 控制图的实现与统计口径:Xbar-R 图的数学细节不能马虎
3.1 三类控制图的分组逻辑:Xbar-R、Xmedian-R、X-Rs 各自的适用场景
这套系统实现了三组控制图:Xbar-R(均值-极差图)、Xmedian-R(中位数-极差图)、X-Rs(单值-移动极差图)。三者的统计口径不同,代码实现也完全不同。
Xbar-R 是最经典的子组控制图,适合每次抽样包含多个样本的场景。典型做法是每隔一段时间取 5 个产品测量,分量均值 Xbar 和子组极差 R,然后分别用这两组统计量画控制图。它要求每个子组内部是同一生产条件下的连续样本,不能把不同批次的样本混在一个子组里。
Xmedian-R 用子组中位数替代均值,优点是抗异常值干扰——子组里有个别极端值不会把统计量拉偏,适合数据本身波动较大或者测量值容易受偶然因素影响的工序。代价是统计效率比均值低,需要更多的子组数才能达到同样的判定灵敏度。
X-Rs 是单值控制图,适合每次只能测一个数据的场景,比如破坏性检测或者检测成本极高的工序。它用移动极差(相邻两个单值的差的绝对值)来估计过程波动,不需要分组。三种图的共通点是最终都转化为中心线和上下控制限的描绘——中心线是统计量的均值,控制限是按 ±3 倍标准差估算出来的。
3.2 控制限的数学公式:UCL、LCL 不是拍脑袋算出来的
SPC 控制限的计算有标准公式,不能拿 Excel 里的 STDEV 函数直接套。以 Xbar-R 图为例:先按子组算出每个子组的均值 Xbar_i 和极差 R_i,然后求所有子组均值的平均值 Xbarbar 和极差平均值 Rbar。Xbar 图的控制限是 Xbarbar 加减 A2 乘以 Rbar,R 图的控制限是 D3 乘以 Rbar(下限)和 D4 乘以 Rbar(上限)。其中 A2、D3、D4 是由子组大小 n 决定的常数,n=5 时 A2=0.577,D3=0,D4=2.114。当 D3=0 时,极差图下控制限不画线,因为极差值不可能为负,出现零值说明子组内样本完全相同,此时应考虑样本是否来自同一件产品。
这套系统的数据表里存储的是原始测量值,控制图的统计量是在窗体加载时动态计算的。也就是把原始数据表按子组分组,对每个子组先做聚合运算,再生成绘图点集。这里的核心代码逻辑如下:
private void CalculateXbarRChart() { // 按子组编号分组获取测量数据 var groups = measurementTable.Where(x => x.ProductCode == currentProductCode) .GroupBy(x => x.SubgroupId); List<decimal> xbarList = new List<decimal>(); List<decimal> rList = new List<decimal>(); foreach (var group in groups) { var values = group.Select(x => x.MeasureValue).ToList(); // 子组均值 xbarList.Add(values.Average()); // 子组极差 = 最大值 - 最小值 decimal max = values.Max(); decimal min = values.Min(); rList.Add(max - min); } decimal xbarbar = xbarList.Average(); decimal rbar = rList.Average(); // n 为子组大小,从分组结果里取第一个子组的元素个数 int n = groups.First().Count(); // 查系数表 A2、D3、D4 SPCConstants constants = GetConstantsByGroupSize(n); // Xbar 图控制限 decimal uclX = xbarbar + constants.A2 * rbar; decimal lclX = xbarbar - constants.A2 * rbar; // R 图控制限,注意 D3 可能为 0,此时不绘制下控制限 decimal uclR = constants.D4 * rbar; decimal lclR = constants.D3 * rbar; }代码里的 GetConstantsByGroupSize 是查表函数,内部用 switch 返回不同 n 对应的 A2、D3、D4 常量。这是整套系统统计计算的核心,如果系数表查错了,画出来的控制图上下限就是错的,后面所有判异结果也全错。最典型的错误是 n=5 的时候当成 n=3 查系数,导致控制限过宽,异常点全部藏进限内。
3.3 绘图控件的选择:为什么用 GDI+ 而不是 Chart 控件
控制图绘制这部分,系统没有用 .NET 自带的 Chart 控件,而是采用 GDI+ 手绘的方式。原因在于控制图的形态比较固定——一条中心线、两条控制限线、若干样本点连成的折线,用 GDI+ 画笔完全能画出来,而且绘制逻辑完全可控。如果你要在毕设答辩时展示,手绘代码反而比拖控件更容易说清楚。
GDI+ 绘制的核心思路:先根据数据范围算出坐标系比例,把数据值映射到画布像素坐标,再依次画线画点。伪代码里最需要注意的一点是控制限不会随数据动态变化——理论上控制限是从历史数据计算出的固定值,新来的数据点只影响点的位置,不影响控制限的位置。这一点很多人做反了:每次刷新时重新计算控制限,导致控制限跟着新数据不断漂移,这在统计上是不成立的。
private void DrawXbarChart(Graphics g) { // 计算画布区域坐标映射比例 float chartHeight = pictureBox.Height - 60; float chartWidth = pictureBox.Width - 80; // 数据点上限用 UCL,下限用 LCL,中心线用 Xbarbar decimal maxY = uclX + (uclX - xbarbar) * 0.2m; decimal minY = lclX - (xbarbar - lclX) * 0.2m; // 把统计值转成像素坐标 float ScaleY(decimal v) => (float)(chartHeight - (v - minY) / (maxY - minY) * chartHeight) + 30; // 画中心线和上下限 Pen centerPen = new Pen(Color.Green, 2); Pen limitPen = new Pen(Color.Red, 1); g.DrawLine(centerPen, 40, ScaleY(xbarbar), 40 + chartWidth, ScaleY(xbarbar)); g.DrawLine(limitPen, 40, ScaleY(uclX), 40 + chartWidth, ScaleY(uclX)); g.DrawLine(limitPen, 40, ScaleY(lclX), 40 + chartWidth, ScaleY(lclX)); // 画样本点并连线 Pen linePen = new Pen(Color.Blue, 1); for (int i = 0; i < xbarList.Count; i++) { float x = 40 + i * (chartWidth / xbarList.Count); float y = ScaleY(xbarList[i]); if (i > 0) g.DrawLine(linePen, prevX, prevY, x, y); // 超过控制限的点用红色高亮 if (xbarList[i] > uclX || xbarList[i] < lclX) { g.FillEllipse(Brushes.Red, x - 4, y - 4, 8, 8); } else { g.FillEllipse(Brushes.Blue, x - 3, y - 3, 6, 6); } } }代码里 ScaleY 函数把数据值映射到画布坐标时做了 20% 的边距扩展,这是为了避免点正好贴住画布边缘。绘制结束后调用 pictureBox.Invalidate() 触发重绘。这里的按钮逻辑是「刷新图形」,点击后先重新计算统计量,再调用 Invalidate。Xmedian-R 和 X-Rs 的绘制逻辑结构相同,区别只在统计量的计算方式上——Xmedian 用 group 的中位数而不是平均值,X-Rs 用相邻两个点的绝对差做移动极差。
4. 判异准则、Cpk 与直方图:从描述到诊断的关键一跳
4.1 判异准则设置:八条准则如何映射为代码逻辑
控制图画出来之后要能自动判断过程是否失控,这是 SPC 系统的灵魂功能。这套系统内置了判异准则设置界面,可以逐条启停准则。标准 SPC 判异准则总共有八条,经典的门徒规则包括:点超出控制限、连续 7 点在中心线同一侧、连续 7 点持续上升或下降、点靠近控制限等。
判异准则不能盲目全开,按行业习惯,初用者通常只启用「超出控制限」和「连续 7 点同侧」两条,因为准则越多误报越多。系统的判异设置界面本质上是一个勾选列表,对应一个配置文件或数据库表,每项存一个布尔值。判定逻辑的代码实现大致如下:
private List<string> CheckControlRules(List<decimal> data, decimal ucl, decimal lcl, decimal center) { List<string> violations = new List<string>(); if (data.Count < 7) return violations; // 数据量不足时不判异 // 准则1:单点超出控制限 for (int i = 0; i < data.Count; i++) { if (data[i] > ucl || data[i] < lcl) { violations.Add($"第{i + 1}点超出控制限"); } } // 准则2:连续7点在中心线同一侧 int aboveCount = 0, belowCount = 0; for (int i = 0; i < data.Count; i++) { if (data[i] > center) { aboveCount++; belowCount = 0; } else if (data[i] < center) { belowCount++; aboveCount = 0; } if (aboveCount >= 7) violations.Add($"连续7点在中心线上方"); if (belowCount >= 7) violations.Add($"连续7点在中心线下方"); } // 准则3:连续7点持续上升或下降(趋势判断) int trend = 0; for (int i = 1; i < data.Count; i++) { if (data[i] > data[i - 1]) trend = (trend > 0) ? trend + 1 : 1; else if (data[i] < data[i - 1]) trend = (trend < 0) ? trend - 1 : -1; if (trend >= 7 || trend <= -7) { violations.Add($"连续{i + 1}点呈持续趋势变化"); break; } } return violations; }这个 CheckControlRules 方法的输入是整个序列的数据,逐条跑启用的规则。失控的判定结果会进失控记录表,状态字段初始为「已失控」,工程师确认后改成「已受理」,原因处理完毕改成「已处理」——这套状态流转对应系统主界面里的三个列表页签。要注意的是,不同准则对最小数据量的要求不同,数据不够就别硬判。代码里 data.Count < 7 直接返回空列表,这是合理的处理方式。
4.2 Cpk 指数计算:能力指数的实现与单位
Cpk 是衡量过程能力的关键指数,系统里也用图形方式展示。Cpk 的计算公式是 Cpk = min(CPU, CPL),其中 CPU = (USL - Xbarbar) / (3 * sigma),CPL = (Xbarbar - LSL) / (3 * sigma)。这里的 sigma 估算方式有讲究,SPC 里通常用 Rbar / d2 作为标准差估计量,而不是直接对原始数据求标准差。d2 也是查表系数,n=5 时 d2=2.326。
private decimal CalculateCpk(decimal usl, decimal lsl, List<decimal> data, int subgroupSize) { // 子组均值的整体均值 decimal mean = data.Average(); // 用极差法估计标准差:Rbar / d2 decimal rbar = 0; for (int i = 0; i < data.Count; i += subgroupSize) { var group = data.Skip(i).Take(subgroupSize).ToList(); if (group.Count > 0) { rbar += (group.Max() - group.Min()); } } rbar /= Math.Ceiling(data.Count / (double)subgroupSize); decimal sigma = rbar / 2.326m; // d2 for n=5 if (sigma == 0) return 0; // 数据无波动时 Cpk 无意义 decimal cpu = (usl - mean) / (3 * sigma); decimal cpl = (mean - lsl) / (3 * sigma); return Math.Min(cpu, cpl); }代码里的 sigma 估算用的是极差法,和直接用标准差公式算出来的结果略有出入。极差法更适合样本量小的子组,因为极差受个别极端值影响比标准差更明显,但它的计算逻辑更简单直观,也更符合 SPC 理论的标准做法。Cpk 结果小于 1.33 通常认为过程能力不足,1.33~1.67 之间为良好,大于 1.67 为过剩——这条行业标准是答辩时老师大概率会问到的知识点。
4.3 直方图的分布形态:为什么过程数据看起来像正态分布
直方图模块的实现逻辑是把测量值分成若干等宽区间,统计每个区间的频数,然后画柱状图。分组数一般取数据量的平方根向上取整,比如 100 个数据分 10 组。这个模块虽然实现不难,但它和 Cpk 图放在一起形成了一个完整的诊断闭环——直方图看分布形态,Cpk 看过程能力,控制图看稳定性。一个系统同时具备这三个维度才算完整的 SPC 在线分析。
5. 部署与避坑指南:这套毕设源码最容易翻车的四个地方
5.1 数据库附加失败:SQL Server 版本不匹配与权限问题
现象:附加 onlineSPC.mdf 时提示「数据库版本 655 不受支持」,或者附加成功后连接字符串报错连不上。原因:这套数据库是用 SQL Server 2012 生成的,如果你本机装的是 SQL Server 2008 或 2005,低版本无法附加高版本生成的数据库文件。解决:换成 SQL Server 2012 或更高版本(2014/2016/2019 均可向上兼容);如果你只能使用 2008,那就只能把数据导出再重建库,或者直接换个数据库环境。连接字符串部分默认写在 app.config 里,服务器名要改成你自己机器上的实例名,常见写法是 .\SQLEXPRESS 或 localhost,不要照抄别人机器上的实例名。
5.2 登录成功后主界面空白:DataSet 与数据库表结构不一致
现象:登录正常,但主窗体的数据列表和下拉框全部空白。原因:我遇到过一种情况,数据库附加后表名和 DataSet 里映射的表名不一致——修改过数据库表名或字段名,但 onlineSPCDataSet.Designer.cs 没有同步更新。解决:在 Visual Studio 里打开 DataSet 设计器,右键选择「配置向导」重新连接数据库,让设计器根据最新表结构重新生成映射。如果改过字段名,所有用到该字段的代码都要同步修改,编译错误列表会一个个指出来,顺着改就行。
5.3 添加测量数据后控制图不刷新:列表和数据未重新绑定
现象:数据保存成功,列表也显示出来了,但控制图上的点没变化,需要重启程序才更新。原因:保存后只是重新 Fill 了 DataSet,但控制图的绘制方法仍在用旧的分组统计结果。解决:保存成功后不仅要重新查询数据,还要重新调用分组统计计算方法,最后调用 pictureBox.Invalidate() 触发重绘。我自己的习惯是把「刷新统计量」和「刷新图形」抽成两个方法,保存按钮事件里依次调用,这样不会漏。
5.4 判异误报:默认开启全部准则导致的频繁告警
现象:系统刚上线就疯狂报失控,几乎每个子组都能触发异常。原因:所有判异准则全开了,而你的采样频率和数据量还达不到八条准则的最低要求。解决:判异准则设置里只保留第 1 条(超限)和第 2 条(连续 7 点同侧),等积累了足够多的稳定历史数据后再逐步放开。这是实践经验,不是系统的 bug。
5.5 DataGridView 绑定下拉框显示数字而不是名称:外键关联没配对
现象:测量数据列表里显示的是产品编号数字,而不是产品名称。原因:DataGridView 的列绑定的是外键字段,但没设置列的 DataSource 来同步显示关联表名称。解决:在 DataGridView 的列属性里,将 DataPropertyName 设为外键字段,把列的 DataSource 绑定到关联表,DisplayMember 设为名称字段。这步设置只影响显示,不影响存储,是最容易踩也最好改的坑。
6. 进阶改造思路:从单品单线扩展到多产线多工序的数据隔离
如果想把这份毕设源码改成能支撑多条产线同时监控的系统,第一个要动刀的地方是数据表结构。原始的测量数据表里如果只有产品编号和工序编号两个维度,多产线场景下同样的产品和工序在不同设备上的数据会互相混叠。改造方式是给测量数据表增加一个产线编号字段,所有统计查询的 WHERE 条件都带上产线编号。控制图的计算方法本身不受影响,只是每个产线的样本序列要分别分组。这里有一个更关键的细节:不同产线的控制限必须分开计算,因为各自的设备精度和工艺参数不一致。如果一个 A 产线的高均值数据混进了 B 产线的子组统计里,控制限会被整体拉高,本来该报警的偏移就检测不出来了。
改造后的查询逻辑用 LINQ 可以写成:
var lineGroups = measurementTable .Where(x => x.ProductCode == currentProductCode && x.LineCode == currentLineCode) .GroupBy(x => x.SubgroupId) .OrderBy(g => g.Key) .ToList();这里的过滤条件同时限定产品编号和产线编号,比原始版本的筛选多了一个维度。完成这一步后,主界面上的产品筛选下拉框需要增加联动——先选产线,再选产品,产品列表随产线切换而刷新。联动用 SelectedIndexChanged 事件触发表重新查询,这是 WinForms 里最常用的二级联动写法。如果还有余力,可以考虑给测量数据表加一个「班次」字段,这样早班和晚班的数据也能分别出控制图,对于 24 小时运转的车间来说,这个需求几乎必然出现。
我对这套系统印象最深的一点,是它把控制图绘制的细节处理得很扎实。从那以后我每次拿到类似的质量分析项目,都会先检查控制限的计算是不是用极差法估标准差,再看判异规则是不是可配置,这两点如果没做对,界面再好看也没用。希望这篇拆解对你的毕设或者工作项目有实际帮助。
本文还有配套的精品资源,点击获取