using Newtonsoft.Json; using StandardScene.Model; using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.IO; using System.Linq; using System.Windows.Forms; namespace LoopViewerApp { public partial class LoopViewer : Form { private readonly string jsonPath = Path.Combine(Application.StartupPath, "tasklist.json"); private List tasks = new List(); // -1 表示新增模式;>=0 表示正在编辑对应索引 private int editingIndex = -1; public LoopViewer() { InitializeComponent(); if (LicenseManager.UsageMode == LicenseUsageMode.Designtime) return; // 应用 ChargeStationManagementForm 风格的运行时样式调整 ApplyChargeStyle(); EnsureComboItems(); // 启用多选并绑定右键菜单与 Delete 键删除功能 try { if (lstTasks != null) { lstTasks.MultiSelect = true; // 右键菜单:删除 var ctx = new ContextMenuStrip(); ctx.Items.Add("删除", null, (s, e) => OnDeleteSelectedTasks()); lstTasks.ContextMenuStrip = ctx; // 键盘删除键绑定 lstTasks.KeyDown += lstTasks_KeyDown; } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"LoopViewer context menu init error: {ex}"); } try { InitOrLoadJson(); RenderListView(); UpdateSaveButtonText(); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"LoopViewer initialization error: {ex}"); } } private void lstTasks_KeyDown(object sender, KeyEventArgs e) { try { if (e.KeyCode == Keys.Delete) { OnDeleteSelectedTasks(); e.Handled = true; } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"lstTasks_KeyDown error: {ex}"); } } /// /// 删除 ListView 中选中的任务(支持多选) /// private void OnDeleteSelectedTasks() { try { if (lstTasks == null || lstTasks.SelectedIndices.Count == 0) return; // 收集被选中的索引并按降序删除,避免索引移动问题 var selectedIndices = lstTasks.SelectedIndices.Cast().OrderByDescending(i => i).ToList(); // 构造确认提示 string prompt; if (selectedIndices.Count == 1) { int idx = selectedIndices[0]; if (idx >= 0 && idx < tasks.Count) prompt = $"确认删除任务 ID={tasks[idx].Id}?"; else prompt = "确认删除选中任务?"; } else { prompt = $"确认删除所选 {selectedIndices.Count} 个任务?"; } if (MessageBox.Show(prompt, "确认", MessageBoxButtons.YesNo, MessageBoxIcon.Question) != DialogResult.Yes) return; // 删除任务 foreach (var idx in selectedIndices) { if (idx >= 0 && idx < tasks.Count) { tasks.RemoveAt(idx); } } // 如果被删除项包含当前正在编辑的项,退出编辑状态 if (editingIndex >= 0) { if (editingIndex >= tasks.Count || selectedIndices.Any(i => i == editingIndex)) { editingIndex = -1; UpdateSaveButtonText(); ClearPanelInputs(); } else { // 重新计算编辑索引在删除后的新位置 int removedBefore = selectedIndices.Count(i => i < editingIndex); editingIndex -= removedBefore; } } // 持久化并刷新列表视图 Save(); RenderListView(); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"OnDeleteSelectedTasks error: {ex}"); MessageBox.Show("删除失败:" + ex.Message); } } /// /// 将 LoopViewer 的运行时样式调整为与 ChargeStationManagementForm 接近的视觉风格: /// - 全局字体设为微软雅黑 /// - 表头暖色替换为蓝色沉稳风格(和充电界面一致) /// - 按钮字号、背景色与充电界面保持一致(保存/删除/取消) /// - 列表视图设置为整行选择、无边框、交替背景等 /// 注意:不修改 Designer 文件,仅在运行时统一控件表现,避免破坏设计器生成代码。 /// private void ApplyChargeStyle() { try { // 窗体级设置 this.StartPosition = FormStartPosition.CenterScreen; this.MinimumSize = new System.Drawing.Size(1327, 738); this.Font = new Font("微软雅黑", 9F, FontStyle.Regular); // 调整 ListView(如果存在) if (lstTasks != null) { lstTasks.View = View.Details; lstTasks.FullRowSelect = true; lstTasks.GridLines = false; lstTasks.HeaderStyle = ColumnHeaderStyle.Nonclickable; lstTasks.OwnerDraw = true; // 已有自定义绘制 lstTasks.BackColor = Color.White; lstTasks.ForeColor = Color.FromArgb(33, 33, 33); // 多选由初始化时控制(这里不强制) } // 下拉框统一字体 if (cmbTaskKind != null) cmbTaskKind.Font = new Font("微软雅黑", 10F, FontStyle.Regular); if (cmbStartType != null) cmbStartType.Font = new Font("微软雅黑", 10F, FontStyle.Regular); // 数值输入框统一字体 if (numCurrent != null) numCurrent.Font = new Font("微软雅黑", 10F, FontStyle.Regular); if (numTarget != null) numTarget.Font = new Font("微软雅黑", 10F, FontStyle.Regular); if (numTraffic != null) numTraffic.Font = new Font("微软雅黑", 10F, FontStyle.Regular); if (numPriority != null) numPriority.Font = new Font("微软雅黑", 10F, FontStyle.Regular); // 标签字体统一 if (lblEditingId != null) lblEditingId.Font = new Font("微软雅黑", 10F, FontStyle.Bold); // 按钮风格:与 ChargeStationManagementForm 保持一致的视觉优先级 if (btnSave != null) { btnSave.BackColor = Color.LightBlue; btnSave.ForeColor = Color.Black; btnSave.Font = new Font("微软雅黑", 11F, FontStyle.Bold); btnSave.FlatStyle = FlatStyle.Flat; } if (btnDelete != null) { btnDelete.BackColor = Color.LightCoral; btnDelete.ForeColor = Color.Black; btnDelete.Font = new Font("微软雅黑", 11F, FontStyle.Bold); btnDelete.FlatStyle = FlatStyle.Flat; } if (btnCancel != null) { btnCancel.BackColor = SystemColors.Control; btnCancel.ForeColor = Color.Black; btnCancel.Font = new Font("微软雅黑", 11F, FontStyle.Regular); btnCancel.FlatStyle = FlatStyle.Flat; } // 如果存在额外的操作按钮(例如在面板上),尝试统一风格(容错) foreach (Control ctrl in this.Controls) { if (ctrl is Panel pnl) { pnl.Padding = new Padding(12); } else if (ctrl is Button btn) { // 已设置主要按钮,其他按钮使用中性风格 if (btn == btnSave || btn == btnDelete || btn == btnCancel) continue; btn.Font = new Font("微软雅黑", 10F, FontStyle.Regular); } } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"ApplyChargeStyle error: {ex}"); } } private void EnsureComboItems() { try { if (cmbTaskKind != null && cmbTaskKind.Items.Count == 0) { cmbTaskKind.Items.AddRange(new object[] { "Loop", "BranchPoint", "JoinPoint" }); cmbTaskKind.SelectedIndex = 0; } if (cmbStartType != null && cmbStartType.Items.Count == 0) { cmbStartType.Items.AddRange(new object[] { "Api", "Plc", "ButtonBox", "AutoLoop" }); cmbStartType.SelectedIndex = 3; } // 确保下拉框字体一致(防止 Designer 未设置) if (cmbTaskKind != null) cmbTaskKind.Font = new Font("微软雅黑", 10F, FontStyle.Regular); if (cmbStartType != null) cmbStartType.Font = new Font("微软雅黑", 10F, FontStyle.Regular); } catch { } } #region 初始化/加载 private void InitOrLoadJson() { try { if (!File.Exists(jsonPath)) File.WriteAllText(jsonPath, "[]"); var text = File.ReadAllText(jsonPath); tasks = JsonConvert.DeserializeObject>(text) ?? new List(); } catch (Exception ex) { tasks = new List(); System.Diagnostics.Debug.WriteLine($"Load tasks failed: {ex}"); } } #endregion #region ID 自增逻辑 /// /// 获取下一个可用的任务ID(当前最大ID + 1) /// /// 新的任务ID private int GetNextTaskId() { if (tasks == null || tasks.Count == 0) return 1; int maxId = tasks.Max(t => t.Id); return maxId + 1; } #endregion #region OwnerDraw 绘制(已按要求:表头加粗黑字 + 醒目底色,选中行为另一种颜色) private void lstTasks_DrawColumnHeader(object sender, DrawListViewColumnHeaderEventArgs e) { try { // 与 ChargeStationManagementForm 表头保持一致的深蓝背景与白色加粗字体 using (var backBrush = new SolidBrush(Color.FromArgb(63, 81, 181))) // 深蓝(与 Charge 界面一致) using (var textBrush = new SolidBrush(Color.White)) // 白色文字 using (var font = new Font("微软雅黑", 9, FontStyle.Bold)) { e.Graphics.FillRectangle(backBrush, e.Bounds); var sf = new StringFormat { LineAlignment = StringAlignment.Center, Alignment = StringAlignment.Near }; var rect = e.Bounds; rect.Inflate(-8, 0); e.Graphics.DrawString(e.Header.Text, font, textBrush, rect, sf); // 分隔线 using (var pen = new Pen(Color.FromArgb(200, 200, 200))) { e.Graphics.DrawLine(pen, e.Bounds.Left, e.Bounds.Bottom - 1, e.Bounds.Right, e.Bounds.Bottom - 1); } } } catch { e.DrawBackground(); e.DrawText(); } } private void lstTasks_DrawItem(object sender, DrawListViewItemEventArgs e) { // 由 DrawSubItem 绘制全部内容以保证每列对齐 } private void lstTasks_DrawSubItem(object sender, DrawListViewSubItemEventArgs e) { try { var item = e.Item; bool selected = item.Selected; Rectangle bounds = e.Bounds; // 选中行颜色:与 ChargeStationManagementForm 保持一致的蓝色强调 Color selectedBack = Color.FromArgb(0, 120, 215); Color selectedFore = Color.White; // 非选中行交替背景 Color evenBack = Color.White; Color oddBack = Color.FromArgb(250, 251, 253); Color normalFore = Color.FromArgb(33, 33, 33); // 填充背景 if (selected) { using (var selBrush = new SolidBrush(selectedBack)) { e.Graphics.FillRectangle(selBrush, bounds); } } else { using (var back = new SolidBrush(e.ItemIndex % 2 == 0 ? evenBack : oddBack)) { e.Graphics.FillRectangle(back, bounds); } } // 绘制文本(加一点内边距) string text = e.SubItem.Text ?? string.Empty; Color fore = selected ? selectedFore : normalFore; TextFormatFlags flags = TextFormatFlags.Left | TextFormatFlags.VerticalCenter; Rectangle textRect = bounds; textRect.Inflate(-6, 0); using (var font = new Font("微软雅黑", 9)) { TextRenderer.DrawText(e.Graphics, text, font, textRect, fore, flags); } } catch { e.DrawBackground(); e.DrawText(); } } #endregion #region 渲染/保存 private void RenderListView() { try { if (lstTasks == null) return; lstTasks.BeginUpdate(); lstTasks.Items.Clear(); foreach (var t in tasks) { var lvi = new ListViewItem(new[] { t.Id.ToString(), // ID 列 t.Kind.ToString(), t.CurrentStationId.ToString(), t.TargetStationId.ToString(), t.TrafficControl.ToString(), t.Priority.ToString(), t.IsViaPoint ? "是" : "否", t.StartType.ToString() }); lstTasks.Items.Add(lvi); } lstTasks.EndUpdate(); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"RenderListView failed: {ex}"); } } private void Save() { try { File.WriteAllText(jsonPath, JsonConvert.SerializeObject(tasks, Formatting.Indented)); } catch (Exception ex) { MessageBox.Show("保存失败:" + ex.Message); } } #endregion #region 按钮事件(在同一界面新增/编辑) private void UpdateSaveButtonText() { if (btnSave != null) { // 文案固定为"保存" btnSave.Text = "保存"; } } private void btnSave_Click(object sender, EventArgs e) { try { // 从面板读取值,直接在界面内编辑/新增 Enum.TryParse(cmbTaskKind?.SelectedItem?.ToString() ?? "Loop", out var kind); Enum.TryParse(cmbStartType?.SelectedItem?.ToString() ?? "AutoLoop", out var st); if (editingIndex >= 0 && editingIndex < tasks.Count) { // 更新模式:保留原有ID var existingTask = tasks[editingIndex]; existingTask.Kind = kind; existingTask.CurrentStationId = (int)(numCurrent?.Value ?? 0); existingTask.TargetStationId = (int)(numTarget?.Value ?? 0); existingTask.TrafficControl = (int)(numTraffic?.Value ?? 0); existingTask.Priority = (int)(numPriority?.Value ?? 1); existingTask.IsViaPoint = chkViaPoint?.Checked ?? false; existingTask.StartType = st; } else { // 新增模式:自动分配新ID var t = new LoopTask { Id = GetNextTaskId(), // 自增ID Kind = kind, CurrentStationId = (int)(numCurrent?.Value ?? 0), TargetStationId = (int)(numTarget?.Value ?? 0), TrafficControl = (int)(numTraffic?.Value ?? 0), Priority = (int)(numPriority?.Value ?? 1), IsViaPoint = chkViaPoint?.Checked ?? false, StartType = st }; tasks.Add(t); } Save(); RenderListView(); // 恢复新增状态 editingIndex = -1; UpdateSaveButtonText(); ClearPanelInputs(); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"btnSave_Click error: {ex}"); MessageBox.Show("操作失败:" + ex.Message); } } private void btnCancel_Click(object sender, EventArgs e) { // 取消编辑,清空面板并回到"添加"模式 editingIndex = -1; UpdateSaveButtonText(); ClearPanelInputs(); } private void btnEdit_Click(object sender, EventArgs e) { try { if (lstTasks == null || lstTasks.SelectedIndices.Count == 0) return; int idx = lstTasks.SelectedIndices[0]; if (idx < 0 || idx >= tasks.Count) return; editingIndex = idx; LoadTaskToPanel(tasks[idx]); UpdateSaveButtonText(); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"btnEdit_Click error: {ex}"); } } private void btnDelete_Click(object sender, EventArgs e) { // 兼容旧的删除按钮:复用统一删除逻辑 OnDeleteSelectedTasks(); } #endregion #region 双击编辑(同面板) private void lstTasks_MouseDoubleClick(object sender, MouseEventArgs e) { try { if (lstTasks == null) return; var item = lstTasks.GetItemAt(e.X, e.Y); if (item == null) return; int idx = item.Index; if (idx < 0 || idx >= tasks.Count) return; editingIndex = idx; LoadTaskToPanel(tasks[idx]); UpdateSaveButtonText(); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"lstTasks_MouseDoubleClick error: {ex}"); } } #endregion #region 辅助:面板读写 private void LoadTaskToPanel(LoopTask t) { if (t == null) return; try { // 显示当前编辑的任务ID(只读显示) if (lblEditingId != null) lblEditingId.Text = $"编辑任务 ID: {t.Id}"; if (cmbTaskKind != null) cmbTaskKind.SelectedItem = t.Kind.ToString(); if (numCurrent != null) numCurrent.Value = Math.Max(numCurrent.Minimum, Math.Min(numCurrent.Maximum, t.CurrentStationId)); if (numTarget != null) numTarget.Value = Math.Max(numTarget.Minimum, Math.Min(numTarget.Maximum, t.TargetStationId)); if (numTraffic != null) numTraffic.Value = Math.Max(numTraffic.Minimum, Math.Min(numTraffic.Maximum, t.TrafficControl)); if (numPriority != null) numPriority.Value = Math.Max(numPriority.Minimum, Math.Min(numPriority.Maximum, t.Priority)); if (chkViaPoint != null) chkViaPoint.Checked = t.IsViaPoint; if (cmbStartType != null) cmbStartType.SelectedItem = t.StartType.ToString(); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"LoadTaskToPanel error: {ex}"); } } private void ClearPanelInputs() { try { // 清除编辑ID显示 if (lblEditingId != null) lblEditingId.Text = "新增任务"; if (cmbTaskKind != null) cmbTaskKind.SelectedIndex = 0; if (numCurrent != null) numCurrent.Value = 0; if (numTarget != null) numTarget.Value = 0; if (numTraffic != null) numTraffic.Value = 0; if (numPriority != null) numPriority.Value = 1; if (chkViaPoint != null) chkViaPoint.Checked = false; if (cmbStartType != null) cmbStartType.SelectedIndex = 3; } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"ClearPanelInputs error: {ex}"); } } #endregion } }