CadParamPluging/UI/TemplateSchemaBindingForm.cs

1055 lines
38 KiB
C#
Raw Permalink 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 System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using CadParamPluging.Common;
namespace CadParamPluging.UI
{
public class TemplateSchemaBindingForm : Form
{
private const string OwnershipForging = "锻件尺寸";
private const string OwnershipPart = "零件尺寸";
private readonly ComboBox _cbProjectType;
private readonly ComboBox _cbDrawingType;
private readonly ComboBox _cbSheetSize;
private readonly ComboBox _cbScale;
private readonly TextBox _txtDisplayName;
private readonly Label _lblTemplateKey;
private readonly ListBox _lbSchemas;
private readonly ListView _lvParams;
private readonly TextBox _txtNoteTemplate;
private readonly Label _lblNotePlaceholderCount;
private readonly DataGridView _gvNoteBindings;
private readonly RichTextBox _rtbNotePreview;
private readonly List<ParamOption> _remarkParamOptions;
private readonly ParamCatalog _catalog;
private readonly DropdownOptions _dropdownOptions;
private TemplateSchemas _schemas;
private sealed class ParamOption
{
public string Key { get; set; }
public string Display { get; set; }
}
private sealed class TemplateOptionItem
{
public string Value { get; set; }
public string Display { get; set; }
public override string ToString()
{
return Display;
}
}
public TemplateSchemaBindingForm(
DropdownOptions dropdownOptions,
ParamCatalog catalog)
{
Text = "模板参数绑定";
Size = new Size(920, 620);
MinimumSize = new Size(920, 620);
StartPosition = FormStartPosition.CenterParent;
FormBorderStyle = FormBorderStyle.Sizable;
MaximizeBox = true;
MinimizeBox = true;
ShowInTaskbar = false;
_catalog = (catalog ?? ParamCatalog.CreateDefault()).Clone();
_catalog.Normalize();
_dropdownOptions = (dropdownOptions ?? DropdownOptions.CreateDefault()).Clone();
_dropdownOptions.Normalize();
_remarkParamOptions = (_catalog.Items ?? Enumerable.Empty<ParamDefinition>())
.Where(p => p != null && string.Equals(p.Group ?? string.Empty, "备注参数", StringComparison.OrdinalIgnoreCase))
.OrderBy(p => p.Order)
.Select(p => new ParamOption { Key = p.Key, Display = string.Format("{0} ({1})", p.Label, p.Key) })
.ToList();
_schemas = TemplateSchemaStore.Load() ?? new TemplateSchemas();
_schemas.Normalize();
var root = new TableLayoutPanel
{
Dock = DockStyle.Fill,
Padding = new Padding(10),
ColumnCount = 2,
RowCount = 2
};
root.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 260));
root.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100f));
root.RowStyles.Add(new RowStyle(SizeType.Percent, 100f));
root.RowStyles.Add(new RowStyle(SizeType.AutoSize));
_lbSchemas = new ListBox { Dock = DockStyle.Fill };
var leftButtons = new FlowLayoutPanel
{
Dock = DockStyle.Bottom,
FlowDirection = FlowDirection.LeftToRight,
AutoSize = true
};
var btnNew = new Button { Text = "新增模板", AutoSize = true };
btnNew.Click += (_, __) => OnNewSchema();
var btnDelete = new Button { Text = "删除模板", AutoSize = true };
btnDelete.Click += (_, __) => OnDeleteSchema();
leftButtons.Controls.Add(btnNew);
leftButtons.Controls.Add(btnDelete);
var leftPanel = new Panel { Dock = DockStyle.Fill };
leftPanel.Controls.Add(_lbSchemas);
leftPanel.Controls.Add(leftButtons);
leftButtons.Dock = DockStyle.Bottom;
var editor = new TableLayoutPanel
{
Dock = DockStyle.Fill,
ColumnCount = 2,
RowCount = 4,
AutoSize = true
};
editor.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));
editor.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100f));
_cbProjectType = CreateCombo(_dropdownOptions.DeliveryStatuses, DropdownOptionCategory.DeliveryStatus);
_cbDrawingType = CreateCombo(_dropdownOptions.ProcessMethods, DropdownOptionCategory.ProcessMethod);
_cbSheetSize = CreateCombo(_dropdownOptions.StructuralFeatures, DropdownOptionCategory.StructuralFeature);
_cbScale = CreateCombo(_dropdownOptions.SpecialConditions, DropdownOptionCategory.SpecialCondition);
_cbProjectType.SelectedIndexChanged += (_, __) => UpdateTemplateKeyLabel();
_cbDrawingType.SelectedIndexChanged += (_, __) => UpdateTemplateKeyLabel();
_cbSheetSize.SelectedIndexChanged += (_, __) => UpdateTemplateKeyLabel();
_cbScale.SelectedIndexChanged += (_, __) => UpdateTemplateKeyLabel();
_txtDisplayName = new TextBox { Width = 380 };
_lblTemplateKey = new Label { AutoSize = true, ForeColor = Color.DarkBlue };
var row = 0;
editor.Controls.Add(new Label { Text = "交付状态", AutoSize = true, TextAlign = ContentAlignment.MiddleLeft }, 0, row);
editor.Controls.Add(_cbProjectType, 1, row++);
editor.Controls.Add(new Label { Text = "工艺方法", AutoSize = true, TextAlign = ContentAlignment.MiddleLeft }, 0, row);
editor.Controls.Add(_cbDrawingType, 1, row++);
editor.Controls.Add(new Label { Text = "结构特征", AutoSize = true, TextAlign = ContentAlignment.MiddleLeft }, 0, row);
editor.Controls.Add(_cbSheetSize, 1, row++);
editor.Controls.Add(new Label { Text = "特殊条件", AutoSize = true, TextAlign = ContentAlignment.MiddleLeft }, 0, row);
editor.Controls.Add(_cbScale, 1, row++);
var topBar = new TableLayoutPanel
{
Dock = DockStyle.Top,
ColumnCount = 2,
RowCount = 2,
AutoSize = true
};
topBar.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));
topBar.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100f));
topBar.Controls.Add(new Label { Text = "显示名称", AutoSize = true, TextAlign = ContentAlignment.MiddleLeft }, 0, 0);
topBar.Controls.Add(_txtDisplayName, 1, 0);
topBar.Controls.Add(new Label { Text = "TemplateKey", AutoSize = true, TextAlign = ContentAlignment.MiddleLeft }, 0, 1);
topBar.Controls.Add(_lblTemplateKey, 1, 1);
_lvParams = new ListView
{
Dock = DockStyle.Fill,
View = View.Details,
FullRowSelect = true,
HideSelection = false,
CheckBoxes = true
};
_lvParams.Columns.Add("Label", 220);
_lvParams.Columns.Add("Key", 160);
_lvParams.Columns.Add("归属", 100);
var paramsButtonPanel = new FlowLayoutPanel
{
Dock = DockStyle.Top,
FlowDirection = FlowDirection.LeftToRight,
AutoSize = true
};
var btnUp = new Button { Text = "上移", AutoSize = true };
btnUp.Click += (_, __) => MoveSelectedParam(-1);
var btnDown = new Button { Text = "下移", AutoSize = true };
btnDown.Click += (_, __) => MoveSelectedParam(1);
var btnSelectAll = new Button { Text = "全选", AutoSize = true };
btnSelectAll.Click += (_, __) => SetAllChecked(true);
var btnUnselectAll = new Button { Text = "全不选", AutoSize = true };
btnUnselectAll.Click += (_, __) => SetAllChecked(false);
var btnSetForging = new Button { Text = "设为锻件尺寸", AutoSize = true };
btnSetForging.Click += (_, __) => SetSelectedOwnership(false);
var btnSetPart = new Button { Text = "设为零件尺寸", AutoSize = true };
btnSetPart.Click += (_, __) => SetSelectedOwnership(true);
paramsButtonPanel.Controls.Add(btnUp);
paramsButtonPanel.Controls.Add(btnDown);
paramsButtonPanel.Controls.Add(btnSelectAll);
paramsButtonPanel.Controls.Add(btnUnselectAll);
paramsButtonPanel.Controls.Add(btnSetForging);
paramsButtonPanel.Controls.Add(btnSetPart);
_txtNoteTemplate = new TextBox
{
Dock = DockStyle.Fill,
Multiline = true,
ScrollBars = ScrollBars.Vertical,
Height = 120
};
_lblNotePlaceholderCount = new Label
{
AutoSize = true,
ForeColor = Color.DarkBlue,
Text = "占位符: 0"
};
_gvNoteBindings = new DataGridView
{
Dock = DockStyle.Fill,
AutoGenerateColumns = false,
AllowUserToAddRows = false,
AllowUserToDeleteRows = false,
RowHeadersVisible = false,
SelectionMode = DataGridViewSelectionMode.FullRowSelect
};
var colIdx = new DataGridViewTextBoxColumn
{
HeaderText = "序号",
Width = 60,
ReadOnly = true
};
var colLine = new DataGridViewTextBoxColumn
{
HeaderText = "所在行",
Width = 80,
ReadOnly = true
};
var colSnippet = new DataGridViewTextBoxColumn
{
HeaderText = "文本定位",
AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill,
ReadOnly = true
};
var colParam = new DataGridViewComboBoxColumn
{
HeaderText = "绑定参数(备注参数)",
Width = 320,
DisplayStyle = DataGridViewComboBoxDisplayStyle.DropDownButton,
DataSource = _remarkParamOptions,
DisplayMember = "Display",
ValueMember = "Key"
};
_gvNoteBindings.Columns.Add(colIdx);
_gvNoteBindings.Columns.Add(colLine);
_gvNoteBindings.Columns.Add(colSnippet);
_gvNoteBindings.Columns.Add(colParam);
_gvNoteBindings.DataError += (_, __) => { };
_gvNoteBindings.SelectionChanged += (_, __) => HighlightSelectedPlaceholderInPreview();
_rtbNotePreview = new RichTextBox
{
Dock = DockStyle.Fill,
ReadOnly = true,
HideSelection = false
};
var btnParseNote = new Button { Text = "解析占位符", AutoSize = true };
btnParseNote.Click += (_, __) => ReloadNoteBindingsFromTemplateText();
var rightPanel = new TableLayoutPanel
{
Dock = DockStyle.Fill,
ColumnCount = 1,
RowCount = 3
};
rightPanel.RowStyles.Add(new RowStyle(SizeType.AutoSize));
rightPanel.RowStyles.Add(new RowStyle(SizeType.AutoSize));
rightPanel.RowStyles.Add(new RowStyle(SizeType.Percent, 100f));
rightPanel.Controls.Add(editor, 0, 0);
rightPanel.Controls.Add(topBar, 0, 1);
var paramArea = new TableLayoutPanel
{
Dock = DockStyle.Fill,
ColumnCount = 1,
RowCount = 2
};
paramArea.RowStyles.Add(new RowStyle(SizeType.AutoSize));
paramArea.RowStyles.Add(new RowStyle(SizeType.Percent, 100f));
paramArea.Controls.Add(paramsButtonPanel, 0, 0);
paramArea.Controls.Add(_lvParams, 0, 1);
var noteArea = new TableLayoutPanel
{
Dock = DockStyle.Fill,
ColumnCount = 1,
RowCount = 6,
Padding = new Padding(0)
};
noteArea.RowStyles.Add(new RowStyle(SizeType.AutoSize));
noteArea.RowStyles.Add(new RowStyle(SizeType.AutoSize));
noteArea.RowStyles.Add(new RowStyle(SizeType.AutoSize));
noteArea.RowStyles.Add(new RowStyle(SizeType.Percent, 60f));
noteArea.RowStyles.Add(new RowStyle(SizeType.AutoSize));
noteArea.RowStyles.Add(new RowStyle(SizeType.Percent, 40f));
noteArea.Controls.Add(new Label { Text = "附注模板(多行文本,使用 * 作为占位符;**** 会忽略)", AutoSize = true }, 0, 0);
noteArea.Controls.Add(_txtNoteTemplate, 0, 1);
var noteToolbar = new FlowLayoutPanel
{
Dock = DockStyle.Fill,
FlowDirection = FlowDirection.LeftToRight,
AutoSize = true
};
noteToolbar.Controls.Add(btnParseNote);
noteToolbar.Controls.Add(_lblNotePlaceholderCount);
noteArea.Controls.Add(noteToolbar, 0, 2);
noteArea.Controls.Add(_gvNoteBindings, 0, 3);
noteArea.Controls.Add(new Label { Text = "编号预览(用于对照绑定)", AutoSize = true }, 0, 4);
noteArea.Controls.Add(_rtbNotePreview, 0, 5);
var tabs = new TabControl { Dock = DockStyle.Fill };
var tabParams = new TabPage { Text = "参数绑定" };
tabParams.Controls.Add(paramArea);
var tabNotes = new TabPage { Text = "附注绑定" };
tabNotes.Controls.Add(noteArea);
tabs.TabPages.Add(tabParams);
tabs.TabPages.Add(tabNotes);
rightPanel.Controls.Add(tabs, 0, 2);
var bottomButtons = new FlowLayoutPanel
{
Dock = DockStyle.Fill,
FlowDirection = FlowDirection.RightToLeft,
AutoSize = true
};
var btnClose = new Button { Text = "关闭", DialogResult = DialogResult.Cancel };
var btnSave = new Button { Text = "保存" };
btnSave.Click += (_, __) => OnSave();
bottomButtons.Controls.Add(btnClose);
bottomButtons.Controls.Add(btnSave);
root.Controls.Add(leftPanel, 0, 0);
root.Controls.Add(rightPanel, 1, 0);
root.Controls.Add(bottomButtons, 0, 1);
root.SetColumnSpan(bottomButtons, 2);
Controls.Add(root);
CancelButton = btnClose;
BuildParamListItems();
UpdateTemplateKeyLabel();
_lbSchemas.SelectedIndexChanged += (_, __) => LoadSelectedSchemaToUi();
ReloadSchemaList();
}
private ComboBox CreateCombo(System.Collections.Generic.IEnumerable<string> items, string category)
{
var cb = new ComboBox { DropDownStyle = ComboBoxStyle.DropDownList, Width = 220 };
var arr = (items ?? Enumerable.Empty<string>())
.Where(s => !string.IsNullOrWhiteSpace(s))
.Select(s => s.Trim())
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToArray();
if (arr.Length > 0)
{
cb.Items.AddRange(arr
.Select(s => new TemplateOptionItem
{
Value = s,
Display = _dropdownOptions.GetDisplayName(category, s)
})
.Cast<object>()
.ToArray());
cb.SelectedIndex = 0;
}
return cb;
}
private void BuildParamListItems()
{
_lvParams.BeginUpdate();
try
{
_lvParams.Items.Clear();
foreach (var p in (_catalog.Items ?? Enumerable.Empty<ParamDefinition>())
.Where(x => x != null)
.Where(x => !string.Equals(x.Group ?? string.Empty, "备注参数", StringComparison.OrdinalIgnoreCase))
.OrderBy(x => x.Order))
{
var it = new ListViewItem(new[] { p.Label, p.Key, GuessOwnershipText(p.Key) })
{
Tag = p.Key,
Checked = false
};
_lvParams.Items.Add(it);
}
}
finally
{
_lvParams.EndUpdate();
}
}
private void ApplySchemaToParamList(TemplateSchemaDefinition schema)
{
var selectedKeys = (schema?.SelectedParamKeys ?? new List<string>()).ToList();
var keySet = new HashSet<string>(selectedKeys, StringComparer.OrdinalIgnoreCase);
var partKeySet = new HashSet<string>(
(schema?.SelectedPartParamKeys ?? new List<string>()).Where(s => !string.IsNullOrWhiteSpace(s)).Select(s => s.Trim()),
StringComparer.OrdinalIgnoreCase);
var all = _lvParams.Items.Cast<ListViewItem>().ToList();
var byKey = all.ToDictionary(i => (string)i.Tag, i => i, StringComparer.OrdinalIgnoreCase);
foreach (var it in all)
{
var key = (string)it.Tag;
if (string.IsNullOrWhiteSpace(key))
{
continue;
}
EnsureOwnershipSubItem(it);
if (partKeySet.Contains(key))
{
it.SubItems[2].Text = OwnershipPart;
}
else if (keySet.Contains(key))
{
it.SubItems[2].Text = OwnershipForging;
}
else if (string.IsNullOrWhiteSpace(it.SubItems[2].Text))
{
it.SubItems[2].Text = GuessOwnershipText(key);
}
}
var ordered = new List<ListViewItem>();
foreach (var k in selectedKeys)
{
if (byKey.TryGetValue(k, out var it))
{
ordered.Add(it);
}
}
foreach (var it in all)
{
if (!keySet.Contains((string)it.Tag))
{
ordered.Add(it);
}
}
_lvParams.BeginUpdate();
try
{
_lvParams.Items.Clear();
foreach (var it in ordered)
{
var key = (string)it.Tag;
it.Checked = keySet.Contains(key);
_lvParams.Items.Add(it);
}
}
finally
{
_lvParams.EndUpdate();
}
}
private void ReloadSchemaList()
{
var prev = _lbSchemas.SelectedIndex;
_lbSchemas.Items.Clear();
foreach (var s in (_schemas.Items ?? Enumerable.Empty<TemplateSchemaDefinition>())
.Where(x => x != null)
.OrderBy(x => x.ProjectType)
.ThenBy(x => x.DrawingType)
.ThenBy(x => x.SheetSize)
.ThenBy(x => x.Scale))
{
_lbSchemas.Items.Add(FormatSchema(s));
}
if (_lbSchemas.Items.Count > 0)
{
_lbSchemas.SelectedIndex = Math.Max(0, Math.Min(prev, _lbSchemas.Items.Count - 1));
}
}
private string FormatSchema(TemplateSchemaDefinition s)
{
if (s == null)
{
return "(null)";
}
var name = string.IsNullOrWhiteSpace(s.DisplayName) ? "(未命名)" : s.DisplayName;
return string.Format(
"{0} | {1}/{2}/{3}/{4}",
name,
_dropdownOptions.GetDisplayName(DropdownOptionCategory.DeliveryStatus, s.ProjectType),
_dropdownOptions.GetDisplayName(DropdownOptionCategory.ProcessMethod, s.DrawingType),
_dropdownOptions.GetDisplayName(DropdownOptionCategory.StructuralFeature, s.SheetSize),
_dropdownOptions.GetDisplayName(DropdownOptionCategory.SpecialCondition, s.Scale));
}
private TemplateSchemaDefinition GetSelectedSchema()
{
var idx = _lbSchemas.SelectedIndex;
if (idx < 0)
{
return null;
}
var ordered = (_schemas.Items ?? Enumerable.Empty<TemplateSchemaDefinition>())
.Where(x => x != null)
.OrderBy(x => x.ProjectType)
.ThenBy(x => x.DrawingType)
.ThenBy(x => x.SheetSize)
.ThenBy(x => x.Scale)
.ToList();
if (idx >= ordered.Count)
{
return null;
}
var key = ordered[idx].TemplateKey;
return (_schemas.Items ?? new List<TemplateSchemaDefinition>())
.Where(x => x != null)
.FirstOrDefault(x => string.Equals(x.TemplateKey, key, StringComparison.OrdinalIgnoreCase));
}
private void LoadSelectedSchemaToUi()
{
var schema = GetSelectedSchema();
if (schema == null)
{
return;
}
SelectComboByText(_cbProjectType, schema.ProjectType);
SelectComboByText(_cbDrawingType, schema.DrawingType);
SelectComboByText(_cbSheetSize, schema.SheetSize);
SelectComboByText(_cbScale, schema.Scale);
_txtDisplayName.Text = schema.DisplayName ?? string.Empty;
UpdateTemplateKeyLabel();
ApplySchemaToParamList(schema);
LoadNoteBindingsToUi(schema);
}
private static void SelectComboByText(ComboBox cb, string value)
{
if (cb == null)
{
return;
}
var v = (value ?? string.Empty).Trim();
for (var i = 0; i < cb.Items.Count; i++)
{
var item = cb.Items[i] as TemplateOptionItem;
var raw = item != null ? item.Value : (cb.Items[i] as string) ?? string.Empty;
if (string.Equals(raw, v, StringComparison.OrdinalIgnoreCase))
{
cb.SelectedIndex = i;
return;
}
}
}
private static string GetComboValue(ComboBox cb)
{
if (cb == null)
{
return string.Empty;
}
var item = cb.SelectedItem as TemplateOptionItem;
if (item != null)
{
return (item.Value ?? string.Empty).Trim();
}
return (cb.Text ?? string.Empty).Trim();
}
private void UpdateTemplateKeyLabel()
{
var key = TemplateKeyBuilder.Build(
GetComboValue(_cbProjectType),
GetComboValue(_cbDrawingType),
GetComboValue(_cbSheetSize),
GetComboValue(_cbScale));
_lblTemplateKey.Text = key ?? string.Empty;
}
private void OnNewSchema()
{
if (string.IsNullOrWhiteSpace(GetComboValue(_cbProjectType)) || string.IsNullOrWhiteSpace(GetComboValue(_cbDrawingType)))
{
MessageBox.Show(this, "请先选择模板四个参数。", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
var candidate = new TemplateSchemaDefinition
{
ProjectType = GetComboValue(_cbProjectType),
DrawingType = GetComboValue(_cbDrawingType),
SheetSize = GetComboValue(_cbSheetSize),
Scale = GetComboValue(_cbScale),
DisplayName = _txtDisplayName.Text
};
candidate.Normalize();
if ((_schemas.Items ?? new List<TemplateSchemaDefinition>())
.Where(x => x != null)
.Any(x => string.Equals(x.TemplateKey, candidate.TemplateKey, StringComparison.OrdinalIgnoreCase)))
{
MessageBox.Show(this, "该模板定义已存在(四参数组合重复)。", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
if (_schemas.Items == null)
{
_schemas.Items = new List<TemplateSchemaDefinition>();
}
_schemas.Items.Add(candidate);
_schemas.Normalize();
ReloadSchemaList();
}
private void OnDeleteSchema()
{
var schema = GetSelectedSchema();
if (schema == null)
{
MessageBox.Show(this, "请先选择一个模板定义。", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
var confirm = MessageBox.Show(this, $"确认删除模板定义:{FormatSchema(schema)}", "删除", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
if (confirm != DialogResult.Yes)
{
return;
}
_schemas.Items = (_schemas.Items ?? Enumerable.Empty<TemplateSchemaDefinition>())
.Where(x => x != null && !string.Equals(x.TemplateKey, schema.TemplateKey, StringComparison.OrdinalIgnoreCase))
.ToList();
_schemas.Normalize();
ReloadSchemaList();
}
private void MoveSelectedParam(int delta)
{
if (_lvParams.SelectedItems.Count == 0)
{
return;
}
var item = _lvParams.SelectedItems[0];
var idx = item.Index;
var newIdx = idx + delta;
if (newIdx < 0 || newIdx >= _lvParams.Items.Count)
{
return;
}
_lvParams.BeginUpdate();
try
{
_lvParams.Items.RemoveAt(idx);
_lvParams.Items.Insert(newIdx, item);
item.Selected = true;
item.Focused = true;
}
finally
{
_lvParams.EndUpdate();
}
}
private void SetSelectedOwnership(bool part)
{
if (_lvParams.SelectedItems.Count == 0)
{
return;
}
_lvParams.BeginUpdate();
try
{
foreach (ListViewItem it in _lvParams.SelectedItems)
{
if (it == null)
{
continue;
}
EnsureOwnershipSubItem(it);
it.SubItems[2].Text = part ? OwnershipPart : OwnershipForging;
}
}
finally
{
_lvParams.EndUpdate();
}
}
private static void EnsureOwnershipSubItem(ListViewItem it)
{
if (it == null)
{
return;
}
while (it.SubItems.Count < 3)
{
it.SubItems.Add(string.Empty);
}
}
private static bool IsPartKey(string key)
{
var k = (key ?? string.Empty).Trim();
return k.EndsWith("Prime", StringComparison.OrdinalIgnoreCase);
}
private static string GuessOwnershipText(string key)
{
return IsPartKey(key) ? OwnershipPart : OwnershipForging;
}
private void SetAllChecked(bool value)
{
_lvParams.BeginUpdate();
try
{
foreach (ListViewItem it in _lvParams.Items)
{
it.Checked = value;
}
}
finally
{
_lvParams.EndUpdate();
}
}
private void LoadNoteBindingsToUi(TemplateSchemaDefinition schema)
{
if (schema == null)
{
_txtNoteTemplate.Text = string.Empty;
ReloadNoteBindingsGrid(string.Empty, null);
return;
}
_txtNoteTemplate.Text = schema.NoteTemplateText ?? string.Empty;
ReloadNoteBindingsGrid(_txtNoteTemplate.Text, schema.NoteBindings);
}
private void ReloadNoteBindingsFromTemplateText()
{
var map = ReadNoteBindingMapFromGrid();
var bindings = map
.Select(kv => new NotePlaceholderBinding { Index = kv.Key, ParamKey = kv.Value })
.ToList();
ReloadNoteBindingsGrid(_txtNoteTemplate.Text ?? string.Empty, bindings);
}
private void ReloadNoteBindingsGrid(string templateText, IEnumerable<NotePlaceholderBinding> bindings)
{
var occ = NoteTemplateEngine.ParseOccurrences(templateText ?? string.Empty);
var count = occ.Count;
_lblNotePlaceholderCount.Text = string.Format("占位符: {0}", count);
_rtbNotePreview.Text = NoteTemplateEngine.BuildNumberedPreviewText(templateText ?? string.Empty);
var map = new Dictionary<int, string>();
foreach (var b in bindings ?? Enumerable.Empty<NotePlaceholderBinding>())
{
if (b == null)
{
continue;
}
if (b.Index <= 0)
{
continue;
}
var key = (b.ParamKey ?? string.Empty).Trim();
if (key.Length == 0)
{
continue;
}
if (!map.ContainsKey(b.Index))
{
map[b.Index] = key;
}
}
_gvNoteBindings.SuspendLayout();
try
{
_gvNoteBindings.Rows.Clear();
foreach (var o in occ)
{
var idx = _gvNoteBindings.Rows.Add();
var row = _gvNoteBindings.Rows[idx];
row.Cells[0].Value = o.Index;
row.Cells[1].Value = string.Format("第{0}行", o.LineNumber);
row.Cells[2].Value = BuildOccurrenceSnippet(o);
row.Cells[3].Value = map.ContainsKey(o.Index) ? map[o.Index] : null;
}
}
finally
{
_gvNoteBindings.ResumeLayout();
}
HighlightSelectedPlaceholderInPreview();
}
private Dictionary<int, string> ReadNoteBindingMapFromGrid()
{
var map = new Dictionary<int, string>();
foreach (DataGridViewRow row in _gvNoteBindings.Rows)
{
if (row == null)
{
continue;
}
var idxObj = row.Cells[0].Value;
var keyObj = row.Cells[3].Value;
int idx;
if (idxObj == null || !int.TryParse(idxObj.ToString(), out idx) || idx <= 0)
{
continue;
}
var key = (keyObj ?? string.Empty).ToString().Trim();
if (key.Length == 0)
{
continue;
}
if (!map.ContainsKey(idx))
{
map[idx] = key;
}
}
return map;
}
private static string BuildOccurrenceSnippet(NoteTemplateEngine.PlaceholderOccurrence o)
{
if (o == null)
{
return string.Empty;
}
var line = o.LineText ?? string.Empty;
var token = string.Format("【#{0}】", o.Index);
var col = o.ColumnInLine;
if (col < 0) col = 0;
if (col >= line.Length)
{
return token;
}
const int ctx = 16;
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));
return prefix + left + token + right + suffix;
}
private void HighlightSelectedPlaceholderInPreview()
{
if (_rtbNotePreview == null || string.IsNullOrEmpty(_rtbNotePreview.Text))
{
return;
}
if (_gvNoteBindings.SelectedRows.Count == 0)
{
return;
}
var row = _gvNoteBindings.SelectedRows[0];
if (row == null)
{
return;
}
var idxObj = row.Cells[0].Value;
int idx;
if (idxObj == null || !int.TryParse(idxObj.ToString(), out idx) || idx <= 0)
{
return;
}
var token = string.Format("【#{0}】", idx);
var pos = _rtbNotePreview.Text.IndexOf(token, StringComparison.Ordinal);
if (pos < 0)
{
return;
}
_rtbNotePreview.Select(pos, token.Length);
_rtbNotePreview.ScrollToCaret();
}
private void OnSave()
{
var schema = GetSelectedSchema();
if (schema == null)
{
MessageBox.Show(this, "请先选择一个模板定义。", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
schema.ProjectType = GetComboValue(_cbProjectType);
schema.DrawingType = GetComboValue(_cbDrawingType);
schema.SheetSize = GetComboValue(_cbSheetSize);
schema.Scale = GetComboValue(_cbScale);
schema.DisplayName = _txtDisplayName.Text;
var selectedParamKeys = _lvParams.Items
.Cast<ListViewItem>()
.Where(i => i.Checked)
.Select(i => (string)i.Tag)
.Where(k => !string.IsNullOrWhiteSpace(k))
.ToList();
// “备注参数”仅在附注绑定中配置,不出现在模板参数绑定里。
selectedParamKeys = selectedParamKeys
.Where(k =>
{
var def = _catalog.FindByKey(k);
if (def == null)
{
return true;
}
return !string.Equals(def.Group ?? string.Empty, "备注参数", StringComparison.OrdinalIgnoreCase);
})
.ToList();
schema.SelectedParamKeys = selectedParamKeys;
var selectedPartKeys = _lvParams.Items
.Cast<ListViewItem>()
.Where(i => i != null && i.Checked)
.Select(i =>
{
var key = ((string)i.Tag ?? string.Empty).Trim();
EnsureOwnershipSubItem(i);
var own = (i.SubItems[2].Text ?? string.Empty).Trim();
if (own.Length == 0)
{
own = GuessOwnershipText(key);
i.SubItems[2].Text = own;
}
var isPart = string.Equals(own, OwnershipPart, StringComparison.OrdinalIgnoreCase);
return new { Key = key, IsPart = isPart };
})
.Where(x => x.Key.Length > 0 && x.IsPart)
.Select(x => x.Key)
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToList();
schema.SelectedPartParamKeys = selectedPartKeys;
schema.PartOwnershipConfigured = true;
schema.NoteTemplateText = _txtNoteTemplate.Text ?? string.Empty;
var placeholderCount = NoteTemplateEngine.CountPlaceholders(schema.NoteTemplateText);
if (_gvNoteBindings.Rows.Count != placeholderCount)
{
ReloadNoteBindingsFromTemplateText();
MessageBox.Show(this, "附注模板已变化,已重新解析占位符。请确认附注绑定后再保存。", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
var noteBindings = new List<NotePlaceholderBinding>();
if (placeholderCount > 0)
{
var map = ReadNoteBindingMapFromGrid();
for (var i = 1; i <= placeholderCount; i++)
{
if (!map.TryGetValue(i, out var key) || string.IsNullOrWhiteSpace(key))
{
MessageBox.Show(this, string.Format("请绑定第 {0} 个占位符(*)对应的参数。", i), "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
var def = _catalog.FindByKey(key);
if (def == null || !string.Equals(def.Group ?? string.Empty, "备注参数", StringComparison.OrdinalIgnoreCase))
{
MessageBox.Show(this, string.Format("占位符 #{0} 绑定的参数不在【备注参数】分组:{1}", i, key), "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
noteBindings.Add(new NotePlaceholderBinding { Index = i, ParamKey = key.Trim() });
}
}
schema.NoteBindings = noteBindings;
schema.Normalize();
if ((_schemas.Items ?? new List<TemplateSchemaDefinition>())
.Where(x => x != null)
.Any(x => !ReferenceEquals(x, schema) && string.Equals(x.TemplateKey, schema.TemplateKey, StringComparison.OrdinalIgnoreCase)))
{
MessageBox.Show(this, "保存失败:存在重复的四参数组合。", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
try
{
TemplateSchemaStore.Save(_schemas);
MessageBox.Show(this, "已保存。", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
ReloadSchemaList();
}
catch (Exception ex)
{
MessageBox.Show(this, $"保存失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
}