Files
StandardSence/StandardScene.Core/Chained/LoopViewer.cs
T
2026-06-14 11:19:15 +08:00

595 lines
22 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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<LoopTask> tasks = new List<LoopTask>();
// -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}");
}
}
/// <summary>
/// 删除 ListView 中选中的任务(支持多选)
/// </summary>
private void OnDeleteSelectedTasks()
{
try
{
if (lstTasks == null || lstTasks.SelectedIndices.Count == 0)
return;
// 收集被选中的索引并按降序删除,避免索引移动问题
var selectedIndices = lstTasks.SelectedIndices.Cast<int>().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);
}
}
/// <summary>
/// 将 LoopViewer 的运行时样式调整为与 ChargeStationManagementForm 接近的视觉风格:
/// - 全局字体设为微软雅黑
/// - 表头暖色替换为蓝色沉稳风格(和充电界面一致)
/// - 按钮字号、背景色与充电界面保持一致(保存/删除/取消)
/// - 列表视图设置为整行选择、无边框、交替背景等
/// 注意:不修改 Designer 文件,仅在运行时统一控件表现,避免破坏设计器生成代码。
/// </summary>
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<List<LoopTask>>(text) ?? new List<LoopTask>();
}
catch (Exception ex)
{
tasks = new List<LoopTask>();
System.Diagnostics.Debug.WriteLine($"Load tasks failed: {ex}");
}
}
#endregion
#region ID 自增逻辑
/// <summary>
/// 获取下一个可用的任务ID(当前最大ID + 1)
/// </summary>
/// <returns>新的任务ID</returns>
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<TaskKind>(cmbTaskKind?.SelectedItem?.ToString() ?? "Loop", out var kind);
Enum.TryParse<TaskStartType>(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
}
}