在当前打开的文件中生成图纸

This commit is contained in:
sladro 2026-01-12 15:03:52 +08:00
parent b3d10f49a6
commit 764907f7ea
4 changed files with 457 additions and 464 deletions

View File

@ -329,6 +329,100 @@ namespace CadParamPluging.Cad
return null;
}
private sealed class FeatureCategoryData
{
public string OriginalTemplate;
public string GroupId;
public string Role;
}
private const string FeatureCategoryDictName = "CadParamPluging_FeatureCategory";
private static void SetFeatureCategoryData(Transaction tr, ObjectId id, string originalTemplate, string groupId, string role)
{
var ent = tr.GetObject(id, OpenMode.ForWrite) as Entity;
if (ent == null) return;
if (ent.ExtensionDictionary.IsNull)
{
ent.CreateExtensionDictionary();
}
var dict = (DBDictionary)tr.GetObject(ent.ExtensionDictionary, OpenMode.ForWrite);
var rb = new ResultBuffer(
new TypedValue((int)DxfCode.Text, originalTemplate ?? string.Empty),
new TypedValue((int)DxfCode.Text, groupId ?? string.Empty),
new TypedValue((int)DxfCode.Text, role ?? string.Empty)
);
var xrec = new Xrecord { Data = rb };
if (dict.Contains(FeatureCategoryDictName))
{
var oldId = dict.GetAt(FeatureCategoryDictName);
var oldXrec = (Xrecord)tr.GetObject(oldId, OpenMode.ForWrite);
oldXrec.Data = rb;
}
else
{
dict.SetAt(FeatureCategoryDictName, xrec);
tr.AddNewlyCreatedDBObject(xrec, true);
}
}
private static FeatureCategoryData GetFeatureCategoryData(Transaction tr, ObjectId id)
{
var ent = tr.GetObject(id, OpenMode.ForRead) as Entity;
if (ent == null || ent.ExtensionDictionary.IsNull) return null;
var dict = (DBDictionary)tr.GetObject(ent.ExtensionDictionary, OpenMode.ForRead);
if (!dict.Contains(FeatureCategoryDictName)) return null;
var xrec = (Xrecord)tr.GetObject(dict.GetAt(FeatureCategoryDictName), OpenMode.ForRead);
var rb = xrec.Data;
if (rb == null) return null;
var texts = new List<string>();
foreach (var tv in rb)
{
if (tv.TypeCode == (int)DxfCode.Text)
{
texts.Add(tv.Value as string);
}
}
if (texts.Count == 0) return null;
return new FeatureCategoryData
{
OriginalTemplate = texts.Count >= 1 ? texts[0] : null,
GroupId = texts.Count >= 2 ? texts[1] : null,
Role = texts.Count >= 3 ? texts[2] : null
};
}
private static string ExtractMTextPlainText(string contents)
{
if (string.IsNullOrEmpty(contents)) return string.Empty;
var s = contents;
// Common pattern we generate: {\fSimSun|...;TEXT}
var semi = s.LastIndexOf(';');
if (semi >= 0)
{
s = s.Substring(semi + 1);
}
s = s.Trim();
if (s.EndsWith("}", StringComparison.Ordinal))
{
s = s.Substring(0, s.Length - 1);
}
s = s.Replace("\\P", "\n");
return s.Trim();
}
public static NoteApplyResult ApplyNoteTemplate(CadContext ctx, string layoutName, bool scanModelSpace, TemplateSchemaDefinition schema, ParamBag bag)
{
if (ctx == null)
@ -1010,25 +1104,109 @@ namespace CadParamPluging.Cad
if (space == null) return 0;
var ids = space.Cast<ObjectId>().ToArray();
int count = 0;
foreach (ObjectId id in space)
// 0) If we previously created fallback feature-category texts, update or remove them (prevents duplication).
var fallbackGroupIdsToRemove = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
foreach (var id in ids)
{
FeatureCategoryData fcData = null;
try { fcData = GetFeatureCategoryData(tr, id); } catch { }
if (fcData == null || string.IsNullOrWhiteSpace(fcData.Role))
{
continue;
}
if (string.Equals(fcData.Role, "FallbackText", StringComparison.OrdinalIgnoreCase))
{
var ent = tr.GetObject(id, OpenMode.ForWrite, false) as Entity;
if (ent == null) continue;
if (string.IsNullOrWhiteSpace(textToShow))
{
if (!ent.IsErased) ent.Erase();
if (!string.IsNullOrWhiteSpace(fcData.GroupId)) fallbackGroupIdsToRemove.Add(fcData.GroupId);
continue;
}
if (ent is MText mt)
{
mt.Contents = @"{\fSimSun|b0|i0|c134|p2;" + ToMTextContents(textToShow) + "}";
count++;
}
}
else if (string.Equals(fcData.Role, "FallbackBox", StringComparison.OrdinalIgnoreCase))
{
if (string.IsNullOrWhiteSpace(textToShow) && !string.IsNullOrWhiteSpace(fcData.GroupId))
{
fallbackGroupIdsToRemove.Add(fcData.GroupId);
}
}
}
if (fallbackGroupIdsToRemove.Count > 0)
{
foreach (var id in ids)
{
FeatureCategoryData fcData = null;
try { fcData = GetFeatureCategoryData(tr, id); } catch { }
if (fcData == null || string.IsNullOrWhiteSpace(fcData.GroupId)) continue;
if (!fallbackGroupIdsToRemove.Contains(fcData.GroupId)) continue;
var ent = tr.GetObject(id, OpenMode.ForWrite, false) as Entity;
if (ent == null) continue;
if (!ent.IsErased) ent.Erase();
}
}
// 1) Update previously replaced placeholder entities using stored original template.
foreach (var id in ids)
{
var ent = tr.GetObject(id, OpenMode.ForWrite, false) as Entity;
if (ent == null) continue;
if (ent is DBText t)
{
if (t.TextString.Contains("******"))
FeatureCategoryData fcData = null;
try { fcData = GetFeatureCategoryData(tr, id); } catch { }
if (fcData != null && string.Equals(fcData.Role, "Placeholder", StringComparison.OrdinalIgnoreCase))
{
t.TextString = t.TextString.Replace("******", textToShow);
var original = fcData.OriginalTemplate ?? string.Empty;
t.TextString = original.Contains("******") ? original.Replace("******", textToShow) : textToShow;
count++;
continue;
}
if (t.TextString != null && t.TextString.Contains("******"))
{
var original = t.TextString;
t.TextString = original.Replace("******", textToShow);
try { SetFeatureCategoryData(tr, id, original, null, "Placeholder"); } catch { }
count++;
}
}
else if (ent is MText mt)
{
if (mt.Contents.Contains("******"))
FeatureCategoryData fcData = null;
try { fcData = GetFeatureCategoryData(tr, id); } catch { }
if (fcData != null && string.Equals(fcData.Role, "Placeholder", StringComparison.OrdinalIgnoreCase))
{
mt.Contents = mt.Contents.Replace("******", textToShow);
var original = fcData.OriginalTemplate ?? string.Empty;
mt.Contents = original.Contains("******") ? original.Replace("******", textToShow) : textToShow;
count++;
continue;
}
if (mt.Contents != null && mt.Contents.Contains("******"))
{
var original = mt.Contents;
mt.Contents = original.Replace("******", textToShow);
try { SetFeatureCategoryData(tr, id, original, null, "Placeholder"); } catch { }
count++;
}
}
@ -1037,15 +1215,124 @@ namespace CadParamPluging.Cad
foreach (ObjectId attId in br.AttributeCollection)
{
var att = tr.GetObject(attId, OpenMode.ForWrite, false) as AttributeReference;
if (att != null && att.TextString.Contains("******"))
if (att == null) continue;
FeatureCategoryData fcData = null;
try { fcData = GetFeatureCategoryData(tr, attId); } catch { }
if (fcData != null && string.Equals(fcData.Role, "Placeholder", StringComparison.OrdinalIgnoreCase))
{
att.TextString = att.TextString.Replace("******", textToShow);
var original = fcData.OriginalTemplate ?? string.Empty;
att.TextString = original.Contains("******") ? original.Replace("******", textToShow) : textToShow;
count++;
continue;
}
if (att.TextString != null && att.TextString.Contains("******"))
{
var original = att.TextString;
att.TextString = original.Replace("******", textToShow);
try { SetFeatureCategoryData(tr, attId, original, null, "Placeholder"); } catch { }
count++;
}
}
}
}
// If the category is blank ("一般件"), also try to remove previously inserted bottom-right category texts/boxes (legacy, unmarked).
if (string.IsNullOrWhiteSpace(textToShow))
{
try
{
var spaceEntList = ids
.Select(x =>
{
try { return tr.GetObject(x, OpenMode.ForRead, false) as Entity; }
catch { return null; }
})
.Where(e => e != null && !e.IsErased)
.ToList();
var frame = ComputeWhiteFrameExtentsFromEntities(tr, spaceEntList);
if (frame.HasValue)
{
var f = frame.Value;
var known = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "关键件", "重要件" };
var regionMinX = f.MaxPoint.X - 220.0;
var regionMaxX = f.MaxPoint.X + 5.0;
var regionMinY = f.MinPoint.Y - 5.0;
var regionMaxY = f.MinPoint.Y + 140.0;
var candidates = new List<Tuple<ObjectId, Extents3d>>();
foreach (var e in spaceEntList)
{
try
{
var ex = e.GeometricExtents;
if (ex.MaxPoint.X < regionMinX || ex.MinPoint.X > regionMaxX || ex.MaxPoint.Y < regionMinY || ex.MinPoint.Y > regionMaxY)
{
continue;
}
if (e is DBText dt)
{
var txt = (dt.TextString ?? string.Empty).Trim();
if (known.Contains(txt)) candidates.Add(Tuple.Create(e.ObjectId, ex));
}
else if (e is MText mtt)
{
var txt = ExtractMTextPlainText(mtt.Contents);
if (known.Contains(txt)) candidates.Add(Tuple.Create(e.ObjectId, ex));
}
}
catch { }
}
if (candidates.Count > 0)
{
foreach (var c in candidates)
{
try
{
var w = tr.GetObject(c.Item1, OpenMode.ForWrite, false) as Entity;
if (w != null && !w.IsErased) w.Erase();
}
catch { }
}
foreach (var e in spaceEntList)
{
if (e is Polyline pl)
{
try
{
if (!pl.Closed) continue;
var ex = pl.GeometricExtents;
var matchesAny = candidates.Any(c =>
ex.MinPoint.X <= c.Item2.MinPoint.X - 0.1
&& ex.MaxPoint.X >= c.Item2.MaxPoint.X + 0.1
&& ex.MinPoint.Y <= c.Item2.MinPoint.Y - 0.1
&& ex.MaxPoint.Y >= c.Item2.MaxPoint.Y + 0.1);
if (matchesAny)
{
var w = tr.GetObject(pl.ObjectId, OpenMode.ForWrite, false) as Entity;
if (w != null && !w.IsErased) w.Erase();
}
}
catch { }
}
}
}
}
}
catch
{
// ignore
}
return count;
}
// Fallback: If no placeholder found AND we have text to show (e.g. "关键件"), create it manually.
if (count == 0 && !string.IsNullOrWhiteSpace(textToShow))
{
@ -1063,6 +1350,134 @@ namespace CadParamPluging.Cad
if (frame.HasValue)
{
var f = frame.Value;
// 1) Heuristic: update/cleanup existing (older) feature-category texts in the bottom-right area
var known = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "关键件", "重要件" };
var regionMinX = f.MaxPoint.X - 220.0;
var regionMaxX = f.MaxPoint.X + 5.0;
var regionMinY = f.MinPoint.Y - 5.0;
var regionMaxY = f.MinPoint.Y + 140.0;
var candidates = new List<Tuple<ObjectId, Extents3d, string>>();
foreach (var ent in spaceEntList)
{
if (ent == null) continue;
try
{
var ex = ent.GeometricExtents;
if (ex.MaxPoint.X < regionMinX || ex.MinPoint.X > regionMaxX || ex.MaxPoint.Y < regionMinY || ex.MinPoint.Y > regionMaxY)
{
continue;
}
if (ent is DBText dt)
{
var txt = (dt.TextString ?? string.Empty).Trim();
if (known.Contains(txt))
{
candidates.Add(Tuple.Create(ent.ObjectId, ex, txt));
}
}
else if (ent is MText mtt)
{
var txt = ExtractMTextPlainText(mtt.Contents);
if (known.Contains(txt))
{
candidates.Add(Tuple.Create(ent.ObjectId, ex, txt));
}
}
}
catch
{
// ignore
}
}
if (candidates.Count > 0)
{
// Keep the right-most (then top-most) one, erase the rest.
var keep = candidates
.OrderByDescending(c => c.Item2.MaxPoint.X)
.ThenByDescending(c => c.Item2.MaxPoint.Y)
.First();
var keepId = keep.Item1;
var keepExt = keep.Item2;
foreach (var c in candidates)
{
if (c.Item1 == keepId) continue;
try
{
var e = tr.GetObject(c.Item1, OpenMode.ForWrite, false) as Entity;
if (e != null && !e.IsErased) e.Erase();
}
catch { }
}
// Remove boxes that belong to removed texts (keep the one containing kept text)
foreach (var ent in spaceEntList)
{
if (ent is Polyline pl)
{
try
{
if (!pl.Closed) continue;
var ex = pl.GeometricExtents;
var containsKept = ex.MinPoint.X <= keepExt.MinPoint.X - 0.1
&& ex.MaxPoint.X >= keepExt.MaxPoint.X + 0.1
&& ex.MinPoint.Y <= keepExt.MinPoint.Y - 0.1
&& ex.MaxPoint.Y >= keepExt.MaxPoint.Y + 0.1;
if (containsKept) continue;
var matchesAnyRemoved = candidates.Any(c => c.Item1 != keepId
&& ex.MinPoint.X <= c.Item2.MinPoint.X - 0.1
&& ex.MaxPoint.X >= c.Item2.MaxPoint.X + 0.1
&& ex.MinPoint.Y <= c.Item2.MinPoint.Y - 0.1
&& ex.MaxPoint.Y >= c.Item2.MaxPoint.Y + 0.1);
if (matchesAnyRemoved)
{
var w = tr.GetObject(pl.ObjectId, OpenMode.ForWrite, false) as Entity;
if (w != null && !w.IsErased) w.Erase();
}
}
catch { }
}
}
// Update kept text and mark it so next run won't fall back.
try
{
var e = tr.GetObject(keepId, OpenMode.ForWrite, false) as Entity;
if (e is DBText dt)
{
dt.TextString = textToShow;
SetFeatureCategoryData(tr, keepId, "******", null, "Placeholder");
return 1;
}
if (e is MText mtt)
{
// Preserve its formatting if any; otherwise use our default.
var plain = ExtractMTextPlainText(mtt.Contents);
if (string.Equals(plain, mtt.Contents, StringComparison.Ordinal))
{
mtt.Contents = @"{\fSimSun|b0|i0|c134|p2;" + ToMTextContents(textToShow) + "}";
}
else
{
mtt.Contents = mtt.Contents.Replace(plain, textToShow);
}
SetFeatureCategoryData(tr, keepId, "******", null, "Placeholder");
return 1;
}
}
catch
{
// ignore
}
}
// Strategy:
// 1. Align Right: Use f.MaxX as the right boundary anchor.
@ -1121,6 +1536,8 @@ namespace CadParamPluging.Cad
var insertY = tableTopY + 2.0;
var insertPoint = new Point3d(insertX, insertY, 0);
var groupId = Guid.NewGuid().ToString("N");
var textHeight = 3.2; // 9号字 (interpreted as 9pt approx 3.2mm)
var mt = new MText();
// Set font to SimSun (宋体)
@ -1171,11 +1588,13 @@ namespace CadParamPluging.Cad
space.AppendEntity(poly);
tr.AddNewlyCreatedDBObject(poly, true);
try { SetFeatureCategoryData(tr, poly.ObjectId, string.Empty, groupId, "FallbackBox"); } catch { }
space.AppendEntity(mt);
tr.AddNewlyCreatedDBObject(mt, true);
try { SetFeatureCategoryData(tr, mt.ObjectId, string.Empty, groupId, "FallbackText"); } catch { }
count++;
}
}

