关于ext form.combobox 不能手动写入

在使用combobox 这个组件使,要防止客户乱输入,除了必备的校验外,使下拉框不能手动输入也是个选择:

editable:fasel(不能输入);editable:true(可以输入)

	//职位分类选择框
	posClassCom[addrindex] = new Ext.form.ComboBox({
		width:250, 
		hiddenName:'positionClass',
		id:'pos'+addrindex,
		store:PositionClassStore,
		displayField:'name',
		valueField:'id',
	    editable:false  ,
		mode: 'local',
		triggerAction: 'all',
		selectOnFocus:true
	});

 

using System; using System.Collections.Generic; using System.Drawing; using System.Windows.Forms; using System.IO.Ports; using System.IO; using System.Globalization; using System.Windows.Forms.DataVisualization.Charting; using OfficeOpenXml; using OfficeOpenXml.Style; using OfficeOpenXml.Drawing.Chart; using System.Diagnostics; using System.Reflection; using System.Text; using System.Linq; namespace PhysicsExperimentDisplay { [DataContract] public class ExperimentRecord { [DataMember] public string TimeStamp { get; set; } [DataMember] public double T1 { get; set; } [DataMember] public double T2 { get; set; } [DataMember] public double PlateWidth { get; set; } // 挡光片宽度 (m 或 mm,根据使用习惯) [DataMember] public double GateSpacing { get; set; } // 光电门间距 [DataMember] public double V1 { get; set; } [DataMember] public double V2 { get; set; } [DataMember] public double Acc { get; set; } } public partial class MainForm : Form { // 控件声明 private TableLayoutPanel mainLayout; private GroupBox gbSerial; private ComboBox cmbPorts; private ComboBox cmbBaudRate; private Button btnConnect; private Label lblPort; private Label lblBaud; private GroupBox gbData; private DataGridView dgvResults; private GroupBox gbChart; private Button btnExport; private Button btnClear; private SerialPort serialPort; private FlowLayoutPanel buttonPanel; private TableLayoutPanel serialLayout; private Chart chartPlot; // 新增控件:输入挡光片宽度和光电门间距、保存/加载历史 private Label lblPlateWidth; private TextBox txtPlateWidth; private Label lblGateSpacing; private TextBox txtGateSpacing; private Button btnSaveExperiment; private Button btnLoadExperiment; // 数据存储 private List<double> accelerationData = new List<double>(); private int dataPointCount = 0; private DateTime experimentStartTime = DateTime.Now; // 当前实验完整原始记录 private List<ExperimentRecord> currentRecords = new List<ExperimentRecord>(); // 保存目录 private readonly string experimentsFolder; public object JsonConvert { get; private set; } public object Newtonsoft { get; private set; } public MainForm() { // 初始化保存目录路径 experimentsFolder = Path.Combine(Application.StartupPath, "Experiments"); Directory.CreateDirectory(experimentsFolder); // 初始化组件 InitializeComponent(); // 初始化串口和图表 LoadAvailablePorts(); InitializePlot(); // 绑定按钮事件(确保事件处理器已实现) btnConnect.Click += btnConnect_Click; btnExport.Click += btnExport_Click; btnClear.Click += btnClear_Click; btnSaveExperiment.Click += btnSaveExperiment_Click; btnLoadExperiment.Click += btnLoadExperiment_Click; } private void btnLoadExperiment_Click(object sender, EventArgs e) { string selectedFile = null; OpenFileDialog ofd = null; try { // 在 try 内创建对话框,确保构造期抛出的异常也能被捕获 ofd = new System.Windows.Forms.OpenFileDialog(); ofd.InitialDirectory = experimentsFolder; ofd.Filter = "CSV 文件 (*.csv)|*.csv|JSON 文件 (*.json)|*.json|所有文件 (*.*)|*.*"; ofd.Title = "选择要加载的实验文件"; // 优先在标准 UI 环境中弹出系统对话框(传入父窗口) if (ofd.ShowDialog(this) == DialogResult.OK) { selectedFile = ofd.FileName; } else { return; } } catch (NotImplementedException nie) { // 记录诊断信息到日志文件,便于后续调查运行环境 try { string logPath = Path.Combine(experimentsFolder, "dialog_error.log"); var sbLog = new StringBuilder(); sbLog.AppendLine("=== OpenFileDialog NotImplementedException ==="); sbLog.AppendLine(DateTime.Now.ToString("o")); sbLog.AppendLine(nie.ToString()); try { sbLog.AppendLine($"OpenFileDialog runtime type: {ofd?.GetType().FullName ?? "null"}"); sbLog.AppendLine($"OpenFileDialog assembly: {ofd?.GetType().Assembly.FullName ?? "null"}"); } catch { /* 忽略获取类型信息失败 */ } try { sbLog.AppendLine($"Framework: {System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription}"); sbLog.AppendLine($"OS: {System.Runtime.InteropServices.RuntimeInformation.OSDescription}"); sbLog.AppendLine($"Is64BitProcess: {Environment.Is64BitProcess}"); } catch { /* 运行时信息获取失败则忽略 */ } File.AppendAllText(logPath, sbLog.ToString(), Encoding.UTF8); } catch { // 忽略日志写入错误,避免二次异常 } // 回退到内置选择器 selectedFile = PickFileFallback(); if (string.IsNullOrEmpty(selectedFile)) return; } catch (Exception ex) { MessageBox.Show($"打开文件对话框失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } finally { // 确保 Dispose,即使构造失败也安全 ofd?.Dispose(); } try { dgvResults.Rows.Clear(); accelerationData.Clear(); dataPointCount = 0; string ext = Path.GetExtension(selectedFile).ToLowerInvariant(); if (ext == ".csv") { var lines = File.ReadAllLines(selectedFile); // 假定第一行为表头:Time,T1,T2,V1,V2,Acc foreach (var line in lines.Skip(1)) { if (string.IsNullOrWhiteSpace(line)) continue; var cols = line.Split(','); int row = dgvResults.Rows.Add(); dgvResults.Rows[row].Cells[0].Value = cols.ElementAtOrDefault(0) ?? ""; dgvResults.Rows[row].Cells[1].Value = cols.ElementAtOrDefault(1) ?? ""; dgvResults.Rows[row].Cells[2].Value = cols.ElementAtOrDefault(2) ?? ""; dgvResults.Rows[row].Cells[3].Value = cols.ElementAtOrDefault(3) ?? ""; dgvResults.Rows[row].Cells[4].Value = cols.ElementAtOrDefault(4) ?? ""; dgvResults.Rows[row].Cells[5].Value = cols.ElementAtOrDefault(5) ?? ""; if (double.TryParse(cols.ElementAtOrDefault(5), NumberStyles.Any, CultureInfo.InvariantCulture, out double acc)) accelerationData.Add(acc); } } else if (ext == ".json") { var json = File.ReadAllText(selectedFile); var records = Newtonsoft.Json.JsonConvert.DeserializeObject<List<ExperimentRecord>>(json); if (records != null) { foreach (var r in records) { int row = dgvResults.Rows.Add(); dgvResults.Rows[row].Cells[0].Value = r.TimeStamp; dgvResults.Rows[row].Cells[1].Value = r.T1.ToString("0.000"); dgvResults.Rows[row].Cells[2].Value = r.T2.ToString("0.000"); dgvResults.Rows[row].Cells[3].Value = r.V1.ToString("0.000"); dgvResults.Rows[row].Cells[4].Value = r.V2.ToString("0.000"); dgvResults.Rows[row].Cells[5].Value = r.Acc.ToString("0.000"); accelerationData.Add(r.Acc); } } } dataPointCount = accelerationData.Count; // 用批量数据重建图表(避免每点重绘) chartPlot.Series["加速度"].Points.Clear(); for (int i = 0; i < accelerationData.Count; i++) chartPlot.Series["加速度"].Points.AddXY(i + 1, accelerationData[i]); chartPlot.Titles[0].Text = $"加速度变化曲线 (共 {dataPointCount} 个数据点)"; // 调整轴范围等(可复用 UpdatePlot 的逻辑) } catch (Exception ex) { MessageBox.Show($"加载失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); } } // 简单回退文件选择器:在无法使用系统 OpenFileDialog 时使用(仅列出 experimentsFolder 下文件) private string PickFileFallback() { try { string[] files = Directory.GetFiles(experimentsFolder) .OrderByDescending(f => File.GetCreationTimeUtc(f)) .ToArray(); if (files.Length == 0) { MessageBox.Show("未找到历史实验文件。", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); return null; } using (Form pickForm = new Form()) { pickForm.Text = "选择要加载的实验文件(回退)"; pickForm.StartPosition = FormStartPosition.CenterParent; pickForm.ClientSize = new Size(600, 400); pickForm.FormBorderStyle = FormBorderStyle.FixedDialog; pickForm.MinimizeBox = false; pickForm.MaximizeBox = false; pickForm.ShowInTaskbar = false; var lb = new ListBox { Dock = DockStyle.Fill }; foreach (var f in files) lb.Items.Add(Path.GetFileName(f)); var btnPanel = new FlowLayoutPanel { Dock = DockStyle.Bottom, Height = 40, FlowDirection = FlowDirection.RightToLeft, Padding = new Padding(6) }; var btnOk = new Button { Text = "确定", DialogResult = DialogResult.OK, AutoSize = true, Margin = new Padding(6) }; var btnCancel = new Button { Text = "取消", DialogResult = DialogResult.Cancel, AutoSize = true, Margin = new Padding(6) }; btnPanel.Controls.Add(btnOk); btnPanel.Controls.Add(btnCancel); pickForm.Controls.Add(lb); pickForm.Controls.Add(btnPanel); // 双击快速选择 lb.DoubleClick += (s, e) => { if (lb.SelectedIndex >= 0) pickForm.DialogResult = DialogResult.OK; }; if (pickForm.ShowDialog() == DialogResult.OK && lb.SelectedIndex >= 0) { return files[lb.SelectedIndex]; } } } catch (Exception ex) { MessageBox.Show($"文件选择回退失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); } return null; } private void btnSaveExperiment_Click(object sender, EventArgs e) { if (dgvResults.Rows.Count == 0) { MessageBox.Show("没有数据可保存。", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); return; } try { // 解析附加参数(挡光片宽度、光电门间距) double plateWidth = 0; double gateSpacing = 0; double.TryParse(txtPlateWidth?.Text, NumberStyles.Any, CultureInfo.InvariantCulture, out plateWidth); double.TryParse(txtGateSpacing?.Text, NumberStyles.Any, CultureInfo.InvariantCulture, out gateSpacing); // 从 DataGridView 构建记录列表 var records = new List<ExperimentRecord>(); foreach (DataGridViewRow row in dgvResults.Rows) { if (row.IsNewRow) continue; string timeStamp = row.Cells[0].Value?.ToString() ?? ""; double t1 = 0, t2 = 0, v1 = 0, v2 = 0, acc = 0; double.TryParse(row.Cells[1].Value?.ToString(), NumberStyles.Any, CultureInfo.InvariantCulture, out t1); double.TryParse(row.Cells[2].Value?.ToString(), NumberStyles.Any, CultureInfo.InvariantCulture, out t2); double.TryParse(row.Cells[3].Value?.ToString(), NumberStyles.Any, CultureInfo.InvariantCulture, out v1); double.TryParse(row.Cells[4].Value?.ToString(), NumberStyles.Any, CultureInfo.InvariantCulture, out v2); double.TryParse(row.Cells[5].Value?.ToString(), NumberStyles.Any, CultureInfo.InvariantCulture, out acc); records.Add(new ExperimentRecord { TimeStamp = timeStamp, T1 = t1, T2 = t2, V1 = v1, V2 = v2, Acc = acc, PlateWidth = plateWidth, GateSpacing = gateSpacing }); } // 将当前记录缓存在内存 currentRecords = records; // 生成文件名(时间戳) string baseName = $"Experiment_{DateTime.Now:yyyyMMdd_HHmmss}"; string csvPath = Path.Combine(experimentsFolder, baseName + ".csv"); string jsonPath = Path.Combine(experimentsFolder, baseName + ".json"); // 写 CSV(带表头) var sb = new StringBuilder(); sb.AppendLine("TimeStamp,T1,T2,V1,V2,Acc,PlateWidth,GateSpacing"); foreach (var r in records) { string line = string.Join(",", EscapeCsv(r.TimeStamp), r.T1.ToString("0.###", CultureInfo.InvariantCulture), r.T2.ToString("0.###", CultureInfo.InvariantCulture), r.V1.ToString("0.###", CultureInfo.InvariantCulture), r.V2.ToString("0.###", CultureInfo.InvariantCulture), r.Acc.ToString("0.###", CultureInfo.InvariantCulture), r.PlateWidth.ToString("0.###", CultureInfo.InvariantCulture), r.GateSpacing.ToString("0.###", CultureInfo.InvariantCulture)); sb.AppendLine(line); } File.WriteAllText(csvPath, sb.ToString(), Encoding.UTF8); // 写 JSON(格式化) string json = JsonConvert.SerializeObject(records, Formatting.Indented); File.WriteAllText(jsonPath, json, Encoding.UTF8); MessageBox.Show($"保存成功:\n{csvPath}\n{jsonPath}", "保存完成", MessageBoxButtons.OK, MessageBoxIcon.Information); } catch (Exception ex) { MessageBox.Show($"保存失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); } // 本地函数:CSV 字段转义(简单处理逗号与引号) string EscapeCsv(string s) { if (string.IsNullOrEmpty(s)) return ""; if (s.Contains(",") || s.Contains("\"") || s.Contains("\n")) return $"\"{s.Replace("\"", "\"\"")}\""; return s; } } // 初始化UI private void InitializeComponent() { // 主窗体设置 this.SuspendLayout(); this.AutoScaleDimensions = new SizeF(8F, 15F); this.AutoScaleMode = AutoScaleMode.Font; this.ClientSize = new Size(1200, 800); this.Text = "气垫导轨实验数据监控"; // 创建主布局面板 this.mainLayout = new TableLayoutPanel { ColumnCount = 1, Dock = DockStyle.Fill, Padding = new Padding(10), RowCount = 4 }; mainLayout.RowStyles.Add(new RowStyle(SizeType.Absolute, 100F)); mainLayout.RowStyles.Add(new RowStyle(SizeType.Absolute, 70F)); // 增加高度以容纳输入控件 mainLayout.RowStyles.Add(new RowStyle(SizeType.Percent, 40F)); mainLayout.RowStyles.Add(new RowStyle(SizeType.Percent, 60F)); // 串口设置组 this.gbSerial = new GroupBox { Text = "串口设置", Dock = DockStyle.Fill }; // 串口布局 this.serialLayout = new TableLayoutPanel { ColumnCount = 4, Dock = DockStyle.Fill, RowCount = 2 }; serialLayout.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 100F)); serialLayout.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 30F)); serialLayout.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 100F)); serialLayout.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 30F)); serialLayout.RowStyles.Add(new RowStyle(SizeType.Percent, 50F)); serialLayout.RowStyles.Add(new RowStyle(SizeType.Percent, 50F)); // 串口控件 this.lblPort = new Label { Text = "串口号:", TextAlign = ContentAlignment.MiddleRight, Dock = DockStyle.Fill }; this.cmbPorts = new ComboBox { DropDownStyle = ComboBoxStyle.DropDownList, Dock = DockStyle.Fill }; this.lblBaud = new Label { Text = "波特率:", TextAlign = ContentAlignment.MiddleRight, Dock = DockStyle.Fill }; this.cmbBaudRate = new ComboBox { DropDownStyle = ComboBoxStyle.DropDownList, Dock = DockStyle.Fill }; this.btnConnect = new Button { Text = "连接串口", BackColor = Color.LightGreen, Font = new Font("Microsoft YaHei UI", 10F, FontStyle.Bold), Dock = DockStyle.Fill, Margin = new Padding(5) }; // 添加控件到串口布局 serialLayout.Controls.Add(lblPort, 0, 0); serialLayout.Controls.Add(cmbPorts, 1, 0); serialLayout.Controls.Add(lblBaud, 2, 0); serialLayout.Controls.Add(cmbBaudRate, 3, 0); serialLayout.SetColumnSpan(btnConnect, 4); serialLayout.Controls.Add(btnConnect, 0, 1); gbSerial.Controls.Add(serialLayout); mainLayout.Controls.Add(gbSerial, 0, 0); // 按钮面板(包含输入挡光片宽度、光电门间距、保存历史等) this.buttonPanel = new FlowLayoutPanel { Dock = DockStyle.Fill, Padding = new Padding(10, 6, 10, 6), AutoSize = false, WrapContents = false }; // 挡光片宽度控件 this.lblPlateWidth = new Label { Text = "挡光片宽度:", TextAlign = ContentAlignment.MiddleCenter, AutoSize = true, Margin = new Padding(6, 10, 0, 0) }; this.txtPlateWidth = new TextBox { Width = 100, Text = "0.010", // 默认 10 mm (示例);用户可自行修改 Margin = new Padding(6) }; // 光电门间距控件 this.lblGateSpacing = new Label { Text = "光电门间距:", TextAlign = ContentAlignment.MiddleCenter, AutoSize = true, Margin = new Padding(6, 10, 0, 0) }; this.txtGateSpacing = new TextBox { Width = 100, Text = "0.100", // 默认 100 mm (示例) Margin = new Padding(6) }; // 保存实验按钮 this.btnSaveExperiment = new Button { Text = "保存本次实验", BackColor = Color.LightGoldenrodYellow, Size = new Size(140, 36), Margin = new Padding(6) }; // 加载历史按钮 this.btnLoadExperiment = new Button { Text = "加载历史实验", BackColor = Color.LightSteelBlue, Size = new Size(140, 36), Margin = new Padding(6) }; // 导出与清除按钮 this.btnExport = new Button { Text = "导出数据(Excel)", BackColor = Color.LightBlue, Size = new Size(180, 40), Margin = new Padding(6) }; this.btnClear = new Button { Text = "清除数据", BackColor = Color.LightSalmon, Size = new Size(180, 40), Margin = new Padding(6) }; // 把控件加入面板 buttonPanel.Controls.Add(lblPlateWidth); buttonPanel.Controls.Add(txtPlateWidth); buttonPanel.Controls.Add(lblGateSpacing); buttonPanel.Controls.Add(txtGateSpacing); buttonPanel.Controls.Add(btnSaveExperiment); buttonPanel.Controls.Add(btnLoadExperiment); buttonPanel.Controls.Add(btnExport); buttonPanel.Controls.Add(btnClear); mainLayout.Controls.Add(buttonPanel, 0, 1); // 数据表格 this.gbData = new GroupBox { Text = "实验数据", Dock = DockStyle.Fill }; this.dgvResults = new DataGridView { AllowUserToAddRows = false, AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill, BackgroundColor = SystemColors.Window, BorderStyle = BorderStyle.None, ColumnHeadersHeight = 34, Dock = DockStyle.Fill, ReadOnly = true, RowHeadersVisible = false, ScrollBars = ScrollBars.Vertical }; // 添加列 dgvResults.Columns.Add("Time", "时间"); dgvResults.Columns.Add("T1", "T1(s)"); dgvResults.Columns.Add("T2", "T2(s)"); dgvResults.Columns.Add("V1", "V1(m/s)"); dgvResults.Columns.Add("V2", "V2(m/s)"); dgvResults.Columns.Add("Acc", "加速度(m/s²)"); gbData.Controls.Add(dgvResults); mainLayout.Controls.Add(gbData, 0, 2); // 图表 this.gbChart = new GroupBox { Text = "加速度变化曲线", Dock = DockStyle.Fill }; this.chartPlot = new Chart { Dock = DockStyle.Fill }; gbChart.Controls.Add(chartPlot); mainLayout.Controls.Add(gbChart, 0, 3); // 添加主布局到窗体 this.Controls.Add(mainLayout); this.ResumeLayout(false); } // 初始化图表 private void InitializePlot() { chartPlot.ChartAreas.Clear(); chartPlot.Series.Clear(); chartPlot.Titles.Clear(); // 创建图表区域 ChartArea chartArea = new ChartArea("MainArea") { AxisX = { Title = "实验序号" }, AxisY = { Title = "加速度 (m/s²)" } }; chartArea.AxisX.MajorGrid.LineColor = Color.LightGray; chartArea.AxisY.MajorGrid.LineColor = Color.LightGray; chartArea.AxisY.MajorGrid.LineDashStyle = ChartDashStyle.Dot; chartPlot.ChartAreas.Add(chartArea); // 创建数据系列 Series series = new Series("加速度") { ChartType = SeriesChartType.Line, Color = Color.Blue, BorderWidth = 2, MarkerStyle = MarkerStyle.Circle, MarkerSize = 8, MarkerColor = Color.DarkBlue }; chartPlot.Series.Add(series); // 添加标题 chartPlot.Titles.Add("加速度变化曲线"); chartPlot.Titles[0].Font = new Font("Microsoft YaHei UI", 12, FontStyle.Bold); chartPlot.Titles[0].ForeColor = Color.Black; // 设置初始轴范围 chartArea.AxisX.Minimum = 0; chartArea.AxisX.Maximum = 10; chartArea.AxisY.Minimum = 0; chartArea.AxisY.Maximum = 10; // 添加初始点 series.Points.AddXY(0, 0); } // 加载可用串口 private void LoadAvailablePorts() { cmbPorts.Items.Clear(); string[] ports = SerialPort.GetPortNames(); cmbPorts.Items.AddRange(ports); if (ports.Length > 0) cmbPorts.SelectedIndex = 0; // 设置波特率选项 cmbBaudRate.Items.AddRange(new object[] { 9600, 19200, 38400, 57600, 115200 }); cmbBaudRate.SelectedIndex = 4; // 默认115200 } // 连接/断开串口 private void btnConnect_Click(object sender, EventArgs e) { if (serialPort == null || !serialPort.IsOpen) { try { if (cmbPorts.SelectedItem == null) { MessageBox.Show("请选择有效的串口", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } serialPort = new SerialPort(cmbPorts.Text, (int)cmbBaudRate.SelectedItem); serialPort.DataReceived += SerialPort_DataReceived; serialPort.Open(); btnConnect.Text = "断开连接"; btnConnect.BackColor = Color.LightCoral; // 重置实验数据 experimentStartTime = DateTime.Now; dgvResults.Rows.Clear(); accelerationData.Clear(); dataPointCount = 0; InitializePlot(); } catch (Exception ex) { MessageBox.Show($"串口连接错误: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); } } else { try { serialPort.Close(); serialPort.Dispose(); serialPort = null; btnConnect.Text = "连接串口"; btnConnect.BackColor = Color.LightGreen; } catch (Exception ex) { MessageBox.Show($"断开连接错误: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); } } } // 串口数据接收处理 private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e) { try { string data = serialPort.ReadLine(); this.Invoke(new Action(() => ProcessData(data))); } catch (Exception ex) { this.Invoke(new Action(() => MessageBox.Show($"数据接收错误: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error))); } } // 数据处理和显示 private void ProcessData(string rawData) { try { string[] pairs = rawData.Split(','); double t1 = 0, t2 = 0, v1 = 0, v2 = 0, acc = 0; foreach (string pair in pairs) { string[] keyValue = pair.Split(':'); if (keyValue.Length != 2) continue; string key = keyValue[0].Trim(); string value = keyValue[1].Trim(); switch (key) { case "T1": t1 = ParseDouble(value); break; case "T2": t2 = ParseDouble(value); break; case "V1": v1 = ParseDouble(value); break; case "V2": v2 = ParseDouble(value); break; case "A": acc = ParseDouble(value); break; } } // 计算相对时间 TimeSpan elapsed = DateTime.Now - experimentStartTime; string timeString = $"{elapsed.Minutes:00}:{elapsed.Seconds:00}.{elapsed.Milliseconds:000}"; // 添加新行到DataGridView int rowIndex = dgvResults.Rows.Add(); dgvResults.Rows[rowIndex].Cells[0].Value = timeString; dgvResults.Rows[rowIndex].Cells[1].Value = t1.ToString("0.000"); dgvResults.Rows[rowIndex].Cells[2].Value = t2.ToString("0.000"); dgvResults.Rows[rowIndex].Cells[3].Value = v1.ToString("0.000"); dgvResults.Rows[rowIndex].Cells[4].Value = v2.ToString("0.000"); dgvResults.Rows[rowIndex].Cells[5].Value = acc.ToString("0.000"); // 设置加速度单元格颜色 if (acc > 0) dgvResults.Rows[rowIndex].Cells[5].Style.BackColor = Color.FromArgb(230, 255, 230); else if (acc < 0) dgvResults.Rows[rowIndex].Cells[5].Style.BackColor = Color.FromArgb(255, 230, 230); // 自动滚动到最后一行 if (dgvResults.Rows.Count > 0) dgvResults.FirstDisplayedScrollingRowIndex = dgvResults.Rows.Count - 1; // 更新图表 UpdatePlot(acc); } catch (Exception ex) { MessageBox.Show($"数据处理错误: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); } } // 解析双精度值 private double ParseDouble(string value) { return double.Parse(value.Trim(), CultureInfo.InvariantCulture); } // 更新图表数据 private void UpdatePlot(double acc) { accelerationData.Add(acc); dataPointCount++; // 限制显示点数 if (accelerationData.Count > 100) accelerationData.RemoveAt(0); // 更新图表 chartPlot.Series["加速度"].Points.Clear(); for (int i = 0; i < accelerationData.Count; i++) { chartPlot.Series["加速度"].Points.AddXY(i + 1, accelerationData[i]); } // 更新标题 chartPlot.Titles[0].Text = $"加速度变化曲线 (共 {dataPointCount} 个数据点)"; // 自动调整Y轴范围 if (accelerationData.Count > 0) { double min = accelerationData[0]; double max = accelerationData[0]; foreach (double val in accelerationData) { if (val < min) min = val; if (val > max) max = val; } double range = max - min; if (range < 0.1) range = 0.1; chartPlot.ChartAreas["MainArea"].AxisY.Minimum = min - range * 0.1; chartPlot.ChartAreas["MainArea"].AxisY.Maximum = max + range * 0.1; } // 更新X轴范围 chartPlot.ChartAreas["MainArea"].AxisX.Minimum = 1; chartPlot.ChartAreas["MainArea"].AxisX.Maximum = accelerationData.Count > 0 ? accelerationData.Count + 1 : 10; chartPlot.Invalidate(); } // 导出数据到Excel private void btnExport_Click(object sender, EventArgs e) { if (dgvResults.Rows.Count == 0) { MessageBox.Show("没有数据可导出!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); return; } using (var sfd = new System.Windows.Forms.SaveFileDialog()) { sfd.Filter = "Excel 文件|*.xlsx"; sfd.Title = "保存实验数据"; sfd.FileName = $"气垫导轨实验_{DateTime.Now:yyyyMMdd_HHmmss}.xlsx"; if (sfd.ShowDialog() == DialogResult.OK) { try { ExperimentExcelExporter.ExportToExcel( dgvResults, accelerationData, sfd.FileName, chartPlot); MessageBox.Show("数据导出成功!", "导出完成", MessageBoxButtons.OK, MessageBoxIcon.Information); // 尝试打开Excel文件 try { Process.Start(new ProcessStartInfo { FileName = sfd.FileName, UseShellExecute = true }); } catch { /* 忽略打开错误 */ } } catch (Exception ex) { MessageBox.Show($"导出失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); } } } } // 清除数据 private void btnClear_Click(object sender, EventArgs e) { if (MessageBox.Show("确定要清除所有数据吗?", "确认清除", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes) { dgvResults.Rows.Clear(); accelerationData.Clear(); dataPointCount = 0; experimentStartTime = DateTime.Now; InitializePlot(); } } } // Excel导出工具类 public static class ExperimentExcelExporter { public static void ExportToExcel( DataGridView dgv, List<double> accelerationData, string filePath, Chart chart = null) { using (ExcelPackage excelPackage = new ExcelPackage()) { // 创建实验信息表 CreateInfoSheet(excelPackage, dgv, accelerationData); // 创建数据工作表 CreateDataSheet(excelPackage, dgv); // 创建图表工作表 if (accelerationData.Count > 0) { CreateChartSheet(excelPackage, accelerationData); } // 添加图表图像 if (chart != null) { CreateChartImageSheet(excelPackage, chart); } // 保存文件 FileInfo excelFile = new FileInfo(filePath); excelPackage.SaveAs(excelFile); } } private static void CreateInfoSheet(ExcelPackage excelPackage, DataGridView dgv, List<double> accelerationData) { ExcelWorksheet infoSheet = excelPackage.Workbook.Worksheets.Add("实验信息"); // 标题行 infoSheet.Cells[1, 1].Value = "气垫导轨实验报告"; infoSheet.Cells[1, 1, 1, 6].Merge = true; infoSheet.Cells[1, 1].Style.Font.Bold = true; infoSheet.Cells[1, 1].Style.Font.Size = 16; infoSheet.Cells[1, 1].Style.HorizontalAlignment = ExcelHorizontalAlignment.Center; // 实验信息 infoSheet.Cells[3, 1].Value = "实验时间"; infoSheet.Cells[3, 2].Value = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); infoSheet.Cells[4, 1].Value = "数据点数"; infoSheet.Cells[4, 2].Value = dgv.Rows.Count; infoSheet.Cells[5, 1].Value = "平均加速度"; double avgAcc = CalculateAverage(accelerationData); infoSheet.Cells[5, 2].Value = $"{avgAcc:0.000} m/s²"; infoSheet.Cells[6, 1].Value = "最大加速度"; double maxAcc = CalculateMax(accelerationData); infoSheet.Cells[6, 2].Value = $"{maxAcc:0.000} m/s²"; // 设置列宽 infoSheet.Column(1).Width = 15; infoSheet.Column(2).Width = 25; } private static void CreateDataSheet(ExcelPackage excelPackage, DataGridView dgv) { ExcelWorksheet dataSheet = excelPackage.Workbook.Worksheets.Add("实验数据"); // 添加标题行 for (int i = 0; i < dgv.Columns.Count; i++) { dataSheet.Cells[1, i + 1].Value = dgv.Columns[i].HeaderText; dataSheet.Cells[1, i + 1].Style.Fill.PatternType = ExcelFillStyle.Solid; dataSheet.Cells[1, i + 1].Style.Fill.BackgroundColor.SetColor(Color.LightBlue); dataSheet.Cells[1, i + 1].Style.Font.Bold = true; dataSheet.Cells[1, i + 1].Style.HorizontalAlignment = ExcelHorizontalAlignment.Center; } // 添加数据行 for (int row = 0; row < dgv.Rows.Count; row++) { for (int col = 0; col < dgv.Columns.Count; col++) { var cellValue = dgv.Rows[row].Cells[col].Value; ExcelRange cell = dataSheet.Cells[row + 2, col + 1]; if (col == 0) // 时间列 { cell.Value = cellValue?.ToString(); } else if (double.TryParse(cellValue?.ToString(), out double numValue)) { cell.Value = numValue; cell.Style.Numberformat.Format = "0.000"; // 加速度列特殊样式(假设最后一列是加速度) if (dgv.Columns[col].Name == "Acc" || dgv.Columns[col].HeaderText.Contains("加速度")) { cell.Style.Font.Color.SetColor( numValue > 0 ? Color.DarkGreen : Color.DarkRed); } } else { cell.Value = cellValue?.ToString(); } } } // 自动调整列宽 dataSheet.Cells[dataSheet.Dimension.Address].AutoFitColumns(); // 添加表格边框 using (ExcelRange range = dataSheet.Cells[1, 1, dgv.Rows.Count + 1, dgv.Columns.Count]) { range.Style.Border.Top.Style = ExcelBorderStyle.Thin; range.Style.Border.Bottom.Style = ExcelBorderStyle.Thin; range.Style.Border.Left.Style = ExcelBorderStyle.Thin; range.Style.Border.Right.Style = ExcelBorderStyle.Thin; } } private static void CreateChartSheet(ExcelPackage excelPackage, List<double> accelerationData) { ExcelWorksheet chartSheet = excelPackage.Workbook.Worksheets.Add("加速度曲线"); // 添加标题行 chartSheet.Cells[1, 1].Value = "实验序号"; chartSheet.Cells[1, 2].Value = "加速度 (m/s²)"; // 添加数据 for (int i = 0; i < accelerationData.Count; i++) { chartSheet.Cells[i + 2, 1].Value = i + 1; chartSheet.Cells[i + 2, 2].Value = accelerationData[i]; } // 创建折线图 var chart = chartSheet.Drawings.AddChart("加速度曲线", eChartType.Line) as ExcelLineChart; chart.Title.Text = "加速度变化曲线"; chart.SetPosition(1, 0, 3, 0); chart.SetSize(800, 400); // 添加数据系列 var series = chart.Series.Add( chartSheet.Cells[2, 2, accelerationData.Count + 1, 2], chartSheet.Cells[2, 1, accelerationData.Count + 1, 1]); series.Header = "加速度"; // 设置图表样式 chart.YAxis.Title.Text = "加速度 (m/s²)"; chart.XAxis.Title.Text = "实验序号"; // 添加趋势线 var trendline = series.TrendLines.Add(eTrendLine.Linear); trendline.DisplayEquation = true; } // 修复3: 正确处理Bitmap到Excel图片的转换 private static void CreateChartImageSheet(ExcelPackage excelPackage, Chart winFormsChart) { ExcelWorksheet imageSheet = excelPackage.Workbook.Worksheets.Add("图表图像"); imageSheet.Cells[1, 1].Value = "加速度变化曲线图"; imageSheet.Cells[1, 1].Style.Font.Bold = true; imageSheet.Cells[1, 1].Style.Font.Size = 14; // 使用内存流保存图像 using (Bitmap chartImage = new Bitmap(winFormsChart.Width, winFormsChart.Height)) using (MemoryStream ms = new MemoryStream()) { winFormsChart.DrawToBitmap(chartImage, new Rectangle(0, 0, winFormsChart.Width, winFormsChart.Height)); chartImage.Save(ms, System.Drawing.Imaging.ImageFormat.Png); ms.Position = 0; // 添加图像到Excel var picture = imageSheet.Drawings.AddPicture("加速度图表", ms); picture.SetPosition(10, 5); picture.SetSize(800, 400); } } private static double CalculateAverage(List<double> data) { if (data.Count == 0) return 0; double sum = 0; foreach (double val in data) sum += val; return sum / data.Count; } private static double CalculateMax(List<double> data) { if (data.Count == 0) return 0; double max = data[0]; foreach (double val in data) if (Math.Abs(val) > Math.Abs(max)) max = val; return max; } } // 程序入口 static class Program { [STAThread] static void Main() { // 初始化 EPPlus 许可(兼容 EPPlus 8+ 与旧版本) InitEpplusLicense(); Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new MainForm()); } // 使用反射以同时兼容 EPPlus 8+ 的 ExcelPackage.License 和 旧版的 ExcelPackage.LicenseContext private static void InitEpplusLicense() { try { Type excelPackageType = typeof(ExcelPackage); // 优先尝试 EPPlus 8+:ExcelPackage.License (type OfficeOpenXml.License) PropertyInfo licenseProp = excelPackageType.GetProperty("License", BindingFlags.Static | BindingFlags.Public); if (licenseProp != null && licenseProp.CanWrite) { Type licenseType = licenseProp.PropertyType; object licenseInstance = Activator.CreateInstance(licenseType); PropertyInfo contextProp = licenseType.GetProperty("Context", BindingFlags.Instance | BindingFlags.Public); if (contextProp != null) { Type contextEnumType = contextProp.PropertyType; object nonCommercial = Enum.Parse(contextEnumType, "NonCommercial"); contextProp.SetValue(licenseInstance, nonCommercial); } licenseProp.SetValue(null, licenseInstance); return; } // 回退:旧的静态属性 ExcelPackage.LicenseContext PropertyInfo oldContextProp = excelPackageType.GetProperty("LicenseContext", BindingFlags.Static | BindingFlags.Public); if (oldContextProp != null && oldContextProp.CanWrite) { Type ctxEnumType = oldContextProp.PropertyType; object nonCommercialOld = Enum.Parse(ctxEnumType, "NonCommercial"); oldContextProp.SetValue(null, nonCommercialOld); return; } Debug.WriteLine("未找到可写的 EPPlus 许可 API(License 或 LicenseContext)。"); } catch (Exception ex) { Debug.WriteLine("EPPlus 许可初始化失败: " + ex.Message); } } } } 请你把我修复代码
最新发布
11-17
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值