using System; using System; using System.Collections.Generic; using System.Drawing; using System.Globalization; using System.Linq; using System.Windows.Forms; using CadParamPluging.Common; using Autodesk.AutoCAD.ApplicationServices; using Autodesk.AutoCAD.EditorInput; namespace CadParamPluging.UI { public class DrawingParamsForm : Form { private readonly TableLayoutPanel _grid; private readonly Dictionary _controlsByKey; private readonly Dictionary _defsByControlKey; private readonly ParamCatalog _catalog; private readonly TemplateSchemaDefinition _schema; private readonly DropdownOptions _dropdownOptions; private readonly ToolTip _toolTip; private readonly TabControl _notePreviewTabs; private readonly RichTextBox _rtbNoteNumbered; private readonly RichTextBox _rtbNoteRendered; private Dictionary _occByIndex; // 最小壁厚T自动计算相关 private bool _canAutoCalcMinWallThickness; private Control _ctrlMinWallThickness; private const string KeyOuterDiameter1 = "OuterDiameter1"; private const string KeyOuterDiameter1TolMinus = "OuterDiameter1TolMinus"; private const string KeyInnerDiameter2 = "InnerDiameter2"; private const string KeyInnerDiameter2TolPlus = "InnerDiameter2TolPlus"; private const string KeyMinWallThickness = "MinWallThickness"; public ParamBag Result { get; private set; } private ParamBag _loadedDefaults; // 新增:外部传入的默认值 public DrawingParamsForm(ParamCatalog catalog, TemplateSchemaDefinition schema, ParamBag loadedDefaults = null, DropdownOptions dropdownOptions = null) { _catalog = (catalog ?? ParamCatalog.CreateDefault()).Clone(); _catalog.Normalize(); _schema = schema; _loadedDefaults = loadedDefaults; // 存储传入的默认值 _dropdownOptions = (dropdownOptions ?? DropdownOptions.CreateDefault()).Clone(); _dropdownOptions.Normalize(); Text = "填写参数"; Size = new Size(980, 680); MinimumSize = new Size(900, 620); StartPosition = FormStartPosition.CenterParent; FormBorderStyle = FormBorderStyle.Sizable; MaximizeBox = true; MinimizeBox = true; ShowInTaskbar = false; _controlsByKey = new Dictionary(StringComparer.OrdinalIgnoreCase); _defsByControlKey = new Dictionary(StringComparer.OrdinalIgnoreCase); _toolTip = new ToolTip(); var root = new TableLayoutPanel { Dock = DockStyle.Fill, Padding = new Padding(10), ColumnCount = 1, RowCount = 3 }; root.RowStyles.Add(new RowStyle(SizeType.AutoSize)); root.RowStyles.Add(new RowStyle(SizeType.Percent, 100f)); root.RowStyles.Add(new RowStyle(SizeType.AutoSize)); var header = new Label { AutoSize = true, ForeColor = Color.DarkBlue, Text = BuildHeaderText(schema) }; var scrollPanel = new Panel { Dock = DockStyle.Fill, AutoScroll = true }; _notePreviewTabs = new TabControl { Dock = DockStyle.Fill }; _rtbNoteNumbered = new RichTextBox { Dock = DockStyle.Fill, ReadOnly = true, HideSelection = false }; _rtbNoteRendered = new RichTextBox { Dock = DockStyle.Fill, ReadOnly = true, HideSelection = false }; var tabNumbered = new TabPage { Text = "附注编号预览" }; tabNumbered.Controls.Add(_rtbNoteNumbered); var tabRendered = new TabPage { Text = "填充值预览" }; tabRendered.Controls.Add(_rtbNoteRendered); _notePreviewTabs.TabPages.Add(tabNumbered); _notePreviewTabs.TabPages.Add(tabRendered); _grid = new TableLayoutPanel { Dock = DockStyle.Top, AutoSize = true, ColumnCount = 2, RowCount = 1, Padding = new Padding(0) }; _grid.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize)); _grid.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100f)); BuildFields(); scrollPanel.Controls.Add(_grid); var split = new SplitContainer { Dock = DockStyle.Fill, Orientation = Orientation.Vertical }; split.Panel1.Controls.Add(scrollPanel); split.Panel2.Controls.Add(_notePreviewTabs); split.SizeChanged += (_, __) => EnsureValidSplitterDistance(split); var buttons = 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(); buttons.Controls.Add(btnCancel); buttons.Controls.Add(btnOk); root.Controls.Add(header, 0, 0); root.Controls.Add(split, 0, 1); root.Controls.Add(buttons, 0, 2); Controls.Add(root); AcceptButton = btnOk; CancelButton = btnCancel; Shown += (_, __) => { split.Panel1MinSize = 360; split.Panel2MinSize = 280; EnsureValidSplitterDistance(split); UpdateNotePreviews(); CheckMinWallThicknessAutoCalc(); }; } private static void EnsureValidSplitterDistance(SplitContainer split) { if (split == null) { return; } // SplitterDistance must be within [Panel1MinSize, Width - Panel2MinSize - SplitterWidth] var width = split.Width; if (width <= 0) { return; } // If container is too narrow, collapse preview panel to avoid throwing. var required = split.Panel1MinSize + split.Panel2MinSize + split.SplitterWidth + 8; if (width < required) { split.Panel2Collapsed = true; return; } split.Panel2Collapsed = false; var min = split.Panel1MinSize; var max = width - split.Panel2MinSize - split.SplitterWidth; if (max < min) { // Too narrow: keep it at minimum to avoid exception. max = min; } var desired = (int)(width * 0.6); if (desired < min) desired = min; if (desired > max) desired = max; try { split.SplitterDistance = desired; } catch { // ignore } } private string BuildHeaderText(TemplateSchemaDefinition schema) { if (schema == null) { return "模板参数未配置。"; } var name = string.IsNullOrWhiteSpace(schema.DisplayName) ? "(未命名模板)" : schema.DisplayName; return string.Format( "模板: {0} | {1}/{2}/{3}/{4}", name, _dropdownOptions.GetDisplayName(DropdownOptionCategory.DeliveryStatus, schema.ProjectType), _dropdownOptions.GetDisplayName(DropdownOptionCategory.ProcessMethod, schema.DrawingType), _dropdownOptions.GetDisplayName(DropdownOptionCategory.StructuralFeature, schema.SheetSize), _dropdownOptions.GetDisplayName(DropdownOptionCategory.SpecialCondition, schema.Scale)); } private void BuildFields() { _occByIndex = NoteTemplateEngine.ParseOccurrences(_schema?.NoteTemplateText ?? string.Empty) .Where(o => o != null) .GroupBy(o => o.Index) .Select(g => g.First()) .ToDictionary(o => o.Index, o => o); var forgingDefs = new List(); var partDefs = new List(); var seenMain = new HashSet(StringComparer.OrdinalIgnoreCase); var partKeySet = new HashSet( (_schema?.SelectedPartParamKeys ?? new List()) .Where(s => !string.IsNullOrWhiteSpace(s)) .Select(s => s.Trim()), StringComparer.OrdinalIgnoreCase); foreach (var key in (_schema?.SelectedParamKeys ?? new List())) { if (string.IsNullOrWhiteSpace(key)) { continue; } var def = _catalog.FindByKey(key); if (def == null) { continue; } if (string.Equals(def.Group ?? string.Empty, "备注参数", StringComparison.OrdinalIgnoreCase)) { // 备注参数只在附注里填写 continue; } if (!seenMain.Add(def.Key)) { continue; } if (partKeySet.Contains(def.Key)) { partDefs.Add(def); } else { forgingDefs.Add(def); } } var noteBindings = (_schema?.NoteBindings ?? new List()) .Where(b => b != null && b.Index > 0 && !string.IsNullOrWhiteSpace(b.ParamKey)) .OrderBy(b => b.Index) .ToList(); if (forgingDefs.Count == 0 && partDefs.Count == 0 && noteBindings.Count == 0) { var rowIdx = _grid.RowCount++; _grid.RowStyles.Add(new RowStyle(SizeType.AutoSize)); _grid.Controls.Add(new Label { AutoSize = true, Text = "(该模板未绑定任何参数)" }, 0, rowIdx); _grid.SetColumnSpan(_grid.Controls[_grid.Controls.Count - 1], 2); return; } if (forgingDefs.Count > 0) { AddSectionHeader("锻件尺寸"); foreach (var def in forgingDefs) { AddField(def, def.Key, def.Label, null); } } if (partDefs.Count > 0) { AddSectionHeader("零件尺寸"); foreach (var def in partDefs) { AddField(def, def.Key, def.Label, null); } } if (noteBindings.Count > 0) { AddSectionHeader("备注参数(附注)"); foreach (var b in noteBindings) { var baseKey = (b.ParamKey ?? string.Empty).Trim(); var def = _catalog.FindByKey(baseKey); if (def == null) { continue; } // If same ParamKey is used by multiple placeholders, give each placeholder its own value key. var valueKey = baseKey; var dupCount = noteBindings.Count(x => string.Equals((x.ParamKey ?? string.Empty).Trim(), baseKey, StringComparison.OrdinalIgnoreCase)); if (dupCount > 1) { valueKey = string.Format("{0}@{1}", baseKey, b.Index); } var labelText = string.Format("{0} (占位符: #{1})", def.Label, b.Index); var tip = BuildOccurrenceTip(b.Index); AddField(def, valueKey, labelText, tip); } } // 收集所有已绑定的参数 key var boundKeys = new HashSet(StringComparer.OrdinalIgnoreCase); foreach (var key in (_schema?.SelectedParamKeys ?? new List())) { if (!string.IsNullOrWhiteSpace(key)) { boundKeys.Add(key.Trim()); } } foreach (var b in noteBindings) { var key = (b.ParamKey ?? string.Empty).Trim(); if (key.Length > 0) { boundKeys.Add(key); } } // 查找必填但未绑定的参数 var unboundRequiredDefs = (_catalog.Items ?? Enumerable.Empty()) .Where(p => p != null && p.Required && !boundKeys.Contains(p.Key ?? string.Empty)) .OrderBy(p => p.Order) .ToList(); if (unboundRequiredDefs.Count > 0) { AddSectionHeader("其他必填参数"); foreach (var def in unboundRequiredDefs) { AddField(def, def.Key, def.Label, null); } } } private string BuildOccurrenceTip(int placeholderIndex) { if (_occByIndex == null) { return null; } if (!_occByIndex.TryGetValue(placeholderIndex, out var o) || o == null) { return null; } var line = o.LineText ?? string.Empty; var col = o.ColumnInLine; if (col < 0) col = 0; if (col >= line.Length) { return string.Format("第{0}行: 【#{1}】", o.LineNumber, placeholderIndex); } const int ctx = 18; var start = Math.Max(0, col - ctx); var end = Math.Min(line.Length, col + 1 + ctx); var prefix = start > 0 ? "…" : string.Empty; var suffix = end < line.Length ? "…" : string.Empty; var left = line.Substring(start, col - start); var right = line.Substring(col + 1, end - (col + 1)); var snippet = prefix + left + string.Format("【#{0}】", placeholderIndex) + right + suffix; return string.Format("第{0}行: {1}", o.LineNumber, snippet); } private void AddSectionHeader(string text) { var rowIdx = _grid.RowCount++; _grid.RowStyles.Add(new RowStyle(SizeType.AutoSize)); var lbl = new Label { AutoSize = true, ForeColor = Color.DarkBlue, Text = text, Padding = new Padding(0, 10, 0, 4) }; _grid.Controls.Add(lbl, 0, rowIdx); _grid.SetColumnSpan(lbl, 2); } private void AddField(ParamDefinition def, string controlKey, string labelText, string extraToolTip) { var rowIdx = _grid.RowCount++; _grid.RowStyles.Add(new RowStyle(SizeType.AutoSize)); if (string.IsNullOrWhiteSpace(labelText)) { labelText = def.Label; } if (def.Required) { labelText += " *"; } var lbl = new Label { AutoSize = true, Text = labelText, TextAlign = ContentAlignment.MiddleLeft, Padding = new Padding(0, 6, 8, 6) }; var ctrl = CreateControl(def, controlKey); ctrl.Width = 260; if (!string.IsNullOrWhiteSpace(def.Hint)) { _toolTip.SetToolTip(lbl, def.Hint); _toolTip.SetToolTip(ctrl, def.Hint); } if (!string.IsNullOrWhiteSpace(extraToolTip)) { _toolTip.SetToolTip(lbl, extraToolTip); _toolTip.SetToolTip(ctrl, extraToolTip); } Control finalCtrl = ctrl; if (string.Equals(def.Key, "MarkingContent", StringComparison.OrdinalIgnoreCase)) { var flow = new FlowLayoutPanel { AutoSize = true, FlowDirection = FlowDirection.LeftToRight, WrapContents = false, Margin = new Padding(0) }; bool isChecked = true; if (_loadedDefaults != null) { var showVal = _loadedDefaults.GetString(controlKey + "_ShowInNote"); if (showVal != null) { isChecked = showVal == "1" || showVal.Equals("true", StringComparison.OrdinalIgnoreCase); } } var chk = new CheckBox { Text = "显示在附注中", AutoSize = true, Checked = isChecked, Margin = new Padding(10, 3, 0, 0) }; _controlsByKey[controlKey + "_ShowInNote"] = chk; chk.CheckedChanged += (_, __) => UpdateNotePreviews(); flow.Controls.Add(ctrl); flow.Controls.Add(chk); finalCtrl = flow; } _grid.Controls.Add(lbl, 0, rowIdx); _grid.Controls.Add(finalCtrl, 1, rowIdx); _controlsByKey[controlKey] = ctrl; _defsByControlKey[controlKey] = def; AttachPreviewUpdate(ctrl); AttachMinWallThicknessUpdate(controlKey, ctrl); } private void AttachPreviewUpdate(Control ctrl) { if (ctrl == null) { return; } if (ctrl is TextBox tb) { tb.TextChanged += (_, __) => UpdateNotePreviews(); return; } if (ctrl is ComboBox cb) { cb.SelectedIndexChanged += (_, __) => UpdateNotePreviews(); cb.TextChanged += (_, __) => UpdateNotePreviews(); return; } if (ctrl is NumericUpDown num) { num.ValueChanged += (_, __) => UpdateNotePreviews(); return; } if (ctrl is CheckBox chk) { chk.CheckedChanged += (_, __) => UpdateNotePreviews(); } } private void AttachMinWallThicknessUpdate(string paramKey, Control ctrl) { if (ctrl == null || string.IsNullOrWhiteSpace(paramKey)) { return; } var key = paramKey.Trim(); if (!string.Equals(key, KeyOuterDiameter1, StringComparison.OrdinalIgnoreCase) && !string.Equals(key, KeyOuterDiameter1TolMinus, StringComparison.OrdinalIgnoreCase) && !string.Equals(key, KeyInnerDiameter2, StringComparison.OrdinalIgnoreCase) && !string.Equals(key, KeyInnerDiameter2TolPlus, StringComparison.OrdinalIgnoreCase)) { return; } if (ctrl is NumericUpDown num) { num.ValueChanged += (_, __) => UpdateMinWallThickness(); } else if (ctrl is TextBox tb) { tb.TextChanged += (_, __) => UpdateMinWallThickness(); } } private void CheckMinWallThicknessAutoCalc() { _canAutoCalcMinWallThickness = false; if (!_controlsByKey.ContainsKey(KeyMinWallThickness)) { return; } _ctrlMinWallThickness = _controlsByKey[KeyMinWallThickness]; var hasOuter = _controlsByKey.ContainsKey(KeyOuterDiameter1); var hasOuterTol = _controlsByKey.ContainsKey(KeyOuterDiameter1TolMinus); var hasInner = _controlsByKey.ContainsKey(KeyInnerDiameter2); var hasInnerTol = _controlsByKey.ContainsKey(KeyInnerDiameter2TolPlus); if (hasOuter && hasOuterTol && hasInner && hasInnerTol) { _canAutoCalcMinWallThickness = true; UpdateMinWallThickness(); if (_ctrlMinWallThickness != null) { _toolTip.SetToolTip(_ctrlMinWallThickness, "自动计算: T=((Φ1+b1)-(Φ2+a2))/2\n(根据外径Φ1、外径下差b1、内径Φ2、内径上差a2)"); } } else { var missing = new List(); if (!hasOuter) missing.Add("外径Φ1"); if (!hasOuterTol) missing.Add("外径下差b1"); if (!hasInner) missing.Add("内径Φ2"); if (!hasInnerTol) missing.Add("内径上差a2"); if (_ctrlMinWallThickness != null && missing.Count > 0) { var msg = string.Format("需人工填写(缺少参数: {0})", string.Join(", ", missing)); _toolTip.SetToolTip(_ctrlMinWallThickness, msg); } } } private void UpdateMinWallThickness() { if (!_canAutoCalcMinWallThickness || _ctrlMinWallThickness == null) { return; } try { var phi1 = GetNumericValue(KeyOuterDiameter1); var b1 = GetNumericValue(KeyOuterDiameter1TolMinus); var phi2 = GetNumericValue(KeyInnerDiameter2); var a2 = GetNumericValue(KeyInnerDiameter2TolPlus); // 注意:b1通常为负数(如-0.5),a2通常为正数(如0.5) // 最小壁厚(径向):T = ((外径最小) - (内径最大)) / 2 // 外径最小 = Φ1 + b1,内径最大 = Φ2 + a2 // T = ((Φ1 + b1) - (Φ2 + a2)) / 2 var t = ((phi1 + b1) - (phi2 + a2)) / 2.0; if (_ctrlMinWallThickness is NumericUpDown num) { var val = (decimal)t; if (val < num.Minimum) val = num.Minimum; if (val > num.Maximum) val = num.Maximum; num.Value = val; } } catch { // ignore calculation errors } } private double GetNumericValue(string key) { if (!_controlsByKey.TryGetValue(key, out var ctrl) || ctrl == null) { return 0; } if (ctrl is NumericUpDown num) { return (double)num.Value; } if (ctrl is TextBox tb) { if (double.TryParse(tb.Text, NumberStyles.Float, CultureInfo.InvariantCulture, out var v)) { return v; } if (double.TryParse(tb.Text, NumberStyles.Float, CultureInfo.CurrentCulture, out v)) { return v; } } return 0; } private void UpdateNotePreviews() { if (_schema == null) { _rtbNoteNumbered.Text = "(模板参数未配置)"; _rtbNoteRendered.Text = string.Empty; return; } var noteTemplate = _schema.NoteTemplateText ?? string.Empty; if (string.IsNullOrWhiteSpace(noteTemplate)) { _rtbNoteNumbered.Text = "(未配置附注模板文本:请到【设置→模板参数绑定→附注绑定】粘贴附注模板)"; _rtbNoteRendered.Text = string.Empty; return; } _rtbNoteNumbered.Text = NoteTemplateEngine.BuildNumberedPreviewText(noteTemplate); var tempBag = CollectBagFromUi(); var effective = NoteTemplateEngine.BuildEffectiveValueKeyBindings(_schema.NoteBindings); Func getValueW = (k) => { var val = tempBag.GetString(k); if (k != null && k.StartsWith("MarkingContent", StringComparison.OrdinalIgnoreCase)) { if (tempBag.GetString(k + "_ShowInNote") == "0") return "__SKIP_NOTE__"; } return val; }; _rtbNoteNumbered.Text = NoteTemplateEngine.BuildNumberedPreviewText(noteTemplate); _rtbNoteRendered.Text = NoteTemplateEngine.Render(noteTemplate, effective, getValueW); } private ParamBag CollectBagFromUi() { var bag = new ParamBag(); foreach (var key in _controlsByKey.Keys.ToList()) { if (!_controlsByKey.TryGetValue(key, out var ctrl) || ctrl == null) { continue; } if (key.EndsWith("_ShowInNote", StringComparison.OrdinalIgnoreCase)) { if (ctrl is CheckBox chk) { bag.Set(key, chk.Checked ? "1" : "0"); } continue; } if (!_defsByControlKey.TryGetValue(key, out var def) || def == null) { continue; } var value = ReadValue(ctrl, def); bag.Set(key, value); } return bag; } private Control CreateControl(ParamDefinition def, string valueKey) { var initialValue = def.DefaultValue; // 优先使用传入的默认值 (loadedDefaults) if (_loadedDefaults != null) { var val = _loadedDefaults.GetString(valueKey); if (val != null) // only if key exists { initialValue = val; if (valueKey.StartsWith("Hardness")) { // Logging removed } } else { if (valueKey.StartsWith("Hardness")) { // Logging removed } } } // 确保不为 null if (initialValue == null) initialValue = string.Empty; switch (def.Type) { case ParamValueType.Bool: return new CheckBox { Checked = ParseBool(initialValue), AutoSize = true }; case ParamValueType.Enum: return CreateEnumCombo(def, initialValue); case ParamValueType.Int: return CreateNumber(def, isInt: true, overrideValue: initialValue); case ParamValueType.Double: return CreateNumber(def, isInt: false, overrideValue: initialValue); default: return new TextBox { Text = initialValue }; } } private static bool ParseBool(string s) { if (string.IsNullOrWhiteSpace(s)) { return false; } if (bool.TryParse(s, out var v)) { return v; } return string.Equals(s.Trim(), "1", StringComparison.OrdinalIgnoreCase) || string.Equals(s.Trim(), "是", StringComparison.OrdinalIgnoreCase); } private ComboBox CreateEnumCombo(ParamDefinition def, string initialValue) { // Allow manual edit to match many "下拉列表...可手动编辑" cases. var cb = new ComboBox { DropDownStyle = ComboBoxStyle.DropDown, DrawMode = DrawMode.OwnerDrawFixed }; cb.DrawItem += (s, e) => { if (e.Index < 0) return; e.DrawBackground(); var text = cb.GetItemText(cb.Items[e.Index]); TextRenderer.DrawText(e.Graphics, text, e.Font, e.Bounds, e.ForeColor, TextFormatFlags.Left | TextFormatFlags.VerticalCenter); e.DrawFocusRectangle(); if (cb.DroppedDown && (e.State & DrawItemState.Selected) == DrawItemState.Selected) { int lastHovered = cb.Tag is int i ? i : -1; if (lastHovered != e.Index) { cb.Tag = e.Index; var point = cb.PointToClient(Cursor.Position); point.Y += 20; _toolTip.Show(text, cb, point.X, point.Y); } } }; cb.DropDownClosed += (s, e) => { _toolTip.Hide(cb); cb.Tag = -1; }; var list = (def.EnumOptions ?? new List()) .Where(x => !string.IsNullOrWhiteSpace(x)) .Select(x => x.Trim()) .ToList(); var arr = list.ToArray(); if (arr.Length > 0) { cb.Items.AddRange(arr); var idx = Array.FindIndex(arr, x => string.Equals(x, initialValue, StringComparison.OrdinalIgnoreCase)); if (idx >= 0) { cb.SelectedIndex = idx; } } if (!string.IsNullOrWhiteSpace(initialValue)) { cb.Text = initialValue; } // Auto-match functionality: filter items as user types (Contains logic) cb.TextUpdate += (s, e) => { var text = cb.Text; var selectionStart = cb.SelectionStart; var selectionLength = cb.SelectionLength; // Empty text -> Show all? Or keep filtered? // Usually empty -> Show all. var matches = string.IsNullOrEmpty(text) ? arr : list.Where(x => x.IndexOf(text, StringComparison.OrdinalIgnoreCase) >= 0).ToArray(); cb.BeginUpdate(); cb.Items.Clear(); if (matches.Length > 0) { cb.Items.AddRange(matches); } cb.EndUpdate(); // Restoring SelectionStart is critical because updating Items resets it cb.SelectionStart = selectionStart; cb.SelectionLength = selectionLength; if (!cb.DroppedDown) { cb.DroppedDown = true; } // Keep the mouse cursor visible/normal Cursor.Current = Cursors.Default; }; return cb; } private Control CreateNumber(ParamDefinition def, bool isInt, string overrideValue) { var num = new NumericUpDown { DecimalPlaces = isInt ? 0 : 3, Minimum = -1000000000, Maximum = 1000000000, ThousandsSeparator = true }; if (TryParseDecimal(def.Min, out var min)) { num.Minimum = min; } if (TryParseDecimal(def.Max, out var max)) { num.Maximum = max; } // 使用 overrideValue 或 def.DefaultValue // 但 CreateNumber 被调用时,overrideValue 已经是处理过的“最终初始值” if (TryParseDecimal(overrideValue, out var dv)) { if (dv < num.Minimum) dv = num.Minimum; if (dv > num.Maximum) dv = num.Maximum; num.Value = dv; } return num; } private static bool TryParseDecimal(string s, out decimal value) { value = 0; if (string.IsNullOrWhiteSpace(s)) { return false; } if (decimal.TryParse(s, NumberStyles.Float, CultureInfo.InvariantCulture, out value)) { return true; } return decimal.TryParse(s, NumberStyles.Float, CultureInfo.CurrentCulture, out value); } private void OnOk() { var bag = new ParamBag(); foreach (var key in _controlsByKey.Keys.ToList()) { if (!_controlsByKey.TryGetValue(key, out var ctrl) || ctrl == null) { continue; } if (key.EndsWith("_ShowInNote", StringComparison.OrdinalIgnoreCase)) { if (ctrl is CheckBox chk) { bag.Set(key, chk.Checked ? "1" : "0"); } continue; } if (!_defsByControlKey.TryGetValue(key, out var def) || def == null) { continue; } var value = ReadValue(ctrl, def); if (def.Required && string.IsNullOrWhiteSpace(value)) { MessageBox.Show(this, $"请填写:{def.Label} ({def.Key})", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning); ctrl.Focus(); return; } bag.Set(key, value); if (key.StartsWith("Hardness")) { // Logging removed } } Result = bag; DialogResult = DialogResult.OK; Close(); } private static string ReadValue(Control ctrl, ParamDefinition def) { if (ctrl is CheckBox chk) { return chk.Checked ? "true" : "false"; } if (ctrl is NumericUpDown num) { return num.Value.ToString(CultureInfo.InvariantCulture); } if (ctrl is ComboBox cb) { // Handles both Enum type and String type rendered as ComboBox return (cb.Text ?? string.Empty).Trim(); } if (ctrl is TextBox tb) { return (tb.Text ?? string.Empty).Trim(); } return string.Empty; } } }