附注参数已经可以填写,下一步要开始生图了,过去这一步就没有难度了
This commit is contained in:
parent
0a4a003a11
commit
c1d18663bb
@ -5,6 +5,7 @@ using System.Text.RegularExpressions;
|
||||
using Autodesk.AutoCAD.ApplicationServices;
|
||||
using Autodesk.AutoCAD.DatabaseServices;
|
||||
using Autodesk.AutoCAD.Runtime;
|
||||
using CadParamPluging.Common;
|
||||
using CadParamPluging.Domain.Models;
|
||||
|
||||
namespace CadParamPluging.Cad
|
||||
@ -180,6 +181,359 @@ namespace CadParamPluging.Cad
|
||||
return s;
|
||||
}
|
||||
|
||||
public sealed class NoteApplyResult
|
||||
{
|
||||
public bool Applied { get; set; }
|
||||
public string RenderedText { get; set; }
|
||||
public int PlaceholderCountInDwg { get; set; }
|
||||
public string TargetKind { get; set; }
|
||||
public string Message { get; set; }
|
||||
}
|
||||
|
||||
private sealed class NoteCandidate
|
||||
{
|
||||
public int Score;
|
||||
public string Kind;
|
||||
public string PlainText;
|
||||
public Action<string> Apply;
|
||||
}
|
||||
|
||||
public static NoteApplyResult ApplyNoteTemplate(CadContext ctx, string layoutName, bool scanModelSpace, TemplateSchemaDefinition schema, ParamBag bag)
|
||||
{
|
||||
if (ctx == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(ctx));
|
||||
}
|
||||
|
||||
if (schema == null)
|
||||
{
|
||||
return new NoteApplyResult { Applied = false, Message = "Schema is null" };
|
||||
}
|
||||
|
||||
if (bag == null)
|
||||
{
|
||||
return new NoteApplyResult { Applied = false, Message = "ParamBag is null" };
|
||||
}
|
||||
|
||||
if (schema.NoteBindings == null || schema.NoteBindings.Count == 0)
|
||||
{
|
||||
return new NoteApplyResult { Applied = false, Message = "未配置附注绑定,跳过。" };
|
||||
}
|
||||
|
||||
var tr = ctx.Transaction;
|
||||
var db = ctx.Database;
|
||||
|
||||
var space = GetTargetSpace(tr, db, layoutName, scanModelSpace);
|
||||
if (space == null)
|
||||
{
|
||||
return new NoteApplyResult { Applied = false, Message = "未找到目标空间,跳过附注替换。" };
|
||||
}
|
||||
|
||||
var candidates = new List<NoteCandidate>();
|
||||
var visitedBlocks = new HashSet<ObjectId>();
|
||||
foreach (ObjectId id in space)
|
||||
{
|
||||
var ent = tr.GetObject(id, OpenMode.ForRead, false) as Entity;
|
||||
if (ent != null)
|
||||
{
|
||||
CollectNoteCandidates(tr, ent, visitedBlocks, candidates);
|
||||
}
|
||||
}
|
||||
|
||||
if (candidates.Count == 0)
|
||||
{
|
||||
return new NoteApplyResult { Applied = false, Message = "未找到附注文本目标(包含‘附注’且包含占位符*),跳过。" };
|
||||
}
|
||||
|
||||
var best = candidates.OrderByDescending(c => c.Score).FirstOrDefault();
|
||||
if (best == null || best.Apply == null)
|
||||
{
|
||||
return new NoteApplyResult { Applied = false, Message = "未找到可替换的附注文本目标,跳过。" };
|
||||
}
|
||||
|
||||
var templateText = best.PlainText ?? string.Empty;
|
||||
var placeholderCount = NoteTemplateEngine.CountPlaceholders(templateText);
|
||||
if (placeholderCount <= 0)
|
||||
{
|
||||
return new NoteApplyResult
|
||||
{
|
||||
Applied = false,
|
||||
TargetKind = best.Kind,
|
||||
PlaceholderCountInDwg = placeholderCount,
|
||||
Message = "附注目标中未检测到有效占位符(*,忽略****),跳过。"
|
||||
};
|
||||
}
|
||||
|
||||
var effective = NoteTemplateEngine.BuildEffectiveValueKeyBindings(schema.NoteBindings);
|
||||
var rendered = NoteTemplateEngine.Render(templateText, effective, bag.GetString);
|
||||
best.Apply(rendered);
|
||||
|
||||
return new NoteApplyResult
|
||||
{
|
||||
Applied = true,
|
||||
TargetKind = best.Kind,
|
||||
PlaceholderCountInDwg = placeholderCount,
|
||||
RenderedText = rendered,
|
||||
Message = "附注已替换。"
|
||||
};
|
||||
}
|
||||
|
||||
private static BlockTableRecord GetTargetSpace(Transaction tr, Database db, string layoutName, bool scanModelSpace)
|
||||
{
|
||||
if (tr == null || db == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (scanModelSpace)
|
||||
{
|
||||
var bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
|
||||
return (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForRead);
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(layoutName))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var layoutDict = (DBDictionary)tr.GetObject(db.LayoutDictionaryId, OpenMode.ForRead);
|
||||
ObjectId layoutId = ObjectId.Null;
|
||||
foreach (DBDictionaryEntry entry in layoutDict)
|
||||
{
|
||||
if (string.Equals(entry.Key, layoutName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
layoutId = entry.Value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (layoutId.IsNull)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var layout = (Layout)tr.GetObject(layoutId, OpenMode.ForRead);
|
||||
return (BlockTableRecord)tr.GetObject(layout.BlockTableRecordId, OpenMode.ForRead);
|
||||
}
|
||||
|
||||
private static void CollectNoteCandidates(Transaction tr, Entity ent, HashSet<ObjectId> visitedBlocks, List<NoteCandidate> candidates)
|
||||
{
|
||||
if (tr == null || ent == null || candidates == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ent is DBText t)
|
||||
{
|
||||
AddNoteCandidateIfMatch(t.TextString, "DBText", t.ObjectId, tr, candidates);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ent is MText mt)
|
||||
{
|
||||
var plain = SimplifyMText(mt.Contents);
|
||||
AddNoteCandidateIfMatch(plain, "MText", mt.ObjectId, tr, candidates);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ent is BlockReference br)
|
||||
{
|
||||
foreach (ObjectId attId in br.AttributeCollection)
|
||||
{
|
||||
var att = tr.GetObject(attId, OpenMode.ForRead, false) as AttributeReference;
|
||||
if (att == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
AddNoteCandidateIfMatch(att.TextString, "Attribute", attId, tr, candidates);
|
||||
}
|
||||
|
||||
var blockId = br.BlockTableRecord;
|
||||
if (blockId.IsNull)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (visitedBlocks == null)
|
||||
{
|
||||
visitedBlocks = new HashSet<ObjectId>();
|
||||
}
|
||||
|
||||
if (visitedBlocks.Contains(blockId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
visitedBlocks.Add(blockId);
|
||||
|
||||
var btr = tr.GetObject(blockId, OpenMode.ForRead, false) as BlockTableRecord;
|
||||
if (btr == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (ObjectId childId in btr)
|
||||
{
|
||||
var child = tr.GetObject(childId, OpenMode.ForRead, false) as Entity;
|
||||
if (child != null)
|
||||
{
|
||||
CollectNoteCandidates(tr, child, visitedBlocks, candidates);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddNoteCandidateIfMatch(string plainText, string kind, ObjectId id, Transaction tr, List<NoteCandidate> candidates)
|
||||
{
|
||||
if (!IsLikelyNoteText(plainText))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var score = ComputeNoteScore(plainText);
|
||||
if (score <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
candidates.Add(new NoteCandidate
|
||||
{
|
||||
Score = score,
|
||||
Kind = kind,
|
||||
PlainText = plainText,
|
||||
Apply = rendered => ApplyNoteTextToObject(tr, id, kind, rendered)
|
||||
});
|
||||
}
|
||||
|
||||
private static bool IsLikelyNoteText(string plain)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(plain))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!plain.Contains("*"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (plain.Contains("附注"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// fallback: numbered note line
|
||||
return plain.Contains("1*") || plain.Contains("1 *");
|
||||
}
|
||||
|
||||
private static int ComputeNoteScore(string plain)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(plain))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
var score = 0;
|
||||
var lines = SplitLines(plain).ToList();
|
||||
|
||||
if (plain.Contains("附注"))
|
||||
{
|
||||
score += 10;
|
||||
}
|
||||
|
||||
if (lines.Count > 0 && string.Equals(lines[0].Trim(), "附注", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
score += 10;
|
||||
}
|
||||
|
||||
if (plain.Contains("*"))
|
||||
{
|
||||
score += 5;
|
||||
}
|
||||
|
||||
if (plain.Contains("1*"))
|
||||
{
|
||||
score += 3;
|
||||
}
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
private static IEnumerable<string> SplitLines(string text)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
{
|
||||
return Enumerable.Empty<string>();
|
||||
}
|
||||
|
||||
return (text ?? string.Empty)
|
||||
.Replace("\r\n", "\n")
|
||||
.Replace("\r", "\n")
|
||||
.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(s => (s ?? string.Empty).Trim());
|
||||
}
|
||||
|
||||
private static void ApplyNoteTextToObject(Transaction tr, ObjectId id, string kind, string rendered)
|
||||
{
|
||||
if (tr == null || id.IsNull)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
rendered = rendered ?? string.Empty;
|
||||
|
||||
// We try multiple types regardless of recorded kind to be safe.
|
||||
var obj = tr.GetObject(id, OpenMode.ForWrite, false);
|
||||
if (obj is MText mt)
|
||||
{
|
||||
mt.Contents = ToMTextContents(rendered);
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj is DBText t)
|
||||
{
|
||||
t.TextString = CollapseToSingleLine(rendered);
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj is AttributeReference att)
|
||||
{
|
||||
att.TextString = CollapseToSingleLine(rendered);
|
||||
}
|
||||
}
|
||||
|
||||
private static string ToMTextContents(string plainText)
|
||||
{
|
||||
if (string.IsNullOrEmpty(plainText))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
var s = plainText
|
||||
.Replace("\r\n", "\n")
|
||||
.Replace("\r", "\n")
|
||||
.Replace("\n", "\\P");
|
||||
return s;
|
||||
}
|
||||
|
||||
private static string CollapseToSingleLine(string text)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
var parts = (text ?? string.Empty)
|
||||
.Replace("\r\n", "\n")
|
||||
.Replace("\r", "\n")
|
||||
.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(s => (s ?? string.Empty).Trim())
|
||||
.Where(s => s.Length > 0)
|
||||
.ToArray();
|
||||
|
||||
return string.Join(" ", parts);
|
||||
}
|
||||
|
||||
public static void KeepOnlyLayout(Document doc, string layoutName)
|
||||
{
|
||||
if (doc == null)
|
||||
|
||||
@ -65,9 +65,12 @@
|
||||
<Compile Include="Common\ParamCatalogStore.cs" />
|
||||
<Compile Include="Common\ParamDefinition.cs" />
|
||||
<Compile Include="Common\ParamValueType.cs" />
|
||||
<Compile Include="Common\NotePlaceholderBinding.cs" />
|
||||
<Compile Include="Common\NoteTemplateEngine.cs" />
|
||||
<Compile Include="Common\TemplateKeyBuilder.cs" />
|
||||
<Compile Include="Common\TemplateSchemaDefinition.cs" />
|
||||
<Compile Include="Common\TemplateSchemas.cs" />
|
||||
<Compile Include="Common\TemplateSchemaDefaults.cs" />
|
||||
<Compile Include="Common\TemplateSchemaStore.cs" />
|
||||
<Compile Include="Common\DropdownOptions.cs" />
|
||||
<Compile Include="Common\DropdownOptionsStore.cs" />
|
||||
|
||||
11
Common/NotePlaceholderBinding.cs
Normal file
11
Common/NotePlaceholderBinding.cs
Normal file
@ -0,0 +1,11 @@
|
||||
using System;
|
||||
|
||||
namespace CadParamPluging.Common
|
||||
{
|
||||
[Serializable]
|
||||
public class NotePlaceholderBinding
|
||||
{
|
||||
public int Index { get; set; }
|
||||
public string ParamKey { get; set; }
|
||||
}
|
||||
}
|
||||
293
Common/NoteTemplateEngine.cs
Normal file
293
Common/NoteTemplateEngine.cs
Normal file
@ -0,0 +1,293 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace CadParamPluging.Common
|
||||
{
|
||||
public static class NoteTemplateEngine
|
||||
{
|
||||
public sealed class PlaceholderOccurrence
|
||||
{
|
||||
public int Index { get; set; }
|
||||
public int Start { get; set; }
|
||||
public int LineNumber { get; set; }
|
||||
public int ColumnInLine { get; set; }
|
||||
public string LineText { get; set; }
|
||||
}
|
||||
|
||||
public static int CountPlaceholders(string text)
|
||||
{
|
||||
if (string.IsNullOrEmpty(text))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
var count = 0;
|
||||
for (var i = 0; i < text.Length;)
|
||||
{
|
||||
if (text[i] != '*')
|
||||
{
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
var j = i;
|
||||
while (j < text.Length && text[j] == '*')
|
||||
{
|
||||
j++;
|
||||
}
|
||||
|
||||
var len = j - i;
|
||||
if (len != 4)
|
||||
{
|
||||
count += len;
|
||||
}
|
||||
|
||||
i = j;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
public static List<PlaceholderOccurrence> ParseOccurrences(string text)
|
||||
{
|
||||
var result = new List<PlaceholderOccurrence>();
|
||||
if (string.IsNullOrEmpty(text))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
var s = NormalizeNewLines(text);
|
||||
|
||||
// Keep empty lines for correct line number mapping.
|
||||
var lines = s.Split(new[] { '\n' }, StringSplitOptions.None);
|
||||
|
||||
// Build line start offsets.
|
||||
var lineStartOffsets = new int[lines.Length];
|
||||
var offset = 0;
|
||||
for (var i = 0; i < lines.Length; i++)
|
||||
{
|
||||
lineStartOffsets[i] = offset;
|
||||
offset += (lines[i] ?? string.Empty).Length;
|
||||
if (i < lines.Length - 1)
|
||||
{
|
||||
offset += 1; // '\n'
|
||||
}
|
||||
}
|
||||
|
||||
var placeholderIndex = 0;
|
||||
var lineIdx = 0;
|
||||
var lineStart = lines.Length > 0 ? lineStartOffsets[0] : 0;
|
||||
|
||||
for (var i = 0; i < s.Length;)
|
||||
{
|
||||
// Move line index if needed.
|
||||
while (lineIdx + 1 < lineStartOffsets.Length && i >= lineStartOffsets[lineIdx] + (lines[lineIdx] ?? string.Empty).Length + 1)
|
||||
{
|
||||
lineIdx++;
|
||||
lineStart = lineStartOffsets[lineIdx];
|
||||
}
|
||||
|
||||
if (s[i] != '*')
|
||||
{
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
var j = i;
|
||||
while (j < s.Length && s[j] == '*')
|
||||
{
|
||||
j++;
|
||||
}
|
||||
|
||||
var runLen = j - i;
|
||||
if (runLen == 4)
|
||||
{
|
||||
i = j;
|
||||
continue;
|
||||
}
|
||||
|
||||
var lineText = (lineIdx >= 0 && lineIdx < lines.Length) ? (lines[lineIdx] ?? string.Empty) : string.Empty;
|
||||
|
||||
for (var k = 0; k < runLen; k++)
|
||||
{
|
||||
placeholderIndex++;
|
||||
var pos = i + k;
|
||||
var col = pos - lineStart;
|
||||
if (col < 0) col = 0;
|
||||
|
||||
result.Add(new PlaceholderOccurrence
|
||||
{
|
||||
Index = placeholderIndex,
|
||||
Start = pos,
|
||||
LineNumber = lineIdx + 1,
|
||||
ColumnInLine = col,
|
||||
LineText = lineText
|
||||
});
|
||||
}
|
||||
|
||||
i = j;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static string BuildNumberedPreviewText(string templateText)
|
||||
{
|
||||
templateText = templateText ?? string.Empty;
|
||||
var s = NormalizeNewLines(templateText);
|
||||
|
||||
var sb = new StringBuilder(s.Length + 64);
|
||||
var placeholderIndex = 0;
|
||||
|
||||
for (var i = 0; i < s.Length;)
|
||||
{
|
||||
if (s[i] != '*')
|
||||
{
|
||||
sb.Append(s[i]);
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
var j = i;
|
||||
while (j < s.Length && s[j] == '*')
|
||||
{
|
||||
j++;
|
||||
}
|
||||
|
||||
var runLen = j - i;
|
||||
if (runLen == 4)
|
||||
{
|
||||
sb.Append("****");
|
||||
i = j;
|
||||
continue;
|
||||
}
|
||||
|
||||
for (var k = 0; k < runLen; k++)
|
||||
{
|
||||
placeholderIndex++;
|
||||
sb.Append("【#");
|
||||
sb.Append(placeholderIndex);
|
||||
sb.Append("】");
|
||||
}
|
||||
|
||||
i = j;
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static string Render(string templateText, IEnumerable<NotePlaceholderBinding> bindings, Func<string, string> getValue)
|
||||
{
|
||||
templateText = templateText ?? string.Empty;
|
||||
|
||||
var s = NormalizeNewLines(templateText);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
var sb = new StringBuilder(s.Length + 64);
|
||||
var placeholderIndex = 0;
|
||||
|
||||
for (var i = 0; i < s.Length;)
|
||||
{
|
||||
if (s[i] != '*')
|
||||
{
|
||||
sb.Append(s[i]);
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
var j = i;
|
||||
while (j < s.Length && s[j] == '*')
|
||||
{
|
||||
j++;
|
||||
}
|
||||
|
||||
var runLen = j - i;
|
||||
if (runLen == 4)
|
||||
{
|
||||
sb.Append("****");
|
||||
i = j;
|
||||
continue;
|
||||
}
|
||||
|
||||
for (var k = 0; k < runLen; k++)
|
||||
{
|
||||
placeholderIndex++;
|
||||
if (map.TryGetValue(placeholderIndex, out var key) && getValue != null)
|
||||
{
|
||||
var v = getValue(key);
|
||||
if (!string.IsNullOrWhiteSpace(v))
|
||||
{
|
||||
sb.Append(v);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
sb.Append('*');
|
||||
}
|
||||
|
||||
i = j;
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When the same ParamKey is bound by multiple placeholders, each placeholder should have its own value key,
|
||||
/// otherwise values would overwrite each other.
|
||||
/// This method returns an equivalent bindings list where ParamKey is replaced by an effective value key.
|
||||
/// </summary>
|
||||
public static List<NotePlaceholderBinding> BuildEffectiveValueKeyBindings(IEnumerable<NotePlaceholderBinding> bindings)
|
||||
{
|
||||
var list = (bindings ?? Enumerable.Empty<NotePlaceholderBinding>())
|
||||
.Where(b => b != null && b.Index > 0 && !string.IsNullOrWhiteSpace(b.ParamKey))
|
||||
.Select(b => new NotePlaceholderBinding { Index = b.Index, ParamKey = b.ParamKey.Trim() })
|
||||
.ToList();
|
||||
|
||||
var dup = list
|
||||
.GroupBy(b => b.ParamKey, StringComparer.OrdinalIgnoreCase)
|
||||
.ToDictionary(g => g.Key, g => g.Count(), StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
foreach (var b in list)
|
||||
{
|
||||
if (dup.TryGetValue(b.ParamKey, out var cnt) && cnt > 1)
|
||||
{
|
||||
b.ParamKey = string.Format("{0}@{1}", b.ParamKey, b.Index);
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
private static string NormalizeNewLines(string text)
|
||||
{
|
||||
return (text ?? string.Empty)
|
||||
.Replace("\r\n", "\n")
|
||||
.Replace("\r", "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -69,7 +69,7 @@ namespace CadParamPluging.Common
|
||||
DefaultValue = "",
|
||||
Hint = "数据库下拉列表(支持数据扩充),点选映射",
|
||||
Order = 100,
|
||||
Group = "材料"
|
||||
Group = "备注参数"
|
||||
},
|
||||
new ParamDefinition
|
||||
{
|
||||
@ -81,7 +81,7 @@ namespace CadParamPluging.Common
|
||||
EnumOptions = new List<string> { "一般件", "关键件", "重要件" },
|
||||
Hint = "下拉列表(一般件/关键件/重要件),点选;关键件/重要件需加方框标注",
|
||||
Order = 110,
|
||||
Group = "材料"
|
||||
Group = "备注参数"
|
||||
},
|
||||
new ParamDefinition
|
||||
{
|
||||
@ -92,7 +92,7 @@ namespace CadParamPluging.Common
|
||||
DefaultValue = "",
|
||||
Hint = "多选下拉列表(支持数据扩充),选项用\"或\"连接",
|
||||
Order = 120,
|
||||
Group = "材料"
|
||||
Group = "备注参数"
|
||||
},
|
||||
new ParamDefinition
|
||||
{
|
||||
@ -103,7 +103,7 @@ namespace CadParamPluging.Common
|
||||
DefaultValue = "",
|
||||
Hint = "多选下拉列表(支持数据扩充),选项用\"或\"连接",
|
||||
Order = 130,
|
||||
Group = "材料"
|
||||
Group = "备注参数"
|
||||
},
|
||||
|
||||
// --- Inspection / heat treatment ---
|
||||
@ -117,7 +117,7 @@ namespace CadParamPluging.Common
|
||||
EnumOptions = new List<string> { "Ⅱ", "Ⅱa", "Ⅱ大", "Ⅲ", "Ⅳ" },
|
||||
Hint = "单选下拉列表",
|
||||
Order = 200,
|
||||
Group = "检验/热处理"
|
||||
Group = "备注参数"
|
||||
},
|
||||
new ParamDefinition
|
||||
{
|
||||
@ -129,7 +129,7 @@ namespace CadParamPluging.Common
|
||||
EnumOptions = new List<string> { "Ⅱ", "Ⅲ", "Ⅳ" },
|
||||
Hint = "单选下拉列表",
|
||||
Order = 210,
|
||||
Group = "检验/热处理"
|
||||
Group = "备注参数"
|
||||
},
|
||||
new ParamDefinition
|
||||
{
|
||||
@ -141,7 +141,7 @@ namespace CadParamPluging.Common
|
||||
EnumOptions = new List<string> { "预备热处理", "热处理" },
|
||||
Hint = "单选下拉列表",
|
||||
Order = 220,
|
||||
Group = "检验/热处理"
|
||||
Group = "备注参数"
|
||||
},
|
||||
new ParamDefinition
|
||||
{
|
||||
@ -152,7 +152,7 @@ namespace CadParamPluging.Common
|
||||
DefaultValue = "",
|
||||
Hint = "数据库下拉列表(支持数据扩充),点选",
|
||||
Order = 230,
|
||||
Group = "检验/热处理"
|
||||
Group = "备注参数"
|
||||
},
|
||||
new ParamDefinition
|
||||
{
|
||||
@ -163,7 +163,7 @@ namespace CadParamPluging.Common
|
||||
DefaultValue = "",
|
||||
Hint = "组合选择:硬度符号+连接符+数值,可空",
|
||||
Order = 240,
|
||||
Group = "检验/热处理"
|
||||
Group = "备注参数"
|
||||
},
|
||||
new ParamDefinition
|
||||
{
|
||||
@ -174,7 +174,7 @@ namespace CadParamPluging.Common
|
||||
DefaultValue = "",
|
||||
Hint = "手动输入,可空",
|
||||
Order = 250,
|
||||
Group = "检验/热处理"
|
||||
Group = "备注参数"
|
||||
},
|
||||
|
||||
// --- Marking / notes ---
|
||||
@ -187,7 +187,7 @@ namespace CadParamPluging.Common
|
||||
DefaultValue = "",
|
||||
Hint = "多选下拉列表:零件尾号/零件号、材料牌号、熔炼炉号、锭节号、热处理炉次号、热处理炉批号、批次号",
|
||||
Order = 300,
|
||||
Group = "标注/说明"
|
||||
Group = "备注参数"
|
||||
},
|
||||
new ParamDefinition
|
||||
{
|
||||
@ -198,7 +198,7 @@ namespace CadParamPluging.Common
|
||||
DefaultValue = "1",
|
||||
Hint = "手动编辑,默认为\"1\"",
|
||||
Order = 310,
|
||||
Group = "标注/说明"
|
||||
Group = "备注参数"
|
||||
},
|
||||
new ParamDefinition
|
||||
{
|
||||
@ -210,7 +210,7 @@ namespace CadParamPluging.Common
|
||||
EnumOptions = new List<string> { "空", "0.8", "1.6", "3.2" },
|
||||
Hint = "单选下拉列表,默认为空",
|
||||
Order = 320,
|
||||
Group = "标注/说明"
|
||||
Group = "备注参数"
|
||||
},
|
||||
new ParamDefinition
|
||||
{
|
||||
@ -222,7 +222,7 @@ namespace CadParamPluging.Common
|
||||
EnumOptions = new List<string> { "空", "A", "B", "C", "D", "E", "F" },
|
||||
Hint = "下拉列表(A-F),可手动编辑,支持空选项",
|
||||
Order = 330,
|
||||
Group = "标注/说明"
|
||||
Group = "备注参数"
|
||||
},
|
||||
new ParamDefinition
|
||||
{
|
||||
@ -233,7 +233,7 @@ namespace CadParamPluging.Common
|
||||
DefaultValue = "",
|
||||
Hint = "下拉列表模板\"X个A零件和X个B零件或X个C零件\",点选后可编辑",
|
||||
Order = 340,
|
||||
Group = "标注/说明"
|
||||
Group = "备注参数"
|
||||
},
|
||||
new ParamDefinition
|
||||
{
|
||||
@ -244,7 +244,7 @@ namespace CadParamPluging.Common
|
||||
DefaultValue = "",
|
||||
Hint = "下拉列表模板\"每批/每*件多投*件\",点选后可编辑",
|
||||
Order = 350,
|
||||
Group = "标注/说明"
|
||||
Group = "备注参数"
|
||||
},
|
||||
new ParamDefinition
|
||||
{
|
||||
@ -255,7 +255,7 @@ namespace CadParamPluging.Common
|
||||
DefaultValue = "",
|
||||
Hint = "多选下拉列表:传递抗拉强度实测值、机加夹头标注、使用温度300℃以上、每批投产数量限制,点选后可编辑",
|
||||
Order = 360,
|
||||
Group = "标注/说明"
|
||||
Group = "备注参数"
|
||||
},
|
||||
|
||||
// --- Template key fields (also exist in panel dropdowns) ---
|
||||
@ -457,15 +457,37 @@ namespace CadParamPluging.Common
|
||||
},
|
||||
new ParamDefinition
|
||||
{
|
||||
Key = "PartDimensionsPrime",
|
||||
Label = "零件尺寸(Φ'1/Φ'2/H'1等)",
|
||||
Type = ParamValueType.String,
|
||||
Key = "OuterDiameter1Prime",
|
||||
Label = "外径Φ'1",
|
||||
Type = ParamValueType.Double,
|
||||
Required = false,
|
||||
DefaultValue = "",
|
||||
Hint = "手动编辑",
|
||||
Order = 630,
|
||||
Group = "尺寸-环形"
|
||||
},
|
||||
new ParamDefinition
|
||||
{
|
||||
Key = "InnerDiameter2Prime",
|
||||
Label = "内径Φ'2",
|
||||
Type = ParamValueType.Double,
|
||||
Required = false,
|
||||
DefaultValue = "",
|
||||
Hint = "手动编辑",
|
||||
Order = 640,
|
||||
Group = "尺寸-环形"
|
||||
},
|
||||
new ParamDefinition
|
||||
{
|
||||
Key = "Height1Prime",
|
||||
Label = "高度H'1",
|
||||
Type = ParamValueType.Double,
|
||||
Required = false,
|
||||
DefaultValue = "",
|
||||
Hint = "手动编辑",
|
||||
Order = 650,
|
||||
Group = "尺寸-环形"
|
||||
},
|
||||
|
||||
// --- Dimensions: shaft/rod (diameter/length) ---
|
||||
new ParamDefinition
|
||||
|
||||
@ -1,5 +1,8 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace CadParamPluging.Common
|
||||
@ -65,11 +68,51 @@ namespace CadParamPluging.Common
|
||||
}
|
||||
}
|
||||
|
||||
// Migration: move remark-like fields into "备注参数" group if they are still using older default groups.
|
||||
var remarkKeys = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
"MaterialGrade",
|
||||
"FeatureCategory",
|
||||
"MaterialTechnicalCondition",
|
||||
"ForgingTechnicalCondition",
|
||||
"InspectionCategory",
|
||||
"ForgingCategory",
|
||||
"HeatTreatmentState",
|
||||
"HeatTreatmentProcess",
|
||||
"Hardness",
|
||||
"UltrasonicRequirement",
|
||||
"MarkingContent",
|
||||
"PartsPerForging",
|
||||
"SurfaceRoughness",
|
||||
"DesignRevision",
|
||||
"SerialInfo",
|
||||
"MachiningSpecimenRequirement",
|
||||
"AdditionalNotes"
|
||||
};
|
||||
foreach (var key in remarkKeys)
|
||||
{
|
||||
var existing = obj.FindByKey(key);
|
||||
if (existing == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var g = existing.Group ?? string.Empty;
|
||||
if (string.IsNullOrWhiteSpace(g)
|
||||
|| string.Equals(g, "材料", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(g, "检验/热处理", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(g, "标注/说明", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
existing.Group = "备注参数";
|
||||
}
|
||||
}
|
||||
|
||||
// Migration: remove legacy demo parameters if they exist.
|
||||
obj.Items = (obj.Items ?? new System.Collections.Generic.List<ParamDefinition>())
|
||||
.Where(x => x != null
|
||||
&& !string.Equals(x.Key, "Width", StringComparison.OrdinalIgnoreCase)
|
||||
&& !string.Equals(x.Key, "Height", StringComparison.OrdinalIgnoreCase))
|
||||
&& !string.Equals(x.Key, "Height", StringComparison.OrdinalIgnoreCase)
|
||||
&& !string.Equals(x.Key, "PartDimensionsPrime", StringComparison.OrdinalIgnoreCase))
|
||||
.ToList();
|
||||
|
||||
obj.Normalize();
|
||||
|
||||
43
Common/TemplateSchemaDefaults.cs
Normal file
43
Common/TemplateSchemaDefaults.cs
Normal file
@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace CadParamPluging.Common
|
||||
{
|
||||
public static class TemplateSchemaDefaults
|
||||
{
|
||||
public static TemplateSchemas CreateDefault()
|
||||
{
|
||||
var schemas = new TemplateSchemas();
|
||||
|
||||
// 交付状态=车加工, 工艺方法=轧制, 结构特征=环形, 特殊条件=无
|
||||
// TemplateKey: "车加工|轧制|环形|" ("无" 会被归一化为空)
|
||||
schemas.Items.Add(new TemplateSchemaDefinition
|
||||
{
|
||||
ProjectType = "车加工",
|
||||
DrawingType = "轧制",
|
||||
SheetSize = "环形",
|
||||
Scale = "无",
|
||||
DisplayName = "环形(车加工/轧制)",
|
||||
SelectedParamKeys = new List<string>
|
||||
{
|
||||
"OuterDiameter1",
|
||||
"OuterDiameter1TolPlus",
|
||||
"OuterDiameter1TolMinus",
|
||||
"InnerDiameter2",
|
||||
"InnerDiameter2TolPlus",
|
||||
"InnerDiameter2TolMinus",
|
||||
"Height1",
|
||||
"Height1TolPlus",
|
||||
"Height1TolMinus",
|
||||
"MinWallThickness",
|
||||
"OuterDiameter1Prime",
|
||||
"InnerDiameter2Prime",
|
||||
"Height1Prime"
|
||||
}
|
||||
});
|
||||
|
||||
schemas.Normalize();
|
||||
return schemas;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -18,9 +18,14 @@ namespace CadParamPluging.Common
|
||||
|
||||
public List<string> SelectedParamKeys { get; set; }
|
||||
|
||||
public string NoteTemplateText { get; set; }
|
||||
public List<NotePlaceholderBinding> NoteBindings { get; set; }
|
||||
|
||||
public TemplateSchemaDefinition()
|
||||
{
|
||||
SelectedParamKeys = new List<string>();
|
||||
NoteTemplateText = string.Empty;
|
||||
NoteBindings = new List<NotePlaceholderBinding>();
|
||||
}
|
||||
|
||||
public void Normalize()
|
||||
@ -31,6 +36,8 @@ namespace CadParamPluging.Common
|
||||
Scale = (Scale ?? string.Empty).Trim();
|
||||
DisplayName = (DisplayName ?? string.Empty).Trim();
|
||||
|
||||
NoteTemplateText = NoteTemplateText ?? string.Empty;
|
||||
|
||||
TemplateKey = TemplateKeyBuilder.Build(ProjectType, DrawingType, SheetSize, Scale);
|
||||
|
||||
if (SelectedParamKeys == null)
|
||||
@ -43,6 +50,18 @@ namespace CadParamPluging.Common
|
||||
.Select(s => s.Trim())
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
|
||||
if (NoteBindings == null)
|
||||
{
|
||||
NoteBindings = new List<NotePlaceholderBinding>();
|
||||
}
|
||||
|
||||
NoteBindings = NoteBindings
|
||||
.Where(b => b != null && b.Index > 0 && !string.IsNullOrWhiteSpace(b.ParamKey))
|
||||
.GroupBy(b => b.Index)
|
||||
.Select(g => g.First())
|
||||
.OrderBy(b => b.Index)
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,10 +22,13 @@ namespace CadParamPluging.Common
|
||||
{
|
||||
try
|
||||
{
|
||||
var defaults = TemplateSchemaDefaults.CreateDefault();
|
||||
defaults.Normalize();
|
||||
|
||||
var path = GetFilePath();
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
return new TemplateSchemas();
|
||||
return defaults;
|
||||
}
|
||||
|
||||
using (var stream = File.OpenRead(path))
|
||||
@ -34,7 +37,25 @@ namespace CadParamPluging.Common
|
||||
var obj = serializer.Deserialize(stream) as TemplateSchemas;
|
||||
if (obj == null)
|
||||
{
|
||||
return new TemplateSchemas();
|
||||
return defaults;
|
||||
}
|
||||
|
||||
obj.Normalize();
|
||||
|
||||
// Merge: keep local modifications, but add any missing built-in defaults.
|
||||
foreach (var def in defaults.Items ?? Enumerable.Empty<TemplateSchemaDefinition>())
|
||||
{
|
||||
if (def == null || string.IsNullOrWhiteSpace(def.TemplateKey))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var exists = (obj.Items ?? Enumerable.Empty<TemplateSchemaDefinition>())
|
||||
.Any(x => x != null && string.Equals(x.TemplateKey, def.TemplateKey, StringComparison.OrdinalIgnoreCase));
|
||||
if (!exists)
|
||||
{
|
||||
obj.Items.Add(def);
|
||||
}
|
||||
}
|
||||
|
||||
obj.Normalize();
|
||||
@ -51,7 +72,7 @@ namespace CadParamPluging.Common
|
||||
}
|
||||
catch
|
||||
{
|
||||
return new TemplateSchemas();
|
||||
return TemplateSchemaDefaults.CreateDefault();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Globalization;
|
||||
@ -12,10 +13,16 @@ namespace CadParamPluging.UI
|
||||
{
|
||||
private readonly TableLayoutPanel _grid;
|
||||
private readonly Dictionary<string, Control> _controlsByKey;
|
||||
private readonly Dictionary<string, ParamDefinition> _defsByControlKey;
|
||||
private readonly ParamCatalog _catalog;
|
||||
private readonly TemplateSchemaDefinition _schema;
|
||||
private readonly ToolTip _toolTip;
|
||||
|
||||
private readonly TabControl _notePreviewTabs;
|
||||
private readonly RichTextBox _rtbNoteNumbered;
|
||||
private readonly RichTextBox _rtbNoteRendered;
|
||||
private Dictionary<int, NoteTemplateEngine.PlaceholderOccurrence> _occByIndex;
|
||||
|
||||
public ParamBag Result { get; private set; }
|
||||
|
||||
public DrawingParamsForm(ParamCatalog catalog, TemplateSchemaDefinition schema)
|
||||
@ -25,14 +32,16 @@ namespace CadParamPluging.UI
|
||||
_schema = schema;
|
||||
|
||||
Text = "填写参数";
|
||||
Size = new Size(560, 620);
|
||||
Size = new Size(980, 680);
|
||||
MinimumSize = new Size(900, 620);
|
||||
StartPosition = FormStartPosition.CenterParent;
|
||||
FormBorderStyle = FormBorderStyle.FixedDialog;
|
||||
MaximizeBox = false;
|
||||
MinimizeBox = false;
|
||||
FormBorderStyle = FormBorderStyle.Sizable;
|
||||
MaximizeBox = true;
|
||||
MinimizeBox = true;
|
||||
ShowInTaskbar = false;
|
||||
|
||||
_controlsByKey = new Dictionary<string, Control>(StringComparer.OrdinalIgnoreCase);
|
||||
_defsByControlKey = new Dictionary<string, ParamDefinition>(StringComparer.OrdinalIgnoreCase);
|
||||
_toolTip = new ToolTip();
|
||||
|
||||
var root = new TableLayoutPanel
|
||||
@ -59,6 +68,17 @@ namespace CadParamPluging.UI
|
||||
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,
|
||||
@ -74,6 +94,16 @@ namespace CadParamPluging.UI
|
||||
|
||||
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,
|
||||
@ -87,12 +117,66 @@ namespace CadParamPluging.UI
|
||||
buttons.Controls.Add(btnOk);
|
||||
|
||||
root.Controls.Add(header, 0, 0);
|
||||
root.Controls.Add(scrollPanel, 0, 1);
|
||||
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();
|
||||
};
|
||||
}
|
||||
|
||||
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 static string BuildHeaderText(TemplateSchemaDefinition schema)
|
||||
@ -108,8 +192,48 @@ namespace CadParamPluging.UI
|
||||
|
||||
private void BuildFields()
|
||||
{
|
||||
var keys = (_schema?.SelectedParamKeys ?? new List<string>()).ToList();
|
||||
if (keys.Count == 0)
|
||||
_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 mainDefs = new List<ParamDefinition>();
|
||||
var seenMain = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
foreach (var key in (_schema?.SelectedParamKeys ?? new List<string>()))
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
mainDefs.Add(def);
|
||||
}
|
||||
|
||||
var noteBindings = (_schema?.NoteBindings ?? new List<NotePlaceholderBinding>())
|
||||
.Where(b => b != null && b.Index > 0 && !string.IsNullOrWhiteSpace(b.ParamKey))
|
||||
.OrderBy(b => b.Index)
|
||||
.ToList();
|
||||
|
||||
if (mainDefs.Count == 0 && noteBindings.Count == 0)
|
||||
{
|
||||
var rowIdx = _grid.RowCount++;
|
||||
_grid.RowStyles.Add(new RowStyle(SizeType.AutoSize));
|
||||
@ -118,24 +242,99 @@ namespace CadParamPluging.UI
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var key in keys)
|
||||
if (mainDefs.Count > 0)
|
||||
{
|
||||
var def = _catalog.FindByKey(key);
|
||||
AddSectionHeader("模板参数");
|
||||
foreach (var def in mainDefs)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
AddField(def);
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AddField(ParamDefinition def)
|
||||
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));
|
||||
|
||||
var labelText = def.Label;
|
||||
if (string.IsNullOrWhiteSpace(labelText))
|
||||
{
|
||||
labelText = def.Label;
|
||||
}
|
||||
|
||||
if (def.Required)
|
||||
{
|
||||
labelText += " *";
|
||||
@ -151,9 +350,96 @@ namespace CadParamPluging.UI
|
||||
_toolTip.SetToolTip(ctrl, def.Hint);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(extraToolTip))
|
||||
{
|
||||
_toolTip.SetToolTip(lbl, extraToolTip);
|
||||
_toolTip.SetToolTip(ctrl, extraToolTip);
|
||||
}
|
||||
|
||||
_grid.Controls.Add(lbl, 0, rowIdx);
|
||||
_grid.Controls.Add(ctrl, 1, rowIdx);
|
||||
_controlsByKey[def.Key] = ctrl;
|
||||
_controlsByKey[controlKey] = ctrl;
|
||||
_defsByControlKey[controlKey] = def;
|
||||
|
||||
AttachPreviewUpdate(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 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);
|
||||
_rtbNoteRendered.Text = NoteTemplateEngine.Render(noteTemplate, effective, tempBag.GetString);
|
||||
}
|
||||
|
||||
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 (!_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)
|
||||
@ -256,8 +542,7 @@ namespace CadParamPluging.UI
|
||||
continue;
|
||||
}
|
||||
|
||||
var def = _catalog.FindByKey(key);
|
||||
if (def == null)
|
||||
if (!_defsByControlKey.TryGetValue(key, out var def) || def == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@ -270,7 +555,7 @@ namespace CadParamPluging.UI
|
||||
return;
|
||||
}
|
||||
|
||||
bag.Set(def.Key, value);
|
||||
bag.Set(key, value);
|
||||
}
|
||||
|
||||
Result = bag;
|
||||
|
||||
@ -589,6 +589,24 @@ namespace CadParamPluging.UI
|
||||
AppendLog($"已移除模板匹配参数标注文本: {removed} 处");
|
||||
}
|
||||
|
||||
var noteResult = TemplateDrawingService.ApplyNoteTemplate(
|
||||
ctx,
|
||||
_selectedTemplate.LayoutName,
|
||||
_selectedModelWindow.HasValue,
|
||||
schema,
|
||||
bag);
|
||||
if (noteResult != null)
|
||||
{
|
||||
if (noteResult.Applied)
|
||||
{
|
||||
AppendLog($"附注替换成功(目标={noteResult.TargetKind}, 占位符={noteResult.PlaceholderCountInDwg})");
|
||||
}
|
||||
else if (!string.IsNullOrWhiteSpace(noteResult.Message))
|
||||
{
|
||||
AppendLog($"附注替换跳过: {noteResult.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
var cadService = new CadDrawingService(ctx);
|
||||
DomainFacade.DrawByParams(_selectedTemplate, drawingParams, cadService);
|
||||
ctx.Commit();
|
||||
|
||||
@ -142,7 +142,7 @@ namespace CadParamPluging.UI
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show(this, $"打开模板参数绑定失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
MessageBox.Show(this, $"打开模板参数绑定失败:\n\n{ex}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -18,9 +18,22 @@ namespace CadParamPluging.UI
|
||||
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 TemplateSchemas _schemas;
|
||||
|
||||
private sealed class ParamOption
|
||||
{
|
||||
public string Key { get; set; }
|
||||
public string Display { get; set; }
|
||||
}
|
||||
|
||||
public TemplateSchemaBindingForm(
|
||||
System.Collections.Generic.IEnumerable<string> projectTypes,
|
||||
System.Collections.Generic.IEnumerable<string> drawingTypes,
|
||||
@ -30,16 +43,23 @@ namespace CadParamPluging.UI
|
||||
{
|
||||
Text = "模板参数绑定";
|
||||
Size = new Size(920, 620);
|
||||
MinimumSize = new Size(920, 620);
|
||||
StartPosition = FormStartPosition.CenterParent;
|
||||
FormBorderStyle = FormBorderStyle.FixedDialog;
|
||||
MaximizeBox = false;
|
||||
MinimizeBox = false;
|
||||
FormBorderStyle = FormBorderStyle.Sizable;
|
||||
MaximizeBox = true;
|
||||
MinimizeBox = true;
|
||||
ShowInTaskbar = false;
|
||||
|
||||
_catalog = (catalog ?? ParamCatalog.CreateDefault()).Clone();
|
||||
_catalog.Normalize();
|
||||
|
||||
_schemas = TemplateSchemaStore.Load();
|
||||
_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
|
||||
@ -55,8 +75,6 @@ namespace CadParamPluging.UI
|
||||
root.RowStyles.Add(new RowStyle(SizeType.AutoSize));
|
||||
|
||||
_lbSchemas = new ListBox { Dock = DockStyle.Fill };
|
||||
_lbSchemas.SelectedIndexChanged += (_, __) => LoadSelectedSchemaToUi();
|
||||
ReloadSchemaList();
|
||||
|
||||
var leftButtons = new FlowLayoutPanel
|
||||
{
|
||||
@ -155,6 +173,80 @@ namespace CadParamPluging.UI
|
||||
paramsButtonPanel.Controls.Add(btnSelectAll);
|
||||
paramsButtonPanel.Controls.Add(btnUnselectAll);
|
||||
|
||||
_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,
|
||||
@ -179,7 +271,45 @@ namespace CadParamPluging.UI
|
||||
paramArea.Controls.Add(paramsButtonPanel, 0, 0);
|
||||
paramArea.Controls.Add(_lvParams, 0, 1);
|
||||
|
||||
rightPanel.Controls.Add(paramArea, 0, 2);
|
||||
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
|
||||
{
|
||||
@ -203,10 +333,9 @@ namespace CadParamPluging.UI
|
||||
|
||||
BuildParamListItems();
|
||||
UpdateTemplateKeyLabel();
|
||||
if (_lbSchemas.Items.Count > 0)
|
||||
{
|
||||
_lbSchemas.SelectedIndex = 0;
|
||||
}
|
||||
|
||||
_lbSchemas.SelectedIndexChanged += (_, __) => LoadSelectedSchemaToUi();
|
||||
ReloadSchemaList();
|
||||
}
|
||||
|
||||
private static ComboBox CreateCombo(System.Collections.Generic.IEnumerable<string> items)
|
||||
@ -231,7 +360,10 @@ namespace CadParamPluging.UI
|
||||
try
|
||||
{
|
||||
_lvParams.Items.Clear();
|
||||
foreach (var p in (_catalog.Items ?? Enumerable.Empty<ParamDefinition>()).OrderBy(x => x.Order))
|
||||
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 })
|
||||
{
|
||||
@ -295,6 +427,7 @@ namespace CadParamPluging.UI
|
||||
|
||||
_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)
|
||||
@ -329,6 +462,7 @@ namespace CadParamPluging.UI
|
||||
}
|
||||
|
||||
var ordered = (_schemas.Items ?? Enumerable.Empty<TemplateSchemaDefinition>())
|
||||
.Where(x => x != null)
|
||||
.OrderBy(x => x.ProjectType)
|
||||
.ThenBy(x => x.DrawingType)
|
||||
.ThenBy(x => x.SheetSize)
|
||||
@ -341,7 +475,9 @@ namespace CadParamPluging.UI
|
||||
}
|
||||
|
||||
var key = ordered[idx].TemplateKey;
|
||||
return (_schemas.Items ?? new List<TemplateSchemaDefinition>()).FirstOrDefault(x => string.Equals(x.TemplateKey, key, StringComparison.OrdinalIgnoreCase));
|
||||
return (_schemas.Items ?? new List<TemplateSchemaDefinition>())
|
||||
.Where(x => x != null)
|
||||
.FirstOrDefault(x => string.Equals(x.TemplateKey, key, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
private void LoadSelectedSchemaToUi()
|
||||
@ -359,6 +495,7 @@ namespace CadParamPluging.UI
|
||||
_txtDisplayName.Text = schema.DisplayName ?? string.Empty;
|
||||
UpdateTemplateKeyLabel();
|
||||
ApplySchemaToParamList(schema);
|
||||
LoadNoteBindingsToUi(schema);
|
||||
}
|
||||
|
||||
private static void SelectComboByText(ComboBox cb, string value)
|
||||
@ -403,7 +540,9 @@ namespace CadParamPluging.UI
|
||||
};
|
||||
candidate.Normalize();
|
||||
|
||||
if ((_schemas.Items ?? new List<TemplateSchemaDefinition>()).Any(x => string.Equals(x.TemplateKey, candidate.TemplateKey, StringComparison.OrdinalIgnoreCase)))
|
||||
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;
|
||||
@ -434,7 +573,7 @@ namespace CadParamPluging.UI
|
||||
}
|
||||
|
||||
_schemas.Items = (_schemas.Items ?? Enumerable.Empty<TemplateSchemaDefinition>())
|
||||
.Where(x => !string.Equals(x.TemplateKey, schema.TemplateKey, StringComparison.OrdinalIgnoreCase))
|
||||
.Where(x => x != null && !string.Equals(x.TemplateKey, schema.TemplateKey, StringComparison.OrdinalIgnoreCase))
|
||||
.ToList();
|
||||
_schemas.Normalize();
|
||||
ReloadSchemaList();
|
||||
@ -485,6 +624,185 @@ namespace CadParamPluging.UI
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
@ -499,14 +817,66 @@ namespace CadParamPluging.UI
|
||||
schema.SheetSize = _cbSheetSize.Text;
|
||||
schema.Scale = _cbScale.Text;
|
||||
schema.DisplayName = _txtDisplayName.Text;
|
||||
schema.SelectedParamKeys = _lvParams.Items
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user