CadParamPluging/UI/ParamDefinitionForm.cs
2025-12-17 10:04:08 +08:00

298 lines
11 KiB
C#

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Globalization;
using System.Linq;
using System.Windows.Forms;
using CadParamPluging.Common;
namespace CadParamPluging.UI
{
public class ParamDefinitionForm : Form
{
private readonly bool _isEdit;
private readonly TextBox _txtKey;
private readonly TextBox _txtLabel;
private readonly ComboBox _cbType;
private readonly CheckBox _chkRequired;
private readonly TextBox _txtHint;
private readonly TextBox _txtDefault;
private readonly TextBox _txtMin;
private readonly TextBox _txtMax;
private readonly TextBox _txtEnumOptions;
private readonly TextBox _txtGroup;
private readonly NumericUpDown _numOrder;
public ParamDefinition Result { get; private set; }
public ParamDefinitionForm(string title, ParamDefinition initial, bool isEdit)
{
_isEdit = isEdit;
Text = title;
Size = new Size(520, 520);
StartPosition = FormStartPosition.CenterParent;
FormBorderStyle = FormBorderStyle.FixedDialog;
MaximizeBox = false;
MinimizeBox = false;
ShowInTaskbar = false;
var def = (initial ?? new ParamDefinition()).Clone();
def.Normalize();
var layout = new TableLayoutPanel
{
Dock = DockStyle.Fill,
Padding = new Padding(10),
ColumnCount = 2,
RowCount = 12,
AutoSize = true
};
layout.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));
layout.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100f));
_txtKey = new TextBox { Width = 260, Text = def.Key };
_txtLabel = new TextBox { Width = 260, Text = def.Label };
_cbType = new ComboBox { DropDownStyle = ComboBoxStyle.DropDownList, Width = 260 };
_cbType.Items.AddRange(Enum.GetNames(typeof(ParamValueType)));
_cbType.SelectedItem = def.Type.ToString();
_cbType.SelectedIndexChanged += (_, __) => UpdateTypeUi();
_chkRequired = new CheckBox { Text = "必填", Checked = def.Required, AutoSize = true };
_txtHint = new TextBox { Width = 260, Text = def.Hint, Multiline = true, Height = 60, ScrollBars = ScrollBars.Vertical };
_txtDefault = new TextBox { Width = 260, Text = def.DefaultValue };
_txtMin = new TextBox { Width = 260, Text = def.Min };
_txtMax = new TextBox { Width = 260, Text = def.Max };
_txtEnumOptions = new TextBox { Width = 260, Height = 120, Multiline = true, ScrollBars = ScrollBars.Vertical };
_txtEnumOptions.Text = string.Join(Environment.NewLine, def.EnumOptions ?? new List<string>());
_txtGroup = new TextBox { Width = 260, Text = def.Group };
_numOrder = new NumericUpDown { Width = 120, Minimum = -100000, Maximum = 100000, Value = def.Order };
if (_isEdit)
{
_txtKey.ReadOnly = true;
}
var row = 0;
layout.Controls.Add(new Label { Text = "Key(英文)", AutoSize = true, TextAlign = ContentAlignment.MiddleLeft }, 0, row);
layout.Controls.Add(_txtKey, 1, row++);
layout.Controls.Add(new Label { Text = "Label(中文)", AutoSize = true, TextAlign = ContentAlignment.MiddleLeft }, 0, row);
layout.Controls.Add(_txtLabel, 1, row++);
layout.Controls.Add(new Label { Text = "类型", AutoSize = true, TextAlign = ContentAlignment.MiddleLeft }, 0, row);
layout.Controls.Add(_cbType, 1, row++);
layout.Controls.Add(new Label { Text = "", AutoSize = true }, 0, row);
layout.Controls.Add(_chkRequired, 1, row++);
layout.Controls.Add(new Label { Text = "提示/填写方式", AutoSize = true, TextAlign = ContentAlignment.MiddleLeft }, 0, row);
layout.Controls.Add(_txtHint, 1, row++);
layout.Controls.Add(new Label { Text = "默认值", AutoSize = true, TextAlign = ContentAlignment.MiddleLeft }, 0, row);
layout.Controls.Add(_txtDefault, 1, row++);
layout.Controls.Add(new Label { Text = "最小值(数字)", AutoSize = true, TextAlign = ContentAlignment.MiddleLeft }, 0, row);
layout.Controls.Add(_txtMin, 1, row++);
layout.Controls.Add(new Label { Text = "最大值(数字)", AutoSize = true, TextAlign = ContentAlignment.MiddleLeft }, 0, row);
layout.Controls.Add(_txtMax, 1, row++);
layout.Controls.Add(new Label { Text = "枚举选项(每行一项)", AutoSize = true, TextAlign = ContentAlignment.MiddleLeft }, 0, row);
layout.Controls.Add(_txtEnumOptions, 1, row++);
layout.Controls.Add(new Label { Text = "分组", AutoSize = true, TextAlign = ContentAlignment.MiddleLeft }, 0, row);
layout.Controls.Add(_txtGroup, 1, row++);
layout.Controls.Add(new Label { Text = "排序", AutoSize = true, TextAlign = ContentAlignment.MiddleLeft }, 0, row);
layout.Controls.Add(_numOrder, 1, row++);
var btnPanel = new FlowLayoutPanel
{
Dock = DockStyle.Fill,
FlowDirection = FlowDirection.RightToLeft,
AutoSize = true
};
var btnCancel = new Button { Text = "取消", DialogResult = DialogResult.Cancel };
var btnOk = new Button { Text = "确定" };
btnOk.Click += (_, __) => OnOk();
btnPanel.Controls.Add(btnCancel);
btnPanel.Controls.Add(btnOk);
layout.Controls.Add(btnPanel, 0, row);
layout.SetColumnSpan(btnPanel, 2);
Controls.Add(layout);
AcceptButton = btnOk;
CancelButton = btnCancel;
UpdateTypeUi();
}
private void UpdateTypeUi()
{
var type = GetSelectedType();
var showEnum = type == ParamValueType.Enum;
_txtEnumOptions.Enabled = showEnum;
var showMinMax = type == ParamValueType.Int || type == ParamValueType.Double;
_txtMin.Enabled = showMinMax;
_txtMax.Enabled = showMinMax;
}
private ParamValueType GetSelectedType()
{
var selected = _cbType.SelectedItem as string;
if (Enum.TryParse(selected, out ParamValueType t))
{
return t;
}
return ParamValueType.String;
}
private void OnOk()
{
var key = (_txtKey.Text ?? string.Empty).Trim();
var label = (_txtLabel.Text ?? string.Empty).Trim();
if (key.Length == 0)
{
MessageBox.Show(this, "Key 不能为空。", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
if (!IsValidKey(key))
{
MessageBox.Show(this, "Key 格式不合法:必须以字母开头,仅允许字母/数字/下划线。", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
if (label.Length == 0)
{
MessageBox.Show(this, "Label 不能为空。", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
var type = GetSelectedType();
var def = new ParamDefinition
{
Key = key,
Label = label,
Type = type,
Required = _chkRequired.Checked,
Hint = (_txtHint.Text ?? string.Empty).Trim(),
DefaultValue = (_txtDefault.Text ?? string.Empty).Trim(),
Min = (_txtMin.Text ?? string.Empty).Trim(),
Max = (_txtMax.Text ?? string.Empty).Trim(),
Group = (_txtGroup.Text ?? string.Empty).Trim(),
Order = (int)_numOrder.Value,
EnumOptions = ParseLines(_txtEnumOptions.Text)
};
def.Normalize();
var err = ValidateDefinition(def);
if (err != null)
{
MessageBox.Show(this, err, "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
Result = def;
DialogResult = DialogResult.OK;
Close();
}
private static List<string> ParseLines(string text)
{
if (string.IsNullOrWhiteSpace(text))
{
return new List<string>();
}
return text
.Replace("\r\n", "\n")
.Replace("\r", "\n")
.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries)
.Select(s => (s ?? string.Empty).Trim())
.Where(s => s.Length > 0)
.ToList();
}
private static bool IsValidKey(string key)
{
if (string.IsNullOrWhiteSpace(key))
{
return false;
}
if (!char.IsLetter(key[0]))
{
return false;
}
foreach (var ch in key)
{
if (char.IsLetterOrDigit(ch) || ch == '_')
{
continue;
}
return false;
}
return true;
}
private static string ValidateDefinition(ParamDefinition def)
{
if (def == null)
{
return "参数定义不能为空。";
}
if (def.Type == ParamValueType.Enum && (def.EnumOptions == null || def.EnumOptions.Count == 0))
{
return "Enum 类型必须提供至少 1 个枚举选项。";
}
if ((def.Type == ParamValueType.Int || def.Type == ParamValueType.Double) && (def.Min.Length > 0 || def.Max.Length > 0))
{
if (!TryParseDouble(def.Min, out var min, allowEmpty: true))
{
return "最小值不是有效数字。";
}
if (!TryParseDouble(def.Max, out var max, allowEmpty: true))
{
return "最大值不是有效数字。";
}
if (def.Min.Length > 0 && def.Max.Length > 0 && min > max)
{
return "最小值不能大于最大值。";
}
}
return null;
}
private static bool TryParseDouble(string text, out double value, bool allowEmpty)
{
value = 0;
if (string.IsNullOrWhiteSpace(text))
{
return allowEmpty;
}
var s = text.Trim();
if (double.TryParse(s, NumberStyles.Float, CultureInfo.InvariantCulture, out value))
{
return true;
}
return double.TryParse(s, NumberStyles.Float, CultureInfo.CurrentCulture, out value);
}
}
}