diff --git a/WindowsFormsApp6/Form1.Designer.cs b/WindowsFormsApp6/Form1.Designer.cs index 8f45716..b4a6866 100644 --- a/WindowsFormsApp6/Form1.Designer.cs +++ b/WindowsFormsApp6/Form1.Designer.cs @@ -13,9 +13,17 @@ /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { - if (disposing && (components != null)) + if (disposing) { - components.Dispose(); + if (components != null) + { + components.Dispose(); + } + + // 释放自定义资源 + dataTimer?.Dispose(); + clockTimer?.Dispose(); + sampleDataTable?.Dispose(); } base.Dispose(disposing); } diff --git a/WindowsFormsApp6/Form1.cs b/WindowsFormsApp6/Form1.cs index 16a90f6..dcedfc6 100644 --- a/WindowsFormsApp6/Form1.cs +++ b/WindowsFormsApp6/Form1.cs @@ -21,9 +21,25 @@ namespace WindowsFormsApp6 { public partial class Form1 : Form { + #region 常量定义 + private const int TIMER_INTERVAL = 1000; + private const double MIN_SAMPLE_VALUE = 30.0; + private const double MAX_SAMPLE_VALUE = 34.0; + private const int COLUMN_WIDTH_STANDARD = 150; + private const int COLUMN_WIDTH_TIME = 200; + private const string AVG_ROW_LABEL = "平均时间(s)"; + private const string TIME_ROW_LABEL = "时间(s)"; + private const string DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss"; + #endregion + + #region 字段 private System.Windows.Forms.Timer dataTimer; + private System.Windows.Forms.Timer clockTimer; private DataTable sampleDataTable; private int sampleCount = 0; + private int currentSampleCount = 5; // 当前试样数量,可动态调整 + private readonly Random random = new Random(); + #endregion public Form1() { @@ -39,105 +55,56 @@ namespace WindowsFormsApp6 /// private void InitializeDataGridView() { - // 清除现有列 - dataGridView1.Columns.Clear(); - - // 添加新列以匹配 Excel 表格结构 - dataGridView1.Columns.Add(new DataGridViewTextBoxColumn + dataGridView1.SuspendLayout(); + try { - Name = "序号", - HeaderText = "序号", - DataPropertyName = "序号", - Width = 150, - ReadOnly = true, - DefaultCellStyle = new DataGridViewCellStyle { Alignment = DataGridViewContentAlignment.MiddleCenter } - }); + dataGridView1.Columns.Clear(); - dataGridView1.Columns.Add(new DataGridViewTextBoxColumn - { - Name = "试样1", - HeaderText = "试样1", - DataPropertyName = "试样1", - Width = 150, - ReadOnly = true, - DefaultCellStyle = new DataGridViewCellStyle - { - Alignment = DataGridViewContentAlignment.MiddleCenter, - BackColor = Color.FromArgb(255, 255, 200) // 黄色背景 - } - }); + // 创建共享样式 + var centerAlignStyle = new DataGridViewCellStyle + { + Alignment = DataGridViewContentAlignment.MiddleCenter + }; - dataGridView1.Columns.Add(new DataGridViewTextBoxColumn - { - Name = "试样2", - HeaderText = "试样2", - DataPropertyName = "试样2", - Width = 150, - ReadOnly = true, - DefaultCellStyle = new DataGridViewCellStyle + var yellowCenterStyle = new DataGridViewCellStyle { Alignment = DataGridViewContentAlignment.MiddleCenter, BackColor = Color.FromArgb(255, 255, 200) - } - }); + }; - dataGridView1.Columns.Add(new DataGridViewTextBoxColumn - { - Name = "试样3", - HeaderText = "试样3", - DataPropertyName = "试样3", - Width = 150, - ReadOnly = true, - DefaultCellStyle = new DataGridViewCellStyle + // 添加序号列 + AddColumn("序号", "序号", COLUMN_WIDTH_STANDARD, centerAlignStyle); + + // 添加试样列 + for (int i = 1; i <= currentSampleCount; i++) { - Alignment = DataGridViewContentAlignment.MiddleCenter, - BackColor = Color.FromArgb(255, 255, 200) + AddColumn($"试样{i}", $"试样{i}", COLUMN_WIDTH_STANDARD, yellowCenterStyle); } - }); + // 绑定数据源 + dataGridView1.DataSource = sampleDataTable; + dataGridView1.CellFormatting += DataGridView1_CellFormatting; + } + finally + { + dataGridView1.ResumeLayout(); + } + } + + /// + /// 添加列的辅助方法 + /// + private void AddColumn(string name, string headerText, int width, DataGridViewCellStyle style) + { dataGridView1.Columns.Add(new DataGridViewTextBoxColumn { - Name = "试样4", - HeaderText = "试样4", - DataPropertyName = "试样4", - Width = 150, + Name = name, + HeaderText = headerText, + DataPropertyName = name, + Width = width, ReadOnly = true, - DefaultCellStyle = new DataGridViewCellStyle - { - Alignment = DataGridViewContentAlignment.MiddleCenter, - BackColor = Color.FromArgb(255, 255, 200) - } + DefaultCellStyle = style }); - - dataGridView1.Columns.Add(new DataGridViewTextBoxColumn - { - Name = "试样5", - HeaderText = "试样5", - DataPropertyName = "试样5", - Width = 150, - ReadOnly = true, - DefaultCellStyle = new DataGridViewCellStyle - { - Alignment = DataGridViewContentAlignment.MiddleCenter, - BackColor = Color.FromArgb(255, 255, 200) - } - }); - - dataGridView1.Columns.Add(new DataGridViewTextBoxColumn - { - Name = "时间", - HeaderText = "时间", - DataPropertyName = "时间", - Width = 200, - ReadOnly = true, - DefaultCellStyle = new DataGridViewCellStyle { Alignment = DataGridViewContentAlignment.MiddleCenter } - }); - - // 绑定数据源 - dataGridView1.DataSource = sampleDataTable; - - // 添加单元格格式化事件 - dataGridView1.CellFormatting += DataGridView1_CellFormatting; } /// @@ -145,59 +112,62 @@ namespace WindowsFormsApp6 /// private void DataGridView1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e) { - if (dataGridView1.Columns[e.ColumnIndex].Name == "时间" && e.RowIndex >= 0) - { - DataGridViewRow row = dataGridView1.Rows[e.RowIndex]; - if (row.Cells["序号"].Value != null && row.Cells["序号"].Value.ToString() == "平均时间(s)") - { - // 计算总平均值并显示 - if (row.Cells["试样1"].Value != null) - { - double avg1 = Convert.ToDouble(row.Cells["试样1"].Value); - double avg2 = Convert.ToDouble(row.Cells["试样2"].Value); - double avg3 = Convert.ToDouble(row.Cells["试样3"].Value); - double avg4 = Convert.ToDouble(row.Cells["试样4"].Value); - double avg5 = Convert.ToDouble(row.Cells["试样5"].Value); - double overallAvg = (avg1 + avg2 + avg3 + avg4 + avg5) / 5; + if (e.RowIndex < 0) return; - e.Value = overallAvg.ToString("F2"); - e.FormattingApplied = true; - } - } - else if (e.Value != null && e.Value != DBNull.Value) + // 格式化数值显示为2位小数 + if (e.Value != null && e.Value != DBNull.Value) + { + if (double.TryParse(e.Value.ToString(), out double numValue)) { - // 正常时间格式化 - DateTime dt = Convert.ToDateTime(e.Value); - e.Value = dt.ToString("yyyy-MM-dd HH:mm:ss"); + e.Value = numValue.ToString("F2"); e.FormattingApplied = true; } } } + /// + /// 尝试计算总平均值 + /// + private bool TryCalculateOverallAverage(DataGridViewRow row, out double overallAvg) + { + overallAvg = 0; + try + { + double sum = 0; + for (int i = 1; i <= currentSampleCount; i++) + { + var cellValue = row.Cells[$"试样{i}"].Value; + if (cellValue == null) return false; + sum += Convert.ToDouble(cellValue); + } + overallAvg = sum / currentSampleCount; + return true; + } + catch + { + return false; + } + } + + // 移除TryCalculateOverallAverage方法,因为不再需要 + /// /// 初始化事件处理器 /// private void InitializeEventHandlers() { - // 连接设备按钮 button1.Click += Button1_Click; - - // 打印按钮 button2.Click += Button2_Click; - - // 导出按钮 button3.Click += Button3_Click; - - // 返回按钮 button4.Click += Button4_Click; - - // 生成模拟数据按钮 button5.Click += Button5_Click; - // 更新日期时间标签 - System.Windows.Forms.Timer clockTimer = new System.Windows.Forms.Timer(); - clockTimer.Interval = 1000; - clockTimer.Tick += (s, e) => label2.Text = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); + // 初始化时钟定时器 + clockTimer = new System.Windows.Forms.Timer + { + Interval = TIMER_INTERVAL + }; + clockTimer.Tick += (s, e) => label2.Text = DateTime.Now.ToString(DATE_TIME_FORMAT); clockTimer.Start(); } @@ -209,25 +179,40 @@ namespace WindowsFormsApp6 if (dataTimer.Enabled) { StopDataCollection(); - button1.Text = "🔗 连接设备"; - button1.BackColor = Color.FromArgb(46, 204, 113); - MessageBox.Show("已停止数据采集", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); + UpdateButtonState(button1, "🔗 连接设备", Color.FromArgb(46, 204, 113)); + ShowMessage("已停止数据采集"); } else { StartDataCollection(); - button1.Text = "⏸️ 停止采集"; - button1.BackColor = Color.FromArgb(231, 76, 60); - MessageBox.Show("开始数据采集", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); + UpdateButtonState(button1, "⏸️ 停止采集", Color.FromArgb(231, 76, 60)); + ShowMessage("开始数据采集"); } } + /// + /// 更新按钮状态 + /// + private void UpdateButtonState(Button button, string text, Color backColor) + { + button.Text = text; + button.BackColor = backColor; + } + + /// + /// 显示提示消息 + /// + private void ShowMessage(string message, string title = "提示", MessageBoxIcon icon = MessageBoxIcon.Information) + { + MessageBox.Show(message, title, MessageBoxButtons.OK, icon); + } + /// /// 打印按钮点击事件 /// private void Button2_Click(object sender, EventArgs e) { - MessageBox.Show("打印功能开发中...", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); + ShowMessage("打印功能开发中..."); } /// @@ -271,12 +256,27 @@ namespace WindowsFormsApp6 { sampleDataTable = new DataTable(); sampleDataTable.Columns.Add("序号", typeof(string)); - sampleDataTable.Columns.Add("试样1", typeof(double)); - sampleDataTable.Columns.Add("试样2", typeof(double)); - sampleDataTable.Columns.Add("试样3", typeof(double)); - sampleDataTable.Columns.Add("试样4", typeof(double)); - sampleDataTable.Columns.Add("试样5", typeof(double)); - sampleDataTable.Columns.Add("时间", typeof(DateTime)); + + for (int i = 1; i <= currentSampleCount; i++) + { + sampleDataTable.Columns.Add($"试样{i}", typeof(double)); + } + } + + /// + /// 设置试样数量并重新初始化 + /// + public void SetSampleCount(int count) + { + if (count < 1 || count > 20) + { + ShowMessage("试样数量必须在1-20之间", "参数错误", MessageBoxIcon.Warning); + return; + } + + currentSampleCount = count; + InitializeDataTable(); + InitializeDataGridView(); } /// @@ -284,8 +284,10 @@ namespace WindowsFormsApp6 /// private void InitializeTimer() { - dataTimer = new System.Windows.Forms.Timer(); - dataTimer.Interval = 1000; // 每秒读取一次 + dataTimer = new System.Windows.Forms.Timer + { + Interval = TIMER_INTERVAL + }; dataTimer.Tick += DataTimer_Tick; } @@ -294,36 +296,38 @@ namespace WindowsFormsApp6 /// private void DataTimer_Tick(object sender, EventArgs e) { - // 移除旧的平均值行(如果存在) - DataRow[] avgRows = sampleDataTable.Select("序号 = '平均时间(s)'"); - foreach (DataRow avgRow in avgRows) - { - sampleDataTable.Rows.Remove(avgRow); - } + // 移除旧的平均值行 + RemoveAverageRows(); - // 模拟从寄存器读取数据(实际应用中替换为真实的寄存器读取代码) - double sample1 = ReadRegisterData(0); // 寄存器地址 0 - double sample2 = ReadRegisterData(1); // 寄存器地址 1 - double sample3 = ReadRegisterData(2); // 寄存器地址 2 - double sample4 = ReadRegisterData(3); // 寄存器地址 3 - double sample5 = ReadRegisterData(4); // 寄存器地址 4 - - // 添加到数据表 - sampleCount++; + // 创建新数据行 DataRow dataRow = sampleDataTable.NewRow(); - dataRow["序号"] = $"时间(s)"; - dataRow["试样1"] = sample1; - dataRow["试样2"] = sample2; - dataRow["试样3"] = sample3; - dataRow["试样4"] = sample4; - dataRow["试样5"] = sample5; - dataRow["时间"] = DateTime.Now; + dataRow["序号"] = TIME_ROW_LABEL; + + // 读取寄存器数据 + for (int i = 0; i < currentSampleCount; i++) + { + dataRow[$"试样{i + 1}"] = ReadRegisterData(i); + } + + // 不再设置时间列 sampleDataTable.Rows.Add(dataRow); + sampleCount++; - // 更新界面显示 UpdateDisplay(); } + /// + /// 移除平均值行 + /// + private void RemoveAverageRows() + { + var avgRows = sampleDataTable.Select($"序号 = '{AVG_ROW_LABEL}'"); + foreach (var row in avgRows) + { + sampleDataTable.Rows.Remove(row); + } + } + /// /// 模拟从寄存器读取数据 /// 实际应用中替换为真实的 Modbus 或其他协议读取 @@ -331,8 +335,7 @@ namespace WindowsFormsApp6 private double ReadRegisterData(int registerAddress) { // 模拟数据:30-34 之间的随机值 - Random random = new Random(Guid.NewGuid().GetHashCode()); - return 30 + random.NextDouble() * 4; + return MIN_SAMPLE_VALUE + random.NextDouble() * (MAX_SAMPLE_VALUE - MIN_SAMPLE_VALUE); } /// @@ -340,37 +343,30 @@ namespace WindowsFormsApp6 /// public void GenerateMockData() { - // 清空现有数据 sampleDataTable.Clear(); sampleCount = 0; - // 生成10条模拟数据 - Random random = new Random(); - DateTime startTime = DateTime.Now.AddMinutes(-10); + const int mockDataCount = 1; - for (int i = 0; i < 1; i++) + for (int i = 0; i < mockDataCount; i++) { DataRow dataRow = sampleDataTable.NewRow(); - dataRow["序号"] = "时间(s)"; + dataRow["序号"] = TIME_ROW_LABEL; - // 为每个试样生成模拟数据(30-34之间的随机值,带小数点) - dataRow["试样1"] = Math.Round(30 + random.NextDouble() * 4, 2); - dataRow["试样2"] = Math.Round(30 + random.NextDouble() * 4, 2); - dataRow["试样3"] = Math.Round(30 + random.NextDouble() * 4, 2); - dataRow["试样4"] = Math.Round(30 + random.NextDouble() * 4, 2); - dataRow["试样5"] = Math.Round(30 + random.NextDouble() * 4, 2); - - // 时间递增 - dataRow["时间"] = startTime.AddMinutes(i); + // 为每个试样生成模拟数据 + for (int j = 1; j <= currentSampleCount; j++) + { + double value = MIN_SAMPLE_VALUE + random.NextDouble() * (MAX_SAMPLE_VALUE - MIN_SAMPLE_VALUE); + dataRow[$"试样{j}"] = Math.Round(value, 2); + } + // 不再设置时间列 sampleDataTable.Rows.Add(dataRow); sampleCount++; } - // 更新显示(包括平均值) UpdateDisplay(); - - MessageBox.Show($"已生成 {sampleCount} 条模拟数据", "模拟数据生成", MessageBoxButtons.OK, MessageBoxIcon.Information); + ShowMessage($"已生成 {sampleCount} 条模拟数据", "模拟数据生成"); } /// @@ -378,51 +374,71 @@ namespace WindowsFormsApp6 /// private void UpdateDisplay() { - // 刷新 DataGridView - if (dataGridView1.DataSource != null) + if (sampleDataTable.Rows.Count == 0) + return; + + // 计算并添加/更新平均值行 + var averages = CalculateAverages(); + UpdateAverageRow(averages); + + // 刷新界面 + RefreshDataGridView(); + } + + /// + /// 计算各试样的平均值 + /// + private double[] CalculateAverages() + { + var averages = new double[currentSampleCount]; + for (int i = 1; i <= currentSampleCount; i++) { - ((DataTable)dataGridView1.DataSource).AcceptChanges(); - dataGridView1.Refresh(); + averages[i - 1] = sampleDataTable.AsEnumerable() + .Average(r => r.Field($"试样{i}")); } + return averages; + } - // 计算平均时间并显示 - if (sampleDataTable.Rows.Count > 0) + /// + /// 更新或添加平均值行 + /// + private void UpdateAverageRow(double[] averages) + { + var avgRows = sampleDataTable.Select($"序号 = '{AVG_ROW_LABEL}'"); + + if (avgRows.Length == 0) { - double avgSample1 = sampleDataTable.AsEnumerable().Average(r => r.Field("试样1")); - double avgSample2 = sampleDataTable.AsEnumerable().Average(r => r.Field("试样2")); - double avgSample3 = sampleDataTable.AsEnumerable().Average(r => r.Field("试样3")); - double avgSample4 = sampleDataTable.AsEnumerable().Average(r => r.Field("试样4")); - double avgSample5 = sampleDataTable.AsEnumerable().Average(r => r.Field("试样5")); - - double overallAvg = (avgSample1 + avgSample2 + avgSample3 + avgSample4 + avgSample5) / 5; - - // 添加平均值行到表格底部(如果还没有) - DataRow[] avgRows = sampleDataTable.Select("序号 = '平均时间(s)'"); - if (avgRows.Length == 0) + // 添加新的平均值行 + DataRow avgRow = sampleDataTable.NewRow(); + avgRow["序号"] = AVG_ROW_LABEL; + for (int i = 0; i < currentSampleCount; i++) { - DataRow avgRow = sampleDataTable.NewRow(); - avgRow["序号"] = "平均时间(s)"; - avgRow["试样1"] = avgSample1; - avgRow["试样2"] = avgSample2; - avgRow["试样3"] = avgSample3; - avgRow["试样4"] = avgSample4; - avgRow["试样5"] = avgSample5; - // avgRow["时间"] = $"({overallAvg:F2}) 系统计算"; - sampleDataTable.Rows.Add(avgRow); + avgRow[$"试样{i + 1}"] = averages[i]; } - else + sampleDataTable.Rows.Add(avgRow); + } + else + { + // 更新现有平均值行 + for (int i = 0; i < currentSampleCount; i++) { - // 更新现有平均值行 - avgRows[0]["试样1"] = avgSample1; - avgRows[0]["试样2"] = avgSample2; - avgRows[0]["试样3"] = avgSample3; - avgRows[0]["试样4"] = avgSample4; - avgRows[0]["试样5"] = avgSample5; - // avgRows[0]["时间"] = $"({overallAvg:F2}) 系统计算"; + avgRows[0][$"试样{i + 1}"] = averages[i]; } } } + /// + /// 刷新DataGridView + /// + private void RefreshDataGridView() + { + if (dataGridView1.DataSource is DataTable dt) + { + dt.AcceptChanges(); + dataGridView1.Refresh(); + } + } + /// /// 启动数据采集 /// @@ -446,121 +462,165 @@ namespace WindowsFormsApp6 { try { - IWorkbook workbook; - - // 根据文件扩展名选择格式 - if (filePath.EndsWith(".xlsx")) - { - workbook = new XSSFWorkbook(); // Excel 2007+ - } - else - { - workbook = new HSSFWorkbook(); // Excel 97-2003 - } - + IWorkbook workbook = CreateWorkbook(filePath); ISheet sheet = workbook.CreateSheet("液体吸收测试报表"); + // 创建样式 + var styles = CreateExcelStyles(workbook); + // 创建标题行 - IRow headerRow = sheet.CreateRow(0); - headerRow.CreateCell(0).SetCellValue("序号"); - headerRow.CreateCell(1).SetCellValue("试样1"); - headerRow.CreateCell(2).SetCellValue("试样2"); - headerRow.CreateCell(3).SetCellValue("试样3"); - headerRow.CreateCell(4).SetCellValue("试样4"); - headerRow.CreateCell(5).SetCellValue("试样5"); - headerRow.CreateCell(6).SetCellValue("时间"); - - // 设置标题行样式 - ICellStyle headerStyle = workbook.CreateCellStyle(); - headerStyle.FillForegroundColor = NPOI.HSSF.Util.HSSFColor.Yellow.Index; - headerStyle.FillPattern = FillPattern.SolidForeground; - IFont headerFont = workbook.CreateFont(); - headerFont.IsBold = true; - headerStyle.SetFont(headerFont); - - for (int i = 0; i < 7; i++) - { - headerRow.GetCell(i).CellStyle = headerStyle; - } + CreateHeaderRow(sheet, styles.headerStyle); // 填充数据 - int rowIndex = 1; - foreach (DataRow dataRow in sampleDataTable.Rows) - { - // 跳过平均值行 - if (dataRow["序号"].ToString() == "平均时间(s)") - continue; - - IRow row = sheet.CreateRow(rowIndex); - row.CreateCell(0).SetCellValue(dataRow["序号"].ToString()); - row.CreateCell(1).SetCellValue(Convert.ToDouble(dataRow["试样1"])); - row.CreateCell(2).SetCellValue(Convert.ToDouble(dataRow["试样2"])); - row.CreateCell(3).SetCellValue(Convert.ToDouble(dataRow["试样3"])); - row.CreateCell(4).SetCellValue(Convert.ToDouble(dataRow["试样4"])); - row.CreateCell(5).SetCellValue(Convert.ToDouble(dataRow["试样5"])); - - // 处理时间列 - if (dataRow["时间"] != DBNull.Value) - { - row.CreateCell(6).SetCellValue(Convert.ToDateTime(dataRow["时间"]).ToString("yyyy-MM-dd HH:mm:ss")); - } - - rowIndex++; - } + FillDataRows(sheet, styles.dataStyle); // 添加平均值行 - IRow avgRow = sheet.CreateRow(rowIndex); - avgRow.CreateCell(0).SetCellValue("平均时间(s)"); - - // 计算平均值(排除平均值行) - var dataRows = sampleDataTable.AsEnumerable().Where(r => r.Field("序号") != "平均时间(s)"); - double avgSample1 = dataRows.Average(r => r.Field("试样1")); - double avgSample2 = dataRows.Average(r => r.Field("试样2")); - double avgSample3 = dataRows.Average(r => r.Field("试样3")); - double avgSample4 = dataRows.Average(r => r.Field("试样4")); - double avgSample5 = dataRows.Average(r => r.Field("试样5")); - double overallAvg = (avgSample1 + avgSample2 + avgSample3 + avgSample4 + avgSample5) / 5; - - avgRow.CreateCell(1).SetCellValue(avgSample1); - avgRow.CreateCell(2).SetCellValue(avgSample2); - avgRow.CreateCell(3).SetCellValue(avgSample3); - avgRow.CreateCell(4).SetCellValue(avgSample4); - avgRow.CreateCell(5).SetCellValue(avgSample5); - - // 显示总平均值 - ICell avgCell = avgRow.CreateCell(6); - avgCell.SetCellValue(overallAvg.ToString("F2")); - - // 设置平均值行样式 - ICellStyle avgStyle = workbook.CreateCellStyle(); - avgStyle.FillForegroundColor = NPOI.HSSF.Util.HSSFColor.Yellow.Index; - avgStyle.FillPattern = FillPattern.SolidForeground; - - for (int i = 0; i < 7; i++) - { - if (avgRow.GetCell(i) != null) - { - avgRow.GetCell(i).CellStyle = avgStyle; - } - } + AddAverageRow(sheet, styles.avgStyle); // 自动调整列宽 - for (int i = 0; i < 7; i++) - { - sheet.AutoSizeColumn(i); - } + AutoSizeColumns(sheet, 7); // 保存文件 - using (FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write)) - { - workbook.Write(fs); - } + SaveWorkbook(workbook, filePath); - MessageBox.Show($"数据已成功导出到:{filePath}", "导出成功", MessageBoxButtons.OK, MessageBoxIcon.Information); + ShowMessage($"数据已成功导出到:{filePath}", "导出成功"); } catch (Exception ex) { - MessageBox.Show($"导出失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); + ShowMessage($"导出失败:{ex.Message}", "错误", MessageBoxIcon.Error); + } + } + + /// + /// 创建工作簿 + /// + private IWorkbook CreateWorkbook(string filePath) + { + return filePath.EndsWith(".xlsx") + ? (IWorkbook)new XSSFWorkbook() + : new HSSFWorkbook(); + } + + /// + /// 创建Excel样式 + /// + private (ICellStyle headerStyle, ICellStyle dataStyle, ICellStyle avgStyle) CreateExcelStyles(IWorkbook workbook) + { + // 标题样式 + ICellStyle headerStyle = workbook.CreateCellStyle(); + headerStyle.FillForegroundColor = NPOI.HSSF.Util.HSSFColor.Yellow.Index; + headerStyle.FillPattern = FillPattern.SolidForeground; + IFont headerFont = workbook.CreateFont(); + headerFont.IsBold = true; + headerStyle.SetFont(headerFont); + + // 数据样式 + ICellStyle dataStyle = workbook.CreateCellStyle(); + + // 平均值样式 + ICellStyle avgStyle = workbook.CreateCellStyle(); + avgStyle.FillForegroundColor = NPOI.HSSF.Util.HSSFColor.Yellow.Index; + avgStyle.FillPattern = FillPattern.SolidForeground; + + return (headerStyle, dataStyle, avgStyle); + } + + /// + /// 创建标题行 + /// + private void CreateHeaderRow(ISheet sheet, ICellStyle headerStyle) + { + IRow headerRow = sheet.CreateRow(0); + + // 序号列 + ICell cell0 = headerRow.CreateCell(0); + cell0.SetCellValue("序号"); + cell0.CellStyle = headerStyle; + + // 动态创建试样列 + for (int i = 1; i <= currentSampleCount; i++) + { + ICell cell = headerRow.CreateCell(i); + cell.SetCellValue($"试样{i}"); + cell.CellStyle = headerStyle; + } + } + + /// + /// 填充数据行 + /// + private void FillDataRows(ISheet sheet, ICellStyle dataStyle) + { + int rowIndex = 1; + foreach (DataRow dataRow in sampleDataTable.Rows) + { + if (dataRow["序号"].ToString() == AVG_ROW_LABEL) + continue; + + IRow row = sheet.CreateRow(rowIndex++); + row.CreateCell(0).SetCellValue(dataRow["序号"].ToString()); + + for (int i = 1; i <= currentSampleCount; i++) + { + row.CreateCell(i).SetCellValue(Convert.ToDouble(dataRow[$"试样{i}"])); + } + } + } + + /// + /// 添加平均值行 + /// + private void AddAverageRow(ISheet sheet, ICellStyle avgStyle) + { + var dataRows = sampleDataTable.AsEnumerable() + .Where(r => r.Field("序号") != AVG_ROW_LABEL); + + if (!dataRows.Any()) + return; + + int rowIndex = sheet.LastRowNum + 1; + IRow avgRow = sheet.CreateRow(rowIndex); + avgRow.CreateCell(0).SetCellValue(AVG_ROW_LABEL); + + double[] averages = new double[currentSampleCount]; + for (int i = 1; i <= currentSampleCount; i++) + { + averages[i - 1] = dataRows.Average(r => r.Field($"试样{i}")); + avgRow.CreateCell(i).SetCellValue(averages[i - 1]); + } + + // 不再添加总平均值到时间列 + + // 应用样式 + for (int i = 0; i <= currentSampleCount; i++) + { + var cell = avgRow.GetCell(i); + if (cell != null) + { + cell.CellStyle = avgStyle; + } + } + } + + /// + /// 自动调整列宽 + /// + private void AutoSizeColumns(ISheet sheet, int columnCount) + { + for (int i = 0; i <= currentSampleCount; i++) + { + sheet.AutoSizeColumn(i); + } + } + + /// + /// 保存工作簿 + /// + private void SaveWorkbook(IWorkbook workbook, string filePath) + { + using (FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write)) + { + workbook.Write(fs); } } @@ -575,109 +635,158 @@ namespace WindowsFormsApp6 ISheet sheet = workbook.CreateSheet("液体吸收测试报表"); // 创建样式 - ICellStyle titleStyle = workbook.CreateCellStyle(); - IFont titleFont = workbook.CreateFont(); - titleFont.FontHeightInPoints = 16; - titleFont.IsBold = true; - titleStyle.SetFont(titleFont); - titleStyle.Alignment = NPOIHorizontalAlignment.Center; + var styles = CreateReportStyles(workbook); - ICellStyle headerStyle = workbook.CreateCellStyle(); - headerStyle.FillForegroundColor = NPOI.HSSF.Util.HSSFColor.Yellow.Index; - headerStyle.FillPattern = FillPattern.SolidForeground; - headerStyle.BorderBottom = NPOIBorderStyle.Thin; - headerStyle.BorderTop = NPOIBorderStyle.Thin; - headerStyle.BorderLeft = NPOIBorderStyle.Thin; - headerStyle.BorderRight = NPOIBorderStyle.Thin; - IFont headerFont = workbook.CreateFont(); - headerFont.IsBold = true; - headerStyle.SetFont(headerFont); - headerStyle.Alignment = NPOIHorizontalAlignment.Center; + // 创建标题 + CreateReportTitle(sheet, styles.titleStyle); - ICellStyle dataStyle = workbook.CreateCellStyle(); - dataStyle.BorderBottom = NPOIBorderStyle.Thin; - dataStyle.BorderTop = NPOIBorderStyle.Thin; - dataStyle.BorderLeft = NPOIBorderStyle.Thin; - dataStyle.BorderRight = NPOIBorderStyle.Thin; - dataStyle.Alignment = NPOIHorizontalAlignment.Center; + // 创建表头 + CreateReportHeader(sheet, styles.headerStyle); - // 标题 - IRow titleRow = sheet.CreateRow(0); - ICell titleCell = titleRow.CreateCell(0); - titleCell.SetCellValue("液体吸收测试报表"); - titleCell.CellStyle = titleStyle; - sheet.AddMergedRegion(new CellRangeAddress(0, 0, 0, 6)); - - // 表头 - IRow headerRow = sheet.CreateRow(2); - string[] headers = { "序号", "试样1", "试样2", "试样3", "试样4", "试样5", "根据标准" }; - for (int i = 0; i < headers.Length; i++) - { - ICell cell = headerRow.CreateCell(i); - cell.SetCellValue(headers[i]); - cell.CellStyle = headerStyle; - } - - // 数据行 - int rowIndex = 3; - IRow timeRow = sheet.CreateRow(rowIndex++); - timeRow.CreateCell(0).SetCellValue("时间(s)"); - timeRow.GetCell(0).CellStyle = dataStyle; - - for (int i = 1; i <= 5; i++) - { - double value = 29 + i; // 示例数据:30, 31, 32, 33, 34 - ICell cell = timeRow.CreateCell(i); - cell.SetCellValue(value); - cell.CellStyle = dataStyle; - - // 黄色背景 - ICellStyle yellowStyle = workbook.CreateCellStyle(); - yellowStyle.CloneStyleFrom(dataStyle); - yellowStyle.FillForegroundColor = NPOI.HSSF.Util.HSSFColor.Yellow.Index; - yellowStyle.FillPattern = FillPattern.SolidForeground; - cell.CellStyle = yellowStyle; - } - - ICell standardCell = timeRow.CreateCell(6); - standardCell.CellStyle = dataStyle; - - // 平均时间行 - IRow avgRow = sheet.CreateRow(rowIndex); - avgRow.CreateCell(0).SetCellValue("平均时间(s)"); - avgRow.GetCell(0).CellStyle = dataStyle; - - ICell avgCell = avgRow.CreateCell(1); - avgCell.SetCellValue("32"); - avgCell.CellStyle = dataStyle; - - // 黄色背景 - ICellStyle avgYellowStyle = workbook.CreateCellStyle(); - avgYellowStyle.CloneStyleFrom(dataStyle); - avgYellowStyle.FillForegroundColor = NPOI.HSSF.Util.HSSFColor.Yellow.Index; - avgYellowStyle.FillPattern = FillPattern.SolidForeground; - avgCell.CellStyle = avgYellowStyle; - - sheet.AddMergedRegion(new CellRangeAddress(rowIndex, rowIndex, 1, 5)); + // 创建数据行 + CreateReportDataRows(sheet, styles.dataStyle, styles.yellowStyle); // 设置列宽 - sheet.SetColumnWidth(0, 20 * 256); - for (int i = 1; i <= 6; i++) - { - sheet.SetColumnWidth(i, 15 * 256); - } + SetReportColumnWidths(sheet); // 保存文件 - using (FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write)) - { - workbook.Write(fs); - } + SaveWorkbook(workbook, filePath); - MessageBox.Show($"报表已成功生成:{filePath}", "生成成功", MessageBoxButtons.OK, MessageBoxIcon.Information); + ShowMessage($"报表已成功生成:{filePath}", "生成成功"); } catch (Exception ex) { - MessageBox.Show($"生成报表失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); + ShowMessage($"生成报表失败:{ex.Message}", "错误", MessageBoxIcon.Error); + } + } + + /// + /// 创建报表样式 + /// + private (ICellStyle titleStyle, ICellStyle headerStyle, ICellStyle dataStyle, ICellStyle yellowStyle) + CreateReportStyles(IWorkbook workbook) + { + // 标题样式 + ICellStyle titleStyle = workbook.CreateCellStyle(); + IFont titleFont = workbook.CreateFont(); + titleFont.FontHeightInPoints = 16; + titleFont.IsBold = true; + titleStyle.SetFont(titleFont); + titleStyle.Alignment = NPOIHorizontalAlignment.Center; + + // 表头样式 + ICellStyle headerStyle = workbook.CreateCellStyle(); + headerStyle.FillForegroundColor = NPOI.HSSF.Util.HSSFColor.Yellow.Index; + headerStyle.FillPattern = FillPattern.SolidForeground; + SetBorders(headerStyle); + IFont headerFont = workbook.CreateFont(); + headerFont.IsBold = true; + headerStyle.SetFont(headerFont); + headerStyle.Alignment = NPOIHorizontalAlignment.Center; + + // 数据样式 + ICellStyle dataStyle = workbook.CreateCellStyle(); + SetBorders(dataStyle); + dataStyle.Alignment = NPOIHorizontalAlignment.Center; + + // 黄色背景样式 + ICellStyle yellowStyle = workbook.CreateCellStyle(); + yellowStyle.CloneStyleFrom(dataStyle); + yellowStyle.FillForegroundColor = NPOI.HSSF.Util.HSSFColor.Yellow.Index; + yellowStyle.FillPattern = FillPattern.SolidForeground; + + return (titleStyle, headerStyle, dataStyle, yellowStyle); + } + + /// + /// 设置单元格边框 + /// + private void SetBorders(ICellStyle style) + { + style.BorderBottom = NPOIBorderStyle.Thin; + style.BorderTop = NPOIBorderStyle.Thin; + style.BorderLeft = NPOIBorderStyle.Thin; + style.BorderRight = NPOIBorderStyle.Thin; + } + + /// + /// 创建报表标题 + /// + private void CreateReportTitle(ISheet sheet, ICellStyle titleStyle) + { + IRow titleRow = sheet.CreateRow(0); + ICell titleCell = titleRow.CreateCell(0); + titleCell.SetCellValue("液体吸收测试报表"); + titleCell.CellStyle = titleStyle; + sheet.AddMergedRegion(new CellRangeAddress(0, 0, 0, currentSampleCount)); + } + + /// + /// 创建报表表头 + /// + private void CreateReportHeader(ISheet sheet, ICellStyle headerStyle) + { + IRow headerRow = sheet.CreateRow(2); + + // 序号列 + ICell cell0 = headerRow.CreateCell(0); + cell0.SetCellValue("序号"); + cell0.CellStyle = headerStyle; + + // 动态创建试样列 + for (int i = 1; i <= currentSampleCount; i++) + { + ICell cell = headerRow.CreateCell(i); + cell.SetCellValue($"试样{i}"); + cell.CellStyle = headerStyle; + } + + // 不再添加"根据标准"列 + } + + /// + /// 创建报表数据行 + /// + private void CreateReportDataRows(ISheet sheet, ICellStyle dataStyle, ICellStyle yellowStyle) + { + int rowIndex = 3; + + // 时间行 + IRow timeRow = sheet.CreateRow(rowIndex++); + timeRow.CreateCell(0).SetCellValue(TIME_ROW_LABEL); + timeRow.GetCell(0).CellStyle = dataStyle; + + for (int i = 1; i <= currentSampleCount; i++) + { + double value = 29 + i; // 示例数据 + ICell cell = timeRow.CreateCell(i); + cell.SetCellValue(value); + cell.CellStyle = yellowStyle; + } + + // 不再添加"根据标准"列 + + // 平均时间行 + IRow avgRow = sheet.CreateRow(rowIndex); + avgRow.CreateCell(0).SetCellValue(AVG_ROW_LABEL); + avgRow.GetCell(0).CellStyle = dataStyle; + + ICell avgCell = avgRow.CreateCell(1); + avgCell.SetCellValue("32"); + avgCell.CellStyle = yellowStyle; + + sheet.AddMergedRegion(new CellRangeAddress(rowIndex, rowIndex, 1, currentSampleCount)); + } + + /// + /// 设置报表列宽 + /// + private void SetReportColumnWidths(ISheet sheet) + { + sheet.SetColumnWidth(0, 20 * 256); + for (int i = 1; i <= currentSampleCount; i++) + { + sheet.SetColumnWidth(i, 15 * 256); } } diff --git a/WindowsFormsApp6/Form2.Designer.cs b/WindowsFormsApp6/Form2.Designer.cs index 7848680..c210f91 100644 --- a/WindowsFormsApp6/Form2.Designer.cs +++ b/WindowsFormsApp6/Form2.Designer.cs @@ -13,9 +13,17 @@ /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { - if (disposing && (components != null)) + if (disposing) { - components.Dispose(); + if (components != null) + { + components.Dispose(); + } + + // 释放自定义资源 + dataTimer?.Dispose(); + clockTimer?.Dispose(); + sampleDataTable?.Dispose(); } base.Dispose(disposing); } diff --git a/WindowsFormsApp6/Form2.cs b/WindowsFormsApp6/Form2.cs index 6ef9f92..e8082e0 100644 --- a/WindowsFormsApp6/Form2.cs +++ b/WindowsFormsApp6/Form2.cs @@ -21,9 +21,26 @@ namespace WindowsFormsApp6 { public partial class Form2 : Form { + #region 常量定义 + private const int TIMER_INTERVAL = 1000; + private const double MIN_WEIGHT = 10.0; + private const double MAX_WEIGHT = 20.0; + private const string DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss"; + private const string ROW_INITIAL_WEIGHT = "初始重量(g)"; + private const string ROW_AFTER_WEIGHT = "浸润后重量(g)"; + private const string ROW_ABSORPTION = "液体吸收量(%)"; + private const string ROW_AVG_ABSORPTION = "液体吸收量平均值(%)"; + private const string ROW_MAX_ABSORPTION = "液体吸收量最大值(%)"; + #endregion + + #region 字段 private System.Windows.Forms.Timer dataTimer; + private System.Windows.Forms.Timer clockTimer; private DataTable sampleDataTable; private int sampleCount = 0; + private int currentSampleCount = 5; // 当前试样数量,可动态调整 + private readonly Random random = new Random(); + #endregion public Form2() { @@ -39,136 +56,71 @@ namespace WindowsFormsApp6 /// private void InitializeDataGridView() { - // 清除现有列 - dataGridView1.Columns.Clear(); - - // 添加新列以匹配 Excel 表格结构 - dataGridView1.Columns.Add(new DataGridViewTextBoxColumn + dataGridView1.SuspendLayout(); + try { - Name = "序号", - HeaderText = "序号", - DataPropertyName = "序号", - Width = 150, - ReadOnly = true, - DefaultCellStyle = new DataGridViewCellStyle { Alignment = DataGridViewContentAlignment.MiddleCenter } - }); + dataGridView1.Columns.Clear(); - dataGridView1.Columns.Add(new DataGridViewTextBoxColumn - { - Name = "试样1", - HeaderText = "试样1", - DataPropertyName = "试样1", - Width = 150, - ReadOnly = true, - DefaultCellStyle = new DataGridViewCellStyle + // 创建共享样式 + var centerStyle = new DataGridViewCellStyle { - Alignment = DataGridViewContentAlignment.MiddleCenter, - BackColor = Color.FromArgb(255, 255, 200) // 黄色背景 - } - }); + Alignment = DataGridViewContentAlignment.MiddleCenter + }; - dataGridView1.Columns.Add(new DataGridViewTextBoxColumn - { - Name = "试样2", - HeaderText = "试样2", - DataPropertyName = "试样2", - Width = 150, - ReadOnly = true, - DefaultCellStyle = new DataGridViewCellStyle + var yellowCenterStyle = new DataGridViewCellStyle { Alignment = DataGridViewContentAlignment.MiddleCenter, BackColor = Color.FromArgb(255, 255, 200) - } - }); + }; - dataGridView1.Columns.Add(new DataGridViewTextBoxColumn - { - Name = "试样3", - HeaderText = "试样3", - DataPropertyName = "试样3", - Width = 150, - ReadOnly = true, - DefaultCellStyle = new DataGridViewCellStyle + // 添加序号列 + dataGridView1.Columns.Add(new DataGridViewTextBoxColumn { - Alignment = DataGridViewContentAlignment.MiddleCenter, - BackColor = Color.FromArgb(255, 255, 200) - } - }); + Name = "序号", + HeaderText = "序号", + DataPropertyName = "序号", + Width = 180, + ReadOnly = true, + DefaultCellStyle = centerStyle + }); - dataGridView1.Columns.Add(new DataGridViewTextBoxColumn - { - Name = "试样4", - HeaderText = "试样4", - DataPropertyName = "试样4", - Width = 150, - ReadOnly = true, - DefaultCellStyle = new DataGridViewCellStyle + // 根据当前试样数量动态添加列 + for (int i = 1; i <= currentSampleCount; i++) { - Alignment = DataGridViewContentAlignment.MiddleCenter, - BackColor = Color.FromArgb(255, 255, 200) + dataGridView1.Columns.Add(new DataGridViewTextBoxColumn + { + Name = $"试样{i}", + HeaderText = $"试样{i}", + DataPropertyName = $"试样{i}", + Width = 150, + ReadOnly = true, + DefaultCellStyle = yellowCenterStyle + }); } - }); - dataGridView1.Columns.Add(new DataGridViewTextBoxColumn + // 绑定数据源 + dataGridView1.DataSource = sampleDataTable; + dataGridView1.CellFormatting += DataGridView1_CellFormatting; + } + finally { - Name = "试样5", - HeaderText = "试样5", - DataPropertyName = "试样5", - Width = 150, - ReadOnly = true, - DefaultCellStyle = new DataGridViewCellStyle - { - Alignment = DataGridViewContentAlignment.MiddleCenter, - BackColor = Color.FromArgb(255, 255, 200) - } - }); - - dataGridView1.Columns.Add(new DataGridViewTextBoxColumn - { - Name = "时间", - HeaderText = "时间", - DataPropertyName = "时间", - Width = 200, - ReadOnly = true, - DefaultCellStyle = new DataGridViewCellStyle { Alignment = DataGridViewContentAlignment.MiddleCenter } - }); - - // 绑定数据源 - dataGridView1.DataSource = sampleDataTable; - - // 添加单元格格式化事件 - dataGridView1.CellFormatting += DataGridView1_CellFormatting; + dataGridView1.ResumeLayout(); + } } /// - /// 单元格格式化事件 - 处理平均值行的显示 + /// 单元格格式化事件 /// private void DataGridView1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e) { - if (dataGridView1.Columns[e.ColumnIndex].Name == "时间" && e.RowIndex >= 0) - { - DataGridViewRow row = dataGridView1.Rows[e.RowIndex]; - if (row.Cells["序号"].Value != null && row.Cells["序号"].Value.ToString() == "平均时间(s)") - { - // 计算总平均值并显示 - if (row.Cells["试样1"].Value != null) - { - double avg1 = Convert.ToDouble(row.Cells["试样1"].Value); - double avg2 = Convert.ToDouble(row.Cells["试样2"].Value); - double avg3 = Convert.ToDouble(row.Cells["试样3"].Value); - double avg4 = Convert.ToDouble(row.Cells["试样4"].Value); - double avg5 = Convert.ToDouble(row.Cells["试样5"].Value); - double overallAvg = (avg1 + avg2 + avg3 + avg4 + avg5) / 5; + if (e.RowIndex < 0) return; - e.Value = overallAvg.ToString("F2"); - e.FormattingApplied = true; - } - } - else if (e.Value != null && e.Value != DBNull.Value) + // 格式化数值显示为2位小数 + if (e.Value != null && e.Value != DBNull.Value) + { + if (double.TryParse(e.Value.ToString(), out double numValue)) { - // 正常时间格式化 - DateTime dt = Convert.ToDateTime(e.Value); - e.Value = dt.ToString("yyyy-MM-dd HH:mm:ss"); + e.Value = numValue.ToString("F2"); e.FormattingApplied = true; } } @@ -179,25 +131,17 @@ namespace WindowsFormsApp6 /// private void InitializeEventHandlers() { - // 连接设备按钮 button1.Click += Button1_Click; - - // 打印按钮 button2.Click += Button2_Click; - - // 导出按钮 button3.Click += Button3_Click; - - // 返回按钮 button4.Click += Button4_Click; - - // 生成模拟数据按钮 button5.Click += Button5_Click; - // 更新日期时间标签 - System.Windows.Forms.Timer clockTimer = new System.Windows.Forms.Timer(); - clockTimer.Interval = 1000; - clockTimer.Tick += (s, e) => label2.Text = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); + clockTimer = new System.Windows.Forms.Timer + { + Interval = TIMER_INTERVAL + }; + clockTimer.Tick += (s, e) => label2.Text = DateTime.Now.ToString(DATE_TIME_FORMAT); clockTimer.Start(); } @@ -209,16 +153,14 @@ namespace WindowsFormsApp6 if (dataTimer.Enabled) { StopDataCollection(); - button1.Text = "🔗 连接设备"; - button1.BackColor = Color.FromArgb(46, 204, 113); - MessageBox.Show("已停止数据采集", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); + UpdateButtonState(button1, "🔗 连接设备", Color.FromArgb(46, 204, 113)); + ShowMessage("已停止数据采集"); } else { StartDataCollection(); - button1.Text = "⏸️ 停止采集"; - button1.BackColor = Color.FromArgb(231, 76, 60); - MessageBox.Show("开始数据采集", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); + UpdateButtonState(button1, "⏸️ 停止采集", Color.FromArgb(231, 76, 60)); + ShowMessage("开始数据采集"); } } @@ -227,7 +169,24 @@ namespace WindowsFormsApp6 /// private void Button2_Click(object sender, EventArgs e) { - MessageBox.Show("打印功能开发中...", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); + ShowMessage("打印功能开发中..."); + } + + /// + /// 更新按钮状态 + /// + private void UpdateButtonState(Button button, string text, Color backColor) + { + button.Text = text; + button.BackColor = backColor; + } + + /// + /// 显示提示消息 + /// + private void ShowMessage(string message, string title = "提示", MessageBoxIcon icon = MessageBoxIcon.Information) + { + MessageBox.Show(message, title, MessageBoxButtons.OK, icon); } /// @@ -271,12 +230,28 @@ namespace WindowsFormsApp6 { sampleDataTable = new DataTable(); sampleDataTable.Columns.Add("序号", typeof(string)); - sampleDataTable.Columns.Add("试样1", typeof(double)); - sampleDataTable.Columns.Add("试样2", typeof(double)); - sampleDataTable.Columns.Add("试样3", typeof(double)); - sampleDataTable.Columns.Add("试样4", typeof(double)); - sampleDataTable.Columns.Add("试样5", typeof(double)); - sampleDataTable.Columns.Add("时间", typeof(DateTime)); + + // 根据当前试样数量动态添加列 + for (int i = 1; i <= currentSampleCount; i++) + { + sampleDataTable.Columns.Add($"试样{i}", typeof(object)); + } + } + + /// + /// 设置试样数量并重新初始化 + /// + public void SetSampleCount(int count) + { + if (count < 1 || count > 20) // 限制最大20个试样 + { + ShowMessage("试样数量必须在1-20之间", "参数错误", MessageBoxIcon.Warning); + return; + } + + currentSampleCount = count; + InitializeDataTable(); + InitializeDataGridView(); } /// @@ -284,8 +259,10 @@ namespace WindowsFormsApp6 /// private void InitializeTimer() { - dataTimer = new System.Windows.Forms.Timer(); - dataTimer.Interval = 1000; // 每秒读取一次 + dataTimer = new System.Windows.Forms.Timer + { + Interval = TIMER_INTERVAL + }; dataTimer.Tick += DataTimer_Tick; } @@ -294,45 +271,48 @@ namespace WindowsFormsApp6 /// private void DataTimer_Tick(object sender, EventArgs e) { - // 移除旧的平均值行(如果存在) - DataRow[] avgRows = sampleDataTable.Select("序号 = '平均时间(s)'"); - foreach (DataRow avgRow in avgRows) + // 清除旧的计算行 + RemoveCalculatedRows(); + + // 读取初始重量和浸润后重量 + double[] initialWeights = new double[currentSampleCount]; + double[] afterWeights = new double[currentSampleCount]; + + for (int i = 0; i < currentSampleCount; i++) { - sampleDataTable.Rows.Remove(avgRow); + initialWeights[i] = ReadRegisterData(i * 2); // 初始重量 + afterWeights[i] = ReadRegisterData(i * 2 + 1); // 浸润后重量 } - // 模拟从寄存器读取数据(实际应用中替换为真实的寄存器读取代码) - double sample1 = ReadRegisterData(0); // 寄存器地址 0 - double sample2 = ReadRegisterData(1); // 寄存器地址 1 - double sample3 = ReadRegisterData(2); // 寄存器地址 2 - double sample4 = ReadRegisterData(3); // 寄存器地址 3 - double sample5 = ReadRegisterData(4); // 寄存器地址 4 - - // 添加到数据表 sampleCount++; - DataRow dataRow = sampleDataTable.NewRow(); - dataRow["序号"] = $"时间(s)"; - dataRow["试样1"] = sample1; - dataRow["试样2"] = sample2; - dataRow["试样3"] = sample3; - dataRow["试样4"] = sample4; - dataRow["试样5"] = sample5; - dataRow["时间"] = DateTime.Now; - sampleDataTable.Rows.Add(dataRow); + + // 更新显示 + UpdateDisplay(initialWeights, afterWeights); + } - // 更新界面显示 - UpdateDisplay(); + /// + /// 移除计算行 + /// + private void RemoveCalculatedRows() + { + var rowsToRemove = sampleDataTable.AsEnumerable() + .Where(r => r.Field("序号") == ROW_ABSORPTION || + r.Field("序号") == ROW_AVG_ABSORPTION || + r.Field("序号") == ROW_MAX_ABSORPTION) + .ToList(); + + foreach (var row in rowsToRemove) + { + sampleDataTable.Rows.Remove(row); + } } /// /// 模拟从寄存器读取数据 - /// 实际应用中替换为真实的 Modbus 或其他协议读取 /// private double ReadRegisterData(int registerAddress) { - // 模拟数据:30-34 之间的随机值 - Random random = new Random(Guid.NewGuid().GetHashCode()); - return 30 + random.NextDouble() * 4; + return MIN_WEIGHT + random.NextDouble() * (MAX_WEIGHT - MIN_WEIGHT); } /// @@ -340,86 +320,125 @@ namespace WindowsFormsApp6 /// public void GenerateMockData() { - // 清空现有数据 sampleDataTable.Clear(); sampleCount = 0; - // 生成10条模拟数据 - Random random = new Random(); - DateTime startTime = DateTime.Now.AddMinutes(-10); + // 生成模拟数据 + double[] initialWeights = new double[currentSampleCount]; + double[] afterWeights = new double[currentSampleCount]; - for (int i = 0; i < 10; i++) + for (int i = 0; i < currentSampleCount; i++) { - DataRow dataRow = sampleDataTable.NewRow(); - dataRow["序号"] = "时间(s)"; - - // 为每个试样生成模拟数据(30-34之间的随机值,带小数点) - dataRow["试样1"] = Math.Round(30 + random.NextDouble() * 4, 2); - dataRow["试样2"] = Math.Round(30 + random.NextDouble() * 4, 2); - dataRow["试样3"] = Math.Round(30 + random.NextDouble() * 4, 2); - dataRow["试样4"] = Math.Round(30 + random.NextDouble() * 4, 2); - dataRow["试样5"] = Math.Round(30 + random.NextDouble() * 4, 2); - - // 时间递增 - dataRow["时间"] = startTime.AddMinutes(i); - - sampleDataTable.Rows.Add(dataRow); - sampleCount++; + initialWeights[i] = Math.Round(MIN_WEIGHT + random.NextDouble() * (MAX_WEIGHT - MIN_WEIGHT), 2); + afterWeights[i] = Math.Round(initialWeights[i] * (1.2 + random.NextDouble() * 0.3), 2); // 浸润后增重20%-50% } - // 更新显示(包括平均值) - UpdateDisplay(); + sampleCount = 1; + UpdateDisplay(initialWeights, afterWeights); - MessageBox.Show($"已生成 {sampleCount} 条模拟数据", "模拟数据生成", MessageBoxButtons.OK, MessageBoxIcon.Information); + ShowMessage($"已生成 {currentSampleCount} 个试样的模拟数据", "模拟数据生成"); } /// /// 更新界面显示 /// - private void UpdateDisplay() + private void UpdateDisplay(double[] initialWeights, double[] afterWeights) { - // 刷新 DataGridView - if (dataGridView1.DataSource != null) + sampleDataTable.Clear(); + + int count = Math.Min(initialWeights.Length, currentSampleCount); + + // 1. 初始重量行 + DataRow initialRow = sampleDataTable.NewRow(); + initialRow["序号"] = ROW_INITIAL_WEIGHT; + for (int i = 0; i < count; i++) { - ((DataTable)dataGridView1.DataSource).AcceptChanges(); - dataGridView1.Refresh(); + initialRow[$"试样{i + 1}"] = initialWeights[i]; } + sampleDataTable.Rows.Add(initialRow); - // 计算平均时间并显示 - if (sampleDataTable.Rows.Count > 0) + // 2. 浸润后重量行 + DataRow afterRow = sampleDataTable.NewRow(); + afterRow["序号"] = ROW_AFTER_WEIGHT; + for (int i = 0; i < count; i++) { - double avgSample1 = sampleDataTable.AsEnumerable().Average(r => r.Field("试样1")); - double avgSample2 = sampleDataTable.AsEnumerable().Average(r => r.Field("试样2")); - double avgSample3 = sampleDataTable.AsEnumerable().Average(r => r.Field("试样3")); - double avgSample4 = sampleDataTable.AsEnumerable().Average(r => r.Field("试样4")); - double avgSample5 = sampleDataTable.AsEnumerable().Average(r => r.Field("试样5")); + afterRow[$"试样{i + 1}"] = afterWeights[i]; + } + sampleDataTable.Rows.Add(afterRow); - double overallAvg = (avgSample1 + avgSample2 + avgSample3 + avgSample4 + avgSample5) / 5; + // 3. 液体吸收量行 (%) + DataRow absorptionRow = sampleDataTable.NewRow(); + absorptionRow["序号"] = ROW_ABSORPTION; + double[] absorptions = new double[count]; + for (int i = 0; i < count; i++) + { + absorptions[i] = ((afterWeights[i] - initialWeights[i]) / initialWeights[i]) * 100; + absorptionRow[$"试样{i + 1}"] = absorptions[i]; + } + sampleDataTable.Rows.Add(absorptionRow); - // 添加平均值行到表格底部(如果还没有) - DataRow[] avgRows = sampleDataTable.Select("序号 = '平均时间(s)'"); - if (avgRows.Length == 0) - { - DataRow avgRow = sampleDataTable.NewRow(); - avgRow["序号"] = "平均时间(s)"; - avgRow["试样1"] = avgSample1; - avgRow["试样2"] = avgSample2; - avgRow["试样3"] = avgSample3; - avgRow["试样4"] = avgSample4; - avgRow["试样5"] = avgSample5; - // avgRow["时间"] = $"({overallAvg:F2}) 系统计算"; - sampleDataTable.Rows.Add(avgRow); - } - else - { - // 更新现有平均值行 - avgRows[0]["试样1"] = avgSample1; - avgRows[0]["试样2"] = avgSample2; - avgRows[0]["试样3"] = avgSample3; - avgRows[0]["试样4"] = avgSample4; - avgRows[0]["试样5"] = avgSample5; - // avgRows[0]["时间"] = $"({overallAvg:F2}) 系统计算"; - } + // 4. 液体吸收量平均值行 + DataRow avgRow = sampleDataTable.NewRow(); + avgRow["序号"] = ROW_AVG_ABSORPTION; + double avgAbsorption = absorptions.Average(); + double stdDev = CalculateStandardDeviation(absorptions); + + // 第1列显示平均值 + avgRow["试样1"] = avgAbsorption; + + // 第2列显示标准偏差 + if (count >= 2) + { + avgRow["试样2"] = stdDev; + } + + // 其他列为空 + for (int i = 3; i <= count; i++) + { + avgRow[$"试样{i}"] = DBNull.Value; + } + sampleDataTable.Rows.Add(avgRow); + + // 5. 液体吸收量最大值行 + DataRow maxRow = sampleDataTable.NewRow(); + maxRow["序号"] = ROW_MAX_ABSORPTION; + double maxAbsorption = absorptions.Max(); + + // 第1列显示最大值 + maxRow["试样1"] = maxAbsorption; + + // 其他列为空 + for (int i = 2; i <= count; i++) + { + maxRow[$"试样{i}"] = DBNull.Value; + } + sampleDataTable.Rows.Add(maxRow); + + // 刷新界面 + RefreshDataGridView(); + } + + /// + /// 计算标准偏差 + /// + private double CalculateStandardDeviation(double[] values) + { + if (values.Length == 0) return 0; + + double avg = values.Average(); + double sumOfSquares = values.Sum(val => Math.Pow(val - avg, 2)); + return Math.Sqrt(sumOfSquares / values.Length); + } + + /// + /// 刷新DataGridView + /// + private void RefreshDataGridView() + { + if (dataGridView1.DataSource is DataTable dt) + { + dt.AcceptChanges(); + dataGridView1.Refresh(); } } @@ -439,131 +458,6 @@ namespace WindowsFormsApp6 dataTimer.Stop(); } - /// - /// 导出数据到 Excel (.xlsx) - /// - public void ExportToExcel(string filePath) - { - try - { - IWorkbook workbook; - - // 根据文件扩展名选择格式 - if (filePath.EndsWith(".xlsx")) - { - workbook = new XSSFWorkbook(); // Excel 2007+ - } - else - { - workbook = new HSSFWorkbook(); // Excel 97-2003 - } - - ISheet sheet = workbook.CreateSheet("液体吸收测试报表"); - - // 创建标题行 - IRow headerRow = sheet.CreateRow(0); - headerRow.CreateCell(0).SetCellValue("序号"); - headerRow.CreateCell(1).SetCellValue("试样1"); - headerRow.CreateCell(2).SetCellValue("试样2"); - headerRow.CreateCell(3).SetCellValue("试样3"); - headerRow.CreateCell(4).SetCellValue("试样4"); - headerRow.CreateCell(5).SetCellValue("试样5"); - headerRow.CreateCell(6).SetCellValue("时间"); - - // 设置标题行样式 - ICellStyle headerStyle = workbook.CreateCellStyle(); - headerStyle.FillForegroundColor = NPOI.HSSF.Util.HSSFColor.Yellow.Index; - headerStyle.FillPattern = FillPattern.SolidForeground; - IFont headerFont = workbook.CreateFont(); - headerFont.IsBold = true; - headerStyle.SetFont(headerFont); - - for (int i = 0; i < 7; i++) - { - headerRow.GetCell(i).CellStyle = headerStyle; - } - - // 填充数据 - int rowIndex = 1; - foreach (DataRow dataRow in sampleDataTable.Rows) - { - // 跳过平均值行 - if (dataRow["序号"].ToString() == "平均时间(s)") - continue; - - IRow row = sheet.CreateRow(rowIndex); - row.CreateCell(0).SetCellValue(dataRow["序号"].ToString()); - row.CreateCell(1).SetCellValue(Convert.ToDouble(dataRow["试样1"])); - row.CreateCell(2).SetCellValue(Convert.ToDouble(dataRow["试样2"])); - row.CreateCell(3).SetCellValue(Convert.ToDouble(dataRow["试样3"])); - row.CreateCell(4).SetCellValue(Convert.ToDouble(dataRow["试样4"])); - row.CreateCell(5).SetCellValue(Convert.ToDouble(dataRow["试样5"])); - - // 处理时间列 - if (dataRow["时间"] != DBNull.Value) - { - row.CreateCell(6).SetCellValue(Convert.ToDateTime(dataRow["时间"]).ToString("yyyy-MM-dd HH:mm:ss")); - } - - rowIndex++; - } - - // 添加平均值行 - IRow avgRow = sheet.CreateRow(rowIndex); - avgRow.CreateCell(0).SetCellValue("平均时间(s)"); - - // 计算平均值(排除平均值行) - var dataRows = sampleDataTable.AsEnumerable().Where(r => r.Field("序号") != "平均时间(s)"); - double avgSample1 = dataRows.Average(r => r.Field("试样1")); - double avgSample2 = dataRows.Average(r => r.Field("试样2")); - double avgSample3 = dataRows.Average(r => r.Field("试样3")); - double avgSample4 = dataRows.Average(r => r.Field("试样4")); - double avgSample5 = dataRows.Average(r => r.Field("试样5")); - double overallAvg = (avgSample1 + avgSample2 + avgSample3 + avgSample4 + avgSample5) / 5; - - avgRow.CreateCell(1).SetCellValue(avgSample1); - avgRow.CreateCell(2).SetCellValue(avgSample2); - avgRow.CreateCell(3).SetCellValue(avgSample3); - avgRow.CreateCell(4).SetCellValue(avgSample4); - avgRow.CreateCell(5).SetCellValue(avgSample5); - - // 显示总平均值 - ICell avgCell = avgRow.CreateCell(6); - avgCell.SetCellValue(overallAvg.ToString("F2")); - - // 设置平均值行样式 - ICellStyle avgStyle = workbook.CreateCellStyle(); - avgStyle.FillForegroundColor = NPOI.HSSF.Util.HSSFColor.Yellow.Index; - avgStyle.FillPattern = FillPattern.SolidForeground; - - for (int i = 0; i < 7; i++) - { - if (avgRow.GetCell(i) != null) - { - avgRow.GetCell(i).CellStyle = avgStyle; - } - } - - // 自动调整列宽 - for (int i = 0; i < 7; i++) - { - sheet.AutoSizeColumn(i); - } - - // 保存文件 - using (FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write)) - { - workbook.Write(fs); - } - - MessageBox.Show($"数据已成功导出到:{filePath}", "导出成功", MessageBoxButtons.OK, MessageBoxIcon.Information); - } - catch (Exception ex) - { - MessageBox.Show($"导出失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); - } - } - /// /// 生成报表(带格式的 Excel) /// @@ -575,109 +469,173 @@ namespace WindowsFormsApp6 ISheet sheet = workbook.CreateSheet("液体吸收测试报表"); // 创建样式 - ICellStyle titleStyle = workbook.CreateCellStyle(); - IFont titleFont = workbook.CreateFont(); - titleFont.FontHeightInPoints = 16; - titleFont.IsBold = true; - titleStyle.SetFont(titleFont); - titleStyle.Alignment = NPOIHorizontalAlignment.Center; + var styles = CreateReportStyles(workbook); - ICellStyle headerStyle = workbook.CreateCellStyle(); - headerStyle.FillForegroundColor = NPOI.HSSF.Util.HSSFColor.Yellow.Index; - headerStyle.FillPattern = FillPattern.SolidForeground; - headerStyle.BorderBottom = NPOIBorderStyle.Thin; - headerStyle.BorderTop = NPOIBorderStyle.Thin; - headerStyle.BorderLeft = NPOIBorderStyle.Thin; - headerStyle.BorderRight = NPOIBorderStyle.Thin; - IFont headerFont = workbook.CreateFont(); - headerFont.IsBold = true; - headerStyle.SetFont(headerFont); - headerStyle.Alignment = NPOIHorizontalAlignment.Center; + // 创建标题 + CreateReportTitle(sheet, styles.titleStyle); - ICellStyle dataStyle = workbook.CreateCellStyle(); - dataStyle.BorderBottom = NPOIBorderStyle.Thin; - dataStyle.BorderTop = NPOIBorderStyle.Thin; - dataStyle.BorderLeft = NPOIBorderStyle.Thin; - dataStyle.BorderRight = NPOIBorderStyle.Thin; - dataStyle.Alignment = NPOIHorizontalAlignment.Center; + // 创建表头 + CreateReportHeader(sheet, styles.headerStyle); - // 标题 - IRow titleRow = sheet.CreateRow(0); - ICell titleCell = titleRow.CreateCell(0); - titleCell.SetCellValue("液体吸收测试报表"); - titleCell.CellStyle = titleStyle; - sheet.AddMergedRegion(new CellRangeAddress(0, 0, 0, 6)); - - // 表头 - IRow headerRow = sheet.CreateRow(2); - string[] headers = { "序号", "试样1", "试样2", "试样3", "试样4", "试样5", "根据标准" }; - for (int i = 0; i < headers.Length; i++) - { - ICell cell = headerRow.CreateCell(i); - cell.SetCellValue(headers[i]); - cell.CellStyle = headerStyle; - } - - // 数据行 - int rowIndex = 3; - IRow timeRow = sheet.CreateRow(rowIndex++); - timeRow.CreateCell(0).SetCellValue("时间(s)"); - timeRow.GetCell(0).CellStyle = dataStyle; - - for (int i = 1; i <= 5; i++) - { - double value = 29 + i; // 示例数据:30, 31, 32, 33, 34 - ICell cell = timeRow.CreateCell(i); - cell.SetCellValue(value); - cell.CellStyle = dataStyle; - - // 黄色背景 - ICellStyle yellowStyle = workbook.CreateCellStyle(); - yellowStyle.CloneStyleFrom(dataStyle); - yellowStyle.FillForegroundColor = NPOI.HSSF.Util.HSSFColor.Yellow.Index; - yellowStyle.FillPattern = FillPattern.SolidForeground; - cell.CellStyle = yellowStyle; - } - - ICell standardCell = timeRow.CreateCell(6); - standardCell.CellStyle = dataStyle; - - // 平均时间行 - IRow avgRow = sheet.CreateRow(rowIndex); - avgRow.CreateCell(0).SetCellValue("平均时间(s)"); - avgRow.GetCell(0).CellStyle = dataStyle; - - ICell avgCell = avgRow.CreateCell(1); - avgCell.SetCellValue("32"); - avgCell.CellStyle = dataStyle; - - // 黄色背景 - ICellStyle avgYellowStyle = workbook.CreateCellStyle(); - avgYellowStyle.CloneStyleFrom(dataStyle); - avgYellowStyle.FillForegroundColor = NPOI.HSSF.Util.HSSFColor.Yellow.Index; - avgYellowStyle.FillPattern = FillPattern.SolidForeground; - avgCell.CellStyle = avgYellowStyle; - - sheet.AddMergedRegion(new CellRangeAddress(rowIndex, rowIndex, 1, 5)); + // 填充数据 + FillReportData(sheet, styles.dataStyle, styles.yellowStyle); // 设置列宽 - sheet.SetColumnWidth(0, 20 * 256); - for (int i = 1; i <= 6; i++) - { - sheet.SetColumnWidth(i, 15 * 256); - } + SetReportColumnWidths(sheet); // 保存文件 - using (FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write)) - { - workbook.Write(fs); - } + SaveWorkbook(workbook, filePath); - MessageBox.Show($"报表已成功生成:{filePath}", "生成成功", MessageBoxButtons.OK, MessageBoxIcon.Information); + ShowMessage($"报表已成功生成:{filePath}", "生成成功"); } catch (Exception ex) { - MessageBox.Show($"生成报表失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); + ShowMessage($"生成报表失败:{ex.Message}", "错误", MessageBoxIcon.Error); + } + } + + /// + /// 创建报表样式 + /// + private (ICellStyle titleStyle, ICellStyle headerStyle, ICellStyle dataStyle, ICellStyle yellowStyle) + CreateReportStyles(IWorkbook workbook) + { + // 标题样式 + ICellStyle titleStyle = workbook.CreateCellStyle(); + IFont titleFont = workbook.CreateFont(); + titleFont.FontHeightInPoints = 16; + titleFont.IsBold = true; + titleStyle.SetFont(titleFont); + titleStyle.Alignment = NPOIHorizontalAlignment.Center; + + // 表头样式 + ICellStyle headerStyle = workbook.CreateCellStyle(); + headerStyle.FillForegroundColor = NPOI.HSSF.Util.HSSFColor.Yellow.Index; + headerStyle.FillPattern = FillPattern.SolidForeground; + SetBorders(headerStyle); + IFont headerFont = workbook.CreateFont(); + headerFont.IsBold = true; + headerStyle.SetFont(headerFont); + headerStyle.Alignment = NPOIHorizontalAlignment.Center; + + // 数据样式 + ICellStyle dataStyle = workbook.CreateCellStyle(); + SetBorders(dataStyle); + dataStyle.Alignment = NPOIHorizontalAlignment.Center; + + // 黄色背景样式 + ICellStyle yellowStyle = workbook.CreateCellStyle(); + yellowStyle.CloneStyleFrom(dataStyle); + yellowStyle.FillForegroundColor = NPOI.HSSF.Util.HSSFColor.Yellow.Index; + yellowStyle.FillPattern = FillPattern.SolidForeground; + + return (titleStyle, headerStyle, dataStyle, yellowStyle); + } + + /// + /// 设置单元格边框 + /// + private void SetBorders(ICellStyle style) + { + style.BorderBottom = NPOIBorderStyle.Thin; + style.BorderTop = NPOIBorderStyle.Thin; + style.BorderLeft = NPOIBorderStyle.Thin; + style.BorderRight = NPOIBorderStyle.Thin; + } + + /// + /// 创建报表标题 + /// + private void CreateReportTitle(ISheet sheet, ICellStyle titleStyle) + { + IRow titleRow = sheet.CreateRow(0); + ICell titleCell = titleRow.CreateCell(0); + titleCell.SetCellValue("液体吸收测试报表"); + titleCell.CellStyle = titleStyle; + sheet.AddMergedRegion(new CellRangeAddress(0, 0, 0, currentSampleCount)); + } + + /// + /// 创建报表表头 + /// + private void CreateReportHeader(ISheet sheet, ICellStyle headerStyle) + { + IRow headerRow = sheet.CreateRow(2); + + // 序号列 + ICell cell0 = headerRow.CreateCell(0); + cell0.SetCellValue("序号"); + cell0.CellStyle = headerStyle; + + // 动态创建试样列 + for (int i = 1; i <= currentSampleCount; i++) + { + ICell cell = headerRow.CreateCell(i); + cell.SetCellValue($"试样{i}"); + cell.CellStyle = headerStyle; + } + } + + /// + /// 填充报表数据 + /// + private void FillReportData(ISheet sheet, ICellStyle dataStyle, ICellStyle yellowStyle) + { + int rowIndex = 3; + + foreach (DataRow dataRow in sampleDataTable.Rows) + { + IRow row = sheet.CreateRow(rowIndex++); + string rowName = dataRow["序号"].ToString(); + + // 序号列 + ICell nameCell = row.CreateCell(0); + nameCell.SetCellValue(rowName); + nameCell.CellStyle = dataStyle; + + // 数据列 + for (int i = 1; i <= currentSampleCount; i++) + { + ICell cell = row.CreateCell(i); + var value = dataRow[$"试样{i}"]; + + if (value != null && value != DBNull.Value) + { + if (double.TryParse(value.ToString(), out double numValue)) + { + cell.SetCellValue(numValue); + } + else + { + cell.SetCellValue(value.ToString()); + } + } + + cell.CellStyle = yellowStyle; + } + } + } + + /// + /// 设置报表列宽 + /// + private void SetReportColumnWidths(ISheet sheet) + { + sheet.SetColumnWidth(0, 25 * 256); + for (int i = 1; i <= currentSampleCount; i++) + { + sheet.SetColumnWidth(i, 18 * 256); + } + } + + /// + /// 保存工作簿 + /// + private void SaveWorkbook(IWorkbook workbook, string filePath) + { + using (FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write)) + { + workbook.Write(fs); } } @@ -688,7 +646,7 @@ namespace WindowsFormsApp6 { sampleDataTable.Clear(); sampleCount = 0; - UpdateDisplay(); + RefreshDataGridView(); } } } diff --git a/WindowsFormsApp6/Form3.Designer.cs b/WindowsFormsApp6/Form3.Designer.cs index 27544c8..e8061e5 100644 --- a/WindowsFormsApp6/Form3.Designer.cs +++ b/WindowsFormsApp6/Form3.Designer.cs @@ -13,9 +13,34 @@ /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { - if (disposing && (components != null)) + if (disposing) { - components.Dispose(); + if (components != null) + { + components.Dispose(); + } + + // 释放定时器资源 + if (clockTimer != null) + { + clockTimer.Stop(); + clockTimer.Dispose(); + clockTimer = null; + } + + if (dataTimer != null) + { + dataTimer.Stop(); + dataTimer.Dispose(); + dataTimer = null; + } + + // 释放数据表资源 + if (sampleDataTable != null) + { + sampleDataTable.Dispose(); + sampleDataTable = null; + } } base.Dispose(disposing); } diff --git a/WindowsFormsApp6/Form3.cs b/WindowsFormsApp6/Form3.cs index b87904b..daf3b18 100644 --- a/WindowsFormsApp6/Form3.cs +++ b/WindowsFormsApp6/Form3.cs @@ -21,8 +21,23 @@ namespace WindowsFormsApp6 { public partial class Form3 : Form { + #region 常量定义 + private const int TIMER_INTERVAL = 1000; + private const string DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss"; + private const string ROW_WICKING_TIME = "吸水时间(s)"; + private const string ROW_WICKING_HEIGHT = "吸芯高度(mm)"; + private const string ROW_WICKING_RATE = "芯吸速率(mm/min)"; + private const string ROW_AVG_WICKING_RATE = "平均芯吸速率(mm/min)"; + private const string ROW_STD_DEVIATION = "标准偏差"; + #endregion + + #region 字段 private System.Windows.Forms.Timer dataTimer; + private System.Windows.Forms.Timer clockTimer; private DataTable sampleDataTable; + private int currentSampleCount = 5; // 当前试样数量,可动态调整 + private readonly Random random = new Random(); + #endregion public Form3() { @@ -34,112 +49,103 @@ namespace WindowsFormsApp6 } /// - /// 初始化 DataGridView 列 - 实现复杂多级表头 + /// 设置试样数量(1-20) + /// + public void SetSampleCount(int count) + { + if (count < 1 || count > 20) + { + ShowMessage("试样数量必须在1-20之间", "参数错误", MessageBoxIcon.Warning); + return; + } + + currentSampleCount = count; + + // 重新初始化数据表和界面 + sampleDataTable.Clear(); + InitializeDataTable(); + InitializeDataGridView(); + UpdateDisplay(); + } + + /// + /// 初始化 DataGridView 列 - 实现2级表头 /// private void InitializeDataGridView() { - // 禁用自动生成列 - dataGridView1.AutoGenerateColumns = false; - - // 先解除数据绑定 - dataGridView1.DataSource = null; - - // 清除现有列 - dataGridView1.Columns.Clear(); - - // 先移除可能存在的事件处理器,避免重复绑定 - dataGridView1.CellFormatting -= DataGridView1_CellFormatting; - dataGridView1.CellValueChanged -= DataGridView1_CellValueChanged; - dataGridView1.CellBeginEdit -= DataGridView1_CellBeginEdit; - - // 白色背景样式(只读)- 不设置BackColor,让隔行变色生效 - DataGridViewCellStyle readonlyStyle = new DataGridViewCellStyle + dataGridView1.SuspendLayout(); + try { - Alignment = DataGridViewContentAlignment.MiddleCenter, - SelectionBackColor = Color.LightGray - }; - - // 白色背景样式(可编辑)- 不设置BackColor,让隔行变色生效 - DataGridViewCellStyle editableStyle = new DataGridViewCellStyle - { - Alignment = DataGridViewContentAlignment.MiddleCenter, - SelectionBackColor = Color.LightBlue - }; - - // 序号列 - DataGridViewTextBoxColumn seqCol = new DataGridViewTextBoxColumn - { - Name = "序号", - HeaderText = "序号", - DataPropertyName = "序号", - Width = 120, - ReadOnly = true, - DefaultCellStyle = (DataGridViewCellStyle)readonlyStyle.Clone() - }; - dataGridView1.Columns.Add(seqCol); - - // 为每个试样添加3列(试样次数:1、2、3) - for (int i = 1; i <= 5; i++) - { - // 列1:试样次数1 - DataGridViewTextBoxColumn col1 = new DataGridViewTextBoxColumn + dataGridView1.AutoGenerateColumns = false; + dataGridView1.DataSource = null; + dataGridView1.Columns.Clear(); + + // 移除可能存在的事件处理器 + dataGridView1.CellFormatting -= DataGridView1_CellFormatting; + dataGridView1.CellValueChanged -= DataGridView1_CellValueChanged; + dataGridView1.CellBeginEdit -= DataGridView1_CellBeginEdit; + + // 创建共享样式(不设置背景色,让 AlternatingRowsDefaultCellStyle 生效) + DataGridViewCellStyle centerStyle = new DataGridViewCellStyle { - Name = $"试样{i}_1", - HeaderText = $"试样{i}\n1", - DataPropertyName = $"试样{i}_1", - Width = 100, - ReadOnly = false, - DefaultCellStyle = (DataGridViewCellStyle)editableStyle.Clone() + Alignment = DataGridViewContentAlignment.MiddleCenter }; - dataGridView1.Columns.Add(col1); - // 列2:试样次数2 - DataGridViewTextBoxColumn col2 = new DataGridViewTextBoxColumn + // 序号列 + dataGridView1.Columns.Add(new DataGridViewTextBoxColumn { - Name = $"试样{i}_2", - HeaderText = $"试样{i}\n2", - DataPropertyName = $"试样{i}_2", - Width = 100, - ReadOnly = false, - DefaultCellStyle = (DataGridViewCellStyle)editableStyle.Clone() - }; - dataGridView1.Columns.Add(col2); + Name = "序号", + HeaderText = "序号", + DataPropertyName = "序号", + Width = 180, + ReadOnly = true, + DefaultCellStyle = centerStyle + }); - // 列3:试样次数3 - DataGridViewTextBoxColumn col3 = new DataGridViewTextBoxColumn + // 为每个试样添加3列(2级表头:试样N + 子列1/2/3) + for (int i = 1; i <= currentSampleCount; i++) { - Name = $"试样{i}_3", - HeaderText = $"试样{i}\n3", - DataPropertyName = $"试样{i}_3", - Width = 100, - ReadOnly = false, - DefaultCellStyle = (DataGridViewCellStyle)editableStyle.Clone() - }; - dataGridView1.Columns.Add(col3); + dataGridView1.Columns.Add(new DataGridViewTextBoxColumn + { + Name = $"试样{i}_1", + HeaderText = $"试样{i}\n1", + DataPropertyName = $"试样{i}_1", + Width = 100, + ReadOnly = false, + DefaultCellStyle = (DataGridViewCellStyle)centerStyle.Clone() + }); + + dataGridView1.Columns.Add(new DataGridViewTextBoxColumn + { + Name = $"试样{i}_2", + HeaderText = $"试样{i}\n2", + DataPropertyName = $"试样{i}_2", + Width = 100, + ReadOnly = false, + DefaultCellStyle = (DataGridViewCellStyle)centerStyle.Clone() + }); + + dataGridView1.Columns.Add(new DataGridViewTextBoxColumn + { + Name = $"试样{i}_3", + HeaderText = $"试样{i}\n3", + DataPropertyName = $"试样{i}_3", + Width = 100, + ReadOnly = false, + DefaultCellStyle = (DataGridViewCellStyle)centerStyle.Clone() + }); + } + + // 绑定数据源 + dataGridView1.DataSource = sampleDataTable; + dataGridView1.CellFormatting += DataGridView1_CellFormatting; + dataGridView1.CellValueChanged += DataGridView1_CellValueChanged; + dataGridView1.CellBeginEdit += DataGridView1_CellBeginEdit; } - - // 绑定数据源 - dataGridView1.DataSource = sampleDataTable; - - // 调试:输出列信息 - System.Diagnostics.Debug.WriteLine("=== DataGridView 列信息 ==="); - System.Diagnostics.Debug.WriteLine($"DataTable 列数: {sampleDataTable.Columns.Count}"); - System.Diagnostics.Debug.WriteLine($"DataGridView 列数: {dataGridView1.Columns.Count}"); - - for (int i = 0; i < dataGridView1.Columns.Count; i++) + finally { - var col = dataGridView1.Columns[i]; - System.Diagnostics.Debug.WriteLine($"列{i}: Name={col.Name}, DataPropertyName={col.DataPropertyName}"); + dataGridView1.ResumeLayout(); } - - // 添加单元格格式化事件 - dataGridView1.CellFormatting += DataGridView1_CellFormatting; - - // 添加单元格编辑事件(用于手动输入后重新计算) - dataGridView1.CellValueChanged += DataGridView1_CellValueChanged; - - // 添加单元格编辑前事件(动态控制可编辑性) - dataGridView1.CellBeginEdit += DataGridView1_CellBeginEdit; } /// @@ -147,12 +153,12 @@ namespace WindowsFormsApp6 /// private void DataGridView1_CellBeginEdit(object sender, DataGridViewCellCancelEventArgs e) { - if (e.RowIndex >= 0 && e.ColumnIndex > 0) // 跳过序号列 + if (e.RowIndex >= 0 && e.ColumnIndex > 0) { string rowName = dataGridView1.Rows[e.RowIndex].Cells["序号"].Value?.ToString() ?? ""; - // 吸芯高度(mm)行 - 所有列都可以编辑 - if (rowName == "吸芯高度(mm)") + // 吸芯高度(mm)行 - 所有列都可以编辑 + if (rowName == ROW_WICKING_HEIGHT) { return; // 允许编辑 } @@ -167,71 +173,32 @@ namespace WindowsFormsApp6 /// private void DataGridView1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e) { - if (e.RowIndex >= 0 && e.Value != null && e.Value != DBNull.Value) + if (e.RowIndex < 0 || e.ColumnIndex == 0) return; // 跳过序号列 + + // 格式化数值显示为2位小数 + if (e.Value != null && e.Value != DBNull.Value) { - // 格式化数值列(保留2位小数) - string columnName = dataGridView1.Columns[e.ColumnIndex].Name; - if (columnName.Contains("试样") && columnName != "序号") + if (double.TryParse(e.Value.ToString(), out double numValue)) { - if (double.TryParse(e.Value.ToString(), out double value)) + // 如果值为0或接近0,显示为空白 + if (Math.Abs(numValue) < 0.001) { - // 如果值为0,根据行和列决定是否显示 - if (value == 0) - { - string rowName = dataGridView1.Rows[e.RowIndex].Cells["序号"].Value?.ToString() ?? ""; - - // 根据示意图,某些单元格应该显示为空白 - if (ShouldBeEmpty(rowName, columnName)) - { - e.Value = ""; - e.FormattingApplied = true; - return; - } - } - - e.Value = value.ToString("F2"); + e.Value = ""; e.FormattingApplied = true; + return; } + + // 显示2位小数 + e.Value = numValue.ToString("F2"); + e.FormattingApplied = true; } } - } - - /// - /// 判断单元格是否应该为空白 - /// - private bool ShouldBeEmpty(string rowName, string columnName) - { - // 吸水时间(s) - 只有试样次数1有数据 - if (rowName == "吸水时间(s)") + else { - return columnName.EndsWith("_2") || columnName.EndsWith("_3"); + // null 或 DBNull 显示为空白 + e.Value = ""; + e.FormattingApplied = true; } - - // 吸芯高度(mm) - 所有列都有数据(全部可编辑) - if (rowName == "吸芯高度(mm)") - { - return false; // 不应该为空 - } - - // 芯吸速率(mm/min) - 只有试样次数3有数据 - if (rowName == "芯吸速率(mm/min)") - { - return columnName.EndsWith("_1") || columnName.EndsWith("_2"); - } - - // 平均芯吸速率(mm/min) - 只有试样次数3有数据 - if (rowName == "平均芯吸速率(mm/min)") - { - return columnName.EndsWith("_1") || columnName.EndsWith("_2"); - } - - // 标准偏差 - 只有试样次数3有数据 - if (rowName == "标准偏差") - { - return columnName.EndsWith("_1") || columnName.EndsWith("_2"); - } - - return false; } /// @@ -244,9 +211,10 @@ namespace WindowsFormsApp6 string rowName = dataGridView1.Rows[e.RowIndex].Cells["序号"].Value?.ToString() ?? ""; // 如果是吸芯高度行,任何列的修改都触发重新计算 - if (rowName == "吸芯高度(mm)") + if (rowName == ROW_WICKING_HEIGHT) { - RecalculateRow(e.RowIndex); + CalculateAllRows(); + RefreshDataGridView(); } } } @@ -256,25 +224,17 @@ namespace WindowsFormsApp6 /// private void InitializeEventHandlers() { - // 连接设备按钮 button1.Click += Button1_Click; - - // 打印按钮 button2.Click += Button2_Click; - - // 导出按钮 button3.Click += Button3_Click; - - // 返回按钮 button4.Click += Button4_Click; - - // 生成模拟数据按钮 button5.Click += Button5_Click; - // 更新日期时间标签 - System.Windows.Forms.Timer clockTimer = new System.Windows.Forms.Timer(); - clockTimer.Interval = 1000; - clockTimer.Tick += (s, e) => label2.Text = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); + clockTimer = new System.Windows.Forms.Timer + { + Interval = TIMER_INTERVAL + }; + clockTimer.Tick += (s, e) => label2.Text = DateTime.Now.ToString(DATE_TIME_FORMAT); clockTimer.Start(); } @@ -286,16 +246,14 @@ namespace WindowsFormsApp6 if (dataTimer.Enabled) { StopDataCollection(); - button1.Text = "🔗 连接设备"; - button1.BackColor = Color.FromArgb(46, 204, 113); - MessageBox.Show("已停止数据采集", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); + UpdateButtonState(button1, "🔗 连接设备", Color.FromArgb(46, 204, 113)); + ShowMessage("已停止数据采集"); } else { StartDataCollection(); - button1.Text = "⏸️ 停止采集"; - button1.BackColor = Color.FromArgb(231, 76, 60); - MessageBox.Show("开始数据采集", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); + UpdateButtonState(button1, "⏸️ 停止采集", Color.FromArgb(231, 76, 60)); + ShowMessage("开始数据采集"); } } @@ -304,7 +262,24 @@ namespace WindowsFormsApp6 /// private void Button2_Click(object sender, EventArgs e) { - MessageBox.Show("打印功能开发中...", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); + ShowMessage("打印功能开发中..."); + } + + /// + /// 更新按钮状态 + /// + private void UpdateButtonState(Button button, string text, Color backColor) + { + button.Text = text; + button.BackColor = backColor; + } + + /// + /// 显示提示消息 + /// + private void ShowMessage(string message, string title = "提示", MessageBoxIcon icon = MessageBoxIcon.Information) + { + MessageBox.Show(message, title, MessageBoxButtons.OK, icon); } /// @@ -352,19 +327,19 @@ namespace WindowsFormsApp6 sampleDataTable.Columns.Add("序号", typeof(string)); // 为每个试样添加3列(试样次数:1、2、3) - for (int i = 1; i <= 5; i++) + for (int i = 1; i <= currentSampleCount; i++) { - sampleDataTable.Columns.Add($"试样{i}_1", typeof(double)); // 系统读数 - sampleDataTable.Columns.Add($"试样{i}_2", typeof(double)); // 手动输入 - sampleDataTable.Columns.Add($"试样{i}_3", typeof(double)); // 系统计算 + sampleDataTable.Columns.Add($"试样{i}_1", typeof(double)); + sampleDataTable.Columns.Add($"试样{i}_2", typeof(double)); + sampleDataTable.Columns.Add($"试样{i}_3", typeof(double)); } - // 初始化6行数据 - AddDataRow("吸水时间(s)"); - AddDataRow("吸芯高度(mm)"); - AddDataRow("芯吸速率(mm/min)"); - AddDataRow("平均芯吸速率(mm/min)"); - AddDataRow("标准偏差"); + // 初始化5行数据 + AddDataRow(ROW_WICKING_TIME); + AddDataRow(ROW_WICKING_HEIGHT); + AddDataRow(ROW_WICKING_RATE); + AddDataRow(ROW_AVG_WICKING_RATE); + AddDataRow(ROW_STD_DEVIATION); } /// @@ -376,7 +351,7 @@ namespace WindowsFormsApp6 row["序号"] = rowName; // 初始化所有数值列为0 - for (int i = 1; i <= 5; i++) + for (int i = 1; i <= currentSampleCount; i++) { row[$"试样{i}_1"] = 0.0; row[$"试样{i}_2"] = 0.0; @@ -401,26 +376,22 @@ namespace WindowsFormsApp6 /// private void DataTimer_Tick(object sender, EventArgs e) { - // 读取吸水时间(s)- 从寄存器读取 - DataRow timeRow = sampleDataTable.Rows[0]; // 第一行:吸水时间 - for (int i = 1; i <= 5; i++) + // 读取吸水时间(s) - 从寄存器读取 + DataRow timeRow = sampleDataTable.Rows[0]; + for (int i = 1; i <= currentSampleCount; i++) { double registerValue = ReadRegisterData(i - 1); timeRow[$"试样{i}_1"] = registerValue; } - // 触发界面更新 UpdateDisplay(); } /// /// 模拟从寄存器读取数据 - /// 实际应用中替换为真实的 Modbus 或其他协议读取 /// private double ReadRegisterData(int registerAddress) { - // 模拟数据:30-34 之间的随机值 - Random random = new Random(Guid.NewGuid().GetHashCode()); return 30 + random.NextDouble() * 4; } @@ -429,49 +400,92 @@ namespace WindowsFormsApp6 /// public void GenerateMockData() { - Random random = new Random(); + // 清空所有行的数据(保持表结构) + foreach (DataRow row in sampleDataTable.Rows) + { + for (int i = 1; i <= currentSampleCount; i++) + { + row[$"试样{i}_1"] = 0.0; + row[$"试样{i}_2"] = 0.0; + row[$"试样{i}_3"] = 0.0; + } + } // 第1行:吸水时间(s)- 试样次数1(系统读数) + // 每个试样只在第1次测试时记录吸水时间 DataRow timeRow = sampleDataTable.Rows[0]; - for (int i = 1; i <= 5; i++) + for (int i = 1; i <= currentSampleCount; i++) + { + double timeValue = Math.Round(30 + random.NextDouble() * 4, 2); // 30-34秒 + timeRow[$"试样{i}_1"] = timeValue; + // 试样次数2和3不使用,保持为0 + } + + // 第2行:吸芯高度(mm)- 手动输入,不自动生成 + // 保持为0,等待用户手动输入 + + // 第3-5行:芯吸速率、平均值、标准偏差 - 保持为0 + // 等待用户输入吸芯高度后,会自动计算 + + // 更新显示 + RefreshDataGridView(); + + ShowMessage($"已生成 {currentSampleCount} 个试样的吸水时间数据\n" + + $"- 吸水时间:30-34秒(系统读数)\n" + + $"- 吸芯高度:请手动输入(每个试样3次测试)\n" + + $"- 芯吸速率:输入吸芯高度后自动计算", + "模拟数据生成"); + } + + /// + /// 生成完整的测试数据(包含吸芯高度)- 用于快速测试 + /// + public void GenerateFullMockData() + { + // 清空所有行的数据(保持表结构) + foreach (DataRow row in sampleDataTable.Rows) + { + for (int i = 1; i <= currentSampleCount; i++) + { + row[$"试样{i}_1"] = 0.0; + row[$"试样{i}_2"] = 0.0; + row[$"试样{i}_3"] = 0.0; + } + } + + // 第1行:吸水时间(s) + DataRow timeRow = sampleDataTable.Rows[0]; + for (int i = 1; i <= currentSampleCount; i++) { double timeValue = Math.Round(30 + random.NextDouble() * 4, 2); timeRow[$"试样{i}_1"] = timeValue; - System.Diagnostics.Debug.WriteLine($"吸水时间 - 试样{i}_1 = {timeValue}"); } - // 第2行:吸芯高度(mm)- 所有列都可编辑(手动输入) + // 第2行:吸芯高度(mm)- 生成测试数据 DataRow heightRow = sampleDataTable.Rows[1]; - for (int i = 1; i <= 5; i++) + for (int i = 1; i <= currentSampleCount; i++) { - double height1 = Math.Round(50 + random.NextDouble() * 20, 2); - double height2 = Math.Round(50 + random.NextDouble() * 20, 2); - double height3 = Math.Round(50 + random.NextDouble() * 20, 2); + double baseHeight = 55 + random.NextDouble() * 10; + double height1 = Math.Round(baseHeight + (random.NextDouble() - 0.5) * 4, 2); + double height2 = Math.Round(baseHeight + (random.NextDouble() - 0.5) * 4, 2); + double height3 = Math.Round(baseHeight + (random.NextDouble() - 0.5) * 4, 2); heightRow[$"试样{i}_1"] = height1; heightRow[$"试样{i}_2"] = height2; heightRow[$"试样{i}_3"] = height3; - - System.Diagnostics.Debug.WriteLine($"吸芯高度 - 试样{i}_1 = {height1}"); - System.Diagnostics.Debug.WriteLine($"吸芯高度 - 试样{i}_2 = {height2}"); - System.Diagnostics.Debug.WriteLine($"吸芯高度 - 试样{i}_3 = {height3}"); } // 计算所有行 CalculateAllRows(); // 更新显示 - UpdateDisplay(); + RefreshDataGridView(); - // 调试信息:验证数据 - System.Diagnostics.Debug.WriteLine("=== 模拟数据生成完成 ==="); - DataRow rateRow = sampleDataTable.Rows[2]; - for (int i = 1; i <= 5; i++) - { - System.Diagnostics.Debug.WriteLine($"芯吸速率 - 试样{i}_3 = {rateRow[$"试样{i}_3"]}"); - } - - MessageBox.Show("已生成模拟数据\n请查看输出窗口的调试信息", "模拟数据生成", MessageBoxButtons.OK, MessageBoxIcon.Information); + ShowMessage($"已生成完整测试数据(包含吸芯高度)\n" + + $"- 吸水时间:30-34秒\n" + + $"- 吸芯高度:50-70mm(自动生成)\n" + + $"- 芯吸速率:已自动计算", + "完整测试数据"); } /// @@ -479,10 +493,17 @@ namespace WindowsFormsApp6 /// private void UpdateDisplay() { - // 刷新 DataGridView - if (dataGridView1.DataSource != null) + RefreshDataGridView(); + } + + /// + /// 刷新DataGridView + /// + private void RefreshDataGridView() + { + if (dataGridView1.DataSource is DataTable dt) { - ((DataTable)dataGridView1.DataSource).AcceptChanges(); + dt.AcceptChanges(); dataGridView1.Refresh(); } } @@ -502,15 +523,6 @@ namespace WindowsFormsApp6 CalculateStandardDeviation(); } - /// - /// 重新计算指定行 - /// - private void RecalculateRow(int rowIndex) - { - CalculateAllRows(); - UpdateDisplay(); - } - /// /// 计算芯吸速率(mm/min) /// 公式:芯吸速率 = 吸芯高度(mm) / (吸水时间(s) / 60) @@ -521,7 +533,7 @@ namespace WindowsFormsApp6 DataRow heightRow = sampleDataTable.Rows[1]; // 吸芯高度 DataRow rateRow = sampleDataTable.Rows[2]; // 芯吸速率 - for (int i = 1; i <= 5; i++) + for (int i = 1; i <= currentSampleCount; i++) { // 获取吸水时间(试样次数1:系统读数) double time = ConvertToDouble(timeRow[$"试样{i}_1"]); @@ -584,7 +596,7 @@ namespace WindowsFormsApp6 DataRow avgRow = sampleDataTable.Rows[3]; // 平均芯吸速率 List rates = new List(); - for (int i = 1; i <= 5; i++) + for (int i = 1; i <= currentSampleCount; i++) { double rate = ConvertToDouble(rateRow[$"试样{i}_3"]); if (rate > 0) @@ -596,7 +608,7 @@ namespace WindowsFormsApp6 double average = rates.Count > 0 ? rates.Average() : 0; // 所有试样显示相同的平均值(存储在试样次数3) - for (int i = 1; i <= 5; i++) + for (int i = 1; i <= currentSampleCount; i++) { avgRow[$"试样{i}_3"] = Math.Round(average, 2); } @@ -611,7 +623,7 @@ namespace WindowsFormsApp6 DataRow stdRow = sampleDataTable.Rows[4]; // 标准偏差 List rates = new List(); - for (int i = 1; i <= 5; i++) + for (int i = 1; i <= currentSampleCount; i++) { double rate = ConvertToDouble(rateRow[$"试样{i}_3"]); if (rate > 0) @@ -629,7 +641,7 @@ namespace WindowsFormsApp6 } // 所有试样显示相同的标准偏差(存储在试样次数3) - for (int i = 1; i <= 5; i++) + for (int i = 1; i <= currentSampleCount; i++) { stdRow[$"试样{i}_3"] = Math.Round(stdDev, 2); } @@ -658,162 +670,203 @@ namespace WindowsFormsApp6 { try { - IWorkbook workbook; - - // 根据文件扩展名选择格式 - if (filePath.EndsWith(".xlsx")) - { - workbook = new XSSFWorkbook(); // Excel 2007+ - } - else - { - workbook = new HSSFWorkbook(); // Excel 97-2003 - } + IWorkbook workbook = filePath.EndsWith(".xlsx") + ? (IWorkbook)new XSSFWorkbook() + : new HSSFWorkbook(); - ISheet sheet = workbook.CreateSheet("液体吸收测试报表"); + ISheet sheet = workbook.CreateSheet("液体芯吸速率测试报表"); // 创建样式 - ICellStyle headerStyle = workbook.CreateCellStyle(); - headerStyle.FillForegroundColor = NPOI.HSSF.Util.HSSFColor.Grey25Percent.Index; - headerStyle.FillPattern = FillPattern.SolidForeground; - headerStyle.BorderBottom = NPOIBorderStyle.Thin; - headerStyle.BorderTop = NPOIBorderStyle.Thin; - headerStyle.BorderLeft = NPOIBorderStyle.Thin; - headerStyle.BorderRight = NPOIBorderStyle.Thin; - headerStyle.Alignment = NPOIHorizontalAlignment.Center; - headerStyle.VerticalAlignment = VerticalAlignment.Center; - IFont headerFont = workbook.CreateFont(); - headerFont.IsBold = true; - headerStyle.SetFont(headerFont); + var styles = CreateReportStyles(workbook); - // 白色背景样式(偶数行) - ICellStyle whiteStyle = workbook.CreateCellStyle(); - whiteStyle.BorderBottom = NPOIBorderStyle.Thin; - whiteStyle.BorderTop = NPOIBorderStyle.Thin; - whiteStyle.BorderLeft = NPOIBorderStyle.Thin; - whiteStyle.BorderRight = NPOIBorderStyle.Thin; - whiteStyle.Alignment = NPOIHorizontalAlignment.Center; - whiteStyle.VerticalAlignment = VerticalAlignment.Center; - - // 黄色背景样式(奇数行) - ICellStyle yellowStyle = workbook.CreateCellStyle(); - yellowStyle.FillForegroundColor = NPOI.HSSF.Util.HSSFColor.LightYellow.Index; - yellowStyle.FillPattern = FillPattern.SolidForeground; - yellowStyle.BorderBottom = NPOIBorderStyle.Thin; - yellowStyle.BorderTop = NPOIBorderStyle.Thin; - yellowStyle.BorderLeft = NPOIBorderStyle.Thin; - yellowStyle.BorderRight = NPOIBorderStyle.Thin; - yellowStyle.Alignment = NPOIHorizontalAlignment.Center; - yellowStyle.VerticalAlignment = VerticalAlignment.Center; - - // 创建第一行表头(试样1-5) - IRow headerRow1 = sheet.CreateRow(0); - headerRow1.Height = 400; // 设置行高(单位:1/20点) - - ICell cell0 = headerRow1.CreateCell(0); - cell0.SetCellValue("序号"); - cell0.CellStyle = headerStyle; - sheet.AddMergedRegion(new CellRangeAddress(0, 1, 0, 0)); // 合并序号列 - - int colIndex = 1; - for (int i = 1; i <= 5; i++) - { - ICell cell = headerRow1.CreateCell(colIndex); - cell.SetCellValue($"试样{i}"); - cell.CellStyle = headerStyle; - sheet.AddMergedRegion(new CellRangeAddress(0, 0, colIndex, colIndex + 2)); - colIndex += 3; - } - - // 创建第二行表头(试样次数:1、2、3) - IRow headerRow2 = sheet.CreateRow(1); - headerRow2.Height = 400; // 设置行高 - - colIndex = 1; - for (int i = 1; i <= 5; i++) - { - ICell cell1 = headerRow2.CreateCell(colIndex++); - cell1.SetCellValue("1"); - cell1.CellStyle = headerStyle; - - ICell cell2 = headerRow2.CreateCell(colIndex++); - cell2.SetCellValue("2"); - cell2.CellStyle = headerStyle; - - ICell cell3 = headerRow2.CreateCell(colIndex++); - cell3.SetCellValue("3"); - cell3.CellStyle = headerStyle; - } + // 创建表头(2级) + CreateReportHeader(sheet, styles.headerStyle); // 填充数据 - int rowIndex = 2; - int dataRowIndex = 0; - foreach (DataRow dataRow in sampleDataTable.Rows) - { - IRow row = sheet.CreateRow(rowIndex); - row.Height = 380; // 设置数据行高 - - // 根据行索引选择样式(隔行变色) - ICellStyle rowStyle = (dataRowIndex % 2 == 0) ? whiteStyle : yellowStyle; - - // 序号列 - ICell cellSeq = row.CreateCell(0); - cellSeq.SetCellValue(dataRow["序号"].ToString()); - cellSeq.CellStyle = rowStyle; + FillReportData(sheet, styles.dataStyle, styles.yellowStyle); - colIndex = 1; - for (int i = 1; i <= 5; i++) - { - // 试样次数1 - ICell cell1 = row.CreateCell(colIndex++); - double val1 = ConvertToDouble(dataRow[$"试样{i}_1"]); - if (val1 != 0) - { - cell1.SetCellValue(val1); - } - cell1.CellStyle = rowStyle; - - // 试样次数2 - ICell cell2 = row.CreateCell(colIndex++); - double val2 = ConvertToDouble(dataRow[$"试样{i}_2"]); - if (val2 != 0) - { - cell2.SetCellValue(val2); - } - cell2.CellStyle = rowStyle; - - // 试样次数3 - ICell cell3 = row.CreateCell(colIndex++); - double val3 = ConvertToDouble(dataRow[$"试样{i}_3"]); - if (val3 != 0) - { - cell3.SetCellValue(val3); - } - cell3.CellStyle = rowStyle; - } - - rowIndex++; - dataRowIndex++; - } - - // 手动设置列宽(单位:1/256字符宽度) - sheet.SetColumnWidth(0, 20 * 256); // 序号列:20个字符宽度 - for (int i = 1; i <= 15; i++) - { - sheet.SetColumnWidth(i, 12 * 256); // 数据列:12个字符宽度 - } + // 设置列宽 + SetReportColumnWidths(sheet); // 保存文件 - using (FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write)) - { - workbook.Write(fs); - } + SaveWorkbook(workbook, filePath); - MessageBox.Show($"数据已成功导出到:{filePath}", "导出成功", MessageBoxButtons.OK, MessageBoxIcon.Information); + ShowMessage($"数据已成功导出到:{filePath}", "导出成功"); } catch (Exception ex) { - MessageBox.Show($"导出失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); + ShowMessage($"导出失败:{ex.Message}", "错误", MessageBoxIcon.Error); + } + } + + /// + /// 创建报表样式 + /// + private (ICellStyle headerStyle, ICellStyle dataStyle, ICellStyle yellowStyle) + CreateReportStyles(IWorkbook workbook) + { + // 表头样式 + ICellStyle headerStyle = workbook.CreateCellStyle(); + headerStyle.FillForegroundColor = NPOI.HSSF.Util.HSSFColor.Grey25Percent.Index; + headerStyle.FillPattern = FillPattern.SolidForeground; + SetBorders(headerStyle); + headerStyle.Alignment = NPOIHorizontalAlignment.Center; + headerStyle.VerticalAlignment = VerticalAlignment.Center; + IFont headerFont = workbook.CreateFont(); + headerFont.IsBold = true; + headerStyle.SetFont(headerFont); + + // 白色背景样式 + ICellStyle dataStyle = workbook.CreateCellStyle(); + SetBorders(dataStyle); + dataStyle.Alignment = NPOIHorizontalAlignment.Center; + dataStyle.VerticalAlignment = VerticalAlignment.Center; + + // 黄色背景样式 + ICellStyle yellowStyle = workbook.CreateCellStyle(); + yellowStyle.FillForegroundColor = NPOI.HSSF.Util.HSSFColor.LightYellow.Index; + yellowStyle.FillPattern = FillPattern.SolidForeground; + SetBorders(yellowStyle); + yellowStyle.Alignment = NPOIHorizontalAlignment.Center; + yellowStyle.VerticalAlignment = VerticalAlignment.Center; + + return (headerStyle, dataStyle, yellowStyle); + } + + /// + /// 设置单元格边框 + /// + private void SetBorders(ICellStyle style) + { + style.BorderBottom = NPOIBorderStyle.Thin; + style.BorderTop = NPOIBorderStyle.Thin; + style.BorderLeft = NPOIBorderStyle.Thin; + style.BorderRight = NPOIBorderStyle.Thin; + } + + /// + /// 创建报表表头(2级) + /// + private void CreateReportHeader(ISheet sheet, ICellStyle headerStyle) + { + // 创建第一行表头(试样1-N) + IRow headerRow1 = sheet.CreateRow(0); + headerRow1.Height = 400; + + ICell cell0 = headerRow1.CreateCell(0); + cell0.SetCellValue("序号"); + cell0.CellStyle = headerStyle; + sheet.AddMergedRegion(new CellRangeAddress(0, 1, 0, 0)); // 合并序号列 + + int colIndex = 1; + for (int i = 1; i <= currentSampleCount; i++) + { + ICell cell = headerRow1.CreateCell(colIndex); + cell.SetCellValue($"试样{i}"); + cell.CellStyle = headerStyle; + sheet.AddMergedRegion(new CellRangeAddress(0, 0, colIndex, colIndex + 2)); + colIndex += 3; + } + + // 创建第二行表头(试样次数:1、2、3) + IRow headerRow2 = sheet.CreateRow(1); + headerRow2.Height = 400; + + colIndex = 1; + for (int i = 1; i <= currentSampleCount; i++) + { + ICell cell1 = headerRow2.CreateCell(colIndex++); + cell1.SetCellValue("1"); + cell1.CellStyle = headerStyle; + + ICell cell2 = headerRow2.CreateCell(colIndex++); + cell2.SetCellValue("2"); + cell2.CellStyle = headerStyle; + + ICell cell3 = headerRow2.CreateCell(colIndex++); + cell3.SetCellValue("3"); + cell3.CellStyle = headerStyle; + } + } + + /// + /// 填充报表数据 + /// + private void FillReportData(ISheet sheet, ICellStyle dataStyle, ICellStyle yellowStyle) + { + int rowIndex = 2; + int dataRowIndex = 0; + + foreach (DataRow dataRow in sampleDataTable.Rows) + { + IRow row = sheet.CreateRow(rowIndex); + row.Height = 380; + + // 根据行索引选择样式(隔行变色) + ICellStyle rowStyle = (dataRowIndex % 2 == 0) ? dataStyle : yellowStyle; + + // 序号列 + ICell cellSeq = row.CreateCell(0); + cellSeq.SetCellValue(dataRow["序号"].ToString()); + cellSeq.CellStyle = rowStyle; + + int colIndex = 1; + for (int i = 1; i <= currentSampleCount; i++) + { + // 试样次数1 + ICell cell1 = row.CreateCell(colIndex++); + double val1 = ConvertToDouble(dataRow[$"试样{i}_1"]); + if (Math.Abs(val1) >= 0.001) // 只有非0值才显示 + { + cell1.SetCellValue(val1); + } + cell1.CellStyle = rowStyle; + + // 试样次数2 + ICell cell2 = row.CreateCell(colIndex++); + double val2 = ConvertToDouble(dataRow[$"试样{i}_2"]); + if (Math.Abs(val2) >= 0.001) // 只有非0值才显示 + { + cell2.SetCellValue(val2); + } + cell2.CellStyle = rowStyle; + + // 试样次数3 + ICell cell3 = row.CreateCell(colIndex++); + double val3 = ConvertToDouble(dataRow[$"试样{i}_3"]); + if (Math.Abs(val3) >= 0.001) // 只有非0值才显示 + { + cell3.SetCellValue(val3); + } + cell3.CellStyle = rowStyle; + } + + rowIndex++; + dataRowIndex++; + } + } + + /// + /// 设置报表列宽 + /// + private void SetReportColumnWidths(ISheet sheet) + { + sheet.SetColumnWidth(0, 20 * 256); // 序号列 + int totalColumns = currentSampleCount * 3; + for (int i = 1; i <= totalColumns; i++) + { + sheet.SetColumnWidth(i, 12 * 256); // 数据列 + } + } + + /// + /// 保存工作簿 + /// + private void SaveWorkbook(IWorkbook workbook, string filePath) + { + using (FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write)) + { + workbook.Write(fs); } } @@ -831,10 +884,9 @@ namespace WindowsFormsApp6 /// public void ClearData() { - // 重新初始化数据表 sampleDataTable.Clear(); InitializeDataTable(); - UpdateDisplay(); + RefreshDataGridView(); } } }