using System; using System.Drawing; using System.Globalization; using System.IO; using System.Linq; using System.Windows.Forms; using Autodesk.AutoCAD.ApplicationServices; using Autodesk.AutoCAD.DatabaseServices; using CadParamPluging.Cad; using CadParamPluging.Common; using CadParamPluging.Domain; using CadParamPluging.Domain.Models; using AcadApp = Autodesk.AutoCAD.ApplicationServices.Application; namespace CadParamPluging.UI { public class ParamDrawingPanel : UserControl { private readonly ComboBox _cbProjectType; private readonly ComboBox _cbDrawingType; private readonly ComboBox _cbSheetSize; private readonly ComboBox _cbScale; private readonly TextBox _txtWidth; private readonly TextBox _txtHeight; private Label _lblTemplateInfo; 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() { Dock = DockStyle.Fill; var dropdownOptions = DropdownOptionsStore.Load(); var layout = new TableLayoutPanel { Dock = DockStyle.Fill, ColumnCount = 1, RowCount = 4, AutoSize = true, AutoSizeMode = AutoSizeMode.GrowAndShrink, Padding = new Padding(8) }; _cbProjectType = CreateComboBox(dropdownOptions.DeliveryStatuses); _cbDrawingType = CreateComboBox(dropdownOptions.ProcessMethods); _cbSheetSize = CreateComboBox(dropdownOptions.StructuralFeatures); _cbScale = CreateComboBox(dropdownOptions.SpecialConditions); _txtWidth = CreateTextBox("5000"); _txtHeight = CreateTextBox("3000"); var templateGroup = BuildTemplateGroup(); var drawingGroup = BuildDrawingGroup(); var actionPanel = BuildActionPanel(); var logGroup = BuildLogGroup(); layout.Controls.Add(templateGroup, 0, 0); layout.Controls.Add(drawingGroup, 0, 1); layout.Controls.Add(actionPanel, 0, 2); layout.Controls.Add(logGroup, 0, 3); Controls.Add(layout); } private GroupBox BuildTemplateGroup() { var group = new GroupBox { Text = "模板参数", Dock = DockStyle.Top, AutoSize = true, AutoSizeMode = AutoSizeMode.GrowAndShrink }; var grid = new TableLayoutPanel { Dock = DockStyle.Fill, ColumnCount = 2, RowCount = 6, AutoSize = true }; grid.Controls.Add(new Label { Text = "交付状态", AutoSize = true, TextAlign = ContentAlignment.MiddleLeft }, 0, 0); grid.Controls.Add(_cbProjectType, 1, 0); grid.Controls.Add(new Label { Text = "工艺方法", AutoSize = true, TextAlign = ContentAlignment.MiddleLeft }, 0, 1); grid.Controls.Add(_cbDrawingType, 1, 1); grid.Controls.Add(new Label { Text = "结构特征", AutoSize = true, TextAlign = ContentAlignment.MiddleLeft }, 0, 2); grid.Controls.Add(_cbSheetSize, 1, 2); 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); 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); group.Controls.Add(grid); return group; } private GroupBox BuildDrawingGroup() { var group = new GroupBox { Text = "出图参数", Dock = DockStyle.Top, AutoSize = true, AutoSizeMode = AutoSizeMode.GrowAndShrink }; var grid = new TableLayoutPanel { Dock = DockStyle.Fill, ColumnCount = 2, RowCount = 3, AutoSize = true }; grid.Controls.Add(new Label { Text = "宽度(mm)", AutoSize = true, TextAlign = ContentAlignment.MiddleLeft }, 0, 0); grid.Controls.Add(_txtWidth, 1, 0); grid.Controls.Add(new Label { Text = "高度(mm)", AutoSize = true, TextAlign = ContentAlignment.MiddleLeft }, 0, 1); grid.Controls.Add(_txtHeight, 1, 1); group.Controls.Add(grid); return group; } private FlowLayoutPanel BuildActionPanel() { var panel = new FlowLayoutPanel { Dock = DockStyle.Top, AutoSize = true }; var btnGenerate = new Button { Text = "生成图纸", AutoSize = true }; btnGenerate.Click += (_, __) => OnGenerateDrawing(); var btnSaveAs = new Button { Text = "保存当前图纸", AutoSize = true }; btnSaveAs.Click += (_, __) => OnSaveDrawing(); var btnSettings = new Button { Text = "设置", AutoSize = true }; btnSettings.Click += (_, __) => OnOpenSettings(); panel.Controls.Add(btnGenerate); panel.Controls.Add(btnSaveAs); panel.Controls.Add(btnSettings); return panel; } private GroupBox BuildLogGroup() { var group = new GroupBox { Text = "日志", Dock = DockStyle.Fill }; _txtLog = new TextBox { Multiline = true, ScrollBars = ScrollBars.Vertical, Dock = DockStyle.Fill, Height = 140 }; group.Controls.Add(_txtLog); return group; } private void OnMatchTemplate() { 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; } } _selectedTemplate = null; _selectedModelWindow = null; _selectedSheetName = null; _lblTemplateInfo.Text = "未匹配模板"; AppendLog("未找到匹配模板。"); } catch (BusinessException bex) { if (_selectedTemplate != null) { _selectedTemplate.LayoutName = null; } _selectedModelWindow = null; _selectedSheetName = null; _lblTemplateInfo.Text = _selectedTemplate?.FilePath ?? "未匹配图纸"; AppendLog($"图纸匹配错误: {bex.Message}"); } catch (Exception ex) { if (_selectedTemplate != null) { _selectedTemplate.LayoutName = 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 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 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 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); } 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} 个文件"); } } } } 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 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 void OnGenerateDrawing() { try { var tplParams = CollectTemplateParams(); var drawingParams = CollectDrawingParams(); if (_selectedTemplate == null || string.IsNullOrWhiteSpace(_selectedTemplate.FilePath)) { AppendLog("请先选择路径并匹配模板。"); return; } if (string.IsNullOrWhiteSpace(_selectedTemplate.LayoutName) && !_selectedModelWindow.HasValue) { AppendLog("未匹配图纸,尝试自动匹配..."); OnMatchTemplate(); if (_selectedTemplate == null || (string.IsNullOrWhiteSpace(_selectedTemplate.LayoutName) && !_selectedModelWindow.HasValue)) { return; } } DomainFacade.ValidateParameters(tplParams, drawingParams); var doc = TemplateDrawingService.CreateDocumentFromTemplate(_selectedTemplate); if (_selectedModelWindow.HasValue) { TemplateDrawingService.KeepOnlyModelWindow(doc, _selectedModelWindow.Value); } else { TemplateDrawingService.KeepOnlyLayout(doc, _selectedTemplate.LayoutName); } AppendLog($"已生成图纸窗口,并仅保留图纸: {_selectedSheetName ?? _selectedTemplate.LayoutName}"); using (doc.LockDocument()) using (var ctx = new CadContext(doc)) { var cadService = new CadDrawingService(ctx); DomainFacade.DrawByParams(_selectedTemplate, drawingParams, cadService); ctx.Commit(); } AppendLog("图纸生成完成。"); } catch (BusinessException bex) { AppendLog($"参数错误: {bex.Message}"); } catch (Exception ex) { AppendLog($"生成失败: {ex.Message}"); Logger.Error("GenerateDrawing", ex); } } private void OnSaveDrawing() { try { var doc = AcadApp.DocumentManager.MdiActiveDocument; if (doc == null) { AppendLog("未找到活动图纸,无法保存。"); return; } using (var dialog = new SaveFileDialog { Filter = "AutoCAD Drawing (*.dwg)|*.dwg", FileName = "新图纸.dwg" }) { if (dialog.ShowDialog() == DialogResult.OK) { doc.Database.SaveAs(dialog.FileName, true, DwgVersion.Current, doc.Database.SecurityParameters); AppendLog($"图纸已保存到: {dialog.FileName}"); } } } catch (Exception ex) { AppendLog($"保存失败: {ex.Message}"); Logger.Error("SaveDrawing", ex); } } private void OnOpenSettings() { try { var current = DropdownOptionsStore.Load(); using (var form = new SettingsForm(current)) { if (form.ShowDialog() == DialogResult.OK) { DropdownOptionsStore.Save(form.Result); ReloadDropdownOptions(form.Result); AppendLog("设置已保存"); } } } catch (Exception ex) { AppendLog($"打开设置失败: {ex.Message}"); Logger.Error("OpenSettings", ex); } } private void ReloadDropdownOptions(DropdownOptions options) { if (options == null) { return; } ResetComboBoxItems(_cbProjectType, options.DeliveryStatuses); ResetComboBoxItems(_cbDrawingType, options.ProcessMethods); ResetComboBoxItems(_cbSheetSize, options.StructuralFeatures); ResetComboBoxItems(_cbScale, options.SpecialConditions); } private TemplateParams CollectTemplateParams() { var specialCondition = _cbScale.Text?.Trim(); if (string.Equals(specialCondition, "无", StringComparison.OrdinalIgnoreCase)) { specialCondition = null; } return new TemplateParams { ProjectType = _cbProjectType.Text?.Trim(), DrawingType = _cbDrawingType.Text?.Trim(), SheetSize = _cbSheetSize.Text?.Trim(), Scale = specialCondition }; } private DrawingParams CollectDrawingParams() { return new DrawingParams { Width = ParseDouble(_txtWidth.Text), Height = ParseDouble(_txtHeight.Text) }; } private static ComboBox CreateComboBox(System.Collections.Generic.IEnumerable items) { var cb = new ComboBox { DropDownStyle = ComboBoxStyle.DropDownList, Width = 160 }; var arr = (items ?? Enumerable.Empty()).Where(s => !string.IsNullOrWhiteSpace(s)).Select(s => s.Trim()).ToArray(); if (arr.Length > 0) { cb.Items.AddRange(arr); } if (cb.Items.Count > 0) { cb.SelectedIndex = 0; } return cb; } private static void ResetComboBoxItems(ComboBox cb, System.Collections.Generic.IEnumerable items) { if (cb == null) { return; } var previous = cb.Text; var arr = (items ?? Enumerable.Empty()).Where(s => !string.IsNullOrWhiteSpace(s)).Select(s => s.Trim()).ToArray(); cb.BeginUpdate(); try { cb.Items.Clear(); if (arr.Length > 0) { cb.Items.AddRange(arr); var idx = Array.FindIndex(arr, v => string.Equals(v, previous, StringComparison.OrdinalIgnoreCase)); cb.SelectedIndex = idx >= 0 ? idx : 0; } } finally { cb.EndUpdate(); } } private static TextBox CreateTextBox(string defaultText) { return new TextBox { Width = 160, Text = defaultText }; } private static double ParseDouble(string text) { if (double.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out var value)) { return value; } if (double.TryParse(text, NumberStyles.Float, CultureInfo.CurrentCulture, out value)) { return value; } return 0; } private void AppendLog(string message) { var time = DateTime.Now.ToString("HH:mm:ss"); _txtLog.AppendText($"[{time}] {message}{Environment.NewLine}"); } } }