View File

@ -9,7 +9,7 @@ namespace CadParamPluging.Common
public List<string> ProcessMethods { get; set; }
public List<string> StructuralFeatures { get; set; }
public List<string> SpecialConditions { get; set; }
public string DefaultTemplatePath { get; set; }
public DropdownOptions()
{

View File

@ -21,18 +21,7 @@ namespace CadParamPluging.UI
private readonly ComboBox _cbDrawingType;
private readonly ComboBox _cbSheetSize;
private readonly ComboBox _cbScale;
private ComboBox _cbTemplateList;
private Button _btnRefreshTemplates;
private TextBox _txtLog;
private TextBox _txtTemplatePath;
private string _selectedFolderPath;
private string[] _cadFilePaths = new string[0];
private TemplateInfo _selectedTemplate;
private Extents3d? _selectedModelWindow;
private string _selectedSheetName;
public ParamDrawingPanel()
@ -58,16 +47,6 @@ namespace CadParamPluging.UI
// Initialize template controls here in constructor to follow readonly/non-readonly best practices,
// though they are no longer readonly.
_btnRefreshTemplates = new Button { Text = "刷新列表", AutoSize = true };
_btnRefreshTemplates.Click += (_, __) => RefreshTemplateList();
_cbTemplateList = new ComboBox
{
DropDownStyle = ComboBoxStyle.DropDownList,
Dock = DockStyle.Fill
};
_cbTemplateList.SelectedIndexChanged += OnTemplateSelectionChanged;
var templateGroup = BuildTemplateGroup();
var drawingGroup = BuildDrawingGroup();
var actionPanel = BuildActionPanel();
@ -79,24 +58,6 @@ namespace CadParamPluging.UI
layout.Controls.Add(logGroup, 0, 3);
Controls.Add(layout);
if (!string.IsNullOrWhiteSpace(dropdownOptions.DefaultTemplatePath)
&& Directory.Exists(dropdownOptions.DefaultTemplatePath))
{
_selectedFolderPath = dropdownOptions.DefaultTemplatePath;
_txtTemplatePath.Text = dropdownOptions.DefaultTemplatePath;
_cadFilePaths = LoadCadFilesFromFolder(_selectedFolderPath);
// Use BeginInvoke to ensure UI is ready
var timer = new Timer { Interval = 100 };
timer.Tick += (s, e) =>
{
timer.Stop();
timer.Dispose();
RefreshTemplateList();
};
timer.Start();
}
}
private GroupBox BuildTemplateGroup()
@ -113,7 +74,7 @@ namespace CadParamPluging.UI
{
Dock = DockStyle.Fill,
ColumnCount = 2,
RowCount = 6,
RowCount = 4,
AutoSize = true
};
@ -129,17 +90,6 @@ namespace CadParamPluging.UI
grid.Controls.Add(new Label { Text = "特殊条件", AutoSize = true, TextAlign = ContentAlignment.MiddleLeft }, 0, 3);
grid.Controls.Add(_cbScale, 1, 3);
var btnSelect = new Button { Text = "选择路径", AutoSize = true };
btnSelect.Click += (_, __) => OnSelectTemplate();
_txtTemplatePath = new TextBox { Width = 160, ReadOnly = true };
grid.Controls.Add(btnSelect, 0, 4);
grid.Controls.Add(_txtTemplatePath, 1, 4);
grid.Controls.Add(_btnRefreshTemplates, 0, 5);
grid.Controls.Add(_cbTemplateList, 1, 5);
group.Controls.Add(grid);
return group;
}
@ -217,284 +167,6 @@ namespace CadParamPluging.UI
return group;
}
private void RefreshTemplateList()
{
try
{
_cbTemplateList.Items.Clear();
_selectedTemplate = null;
_selectedModelWindow = null;
_selectedSheetName = null;
if (_cadFilePaths == null || _cadFilePaths.Length == 0)
{
AppendLog("未加载CAD文件请先选择路径。");
return;
}
AppendLog("开始扫描模板...");
var items = new System.Collections.Generic.List<TemplateSelectionItem>();
foreach (var cadPath in _cadFilePaths)
{
if (string.IsNullOrWhiteSpace(cadPath) || !File.Exists(cadPath)) continue;
try
{
// 1) Layouts
var layouts = TemplateLayoutAnnotationExtractor.ExtractPerLayout(cadPath);
foreach (var l in layouts)
{
items.Add(new TemplateSelectionItem(new TemplateInfo
{
Name = Path.GetFileName(cadPath),
FilePath = cadPath,
LayoutName = l.LayoutName
}));
}
// 2) Model Space Windows
var models = TemplateModelSheetExtractor.ExtractCandidates(cadPath);
foreach (var m in models)
{
var item = new TemplateSelectionItem(new TemplateInfo
{
Name = Path.GetFileName(cadPath),
FilePath = cadPath,
LayoutName = null // Model space
});
item.ModelWindow = m.Window;
// Hint: TemplateInfo doesn't hold the 'Name' of the sheet if it's ModelSpace named candidate
// But usually DisplayName handles it. We can store the sheet name in Info.LayoutName or just use ToString override
// Actually TemplateInfo.LayoutName is used for opening.
// For model space candidates, we usually matched them by 'Name' in auto-match.
// Here we just list them.
items.Add(item);
}
// Fallback: if no layouts/model candidates found, add the file as a generic "Model Space" entry
if (layouts.Count == 0 && models.Count == 0)
{
items.Add(new TemplateSelectionItem(new TemplateInfo
{
Name = Path.GetFileName(cadPath),
FilePath = cadPath,
LayoutName = null // Default to Model space
}));
}
}
catch (Exception ex)
{
Console.WriteLine(ex); // silent fail for single file
}
}
if (items.Count == 0)
{
AppendLog("未在路径下找到有效的模板/布局。");
return;
}
_cbTemplateList.Items.AddRange(items.Cast<object>().ToArray());
AppendLog($"已加载 {items.Count} 个模板选项。");
if (_cbTemplateList.Items.Count > 0)
{
_cbTemplateList.SelectedIndex = 0;
}
}
catch (Exception ex)
{
AppendLog($"刷新模板列表失败: {ex.Message}");
Logger.Error("RefreshTemplateList", ex);
}
}
private void OnTemplateSelectionChanged(object sender, EventArgs e)
{
var item = _cbTemplateList.SelectedItem as TemplateSelectionItem;
if (item == null)
{
_selectedTemplate = null;
_selectedModelWindow = null;
_selectedSheetName = null;
return;
}
_selectedTemplate = item.Info;
_selectedModelWindow = item.ModelWindow;
_selectedSheetName = item.ToString(); // Use the display string as sheet name reference
// Log if needed
// AppendLog($"已选择: {_selectedSheetName}");
}
private void OnSelectTemplate()
{
try
{
using (var dialog = new FolderBrowserDialog())
{
dialog.Description = "选择包含 CAD 文件的文件夹";
if (!string.IsNullOrWhiteSpace(_selectedFolderPath) && Directory.Exists(_selectedFolderPath))
{
dialog.SelectedPath = _selectedFolderPath;
}
if (dialog.ShowDialog(this) == DialogResult.OK)
{
var folderPath = dialog.SelectedPath;
if (string.IsNullOrWhiteSpace(folderPath) || !Directory.Exists(folderPath))
{
AppendLog("选择路径无效。");
return;
}
_selectedFolderPath = folderPath;
_txtTemplatePath.Text = folderPath;
_cadFilePaths = LoadCadFilesFromFolder(folderPath);
AppendLog($"已选择路径: {folderPath}");
AppendLog($"检测到 CAD 文件数量: {_cadFilePaths.Length}");
foreach (var p in _cadFilePaths.Take(10))
{
AppendLog($"- {Path.GetFileName(p)}");
}
if (_cadFilePaths.Length > 10)
{
AppendLog($"... 还有 {_cadFilePaths.Length - 10} 个文件");
}
RefreshTemplateList();
}
}
}
catch (Exception ex)
{
AppendLog($"选择路径失败: {ex.Message}");
Logger.Error("SelectCadFolder", ex);
}
}
private static string[] LoadCadFilesFromFolder(string folderPath)
{
if (string.IsNullOrWhiteSpace(folderPath) || !Directory.Exists(folderPath))
{
return new string[0];
}
var patterns = new[] { "*.dwg", "*.dwt", "*.dxf" };
return patterns
.SelectMany(p => Directory.EnumerateFiles(folderPath, p, SearchOption.TopDirectoryOnly))
.Distinct(StringComparer.OrdinalIgnoreCase)
.OrderBy(p => p, StringComparer.OrdinalIgnoreCase)
.ToArray();
}
private void TryReplaceDropdownOptionsFromTemplate(string templatePath)
{
try
{
var result = TemplateAnnotationOptionsExtractor.Extract(templatePath);
var options = result?.Options;
if (options == null || !HasAnyOptions(options))
{
AppendLog("未从模板标注解析到参数,下拉列表不变。");
return;
}
AppendLog("已从模板标注解析到参数,询问是否替换下拉设置。");
var preview = BuildOptionsPreview(options, 5);
var confirm = MessageBox.Show(
this,
$"检测到模板参数(去重后):\n\n{preview}\n\n是否用模板参数替换当前下拉列表并保存\n特殊条件不从模板解析将保持当前设置",
"模板参数",
MessageBoxButtons.YesNo,
MessageBoxIcon.Question);
if (confirm != DialogResult.Yes)
{
AppendLog("用户取消替换下拉设置。");
return;
}
var current = DropdownOptionsStore.Load();
var updated = (current ?? DropdownOptions.CreateDefault()).Clone();
if (options.DeliveryStatuses != null && options.DeliveryStatuses.Count > 0)
{
updated.DeliveryStatuses = options.DeliveryStatuses.ToList();
}
if (options.ProcessMethods != null && options.ProcessMethods.Count > 0)
{
updated.ProcessMethods = options.ProcessMethods.ToList();
}
if (options.StructuralFeatures != null && options.StructuralFeatures.Count > 0)
{
updated.StructuralFeatures = options.StructuralFeatures.ToList();
}
updated.Normalize();
DropdownOptionsStore.Save(updated);
ReloadDropdownOptions(updated);
AppendLog("已从模板替换下拉设置并保存。");
}
catch (Exception ex)
{
AppendLog($"模板解析/替换失败: {ex.Message}");
Logger.Error("TemplateDropdownOptions", ex);
}
}
private static bool HasAnyOptions(DropdownOptions options)
{
return options != null
&& ((options.DeliveryStatuses != null && options.DeliveryStatuses.Count > 0)
|| (options.ProcessMethods != null && options.ProcessMethods.Count > 0)
|| (options.StructuralFeatures != null && options.StructuralFeatures.Count > 0));
}
private static string BuildOptionsPreview(DropdownOptions options, int maxPerField)
{
return string.Join(
Environment.NewLine,
new[]
{
$"交付状态: {PreviewValues(options?.DeliveryStatuses, maxPerField)}",
$"工艺方法: {PreviewValues(options?.ProcessMethods, maxPerField)}",
$"结构特征: {PreviewValues(options?.StructuralFeatures, maxPerField)}"
});
}
private static string PreviewValues(System.Collections.Generic.IEnumerable<string> values, int max)
{
if (values == null)
{
return "(无)";
}
var arr = values.Where(v => !string.IsNullOrWhiteSpace(v)).Select(v => v.Trim()).ToArray();
if (arr.Length == 0)
{
return "(无)";
}
if (arr.Length <= max)
{
return string.Join(", ", arr);
}
return string.Join(", ", arr.Take(max)) + $" ... (共 {arr.Length} 项)";
}
private static double ParseScaleFactor(string raw)
{
@ -543,12 +215,6 @@ namespace CadParamPluging.UI
{
var tplParams = CollectTemplateParams();
if (_selectedTemplate == null || string.IsNullOrWhiteSpace(_selectedTemplate.FilePath))
{
AppendLog("请先选择并确认模板(下拉框)。");
return;
}
var templateKey = TemplateKeyBuilder.Build(tplParams);
if (string.IsNullOrWhiteSpace(templateKey))
{
@ -567,7 +233,8 @@ namespace CadParamPluging.UI
var catalog = ParamCatalogStore.Load();
CadParamPluging.Common.ParamBag bag;
using (var f = new DrawingParamsForm(catalog, schema))
// 若用户点击了【读取参数】,则将读取到的参数作为本次弹窗的默认值回填
using (var f = new DrawingParamsForm(catalog, schema, _lastLoadedBag))
{
var r = f.ShowDialog(this);
if (r != DialogResult.OK)
@ -584,25 +251,26 @@ namespace CadParamPluging.UI
DomainFacade.ValidateParameters(tplParams, drawingParams);
var doc = TemplateDrawingService.OpenTemplateDrawing(_selectedTemplate);
var doc = AcadApp.DocumentManager.MdiActiveDocument;
if (doc == null)
{
AppendLog("未检测到活动图纸,请先打开 CAD 图纸。");
return;
}
if (_selectedModelWindow.HasValue)
{
TemplateDrawingService.KeepOnlyModelWindow(doc, _selectedModelWindow.Value);
}
else
{
TemplateDrawingService.KeepOnlyLayout(doc, _selectedTemplate.LayoutName);
}
AppendLog($"已生成图纸窗口,并仅保留图纸: {_selectedSheetName ?? _selectedTemplate.LayoutName}");
string layoutName = LayoutManager.Current.CurrentLayout;
// 判断是否模型空间。注意LayoutName为"Model"通常表示模型空间
bool isModelSpace = string.Equals(layoutName, "Model", StringComparison.OrdinalIgnoreCase);
AppendLog($"正在当前图纸生成: {doc.Name} ({layoutName})");
using (doc.LockDocument())
using (var ctx = new CadContext(doc))
{
var removed = TemplateDrawingService.RemoveMatchParameterAnnotations(
ctx,
_selectedTemplate.LayoutName,
_selectedModelWindow.HasValue);
layoutName,
isModelSpace);
if (removed > 0)
{
AppendLog($"已移除模板匹配参数标注文本: {removed} 处");
@ -610,8 +278,8 @@ namespace CadParamPluging.UI
var featureCategoryCount = TemplateDrawingService.ApplyFeatureCategory(
ctx,
_selectedTemplate.LayoutName,
_selectedModelWindow.HasValue,
layoutName,
isModelSpace,
bag);
if (featureCategoryCount > 0)
{
@ -620,8 +288,8 @@ namespace CadParamPluging.UI
var noteResult = TemplateDrawingService.ApplyNoteTemplate(
ctx,
_selectedTemplate.LayoutName,
_selectedModelWindow.HasValue,
layoutName,
isModelSpace,
schema,
bag);
if (noteResult != null)
@ -760,8 +428,8 @@ namespace CadParamPluging.UI
// 更新标题栏中的比例属性
var scaleUpdated = TemplateDrawingService.UpdateScaleAttribute(
ctx,
_selectedTemplate.LayoutName,
_selectedModelWindow.HasValue,
layoutName,
isModelSpace,
scaleFactor
);
if (scaleUpdated)
@ -779,8 +447,8 @@ namespace CadParamPluging.UI
{
var categoryUpdated = TemplateDrawingService.UpdateInspectionCategoryAttribute(
ctx,
_selectedTemplate.LayoutName,
_selectedModelWindow.HasValue,
layoutName,
isModelSpace,
inspectionCategory
);
if (categoryUpdated)
@ -802,12 +470,7 @@ namespace CadParamPluging.UI
{
var bagToSave = bag;
// 保存模板信息以便后续“读取参数”功能可以自动填充UI
if (_selectedTemplate != null)
{
bagToSave.Set("_TemplatePath", _selectedTemplate.FilePath);
bagToSave.Set("_LayoutName", _selectedTemplate.LayoutName);
}
// 保存面板选项
bagToSave.Set("ProjectType", tplParams.ProjectType);
bagToSave.Set("DrawingType", tplParams.DrawingType);
@ -913,46 +576,7 @@ namespace CadParamPluging.UI
return;
}
// 1. 尝试根据 path 恢复模板选择
var tplPath = bag.GetString("_TemplatePath");
if (!string.IsNullOrWhiteSpace(tplPath))
{
// 在 _cbTemplateList 中查找
int foundIndex = -1;
for (int i = 0; i < _cbTemplateList.Items.Count; i++)
{
var item = _cbTemplateList.Items[i] as TemplateSelectionItem;
if (item != null && string.Equals(item.Info.FilePath, tplPath, StringComparison.OrdinalIgnoreCase))
{
// 如果原来的选择也是布局,尝试匹配布局名
var originalLayout = bag.GetString("_LayoutName");
if (!string.IsNullOrWhiteSpace(originalLayout))
{
if (string.Equals(item.Info.LayoutName, originalLayout, StringComparison.OrdinalIgnoreCase))
{
foundIndex = i;
break;
}
}
else
{
// 只有 Model Space 情况,或者不精确匹配布局
foundIndex = i;
break;
}
}
}
if (foundIndex >= 0)
{
_cbTemplateList.SelectedIndex = foundIndex;
AppendLog($"已自动选中模板: {_cbTemplateList.Items[foundIndex]}");
}
else
{
AppendLog($"注意: 未在列表中找到原模板文件: {Path.GetFileName(tplPath)},可能文件已移动或未加载路径。");
}
}
// 2. 恢复下拉框选项
// ProjectType

View File

@ -13,7 +13,7 @@ namespace CadParamPluging.UI
private readonly ListBox _lbProcessMethods;
private readonly ListBox _lbStructuralFeatures;
private readonly ListBox _lbSpecialConditions;
private readonly TextBox _txtDefaultTemplatePath;
public DropdownOptions Result { get; private set; }
@ -32,10 +32,9 @@ namespace CadParamPluging.UI
{
Dock = DockStyle.Fill,
Padding = new Padding(12),
RowCount = 3,
RowCount = 2,
ColumnCount = 1
};
mainLayout.RowStyles.Add(new RowStyle(SizeType.AutoSize)); // Default Path
mainLayout.RowStyles.Add(new RowStyle(SizeType.Percent, 100f)); // Tabs
mainLayout.RowStyles.Add(new RowStyle(SizeType.AutoSize)); // Buttons
@ -43,48 +42,16 @@ namespace CadParamPluging.UI
_lbProcessMethods = new ListBox();
_lbStructuralFeatures = new ListBox();
_lbSpecialConditions = new ListBox();
_txtDefaultTemplatePath = new TextBox { Dock = DockStyle.Fill, ReadOnly = true };
var options = (current ?? DropdownOptions.CreateDefault()).Clone();
options.Normalize();
_txtDefaultTemplatePath.Text = options.DefaultTemplatePath ?? string.Empty;
FillList(_lbDeliveryStatuses, options.DeliveryStatuses);
FillList(_lbProcessMethods, options.ProcessMethods);
FillList(_lbStructuralFeatures, options.StructuralFeatures);
FillList(_lbSpecialConditions, options.SpecialConditions);
// 1. Default Path Section
var pathGroup = new GroupBox
{
Text = "默认模板路径",
Dock = DockStyle.Fill,
AutoSize = true,
Padding = new Padding(6)
};
var pathGrid = new TableLayoutPanel
{
Dock = DockStyle.Top,
AutoSize = true,
ColumnCount = 2,
RowCount = 1,
Padding = new Padding(4)
};
pathGrid.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100f));
pathGrid.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));
var btnBrowsePath = new Button
{
Text = "浏览...",
AutoSize = true,
Height = _txtDefaultTemplatePath.Height, // visually match
Anchor = AnchorStyles.Left | AnchorStyles.Right
};
btnBrowsePath.Click += (_, __) => OnBrowseDefaultPath();
pathGrid.Controls.Add(_txtDefaultTemplatePath, 0, 0);
pathGrid.Controls.Add(btnBrowsePath, 1, 0);
pathGroup.Controls.Add(pathGrid);
// 2. Tabs Section
var tabs = new TabControl { Dock = DockStyle.Fill };
@ -139,9 +106,8 @@ namespace CadParamPluging.UI
bottomGrid.Controls.Add(rightActions, 1, 0);
// Assemble Main
mainLayout.Controls.Add(pathGroup, 0, 0);
mainLayout.Controls.Add(tabs, 0, 1);
mainLayout.Controls.Add(bottomGrid, 0, 2);
mainLayout.Controls.Add(tabs, 0, 0);
mainLayout.Controls.Add(bottomGrid, 0, 1);
Controls.Add(mainLayout);
@ -149,22 +115,7 @@ namespace CadParamPluging.UI
CancelButton = btnCancel;
}
private void OnBrowseDefaultPath()
{
using (var dialog = new FolderBrowserDialog())
{
dialog.Description = "选择默认模板路径";
if (System.IO.Directory.Exists(_txtDefaultTemplatePath.Text))
{
dialog.SelectedPath = _txtDefaultTemplatePath.Text;
}
if (dialog.ShowDialog(this) == DialogResult.OK)
{
_txtDefaultTemplatePath.Text = dialog.SelectedPath;
}
}
}
private void OnOpenParamCatalog()
{
@ -351,8 +302,7 @@ namespace CadParamPluging.UI
DeliveryStatuses = _lbDeliveryStatuses.Items.Cast<object>().Select(o => (o as string) ?? string.Empty).ToList(),
ProcessMethods = _lbProcessMethods.Items.Cast<object>().Select(o => (o as string) ?? string.Empty).ToList(),
StructuralFeatures = _lbStructuralFeatures.Items.Cast<object>().Select(o => (o as string) ?? string.Empty).ToList(),
SpecialConditions = _lbSpecialConditions.Items.Cast<object>().Select(o => (o as string) ?? string.Empty).ToList(),
DefaultTemplatePath = _txtDefaultTemplatePath.Text.Trim()
SpecialConditions = _lbSpecialConditions.Items.Cast<object>().Select(o => (o as string) ?? string.Empty).ToList()
};
result.Normalize();