更新匹配逻辑

This commit is contained in:
sladro 2025-12-31 09:58:12 +08:00
parent 78e05608f8
commit b69cca21ac
5 changed files with 263 additions and 214 deletions

View File

@ -118,6 +118,7 @@
<Compile Include="UI\TextInputForm.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="UI\TemplateSelectionItem.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />

View File

@ -9,6 +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,7 +21,9 @@ namespace CadParamPluging.UI
private readonly ComboBox _cbDrawingType;
private readonly ComboBox _cbSheetSize;
private readonly ComboBox _cbScale;
private Label _lblTemplateInfo;
private ComboBox _cbTemplateList;
private Button _btnRefreshTemplates;
private TextBox _txtLog;
private TextBox _txtTemplatePath;
@ -53,6 +55,18 @@ namespace CadParamPluging.UI
_cbSheetSize = CreateComboBox(dropdownOptions.StructuralFeatures);
_cbScale = CreateComboBox(dropdownOptions.SpecialConditions);
// 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();
@ -62,8 +76,26 @@ namespace CadParamPluging.UI
layout.Controls.Add(drawingGroup, 0, 1);
layout.Controls.Add(actionPanel, 0, 2);
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()
@ -104,19 +136,8 @@ namespace CadParamPluging.UI
grid.Controls.Add(btnSelect, 0, 4);
grid.Controls.Add(_txtTemplatePath, 1, 4);
var btnMatch = new Button { Text = "匹配模板", AutoSize = true };
btnMatch.Click += (_, __) => OnMatchTemplate();
_lblTemplateInfo = new Label
{
Text = "未匹配模板",
AutoSize = true,
ForeColor = Color.DarkBlue,
TextAlign = ContentAlignment.MiddleLeft
};
grid.Controls.Add(btnMatch, 0, 5);
grid.Controls.Add(_lblTemplateInfo, 1, 5);
grid.Controls.Add(_btnRefreshTemplates, 0, 5);
grid.Controls.Add(_cbTemplateList, 1, 5);
group.Controls.Add(grid);
return group;
@ -190,162 +211,105 @@ namespace CadParamPluging.UI
return group;
}
private void OnMatchTemplate()
private void RefreshTemplateList()
{
try
{
if (_cadFilePaths == null || _cadFilePaths.Length == 0)
{
AppendLog("请先选择路径。");
return;
}
AppendLog("开始匹配模板...");
var tplParams = CollectTemplateParams();
foreach (var cadPath in _cadFilePaths)
{
if (string.IsNullOrWhiteSpace(cadPath) || !File.Exists(cadPath))
{
continue;
}
// 1) 优先按 Layout(PaperSpace) 匹配
var layoutCandidates = TemplateLayoutAnnotationExtractor.ExtractPerLayout(cadPath);
var layoutMatch = layoutCandidates.FirstOrDefault(c => LayoutMatches(c, tplParams));
if (layoutMatch != null)
{
_selectedTemplate = new TemplateInfo
{
Name = Path.GetFileName(cadPath),
FilePath = cadPath
};
_selectedTemplate.LayoutName = layoutMatch.LayoutName;
_selectedModelWindow = null;
_selectedSheetName = layoutMatch.LayoutName;
_lblTemplateInfo.Text = $"{cadPath} | {layoutMatch.LayoutName}";
AppendLog($"匹配成功: {Path.GetFileName(cadPath)} | Layout: {layoutMatch.LayoutName}");
return;
}
// 2) Layout 未命中时,尝试按 ModelSpace 窗口候选匹配
var modelCandidates = TemplateModelSheetExtractor.ExtractCandidates(cadPath);
var modelMatch = modelCandidates.FirstOrDefault(c => ModelMatches(c, tplParams));
if (modelMatch != null)
{
_selectedTemplate = new TemplateInfo
{
Name = Path.GetFileName(cadPath),
FilePath = cadPath
};
_selectedTemplate.LayoutName = null;
_selectedModelWindow = modelMatch.Window;
_selectedSheetName = modelMatch.Name;
_lblTemplateInfo.Text = $"{cadPath} | {modelMatch.Name}";
AppendLog($"匹配成功: {Path.GetFileName(cadPath)} | Model: {modelMatch.Name}");
return;
}
}
_cbTemplateList.Items.Clear();
_selectedTemplate = null;
_selectedModelWindow = null;
_selectedSheetName = null;
_lblTemplateInfo.Text = "未匹配模板";
AppendLog("未找到匹配模板。");
}
catch (BusinessException bex)
{
if (_selectedTemplate != null)
if (_cadFilePaths == null || _cadFilePaths.Length == 0)
{
_selectedTemplate.LayoutName = null;
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);
}
}
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;
}
_selectedModelWindow = null;
_selectedSheetName = null;
_lblTemplateInfo.Text = _selectedTemplate?.FilePath ?? "未匹配图纸";
AppendLog($"图纸匹配错误: {bex.Message}");
}
catch (Exception ex)
{
if (_selectedTemplate != null)
{
_selectedTemplate.LayoutName = null;
}
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;
_lblTemplateInfo.Text = _selectedTemplate?.FilePath ?? "未匹配图纸";
AppendLog($"匹配失败: {ex.Message}");
Logger.Error("MatchLayout", ex);
}
}
private static bool LayoutMatches(LayoutAnnotation ann, TemplateParams tplParams)
{
if (ann == null || tplParams == null)
{
return false;
return;
}
return ContainsIgnoreCase(ann.DeliveryStatuses, tplParams.ProjectType)
&& ContainsIgnoreCase(ann.ProcessMethods, tplParams.DrawingType)
&& ContainsIgnoreCase(ann.StructuralFeatures, tplParams.SheetSize)
&& ContainsIgnoreCase(ann.SpecialConditions, tplParams.Scale);
}
private static bool ContainsIgnoreCase(System.Collections.Generic.IEnumerable<string> values, string expected)
{
if (string.IsNullOrWhiteSpace(expected))
{
return true;
}
if (values == null)
{
return false;
}
var exp = NormalizeValue(expected);
return values.Any(v => string.Equals(NormalizeValue(v), exp, StringComparison.OrdinalIgnoreCase));
}
private static string NormalizeValue(string s)
{
if (string.IsNullOrWhiteSpace(s))
{
return string.Empty;
}
var trimmed = s.Trim();
var chars = trimmed.Where(c => !char.IsWhiteSpace(c)).ToArray();
return new string(chars);
}
private static string Preview(System.Collections.Generic.IEnumerable<string> values)
{
if (values == null)
{
return "(无)";
}
var arr = values.Where(v => !string.IsNullOrWhiteSpace(v)).Select(v => v.Trim()).ToArray();
return arr.Length == 0 ? "(无)" : string.Join("/", arr);
}
private static bool ModelMatches(ModelSheetCandidate ann, TemplateParams tplParams)
{
if (ann == null || tplParams == null)
{
return false;
}
return ContainsIgnoreCase(ann.DeliveryStatuses, tplParams.ProjectType)
&& ContainsIgnoreCase(ann.ProcessMethods, tplParams.DrawingType)
&& ContainsIgnoreCase(ann.StructuralFeatures, tplParams.SheetSize)
&& ContainsIgnoreCase(ann.SpecialConditions, tplParams.Scale);
_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()
@ -387,6 +351,8 @@ namespace CadParamPluging.UI
{
AppendLog($"... 还有 {_cadFilePaths.Length - 10} 个文件");
}
RefreshTemplateList();
}
}
}
@ -562,20 +528,10 @@ namespace CadParamPluging.UI
if (_selectedTemplate == null || string.IsNullOrWhiteSpace(_selectedTemplate.FilePath))
{
AppendLog("请先选择路径并匹配模板。");
AppendLog("请先选择并确认模板(下拉框)。");
return;
}
if (string.IsNullOrWhiteSpace(_selectedTemplate.LayoutName) && !_selectedModelWindow.HasValue)
{
AppendLog("未匹配图纸,尝试自动匹配...");
OnMatchTemplate();
if (_selectedTemplate == null || (string.IsNullOrWhiteSpace(_selectedTemplate.LayoutName) && !_selectedModelWindow.HasValue))
{
return;
}
}
var templateKey = TemplateKeyBuilder.Build(tplParams);
if (string.IsNullOrWhiteSpace(templateKey))
{

View File

@ -1,5 +1,6 @@
using System;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using CadParamPluging.Common;
@ -12,102 +13,159 @@ 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; }
public SettingsForm(DropdownOptions current)
{
Text = "设置";
Size = new Size(560, 420);
Size = new Size(600, 500);
StartPosition = FormStartPosition.CenterScreen;
FormBorderStyle = FormBorderStyle.FixedDialog;
MaximizeBox = false;
MinimizeBox = false;
ShowInTaskbar = false;
Font = SystemFonts.MessageBoxFont; // Use system font for consistent look
var layout = new TableLayoutPanel
var mainLayout = new TableLayoutPanel
{
Dock = DockStyle.Fill,
Padding = new Padding(10),
RowCount = 2,
Padding = new Padding(12),
RowCount = 3,
ColumnCount = 1
};
layout.RowStyles.Add(new RowStyle(SizeType.Percent, 100f)); // Content area
layout.RowStyles.Add(new RowStyle(SizeType.AutoSize)); // Buttons area
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
_lbDeliveryStatuses = new ListBox();
_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);
var tabs = new TabControl
// 1. Default Path Section
var pathGroup = new GroupBox
{
Dock = DockStyle.Fill
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 };
tabs.TabPages.Add(BuildEditableTab("交付状态", _lbDeliveryStatuses));
tabs.TabPages.Add(BuildEditableTab("工艺方法", _lbProcessMethods));
tabs.TabPages.Add(BuildEditableTab("结构特征", _lbStructuralFeatures));
tabs.TabPages.Add(BuildEditableTab("特殊条件", _lbSpecialConditions));
// Bottom area
var bottom = new TableLayoutPanel
{
Dock = DockStyle.Fill,
ColumnCount = 2,
RowCount = 1
// 3. Bottom Section (Separated into Left (Tools) and Right (Dialog))
var bottomGrid = new TableLayoutPanel
{
Dock = DockStyle.Fill,
RowCount = 1,
ColumnCount = 2,
AutoSize = true,
Margin = new Padding(0, 8, 0, 0) // Top margin for spacing
};
bottom.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100f));
bottom.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));
bottomGrid.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100f));
bottomGrid.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));
var flowLeft = new FlowLayoutPanel
{
Dock = DockStyle.Fill,
FlowDirection = FlowDirection.LeftToRight,
AutoSize = true
// Left tools
var leftTools = new FlowLayoutPanel
{
Dock = DockStyle.Fill,
AutoSize = true,
FlowDirection = FlowDirection.LeftToRight
};
var btnParamCatalog = new Button { Text = "参数总表...", AutoSize = true };
var btnParamCatalog = new Button { Text = "参数总表", AutoSize = true };
btnParamCatalog.Click += (_, __) => OnOpenParamCatalog();
var btnTemplateBinding = new Button { Text = "模板参数绑定...", AutoSize = true };
var btnTemplateBinding = new Button { Text = "模板绑定", AutoSize = true };
btnTemplateBinding.Click += (_, __) => OnOpenTemplateBinding();
flowLeft.Controls.Add(btnParamCatalog);
flowLeft.Controls.Add(btnTemplateBinding);
leftTools.Controls.Add(btnParamCatalog);
leftTools.Controls.Add(btnTemplateBinding);
var flowRight = new FlowLayoutPanel
// Right actions
var rightActions = new FlowLayoutPanel
{
Dock = DockStyle.Fill,
FlowDirection = FlowDirection.RightToLeft,
AutoSize = true
AutoSize = true,
FlowDirection = FlowDirection.RightToLeft
};
var btnCancel = new Button { Text = "取消", DialogResult = DialogResult.Cancel };
var btnOk = new Button { Text = "确定" };
var btnCancel = new Button { Text = "取消", DialogResult = DialogResult.Cancel, Width = 80 };
var btnOk = new Button { Text = "确定", Width = 80 };
btnOk.Click += (_, __) => OnOk();
flowRight.Controls.Add(btnCancel);
flowRight.Controls.Add(btnOk);
rightActions.Controls.Add(btnCancel); // Add in reverse order due to RightToLeft flow
rightActions.Controls.Add(btnOk);
bottom.Controls.Add(flowLeft, 0, 0);
bottom.Controls.Add(flowRight, 1, 0);
bottomGrid.Controls.Add(leftTools, 0, 0);
bottomGrid.Controls.Add(rightActions, 1, 0);
layout.Controls.Add(tabs, 0, 0);
layout.Controls.Add(bottom, 0, 1);
// Assemble Main
mainLayout.Controls.Add(pathGroup, 0, 0);
mainLayout.Controls.Add(tabs, 0, 1);
mainLayout.Controls.Add(bottomGrid, 0, 2);
Controls.Add(mainLayout);
Controls.Add(layout);
AcceptButton = btnOk;
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()
{
try
@ -171,34 +229,36 @@ namespace CadParamPluging.UI
private TabPage BuildEditableTab(string title, ListBox listBox)
{
var page = new TabPage(title);
page.Padding = new Padding(8);
page.UseVisualStyleBackColor = true;
var layout = new TableLayoutPanel
{
Dock = DockStyle.Fill,
ColumnCount = 2,
RowCount = 1,
Padding = new Padding(8)
};
layout.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100f));
layout.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));
layout.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 100f)); // Fixed width for button column
listBox.Dock = DockStyle.Fill;
listBox.IntegralHeight = false; // smoother resizing
var actions = new FlowLayoutPanel
{
Dock = DockStyle.Fill,
FlowDirection = FlowDirection.TopDown,
WrapContents = false,
AutoSize = true
Padding = new Padding(4, 0, 0, 0)
};
var btnAdd = new Button { Text = "新增", AutoSize = true };
var btnAdd = new Button { Text = "新增", Width = 90, Height = 30 };
btnAdd.Click += (_, __) => OnAddItem(title, listBox);
var btnEdit = new Button { Text = "修改", AutoSize = true };
var btnEdit = new Button { Text = "修改", Width = 90, Height = 30 };
btnEdit.Click += (_, __) => OnEditItem(title, listBox);
var btnDelete = new Button { Text = "删除", AutoSize = true };
var btnDelete = new Button { Text = "删除", Width = 90, Height = 30 };
btnDelete.Click += (_, __) => OnDeleteItem(title, listBox);
actions.Controls.Add(btnAdd);
@ -291,7 +351,8 @@ 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()
SpecialConditions = _lbSpecialConditions.Items.Cast<object>().Select(o => (o as string) ?? string.Empty).ToList(),
DefaultTemplatePath = _txtDefaultTemplatePath.Text.Trim()
};
result.Normalize();

View File

@ -0,0 +1,30 @@
using CadParamPluging.Domain.Models;
namespace CadParamPluging.UI
{
public class TemplateSelectionItem
{
public TemplateInfo Info { get; set; }
public TemplateSelectionItem(TemplateInfo info)
{
Info = info;
}
public override string ToString()
{
if (Info == null)
{
return "无效模板";
}
var sheet = !string.IsNullOrWhiteSpace(Info.LayoutName)
? Info.LayoutName
: "Model";
return $"{Info.Name} - {sheet}";
}
public Autodesk.AutoCAD.DatabaseServices.Extents3d? ModelWindow { get; set; }
}
}