等待找用户确认图纸匹配等问题
This commit is contained in:
parent
7987c4ee43
commit
3daac3648a
218
Cad/TemplateAnnotationOptionsExtractor.cs
Normal file
218
Cad/TemplateAnnotationOptionsExtractor.cs
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using Autodesk.AutoCAD.DatabaseServices;
|
||||||
|
using CadParamPluging.Common;
|
||||||
|
|
||||||
|
namespace CadParamPluging.Cad
|
||||||
|
{
|
||||||
|
public sealed class TemplateAnnotationExtractionResult
|
||||||
|
{
|
||||||
|
public DropdownOptions Options { get; set; }
|
||||||
|
public TemplateAnnotationExtractionSummary Summary { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class TemplateAnnotationExtractionSummary
|
||||||
|
{
|
||||||
|
public int LayoutCountScanned { get; set; }
|
||||||
|
public int EntityCountScanned { get; set; }
|
||||||
|
public int TextObjectCountScanned { get; set; }
|
||||||
|
public int MatchedLineCount { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class TemplateAnnotationOptionsExtractor
|
||||||
|
{
|
||||||
|
private static readonly Regex FieldRegex = new Regex(
|
||||||
|
@"^\s*(交付状态|工艺方法|结构特征)\s*[::]\s*(.+?)\s*$",
|
||||||
|
RegexOptions.Compiled);
|
||||||
|
|
||||||
|
private static readonly Regex MTextFormatRegex = new Regex(@"\\[A-Za-z]+[^;]*;", RegexOptions.Compiled);
|
||||||
|
|
||||||
|
public static TemplateAnnotationExtractionResult Extract(string templatePath)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(templatePath))
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(templatePath));
|
||||||
|
}
|
||||||
|
|
||||||
|
var options = new DropdownOptions();
|
||||||
|
var summary = new TemplateAnnotationExtractionSummary();
|
||||||
|
|
||||||
|
using (var db = new Database(false, true))
|
||||||
|
{
|
||||||
|
ReadTemplateDatabase(db, templatePath);
|
||||||
|
db.CloseInput(true);
|
||||||
|
|
||||||
|
using (var tr = db.TransactionManager.StartTransaction())
|
||||||
|
{
|
||||||
|
var layoutDict = (DBDictionary)tr.GetObject(db.LayoutDictionaryId, OpenMode.ForRead);
|
||||||
|
foreach (DBDictionaryEntry entry in layoutDict)
|
||||||
|
{
|
||||||
|
var layout = (Layout)tr.GetObject(entry.Value, OpenMode.ForRead);
|
||||||
|
summary.LayoutCountScanned++;
|
||||||
|
|
||||||
|
var btr = (BlockTableRecord)tr.GetObject(layout.BlockTableRecordId, OpenMode.ForRead);
|
||||||
|
foreach (ObjectId id in btr)
|
||||||
|
{
|
||||||
|
summary.EntityCountScanned++;
|
||||||
|
|
||||||
|
var obj = tr.GetObject(id, OpenMode.ForRead);
|
||||||
|
if (obj is Entity ent)
|
||||||
|
{
|
||||||
|
foreach (var line in ExtractLinesFromEntity(ent, tr, summary))
|
||||||
|
{
|
||||||
|
TryParseLine(line, options, summary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tr.Commit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
options.Normalize();
|
||||||
|
|
||||||
|
return new TemplateAnnotationExtractionResult
|
||||||
|
{
|
||||||
|
Options = options,
|
||||||
|
Summary = summary
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ReadTemplateDatabase(Database db, string templatePath)
|
||||||
|
{
|
||||||
|
// AutoCAD .NET 不同版本 ReadDwgFile 的重载参数略有差异,这里优先使用 FileShare 以允许文件被其它进程占用。
|
||||||
|
try
|
||||||
|
{
|
||||||
|
db.ReadDwgFile(templatePath, FileShare.ReadWrite, true, string.Empty);
|
||||||
|
}
|
||||||
|
catch (MissingMethodException)
|
||||||
|
{
|
||||||
|
db.ReadDwgFile(templatePath, FileOpenMode.OpenForReadAndAllShare, true, string.Empty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IEnumerable<string> ExtractLinesFromEntity(Entity ent, Transaction tr, TemplateAnnotationExtractionSummary summary)
|
||||||
|
{
|
||||||
|
if (ent is DBText text)
|
||||||
|
{
|
||||||
|
summary.TextObjectCountScanned++;
|
||||||
|
return SplitLines(text.TextString);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ent is MText mt)
|
||||||
|
{
|
||||||
|
summary.TextObjectCountScanned++;
|
||||||
|
var plain = SimplifyMText(mt.Contents);
|
||||||
|
return SplitLines(plain);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ent is BlockReference br)
|
||||||
|
{
|
||||||
|
var lines = new List<string>();
|
||||||
|
foreach (ObjectId attId in br.AttributeCollection)
|
||||||
|
{
|
||||||
|
var att = tr.GetObject(attId, OpenMode.ForRead) as AttributeReference;
|
||||||
|
if (att == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
summary.TextObjectCountScanned++;
|
||||||
|
lines.AddRange(SplitLines(att.TextString));
|
||||||
|
}
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Enumerable.Empty<string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
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())
|
||||||
|
.Where(s => s.Length > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string SimplifyMText(string contents)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(contents))
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
var s = contents
|
||||||
|
.Replace("\\P", "\n")
|
||||||
|
.Replace("\\p", "\n")
|
||||||
|
.Replace("\\~", " ");
|
||||||
|
|
||||||
|
s = s.Replace("{", string.Empty).Replace("}", string.Empty);
|
||||||
|
s = MTextFormatRegex.Replace(s, string.Empty);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void TryParseLine(string line, DropdownOptions options, TemplateAnnotationExtractionSummary summary)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(line))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var m = FieldRegex.Match(line);
|
||||||
|
if (!m.Success)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var key = m.Groups[1].Value;
|
||||||
|
var rawVal = m.Groups[2].Value;
|
||||||
|
var values = SplitValues(rawVal);
|
||||||
|
|
||||||
|
if (values.Count == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
summary.MatchedLineCount++;
|
||||||
|
|
||||||
|
switch (key)
|
||||||
|
{
|
||||||
|
case "交付状态":
|
||||||
|
options.DeliveryStatuses.AddRange(values);
|
||||||
|
break;
|
||||||
|
case "工艺方法":
|
||||||
|
options.ProcessMethods.AddRange(values);
|
||||||
|
break;
|
||||||
|
case "结构特征":
|
||||||
|
options.StructuralFeatures.AddRange(values);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<string> SplitValues(string raw)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(raw))
|
||||||
|
{
|
||||||
|
return new List<string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
var parts = Regex.Split(raw, @"\s*[;;,,\|、/]+\s*")
|
||||||
|
.Select(s => (s ?? string.Empty).Trim())
|
||||||
|
.Where(s => s.Length > 0)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
return parts;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,4 +1,9 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using Autodesk.AutoCAD.ApplicationServices;
|
using Autodesk.AutoCAD.ApplicationServices;
|
||||||
|
using Autodesk.AutoCAD.DatabaseServices;
|
||||||
|
using Autodesk.AutoCAD.Runtime;
|
||||||
using CadParamPluging.Domain.Models;
|
using CadParamPluging.Domain.Models;
|
||||||
|
|
||||||
namespace CadParamPluging.Cad
|
namespace CadParamPluging.Cad
|
||||||
@ -11,5 +16,128 @@ namespace CadParamPluging.Cad
|
|||||||
Application.DocumentManager.MdiActiveDocument = doc;
|
Application.DocumentManager.MdiActiveDocument = doc;
|
||||||
return doc;
|
return doc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void KeepOnlyLayout(Document doc, string layoutName)
|
||||||
|
{
|
||||||
|
if (doc == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(doc));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(layoutName))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
using (doc.LockDocument())
|
||||||
|
{
|
||||||
|
var db = doc.Database;
|
||||||
|
var prevDb = HostApplicationServices.WorkingDatabase;
|
||||||
|
HostApplicationServices.WorkingDatabase = db;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
List<string> layoutNames;
|
||||||
|
using (var tr = db.TransactionManager.StartTransaction())
|
||||||
|
{
|
||||||
|
var layoutDict = (DBDictionary)tr.GetObject(db.LayoutDictionaryId, OpenMode.ForRead);
|
||||||
|
layoutNames = layoutDict.Cast<DBDictionaryEntry>().Select(e => e.Key).ToList();
|
||||||
|
tr.Commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!layoutNames.Any(n => string.Equals(n, layoutName, StringComparison.OrdinalIgnoreCase)))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var lm = LayoutManager.Current;
|
||||||
|
lm.CurrentLayout = layoutName;
|
||||||
|
|
||||||
|
foreach (var name in layoutNames)
|
||||||
|
{
|
||||||
|
if (string.Equals(name, "Model", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.Equals(name, layoutName, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
lm.DeleteLayout(name);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
HostApplicationServices.WorkingDatabase = prevDb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void KeepOnlyModelWindow(Document doc, Extents3d window)
|
||||||
|
{
|
||||||
|
if (doc == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(doc));
|
||||||
|
}
|
||||||
|
|
||||||
|
using (doc.LockDocument())
|
||||||
|
{
|
||||||
|
var db = doc.Database;
|
||||||
|
using (var tr = db.TransactionManager.StartTransaction())
|
||||||
|
{
|
||||||
|
var bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
|
||||||
|
var ms = (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite);
|
||||||
|
|
||||||
|
var erased = 0;
|
||||||
|
var kept = 0;
|
||||||
|
|
||||||
|
foreach (ObjectId id in ms)
|
||||||
|
{
|
||||||
|
var ent = tr.GetObject(id, OpenMode.ForWrite) as Entity;
|
||||||
|
if (ent == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var ext = ent.GeometricExtents;
|
||||||
|
if (!Intersects(ext, window))
|
||||||
|
{
|
||||||
|
ent.Erase(true);
|
||||||
|
erased++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
kept++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// 无法获取范围的对象默认保留,避免误删关键对象
|
||||||
|
kept++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tr.Commit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool Intersects(Extents3d a, Extents3d b)
|
||||||
|
{
|
||||||
|
return a.MinPoint.X <= b.MaxPoint.X
|
||||||
|
&& a.MaxPoint.X >= b.MinPoint.X
|
||||||
|
&& a.MinPoint.Y <= b.MaxPoint.Y
|
||||||
|
&& a.MaxPoint.Y >= b.MinPoint.Y;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
283
Cad/TemplateLayoutAnnotationExtractor.cs
Normal file
283
Cad/TemplateLayoutAnnotationExtractor.cs
Normal file
@ -0,0 +1,283 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using Autodesk.AutoCAD.DatabaseServices;
|
||||||
|
|
||||||
|
namespace CadParamPluging.Cad
|
||||||
|
{
|
||||||
|
public sealed class LayoutAnnotation
|
||||||
|
{
|
||||||
|
public string LayoutName { get; set; }
|
||||||
|
public List<string> DeliveryStatuses { get; set; }
|
||||||
|
public List<string> ProcessMethods { get; set; }
|
||||||
|
public List<string> StructuralFeatures { get; set; }
|
||||||
|
|
||||||
|
public LayoutAnnotation()
|
||||||
|
{
|
||||||
|
DeliveryStatuses = new List<string>();
|
||||||
|
ProcessMethods = new List<string>();
|
||||||
|
StructuralFeatures = new List<string>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class TemplateLayoutAnnotationExtractor
|
||||||
|
{
|
||||||
|
private static readonly Regex FieldRegex = new Regex(
|
||||||
|
@"^\s*(交付状态|工艺方法|结构特征)\s*[::]\s*(.+?)\s*$",
|
||||||
|
RegexOptions.Compiled);
|
||||||
|
|
||||||
|
private static readonly Regex MTextFormatRegex = new Regex(@"\\[A-Za-z]+[^;]*;", RegexOptions.Compiled);
|
||||||
|
|
||||||
|
public static List<LayoutAnnotation> ExtractPerLayout(string templatePath)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(templatePath))
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(templatePath));
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = new List<LayoutAnnotation>();
|
||||||
|
|
||||||
|
using (var db = new Database(false, true))
|
||||||
|
{
|
||||||
|
ReadTemplateDatabase(db, templatePath);
|
||||||
|
db.CloseInput(true);
|
||||||
|
|
||||||
|
using (var tr = db.TransactionManager.StartTransaction())
|
||||||
|
{
|
||||||
|
var layoutDict = (DBDictionary)tr.GetObject(db.LayoutDictionaryId, OpenMode.ForRead);
|
||||||
|
foreach (DBDictionaryEntry entry in layoutDict)
|
||||||
|
{
|
||||||
|
var layout = (Layout)tr.GetObject(entry.Value, OpenMode.ForRead);
|
||||||
|
var layoutName = layout.LayoutName;
|
||||||
|
|
||||||
|
// 通常图纸在 PaperSpace,Model 不作为候选。
|
||||||
|
if (string.Equals(layoutName, "Model", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var ann = new LayoutAnnotation { LayoutName = layoutName };
|
||||||
|
|
||||||
|
var btr = (BlockTableRecord)tr.GetObject(layout.BlockTableRecordId, OpenMode.ForRead);
|
||||||
|
var visitedBlocks = new HashSet<ObjectId>();
|
||||||
|
foreach (ObjectId id in btr)
|
||||||
|
{
|
||||||
|
var obj = tr.GetObject(id, OpenMode.ForRead);
|
||||||
|
if (obj is Entity ent)
|
||||||
|
{
|
||||||
|
ExtractInto(ent, tr, visitedBlocks, ann);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Normalize(ann);
|
||||||
|
if (HasAnyValue(ann))
|
||||||
|
{
|
||||||
|
result.Add(ann);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tr.Commit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ReadTemplateDatabase(Database db, string templatePath)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
db.ReadDwgFile(templatePath, FileShare.ReadWrite, true, string.Empty);
|
||||||
|
}
|
||||||
|
catch (MissingMethodException)
|
||||||
|
{
|
||||||
|
db.ReadDwgFile(templatePath, FileOpenMode.OpenForReadAndAllShare, true, string.Empty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ExtractInto(Entity ent, Transaction tr, HashSet<ObjectId> visitedBlocks, LayoutAnnotation ann)
|
||||||
|
{
|
||||||
|
if (ent is DBText text)
|
||||||
|
{
|
||||||
|
foreach (var line in SplitLines(text.TextString))
|
||||||
|
{
|
||||||
|
TryParseLine(line, ann);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ent is MText mt)
|
||||||
|
{
|
||||||
|
var plain = SimplifyMText(mt.Contents);
|
||||||
|
foreach (var line in SplitLines(plain))
|
||||||
|
{
|
||||||
|
TryParseLine(line, ann);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ent is BlockReference br)
|
||||||
|
{
|
||||||
|
foreach (ObjectId attId in br.AttributeCollection)
|
||||||
|
{
|
||||||
|
var att = tr.GetObject(attId, OpenMode.ForRead) as AttributeReference;
|
||||||
|
if (att == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var line in SplitLines(att.TextString))
|
||||||
|
{
|
||||||
|
TryParseLine(line, ann);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 说明文字可能在块定义内部(非属性),需要递归扫描块内容。
|
||||||
|
var blockId = br.BlockTableRecord;
|
||||||
|
if (blockId.IsNull || visitedBlocks.Contains(blockId))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
visitedBlocks.Add(blockId);
|
||||||
|
var blockBtr = tr.GetObject(blockId, OpenMode.ForRead) as BlockTableRecord;
|
||||||
|
if (blockBtr == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (ObjectId childId in blockBtr)
|
||||||
|
{
|
||||||
|
var child = tr.GetObject(childId, OpenMode.ForRead) as Entity;
|
||||||
|
if (child != null)
|
||||||
|
{
|
||||||
|
ExtractInto(child, tr, visitedBlocks, ann);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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())
|
||||||
|
.Where(s => s.Length > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string SimplifyMText(string contents)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(contents))
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
var s = contents
|
||||||
|
.Replace("\\P", "\n")
|
||||||
|
.Replace("\\p", "\n")
|
||||||
|
.Replace("\\~", " ");
|
||||||
|
|
||||||
|
s = s.Replace("{", string.Empty).Replace("}", string.Empty);
|
||||||
|
s = MTextFormatRegex.Replace(s, string.Empty);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void TryParseLine(string line, LayoutAnnotation ann)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(line))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var m = FieldRegex.Match(line);
|
||||||
|
if (!m.Success)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var key = m.Groups[1].Value;
|
||||||
|
var val = (m.Groups[2].Value ?? string.Empty).Trim();
|
||||||
|
if (val.Length == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
val = NormalizeValue(val);
|
||||||
|
|
||||||
|
switch (key)
|
||||||
|
{
|
||||||
|
case "交付状态":
|
||||||
|
ann.DeliveryStatuses.Add(val);
|
||||||
|
break;
|
||||||
|
case "工艺方法":
|
||||||
|
ann.ProcessMethods.Add(val);
|
||||||
|
break;
|
||||||
|
case "结构特征":
|
||||||
|
ann.StructuralFeatures.Add(val);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void Normalize(LayoutAnnotation ann)
|
||||||
|
{
|
||||||
|
ann.DeliveryStatuses = NormalizeList(ann.DeliveryStatuses);
|
||||||
|
ann.ProcessMethods = NormalizeList(ann.ProcessMethods);
|
||||||
|
ann.StructuralFeatures = NormalizeList(ann.StructuralFeatures);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 List<string> NormalizeList(IEnumerable<string> values)
|
||||||
|
{
|
||||||
|
var result = new List<string>();
|
||||||
|
if (values == null)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
var seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
foreach (var raw in values)
|
||||||
|
{
|
||||||
|
var v = (raw ?? string.Empty).Trim();
|
||||||
|
if (v.Length == 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (seen.Add(v))
|
||||||
|
{
|
||||||
|
result.Add(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool HasAnyValue(LayoutAnnotation ann)
|
||||||
|
{
|
||||||
|
return ann != null
|
||||||
|
&& ((ann.DeliveryStatuses != null && ann.DeliveryStatuses.Count > 0)
|
||||||
|
|| (ann.ProcessMethods != null && ann.ProcessMethods.Count > 0)
|
||||||
|
|| (ann.StructuralFeatures != null && ann.StructuralFeatures.Count > 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
564
Cad/TemplateModelSheetExtractor.cs
Normal file
564
Cad/TemplateModelSheetExtractor.cs
Normal file
@ -0,0 +1,564 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using Autodesk.AutoCAD.DatabaseServices;
|
||||||
|
using Autodesk.AutoCAD.Geometry;
|
||||||
|
|
||||||
|
namespace CadParamPluging.Cad
|
||||||
|
{
|
||||||
|
public sealed class ModelSheetCandidate
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public List<string> DeliveryStatuses { get; set; }
|
||||||
|
public List<string> ProcessMethods { get; set; }
|
||||||
|
public List<string> StructuralFeatures { get; set; }
|
||||||
|
public Extents3d Window { get; set; }
|
||||||
|
|
||||||
|
public ModelSheetCandidate()
|
||||||
|
{
|
||||||
|
DeliveryStatuses = new List<string>();
|
||||||
|
ProcessMethods = new List<string>();
|
||||||
|
StructuralFeatures = new List<string>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class TemplateModelSheetExtractor
|
||||||
|
{
|
||||||
|
private static readonly Regex FieldRegex = new Regex(
|
||||||
|
@"^\s*(交付状态|工艺方法|结构特征)\s*[::]\s*(.+?)\s*$",
|
||||||
|
RegexOptions.Compiled);
|
||||||
|
|
||||||
|
private static readonly Regex MTextFormatRegex = new Regex(@"\\[A-Za-z]+[^;]*;", RegexOptions.Compiled);
|
||||||
|
|
||||||
|
private sealed class TextMatch
|
||||||
|
{
|
||||||
|
public string Field;
|
||||||
|
public string Value;
|
||||||
|
public Point3d Pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class VLine
|
||||||
|
{
|
||||||
|
public double X;
|
||||||
|
public double YMin;
|
||||||
|
public double YMax;
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class HLine
|
||||||
|
{
|
||||||
|
public double Y;
|
||||||
|
public double XMin;
|
||||||
|
public double XMax;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<ModelSheetCandidate> ExtractCandidates(string templatePath)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(templatePath))
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(templatePath));
|
||||||
|
}
|
||||||
|
|
||||||
|
var matches = new List<TextMatch>();
|
||||||
|
var vLines = new List<VLine>();
|
||||||
|
var hLines = new List<HLine>();
|
||||||
|
|
||||||
|
using (var db = new Database(false, true))
|
||||||
|
{
|
||||||
|
ReadTemplateDatabase(db, templatePath);
|
||||||
|
db.CloseInput(true);
|
||||||
|
|
||||||
|
using (var tr = db.TransactionManager.StartTransaction())
|
||||||
|
{
|
||||||
|
var bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
|
||||||
|
var ms = (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForRead);
|
||||||
|
|
||||||
|
var visitedBlocks = new HashSet<ObjectId>();
|
||||||
|
foreach (ObjectId id in ms)
|
||||||
|
{
|
||||||
|
var ent = tr.GetObject(id, OpenMode.ForRead) as Entity;
|
||||||
|
if (ent != null)
|
||||||
|
{
|
||||||
|
CollectEntity(ent, Matrix3d.Identity, tr, visitedBlocks, matches, vLines, hLines);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tr.Commit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return BuildCandidates(matches, vLines, hLines);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ReadTemplateDatabase(Database db, string templatePath)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
db.ReadDwgFile(templatePath, FileShare.ReadWrite, true, string.Empty);
|
||||||
|
}
|
||||||
|
catch (MissingMethodException)
|
||||||
|
{
|
||||||
|
db.ReadDwgFile(templatePath, FileOpenMode.OpenForReadAndAllShare, true, string.Empty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void TryCollectLine(Line ln, List<VLine> vLines, List<HLine> hLines)
|
||||||
|
{
|
||||||
|
var a = ln.StartPoint;
|
||||||
|
var b = ln.EndPoint;
|
||||||
|
var dx = Math.Abs(a.X - b.X);
|
||||||
|
var dy = Math.Abs(a.Y - b.Y);
|
||||||
|
const double tol = 1e-3;
|
||||||
|
|
||||||
|
if (dx <= tol && dy > tol)
|
||||||
|
{
|
||||||
|
vLines.Add(new VLine
|
||||||
|
{
|
||||||
|
X = (a.X + b.X) / 2.0,
|
||||||
|
YMin = Math.Min(a.Y, b.Y),
|
||||||
|
YMax = Math.Max(a.Y, b.Y)
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dy <= tol && dx > tol)
|
||||||
|
{
|
||||||
|
hLines.Add(new HLine
|
||||||
|
{
|
||||||
|
Y = (a.Y + b.Y) / 2.0,
|
||||||
|
XMin = Math.Min(a.X, b.X),
|
||||||
|
XMax = Math.Max(a.X, b.X)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void CollectEntity(Entity ent, Matrix3d transform, Transaction tr, HashSet<ObjectId> visitedBlocks, List<TextMatch> matches, List<VLine> vLines, List<HLine> hLines)
|
||||||
|
{
|
||||||
|
if (ent is Line ln)
|
||||||
|
{
|
||||||
|
var a = ln.StartPoint.TransformBy(transform);
|
||||||
|
var b = ln.EndPoint.TransformBy(transform);
|
||||||
|
TryCollectLinePoints(a, b, vLines, hLines);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ent is DBText t)
|
||||||
|
{
|
||||||
|
var p = t.Position.TransformBy(transform);
|
||||||
|
TryCollectText(t.TextString, p, matches);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ent is MText mt)
|
||||||
|
{
|
||||||
|
var p = mt.Location.TransformBy(transform);
|
||||||
|
var plain = SimplifyMText(mt.Contents);
|
||||||
|
TryCollectText(plain, p, matches);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ent is BlockReference br)
|
||||||
|
{
|
||||||
|
// 属性
|
||||||
|
foreach (ObjectId attId in br.AttributeCollection)
|
||||||
|
{
|
||||||
|
var att = tr.GetObject(attId, OpenMode.ForRead) as AttributeReference;
|
||||||
|
if (att == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var p = att.Position.TransformBy(transform);
|
||||||
|
TryCollectText(att.TextString, p, matches);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 块定义内部
|
||||||
|
var blockId = br.BlockTableRecord;
|
||||||
|
if (blockId.IsNull || visitedBlocks.Contains(blockId))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
visitedBlocks.Add(blockId);
|
||||||
|
|
||||||
|
var btr = tr.GetObject(blockId, OpenMode.ForRead) as BlockTableRecord;
|
||||||
|
if (btr == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var nextTransform = br.BlockTransform * transform;
|
||||||
|
foreach (ObjectId childId in btr)
|
||||||
|
{
|
||||||
|
var child = tr.GetObject(childId, OpenMode.ForRead) as Entity;
|
||||||
|
if (child != null)
|
||||||
|
{
|
||||||
|
CollectEntity(child, nextTransform, tr, visitedBlocks, matches, vLines, hLines);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void TryCollectLinePoints(Point3d a, Point3d b, List<VLine> vLines, List<HLine> hLines)
|
||||||
|
{
|
||||||
|
var dx = Math.Abs(a.X - b.X);
|
||||||
|
var dy = Math.Abs(a.Y - b.Y);
|
||||||
|
const double tol = 1e-3;
|
||||||
|
|
||||||
|
if (dx <= tol && dy > tol)
|
||||||
|
{
|
||||||
|
vLines.Add(new VLine
|
||||||
|
{
|
||||||
|
X = (a.X + b.X) / 2.0,
|
||||||
|
YMin = Math.Min(a.Y, b.Y),
|
||||||
|
YMax = Math.Max(a.Y, b.Y)
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dy <= tol && dx > tol)
|
||||||
|
{
|
||||||
|
hLines.Add(new HLine
|
||||||
|
{
|
||||||
|
Y = (a.Y + b.Y) / 2.0,
|
||||||
|
XMin = Math.Min(a.X, b.X),
|
||||||
|
XMax = Math.Max(a.X, b.X)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void TryCollectText(string raw, Point3d pos, List<TextMatch> matches)
|
||||||
|
{
|
||||||
|
foreach (var line in SplitLines(raw))
|
||||||
|
{
|
||||||
|
var m = FieldRegex.Match(line);
|
||||||
|
if (!m.Success)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var field = m.Groups[1].Value;
|
||||||
|
var value = NormalizeValue(m.Groups[2].Value);
|
||||||
|
if (value.Length == 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
matches.Add(new TextMatch { Field = field, Value = value, Pos = pos });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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())
|
||||||
|
.Where(s => s.Length > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string SimplifyMText(string contents)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(contents))
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
var s = contents
|
||||||
|
.Replace("\\P", "\n")
|
||||||
|
.Replace("\\p", "\n")
|
||||||
|
.Replace("\\~", " ");
|
||||||
|
s = s.Replace("{", string.Empty).Replace("}", string.Empty);
|
||||||
|
s = MTextFormatRegex.Replace(s, string.Empty);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 List<ModelSheetCandidate> BuildCandidates(List<TextMatch> matches, List<VLine> vLines, List<HLine> hLines)
|
||||||
|
{
|
||||||
|
if (matches == null || matches.Count == 0)
|
||||||
|
{
|
||||||
|
return new List<ModelSheetCandidate>();
|
||||||
|
}
|
||||||
|
|
||||||
|
var clusters = Cluster(matches);
|
||||||
|
var result = new List<ModelSheetCandidate>();
|
||||||
|
var idx = 1;
|
||||||
|
|
||||||
|
foreach (var cluster in clusters)
|
||||||
|
{
|
||||||
|
var delivery = cluster.Where(m => m.Field == "交付状态").Select(m => m.Value).Distinct(StringComparer.OrdinalIgnoreCase).ToList();
|
||||||
|
var process = cluster.Where(m => m.Field == "工艺方法").Select(m => m.Value).Distinct(StringComparer.OrdinalIgnoreCase).ToList();
|
||||||
|
var structural = cluster.Where(m => m.Field == "结构特征").Select(m => m.Value).Distinct(StringComparer.OrdinalIgnoreCase).ToList();
|
||||||
|
|
||||||
|
if (delivery.Count == 0 || process.Count == 0 || structural.Count == 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var textExt = ExtentsFromPoints(cluster.Select(m => m.Pos));
|
||||||
|
|
||||||
|
// 优先使用“最近的四边线”构建矩形,更适配你这种 4 条直线外框。
|
||||||
|
var rectExt = FindRectangleByNearestLines(textExt, vLines, hLines) ?? FindNearestRectangleExtents(textExt, vLines, hLines);
|
||||||
|
var window = rectExt.HasValue ? Union(rectExt.Value, Expand(textExt, 200.0)) : Expand(textExt, 2000.0);
|
||||||
|
|
||||||
|
result.Add(new ModelSheetCandidate
|
||||||
|
{
|
||||||
|
Name = $"Model#{idx}",
|
||||||
|
DeliveryStatuses = delivery,
|
||||||
|
ProcessMethods = process,
|
||||||
|
StructuralFeatures = structural,
|
||||||
|
Window = window
|
||||||
|
});
|
||||||
|
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<List<TextMatch>> Cluster(List<TextMatch> matches)
|
||||||
|
{
|
||||||
|
var n = matches.Count;
|
||||||
|
var parent = Enumerable.Range(0, n).ToArray();
|
||||||
|
|
||||||
|
int Find(int x)
|
||||||
|
{
|
||||||
|
while (parent[x] != x) { parent[x] = parent[parent[x]]; x = parent[x]; }
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UnionIdx(int a, int b)
|
||||||
|
{
|
||||||
|
var ra = Find(a);
|
||||||
|
var rb = Find(b);
|
||||||
|
if (ra != rb) parent[rb] = ra;
|
||||||
|
}
|
||||||
|
|
||||||
|
var threshold = EstimateThreshold(matches);
|
||||||
|
var threshold2 = threshold * threshold;
|
||||||
|
|
||||||
|
for (var i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
for (var j = i + 1; j < n; j++)
|
||||||
|
{
|
||||||
|
var dx = matches[i].Pos.X - matches[j].Pos.X;
|
||||||
|
var dy = matches[i].Pos.Y - matches[j].Pos.Y;
|
||||||
|
var d2 = dx * dx + dy * dy;
|
||||||
|
if (d2 <= threshold2)
|
||||||
|
{
|
||||||
|
UnionIdx(i, j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var groups = new Dictionary<int, List<TextMatch>>();
|
||||||
|
for (var i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
var r = Find(i);
|
||||||
|
if (!groups.TryGetValue(r, out var list))
|
||||||
|
{
|
||||||
|
list = new List<TextMatch>();
|
||||||
|
groups[r] = list;
|
||||||
|
}
|
||||||
|
list.Add(matches[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return groups.Values.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double EstimateThreshold(List<TextMatch> matches)
|
||||||
|
{
|
||||||
|
if (matches.Count < 2)
|
||||||
|
{
|
||||||
|
return 1000.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
var dists = new List<double>();
|
||||||
|
for (var i = 0; i < matches.Count; i++)
|
||||||
|
{
|
||||||
|
var best = double.MaxValue;
|
||||||
|
for (var j = 0; j < matches.Count; j++)
|
||||||
|
{
|
||||||
|
if (i == j) continue;
|
||||||
|
var dx = matches[i].Pos.X - matches[j].Pos.X;
|
||||||
|
var dy = matches[i].Pos.Y - matches[j].Pos.Y;
|
||||||
|
var d = Math.Sqrt(dx * dx + dy * dy);
|
||||||
|
if (d < best) best = d;
|
||||||
|
}
|
||||||
|
if (best < double.MaxValue) dists.Add(best);
|
||||||
|
}
|
||||||
|
|
||||||
|
dists.Sort();
|
||||||
|
var median = dists[dists.Count / 2];
|
||||||
|
if (median <= 0) median = 500.0;
|
||||||
|
var thr = median * 4.0;
|
||||||
|
if (thr < 500.0) thr = 500.0;
|
||||||
|
if (thr > 20000.0) thr = 20000.0;
|
||||||
|
return thr;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Extents3d ExtentsFromPoints(IEnumerable<Point3d> pts)
|
||||||
|
{
|
||||||
|
var arr = (pts ?? Enumerable.Empty<Point3d>()).ToArray();
|
||||||
|
if (arr.Length == 0)
|
||||||
|
{
|
||||||
|
return new Extents3d(new Point3d(0, 0, 0), new Point3d(0, 0, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
var minX = arr.Min(p => p.X);
|
||||||
|
var minY = arr.Min(p => p.Y);
|
||||||
|
var maxX = arr.Max(p => p.X);
|
||||||
|
var maxY = arr.Max(p => p.Y);
|
||||||
|
return new Extents3d(new Point3d(minX, minY, 0), new Point3d(maxX, maxY, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Extents3d Expand(Extents3d e, double margin)
|
||||||
|
{
|
||||||
|
return new Extents3d(
|
||||||
|
new Point3d(e.MinPoint.X - margin, e.MinPoint.Y - margin, 0),
|
||||||
|
new Point3d(e.MaxPoint.X + margin, e.MaxPoint.Y + margin, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Extents3d Union(Extents3d a, Extents3d b)
|
||||||
|
{
|
||||||
|
return new Extents3d(
|
||||||
|
new Point3d(Math.Min(a.MinPoint.X, b.MinPoint.X), Math.Min(a.MinPoint.Y, b.MinPoint.Y), 0),
|
||||||
|
new Point3d(Math.Max(a.MaxPoint.X, b.MaxPoint.X), Math.Max(a.MaxPoint.Y, b.MaxPoint.Y), 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Extents3d? FindNearestRectangleExtents(Extents3d anchor, List<VLine> vLines, List<HLine> hLines)
|
||||||
|
{
|
||||||
|
if (vLines == null || hLines == null || vLines.Count < 2 || hLines.Count < 2)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var cx = (anchor.MinPoint.X + anchor.MaxPoint.X) / 2.0;
|
||||||
|
var cy = (anchor.MinPoint.Y + anchor.MaxPoint.Y) / 2.0;
|
||||||
|
|
||||||
|
var vCand = vLines.OrderBy(l => Math.Abs(l.X - cx)).Take(30).ToList();
|
||||||
|
var hCand = hLines.OrderBy(l => Math.Abs(l.Y - cy)).Take(30).ToList();
|
||||||
|
|
||||||
|
const double tol = 5.0;
|
||||||
|
Extents3d? best = null;
|
||||||
|
double bestScore = double.MaxValue;
|
||||||
|
|
||||||
|
for (var i = 0; i < vCand.Count; i++)
|
||||||
|
{
|
||||||
|
for (var j = i + 1; j < vCand.Count; j++)
|
||||||
|
{
|
||||||
|
var left = vCand[i].X < vCand[j].X ? vCand[i] : vCand[j];
|
||||||
|
var right = vCand[i].X < vCand[j].X ? vCand[j] : vCand[i];
|
||||||
|
var width = right.X - left.X;
|
||||||
|
if (width < 1000) continue;
|
||||||
|
|
||||||
|
for (var a = 0; a < hCand.Count; a++)
|
||||||
|
{
|
||||||
|
for (var b = a + 1; b < hCand.Count; b++)
|
||||||
|
{
|
||||||
|
var bottom = hCand[a].Y < hCand[b].Y ? hCand[a] : hCand[b];
|
||||||
|
var top = hCand[a].Y < hCand[b].Y ? hCand[b] : hCand[a];
|
||||||
|
var height = top.Y - bottom.Y;
|
||||||
|
if (height < 1000) continue;
|
||||||
|
|
||||||
|
// span check
|
||||||
|
if (left.YMin > bottom.Y + tol || left.YMax < top.Y - tol) continue;
|
||||||
|
if (right.YMin > bottom.Y + tol || right.YMax < top.Y - tol) continue;
|
||||||
|
if (bottom.XMin > left.X + tol || bottom.XMax < right.X - tol) continue;
|
||||||
|
if (top.XMin > left.X + tol || top.XMax < right.X - tol) continue;
|
||||||
|
|
||||||
|
var rect = new Extents3d(
|
||||||
|
new Point3d(left.X, bottom.Y, 0),
|
||||||
|
new Point3d(right.X, top.Y, 0));
|
||||||
|
|
||||||
|
var rcx = (left.X + right.X) / 2.0;
|
||||||
|
var rcy = (bottom.Y + top.Y) / 2.0;
|
||||||
|
var dist = Math.Sqrt((rcx - cx) * (rcx - cx) + (rcy - cy) * (rcy - cy));
|
||||||
|
var area = width * height;
|
||||||
|
var score = dist + area * 1e-9; // distance first, area tie-break
|
||||||
|
|
||||||
|
if (score < bestScore)
|
||||||
|
{
|
||||||
|
bestScore = score;
|
||||||
|
best = rect;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return best;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Extents3d? FindRectangleByNearestLines(Extents3d anchor, List<VLine> vLines, List<HLine> hLines)
|
||||||
|
{
|
||||||
|
if (vLines == null || hLines == null || vLines.Count == 0 || hLines.Count == 0)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var cx = (anchor.MinPoint.X + anchor.MaxPoint.X) / 2.0;
|
||||||
|
var cy = (anchor.MinPoint.Y + anchor.MaxPoint.Y) / 2.0;
|
||||||
|
const double tol = 50.0;
|
||||||
|
|
||||||
|
var left = vLines
|
||||||
|
.Where(l => l.X < cx - tol && l.YMin - tol <= cy && l.YMax + tol >= cy)
|
||||||
|
.OrderByDescending(l => l.X)
|
||||||
|
.FirstOrDefault();
|
||||||
|
|
||||||
|
var right = vLines
|
||||||
|
.Where(l => l.X > cx + tol && l.YMin - tol <= cy && l.YMax + tol >= cy)
|
||||||
|
.OrderBy(l => l.X)
|
||||||
|
.FirstOrDefault();
|
||||||
|
|
||||||
|
var bottom = hLines
|
||||||
|
.Where(l => l.Y < cy - tol && l.XMin - tol <= cx && l.XMax + tol >= cx)
|
||||||
|
.OrderByDescending(l => l.Y)
|
||||||
|
.FirstOrDefault();
|
||||||
|
|
||||||
|
var top = hLines
|
||||||
|
.Where(l => l.Y > cy + tol && l.XMin - tol <= cx && l.XMax + tol >= cx)
|
||||||
|
.OrderBy(l => l.Y)
|
||||||
|
.FirstOrDefault();
|
||||||
|
|
||||||
|
if (left == null || right == null || bottom == null || top == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var width = right.X - left.X;
|
||||||
|
var height = top.Y - bottom.Y;
|
||||||
|
if (width < 1000 || height < 1000)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var rect = new Extents3d(new Point3d(left.X, bottom.Y, 0), new Point3d(right.X, top.Y, 0));
|
||||||
|
if (anchor.MinPoint.X < rect.MinPoint.X - tol || anchor.MaxPoint.X > rect.MaxPoint.X + tol ||
|
||||||
|
anchor.MinPoint.Y < rect.MinPoint.Y - tol || anchor.MaxPoint.Y > rect.MaxPoint.Y + tol)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rect;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -60,9 +60,14 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="PluginEntry.cs" />
|
<Compile Include="PluginEntry.cs" />
|
||||||
<Compile Include="Common\BusinessException.cs" />
|
<Compile Include="Common\BusinessException.cs" />
|
||||||
|
<Compile Include="Common\DropdownOptions.cs" />
|
||||||
|
<Compile Include="Common\DropdownOptionsStore.cs" />
|
||||||
<Compile Include="Common\Logger.cs" />
|
<Compile Include="Common\Logger.cs" />
|
||||||
<Compile Include="Cad\CadContext.cs" />
|
<Compile Include="Cad\CadContext.cs" />
|
||||||
<Compile Include="Cad\CadDrawingService.cs" />
|
<Compile Include="Cad\CadDrawingService.cs" />
|
||||||
|
<Compile Include="Cad\TemplateAnnotationOptionsExtractor.cs" />
|
||||||
|
<Compile Include="Cad\TemplateLayoutAnnotationExtractor.cs" />
|
||||||
|
<Compile Include="Cad\TemplateModelSheetExtractor.cs" />
|
||||||
<Compile Include="Cad\TemplateDrawingService.cs" />
|
<Compile Include="Cad\TemplateDrawingService.cs" />
|
||||||
<Compile Include="Data\DefaultTemplateRepository.cs" />
|
<Compile Include="Data\DefaultTemplateRepository.cs" />
|
||||||
<Compile Include="Data\ITemplateRepository.cs" />
|
<Compile Include="Data\ITemplateRepository.cs" />
|
||||||
@ -74,9 +79,18 @@
|
|||||||
<Compile Include="UI\ParamDrawingPanel.cs">
|
<Compile Include="UI\ParamDrawingPanel.cs">
|
||||||
<SubType>UserControl</SubType>
|
<SubType>UserControl</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="UI\LayoutSelectForm.cs">
|
||||||
|
<SubType>Form</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="UI\ModelSheetSelectForm.cs">
|
||||||
|
<SubType>Form</SubType>
|
||||||
|
</Compile>
|
||||||
<Compile Include="UI\SettingsForm.cs">
|
<Compile Include="UI\SettingsForm.cs">
|
||||||
<SubType>Form</SubType>
|
<SubType>Form</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="UI\TextInputForm.cs">
|
||||||
|
<SubType>Form</SubType>
|
||||||
|
</Compile>
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
|
|||||||
77
Common/DropdownOptions.cs
Normal file
77
Common/DropdownOptions.cs
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace CadParamPluging.Common
|
||||||
|
{
|
||||||
|
public class DropdownOptions
|
||||||
|
{
|
||||||
|
public List<string> DeliveryStatuses { get; set; }
|
||||||
|
public List<string> ProcessMethods { get; set; }
|
||||||
|
public List<string> StructuralFeatures { get; set; }
|
||||||
|
public List<string> SpecialConditions { get; set; }
|
||||||
|
|
||||||
|
public DropdownOptions()
|
||||||
|
{
|
||||||
|
DeliveryStatuses = new List<string>();
|
||||||
|
ProcessMethods = new List<string>();
|
||||||
|
StructuralFeatures = new List<string>();
|
||||||
|
SpecialConditions = new List<string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DropdownOptions CreateDefault()
|
||||||
|
{
|
||||||
|
return new DropdownOptions
|
||||||
|
{
|
||||||
|
DeliveryStatuses = new List<string> { "结构", "示例" },
|
||||||
|
ProcessMethods = new List<string> { "基础平面", "示例图" },
|
||||||
|
StructuralFeatures = new List<string> { "A1", "A3" },
|
||||||
|
SpecialConditions = new List<string> { "无" }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public DropdownOptions Clone()
|
||||||
|
{
|
||||||
|
return new DropdownOptions
|
||||||
|
{
|
||||||
|
DeliveryStatuses = new List<string>(DeliveryStatuses ?? new List<string>()),
|
||||||
|
ProcessMethods = new List<string>(ProcessMethods ?? new List<string>()),
|
||||||
|
StructuralFeatures = new List<string>(StructuralFeatures ?? new List<string>()),
|
||||||
|
SpecialConditions = new List<string>(SpecialConditions ?? new List<string>())
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Normalize()
|
||||||
|
{
|
||||||
|
DeliveryStatuses = NormalizeList(DeliveryStatuses);
|
||||||
|
ProcessMethods = NormalizeList(ProcessMethods);
|
||||||
|
StructuralFeatures = NormalizeList(StructuralFeatures);
|
||||||
|
SpecialConditions = NormalizeList(SpecialConditions);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<string> NormalizeList(IEnumerable<string> values)
|
||||||
|
{
|
||||||
|
var result = new List<string>();
|
||||||
|
if (values == null)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
var seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
foreach (var raw in values)
|
||||||
|
{
|
||||||
|
var v = (raw ?? string.Empty).Trim();
|
||||||
|
if (v.Length == 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (seen.Add(v))
|
||||||
|
{
|
||||||
|
result.Add(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
130
Common/DropdownOptionsStore.cs
Normal file
130
Common/DropdownOptionsStore.cs
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Xml.Serialization;
|
||||||
|
|
||||||
|
namespace CadParamPluging.Common
|
||||||
|
{
|
||||||
|
public static class DropdownOptionsStore
|
||||||
|
{
|
||||||
|
private const string FolderName = "CadParamPluging";
|
||||||
|
private const string FileName = "dropdown-options.xml";
|
||||||
|
|
||||||
|
public static string GetFilePath()
|
||||||
|
{
|
||||||
|
var dir = Path.Combine(
|
||||||
|
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
||||||
|
FolderName);
|
||||||
|
return Path.Combine(dir, FileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DropdownOptions Load()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var path = GetFilePath();
|
||||||
|
if (!File.Exists(path))
|
||||||
|
{
|
||||||
|
return DropdownOptions.CreateDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
using (var stream = File.OpenRead(path))
|
||||||
|
{
|
||||||
|
var serializer = new XmlSerializer(typeof(DropdownOptions));
|
||||||
|
var obj = serializer.Deserialize(stream) as DropdownOptions;
|
||||||
|
if (obj == null)
|
||||||
|
{
|
||||||
|
return DropdownOptions.CreateDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
obj.Normalize();
|
||||||
|
EnsureNonEmptyByFallbackToDefault(obj);
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return DropdownOptions.CreateDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Save(DropdownOptions options)
|
||||||
|
{
|
||||||
|
if (options == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(options));
|
||||||
|
}
|
||||||
|
|
||||||
|
options.Normalize();
|
||||||
|
ValidateNonEmpty(options);
|
||||||
|
|
||||||
|
var path = GetFilePath();
|
||||||
|
var dir = Path.GetDirectoryName(path);
|
||||||
|
if (!string.IsNullOrEmpty(dir))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
var tempPath = path + ".tmp";
|
||||||
|
var serializer = new XmlSerializer(typeof(DropdownOptions));
|
||||||
|
using (var stream = File.Create(tempPath))
|
||||||
|
{
|
||||||
|
serializer.Serialize(stream, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (File.Exists(path))
|
||||||
|
{
|
||||||
|
File.Delete(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
File.Move(tempPath, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ValidateNonEmpty(DropdownOptions options)
|
||||||
|
{
|
||||||
|
if (options.DeliveryStatuses == null || options.DeliveryStatuses.Count == 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("交付状态不能为空。");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.ProcessMethods == null || options.ProcessMethods.Count == 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("工艺方法不能为空。");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.StructuralFeatures == null || options.StructuralFeatures.Count == 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("结构特征不能为空。");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.SpecialConditions == null || options.SpecialConditions.Count == 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("特殊条件不能为空。");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void EnsureNonEmptyByFallbackToDefault(DropdownOptions options)
|
||||||
|
{
|
||||||
|
var defaults = DropdownOptions.CreateDefault();
|
||||||
|
|
||||||
|
if (options.DeliveryStatuses == null || options.DeliveryStatuses.Count == 0)
|
||||||
|
{
|
||||||
|
options.DeliveryStatuses = defaults.DeliveryStatuses;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.ProcessMethods == null || options.ProcessMethods.Count == 0)
|
||||||
|
{
|
||||||
|
options.ProcessMethods = defaults.ProcessMethods;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.StructuralFeatures == null || options.StructuralFeatures.Count == 0)
|
||||||
|
{
|
||||||
|
options.StructuralFeatures = defaults.StructuralFeatures;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.SpecialConditions == null || options.SpecialConditions.Count == 0)
|
||||||
|
{
|
||||||
|
options.SpecialConditions = defaults.SpecialConditions;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -5,5 +5,7 @@ namespace CadParamPluging.Domain.Models
|
|||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public string FilePath { get; set; }
|
public string FilePath { get; set; }
|
||||||
public string Description { get; set; }
|
public string Description { get; set; }
|
||||||
|
|
||||||
|
public string LayoutName { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
118
UI/LayoutSelectForm.cs
Normal file
118
UI/LayoutSelectForm.cs
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using CadParamPluging.Cad;
|
||||||
|
|
||||||
|
namespace CadParamPluging.UI
|
||||||
|
{
|
||||||
|
public class LayoutSelectForm : Form
|
||||||
|
{
|
||||||
|
private readonly ListBox _list;
|
||||||
|
private readonly List<LayoutAnnotation> _items;
|
||||||
|
|
||||||
|
public LayoutAnnotation Selected { get; private set; }
|
||||||
|
|
||||||
|
public LayoutSelectForm(IEnumerable<LayoutAnnotation> candidates)
|
||||||
|
{
|
||||||
|
Text = "选择图纸";
|
||||||
|
Size = new Size(520, 360);
|
||||||
|
StartPosition = FormStartPosition.CenterParent;
|
||||||
|
FormBorderStyle = FormBorderStyle.FixedDialog;
|
||||||
|
MaximizeBox = false;
|
||||||
|
MinimizeBox = false;
|
||||||
|
ShowInTaskbar = false;
|
||||||
|
|
||||||
|
_items = (candidates ?? Enumerable.Empty<LayoutAnnotation>()).ToList();
|
||||||
|
|
||||||
|
var layout = new TableLayoutPanel
|
||||||
|
{
|
||||||
|
Dock = DockStyle.Fill,
|
||||||
|
Padding = new Padding(10),
|
||||||
|
RowCount = 3,
|
||||||
|
ColumnCount = 1
|
||||||
|
};
|
||||||
|
layout.RowStyles.Add(new RowStyle(SizeType.AutoSize));
|
||||||
|
layout.RowStyles.Add(new RowStyle(SizeType.Percent, 100f));
|
||||||
|
layout.RowStyles.Add(new RowStyle(SizeType.AutoSize));
|
||||||
|
|
||||||
|
var lbl = new Label
|
||||||
|
{
|
||||||
|
Text = "匹配到多张图纸,请选择:",
|
||||||
|
Dock = DockStyle.Fill,
|
||||||
|
AutoSize = true
|
||||||
|
};
|
||||||
|
|
||||||
|
_list = new ListBox { Dock = DockStyle.Fill };
|
||||||
|
foreach (var it in _items)
|
||||||
|
{
|
||||||
|
_list.Items.Add(Format(it));
|
||||||
|
}
|
||||||
|
if (_list.Items.Count > 0)
|
||||||
|
{
|
||||||
|
_list.SelectedIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
var buttons = new FlowLayoutPanel
|
||||||
|
{
|
||||||
|
Dock = DockStyle.Fill,
|
||||||
|
FlowDirection = FlowDirection.RightToLeft,
|
||||||
|
AutoSize = true
|
||||||
|
};
|
||||||
|
|
||||||
|
var btnCancel = new Button { Text = "取消", DialogResult = DialogResult.Cancel };
|
||||||
|
var btnOk = new Button { Text = "确定" };
|
||||||
|
btnOk.Click += (_, __) => OnOk();
|
||||||
|
|
||||||
|
buttons.Controls.Add(btnCancel);
|
||||||
|
buttons.Controls.Add(btnOk);
|
||||||
|
|
||||||
|
layout.Controls.Add(lbl, 0, 0);
|
||||||
|
layout.Controls.Add(_list, 0, 1);
|
||||||
|
layout.Controls.Add(buttons, 0, 2);
|
||||||
|
Controls.Add(layout);
|
||||||
|
|
||||||
|
AcceptButton = btnOk;
|
||||||
|
CancelButton = btnCancel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnOk()
|
||||||
|
{
|
||||||
|
var idx = _list.SelectedIndex;
|
||||||
|
if (idx < 0 || idx >= _items.Count)
|
||||||
|
{
|
||||||
|
MessageBox.Show(this, "请选择一项。", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Selected = _items[idx];
|
||||||
|
DialogResult = DialogResult.OK;
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string Format(LayoutAnnotation it)
|
||||||
|
{
|
||||||
|
var d = it?.DeliveryStatuses != null && it.DeliveryStatuses.Count > 0 ? string.Join("/", it.DeliveryStatuses) : "(无)";
|
||||||
|
var p = it?.ProcessMethods != null && it.ProcessMethods.Count > 0 ? string.Join("/", it.ProcessMethods) : "(无)";
|
||||||
|
var s = it?.StructuralFeatures != null && it.StructuralFeatures.Count > 0 ? string.Join("/", it.StructuralFeatures) : "(无)";
|
||||||
|
return $"{it?.LayoutName} | 交付状态={d} | 工艺方法={p} | 结构特征={s}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool TrySelect(IWin32Window owner, IEnumerable<LayoutAnnotation> candidates, out LayoutAnnotation selected)
|
||||||
|
{
|
||||||
|
using (var f = new LayoutSelectForm(candidates))
|
||||||
|
{
|
||||||
|
var r = f.ShowDialog(owner);
|
||||||
|
if (r == DialogResult.OK)
|
||||||
|
{
|
||||||
|
selected = f.Selected;
|
||||||
|
return selected != null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
selected = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
118
UI/ModelSheetSelectForm.cs
Normal file
118
UI/ModelSheetSelectForm.cs
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using CadParamPluging.Cad;
|
||||||
|
|
||||||
|
namespace CadParamPluging.UI
|
||||||
|
{
|
||||||
|
public class ModelSheetSelectForm : Form
|
||||||
|
{
|
||||||
|
private readonly ListBox _list;
|
||||||
|
private readonly List<ModelSheetCandidate> _items;
|
||||||
|
|
||||||
|
public ModelSheetCandidate Selected { get; private set; }
|
||||||
|
|
||||||
|
public ModelSheetSelectForm(IEnumerable<ModelSheetCandidate> candidates)
|
||||||
|
{
|
||||||
|
Text = "选择图纸";
|
||||||
|
Size = new Size(560, 360);
|
||||||
|
StartPosition = FormStartPosition.CenterParent;
|
||||||
|
FormBorderStyle = FormBorderStyle.FixedDialog;
|
||||||
|
MaximizeBox = false;
|
||||||
|
MinimizeBox = false;
|
||||||
|
ShowInTaskbar = false;
|
||||||
|
|
||||||
|
_items = (candidates ?? Enumerable.Empty<ModelSheetCandidate>()).ToList();
|
||||||
|
|
||||||
|
var layout = new TableLayoutPanel
|
||||||
|
{
|
||||||
|
Dock = DockStyle.Fill,
|
||||||
|
Padding = new Padding(10),
|
||||||
|
RowCount = 3,
|
||||||
|
ColumnCount = 1
|
||||||
|
};
|
||||||
|
layout.RowStyles.Add(new RowStyle(SizeType.AutoSize));
|
||||||
|
layout.RowStyles.Add(new RowStyle(SizeType.Percent, 100f));
|
||||||
|
layout.RowStyles.Add(new RowStyle(SizeType.AutoSize));
|
||||||
|
|
||||||
|
var lbl = new Label
|
||||||
|
{
|
||||||
|
Text = "匹配到多张图纸,请选择:",
|
||||||
|
Dock = DockStyle.Fill,
|
||||||
|
AutoSize = true
|
||||||
|
};
|
||||||
|
|
||||||
|
_list = new ListBox { Dock = DockStyle.Fill };
|
||||||
|
foreach (var it in _items)
|
||||||
|
{
|
||||||
|
_list.Items.Add(Format(it));
|
||||||
|
}
|
||||||
|
if (_list.Items.Count > 0)
|
||||||
|
{
|
||||||
|
_list.SelectedIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
var buttons = new FlowLayoutPanel
|
||||||
|
{
|
||||||
|
Dock = DockStyle.Fill,
|
||||||
|
FlowDirection = FlowDirection.RightToLeft,
|
||||||
|
AutoSize = true
|
||||||
|
};
|
||||||
|
|
||||||
|
var btnCancel = new Button { Text = "取消", DialogResult = DialogResult.Cancel };
|
||||||
|
var btnOk = new Button { Text = "确定" };
|
||||||
|
btnOk.Click += (_, __) => OnOk();
|
||||||
|
|
||||||
|
buttons.Controls.Add(btnCancel);
|
||||||
|
buttons.Controls.Add(btnOk);
|
||||||
|
|
||||||
|
layout.Controls.Add(lbl, 0, 0);
|
||||||
|
layout.Controls.Add(_list, 0, 1);
|
||||||
|
layout.Controls.Add(buttons, 0, 2);
|
||||||
|
Controls.Add(layout);
|
||||||
|
|
||||||
|
AcceptButton = btnOk;
|
||||||
|
CancelButton = btnCancel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnOk()
|
||||||
|
{
|
||||||
|
var idx = _list.SelectedIndex;
|
||||||
|
if (idx < 0 || idx >= _items.Count)
|
||||||
|
{
|
||||||
|
MessageBox.Show(this, "请选择一项。", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Selected = _items[idx];
|
||||||
|
DialogResult = DialogResult.OK;
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string Format(ModelSheetCandidate it)
|
||||||
|
{
|
||||||
|
var d = it?.DeliveryStatuses != null && it.DeliveryStatuses.Count > 0 ? string.Join("/", it.DeliveryStatuses) : "(无)";
|
||||||
|
var p = it?.ProcessMethods != null && it.ProcessMethods.Count > 0 ? string.Join("/", it.ProcessMethods) : "(无)";
|
||||||
|
var s = it?.StructuralFeatures != null && it.StructuralFeatures.Count > 0 ? string.Join("/", it.StructuralFeatures) : "(无)";
|
||||||
|
return $"{it?.Name} | 交付状态={d} | 工艺方法={p} | 结构特征={s}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool TrySelect(IWin32Window owner, IEnumerable<ModelSheetCandidate> candidates, out ModelSheetCandidate selected)
|
||||||
|
{
|
||||||
|
using (var f = new ModelSheetSelectForm(candidates))
|
||||||
|
{
|
||||||
|
var r = f.ShowDialog(owner);
|
||||||
|
if (r == DialogResult.OK)
|
||||||
|
{
|
||||||
|
selected = f.Selected;
|
||||||
|
return selected != null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
selected = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using System.Linq;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using Autodesk.AutoCAD.ApplicationServices;
|
using Autodesk.AutoCAD.ApplicationServices;
|
||||||
using Autodesk.AutoCAD.DatabaseServices;
|
using Autodesk.AutoCAD.DatabaseServices;
|
||||||
@ -25,11 +26,15 @@ namespace CadParamPluging.UI
|
|||||||
private TextBox _txtTemplatePath;
|
private TextBox _txtTemplatePath;
|
||||||
|
|
||||||
private TemplateInfo _selectedTemplate;
|
private TemplateInfo _selectedTemplate;
|
||||||
|
private Extents3d? _selectedModelWindow;
|
||||||
|
private string _selectedSheetName;
|
||||||
|
|
||||||
public ParamDrawingPanel()
|
public ParamDrawingPanel()
|
||||||
{
|
{
|
||||||
Dock = DockStyle.Fill;
|
Dock = DockStyle.Fill;
|
||||||
|
|
||||||
|
var dropdownOptions = DropdownOptionsStore.Load();
|
||||||
|
|
||||||
var layout = new TableLayoutPanel
|
var layout = new TableLayoutPanel
|
||||||
{
|
{
|
||||||
Dock = DockStyle.Fill,
|
Dock = DockStyle.Fill,
|
||||||
@ -40,10 +45,10 @@ namespace CadParamPluging.UI
|
|||||||
Padding = new Padding(8)
|
Padding = new Padding(8)
|
||||||
};
|
};
|
||||||
|
|
||||||
_cbProjectType = CreateComboBox(new[] { "结构", "示例" });
|
_cbProjectType = CreateComboBox(dropdownOptions.DeliveryStatuses);
|
||||||
_cbDrawingType = CreateComboBox(new[] { "基础平面", "示例图" });
|
_cbDrawingType = CreateComboBox(dropdownOptions.ProcessMethods);
|
||||||
_cbSheetSize = CreateComboBox(new[] { "A1", "A3" });
|
_cbSheetSize = CreateComboBox(dropdownOptions.StructuralFeatures);
|
||||||
_cbScale = CreateComboBox(new[] { "1:50", "1:100" });
|
_cbScale = CreateComboBox(dropdownOptions.SpecialConditions);
|
||||||
_txtWidth = CreateTextBox("5000");
|
_txtWidth = CreateTextBox("5000");
|
||||||
_txtHeight = CreateTextBox("3000");
|
_txtHeight = CreateTextBox("3000");
|
||||||
|
|
||||||
@ -191,29 +196,170 @@ namespace CadParamPluging.UI
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
if (_selectedTemplate == null || string.IsNullOrWhiteSpace(_selectedTemplate.FilePath))
|
||||||
|
{
|
||||||
|
AppendLog("请先选择模板文件。");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AppendLog("开始匹配图纸...");
|
||||||
|
|
||||||
var tplParams = CollectTemplateParams();
|
var tplParams = CollectTemplateParams();
|
||||||
_selectedTemplate = DomainFacade.SelectTemplate(tplParams);
|
// 1) 优先按 Layout 匹配(适用于多布局模板)
|
||||||
_lblTemplateInfo.Text = $"已匹配: {_selectedTemplate.Name}";
|
var layoutCandidates = TemplateLayoutAnnotationExtractor.ExtractPerLayout(_selectedTemplate.FilePath);
|
||||||
_txtTemplatePath.Text = _selectedTemplate.FilePath;
|
var layoutMatches = layoutCandidates.Where(c => LayoutMatches(c, tplParams)).ToList();
|
||||||
AppendLog($"模板: {_selectedTemplate.FilePath}");
|
|
||||||
|
if (layoutMatches.Count > 0)
|
||||||
|
{
|
||||||
|
LayoutAnnotation selectedLayout;
|
||||||
|
if (layoutMatches.Count == 1)
|
||||||
|
{
|
||||||
|
selectedLayout = layoutMatches[0];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!LayoutSelectForm.TrySelect(this, layoutMatches, out selectedLayout))
|
||||||
|
{
|
||||||
|
AppendLog("用户取消选择图纸。");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_selectedTemplate.LayoutName = selectedLayout.LayoutName;
|
||||||
|
_selectedModelWindow = null;
|
||||||
|
_selectedSheetName = selectedLayout.LayoutName;
|
||||||
|
_lblTemplateInfo.Text = $"{_selectedTemplate.FilePath} | {selectedLayout.LayoutName}";
|
||||||
|
AppendLog($"已匹配图纸: {selectedLayout.LayoutName}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) 如果 Layout 为空或未匹配,按 ModelSpace 多张图纸模式匹配
|
||||||
|
var modelCandidates = TemplateModelSheetExtractor.ExtractCandidates(_selectedTemplate.FilePath);
|
||||||
|
var modelMatches = modelCandidates.Where(c => ModelMatches(c, tplParams)).ToList();
|
||||||
|
|
||||||
|
if (modelMatches.Count == 0)
|
||||||
|
{
|
||||||
|
_selectedTemplate.LayoutName = null;
|
||||||
|
_selectedModelWindow = null;
|
||||||
|
_selectedSheetName = null;
|
||||||
|
_lblTemplateInfo.Text = _selectedTemplate.FilePath;
|
||||||
|
AppendLog("未找到匹配图纸。");
|
||||||
|
AppendLog($"候选图纸数量: {modelCandidates.Count}");
|
||||||
|
foreach (var c in modelCandidates.Take(10))
|
||||||
|
{
|
||||||
|
AppendLog($"候选: {c.Name} | 交付状态={Preview(c.DeliveryStatuses)} | 工艺方法={Preview(c.ProcessMethods)} | 结构特征={Preview(c.StructuralFeatures)}");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ModelSheetCandidate selectedModel;
|
||||||
|
if (modelMatches.Count == 1)
|
||||||
|
{
|
||||||
|
selectedModel = modelMatches[0];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!ModelSheetSelectForm.TrySelect(this, modelMatches, out selectedModel))
|
||||||
|
{
|
||||||
|
AppendLog("用户取消选择图纸。");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_selectedTemplate.LayoutName = null;
|
||||||
|
_selectedModelWindow = selectedModel.Window;
|
||||||
|
_selectedSheetName = selectedModel.Name;
|
||||||
|
_lblTemplateInfo.Text = $"{_selectedTemplate.FilePath} | {selectedModel.Name}";
|
||||||
|
AppendLog($"已匹配图纸: {selectedModel.Name}");
|
||||||
}
|
}
|
||||||
catch (BusinessException bex)
|
catch (BusinessException bex)
|
||||||
{
|
{
|
||||||
_selectedTemplate = null;
|
if (_selectedTemplate != null)
|
||||||
_lblTemplateInfo.Text = "未匹配模板";
|
{
|
||||||
_txtTemplatePath.Text = "";
|
_selectedTemplate.LayoutName = null;
|
||||||
AppendLog($"模板选择错误: {bex.Message}");
|
}
|
||||||
|
_selectedModelWindow = null;
|
||||||
|
_selectedSheetName = null;
|
||||||
|
_lblTemplateInfo.Text = _selectedTemplate?.FilePath ?? "未匹配图纸";
|
||||||
|
AppendLog($"图纸匹配错误: {bex.Message}");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_selectedTemplate = null;
|
if (_selectedTemplate != null)
|
||||||
_lblTemplateInfo.Text = "未匹配模板";
|
{
|
||||||
_txtTemplatePath.Text = "";
|
_selectedTemplate.LayoutName = null;
|
||||||
AppendLog($"异常: {ex.Message}");
|
}
|
||||||
Logger.Error("MatchTemplate", ex);
|
_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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
private void OnSelectTemplate()
|
private void OnSelectTemplate()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@ -235,8 +381,11 @@ namespace CadParamPluging.UI
|
|||||||
};
|
};
|
||||||
|
|
||||||
_txtTemplatePath.Text = filePath;
|
_txtTemplatePath.Text = filePath;
|
||||||
_lblTemplateInfo.Text = $"已选择: {fileName}";
|
|
||||||
|
_lblTemplateInfo.Text = filePath;
|
||||||
AppendLog($"手动选择模板: {filePath}");
|
AppendLog($"手动选择模板: {filePath}");
|
||||||
|
|
||||||
|
TryReplaceDropdownOptionsFromTemplate(filePath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -247,6 +396,106 @@ namespace CadParamPluging.UI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 void OnGenerateDrawing()
|
private void OnGenerateDrawing()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@ -254,16 +503,36 @@ namespace CadParamPluging.UI
|
|||||||
var tplParams = CollectTemplateParams();
|
var tplParams = CollectTemplateParams();
|
||||||
var drawingParams = CollectDrawingParams();
|
var drawingParams = CollectDrawingParams();
|
||||||
|
|
||||||
if (_selectedTemplate == null)
|
if (_selectedTemplate == null || string.IsNullOrWhiteSpace(_selectedTemplate.FilePath))
|
||||||
{
|
{
|
||||||
_selectedTemplate = DomainFacade.SelectTemplate(tplParams);
|
AppendLog("请先选择模板文件。");
|
||||||
_lblTemplateInfo.Text = $"已匹配: {_selectedTemplate.Name}";
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(_selectedTemplate.LayoutName) && !_selectedModelWindow.HasValue)
|
||||||
|
{
|
||||||
|
AppendLog("未匹配图纸,尝试自动匹配...");
|
||||||
|
OnMatchTemplate();
|
||||||
|
if (_selectedTemplate == null || (string.IsNullOrWhiteSpace(_selectedTemplate.LayoutName) && !_selectedModelWindow.HasValue))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DomainFacade.ValidateParameters(tplParams, drawingParams);
|
DomainFacade.ValidateParameters(tplParams, drawingParams);
|
||||||
|
|
||||||
var doc = TemplateDrawingService.CreateDocumentFromTemplate(_selectedTemplate);
|
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 (doc.LockDocument())
|
||||||
using (var ctx = new CadContext(doc))
|
using (var ctx = new CadContext(doc))
|
||||||
{
|
{
|
||||||
@ -320,12 +589,14 @@ namespace CadParamPluging.UI
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using (var form = new SettingsForm())
|
var current = DropdownOptionsStore.Load();
|
||||||
|
using (var form = new SettingsForm(current))
|
||||||
{
|
{
|
||||||
if (form.ShowDialog() == DialogResult.OK)
|
if (form.ShowDialog() == DialogResult.OK)
|
||||||
{
|
{
|
||||||
// TODO: Save settings
|
DropdownOptionsStore.Save(form.Result);
|
||||||
AppendLog("设置已保存 (暂无实际生效项)");
|
ReloadDropdownOptions(form.Result);
|
||||||
|
AppendLog("设置已保存");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -336,14 +607,33 @@ namespace CadParamPluging.UI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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()
|
private TemplateParams CollectTemplateParams()
|
||||||
{
|
{
|
||||||
|
var specialCondition = _cbScale.Text?.Trim();
|
||||||
|
if (string.Equals(specialCondition, "无", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
specialCondition = null;
|
||||||
|
}
|
||||||
|
|
||||||
return new TemplateParams
|
return new TemplateParams
|
||||||
{
|
{
|
||||||
ProjectType = _cbProjectType.Text?.Trim(),
|
ProjectType = _cbProjectType.Text?.Trim(),
|
||||||
DrawingType = _cbDrawingType.Text?.Trim(),
|
DrawingType = _cbDrawingType.Text?.Trim(),
|
||||||
SheetSize = _cbSheetSize.Text?.Trim(),
|
SheetSize = _cbSheetSize.Text?.Trim(),
|
||||||
Scale = _cbScale.Text?.Trim()
|
Scale = specialCondition
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -356,14 +646,19 @@ namespace CadParamPluging.UI
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ComboBox CreateComboBox(string[] items)
|
private static ComboBox CreateComboBox(System.Collections.Generic.IEnumerable<string> items)
|
||||||
{
|
{
|
||||||
var cb = new ComboBox
|
var cb = new ComboBox
|
||||||
{
|
{
|
||||||
DropDownStyle = ComboBoxStyle.DropDownList,
|
DropDownStyle = ComboBoxStyle.DropDownList,
|
||||||
Width = 160
|
Width = 160
|
||||||
};
|
};
|
||||||
cb.Items.AddRange(items);
|
|
||||||
|
var arr = (items ?? Enumerable.Empty<string>()).Where(s => !string.IsNullOrWhiteSpace(s)).Select(s => s.Trim()).ToArray();
|
||||||
|
if (arr.Length > 0)
|
||||||
|
{
|
||||||
|
cb.Items.AddRange(arr);
|
||||||
|
}
|
||||||
if (cb.Items.Count > 0)
|
if (cb.Items.Count > 0)
|
||||||
{
|
{
|
||||||
cb.SelectedIndex = 0;
|
cb.SelectedIndex = 0;
|
||||||
@ -371,6 +666,33 @@ namespace CadParamPluging.UI
|
|||||||
return cb;
|
return cb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void ResetComboBoxItems(ComboBox cb, System.Collections.Generic.IEnumerable<string> items)
|
||||||
|
{
|
||||||
|
if (cb == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var previous = cb.Text;
|
||||||
|
var arr = (items ?? Enumerable.Empty<string>()).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)
|
private static TextBox CreateTextBox(string defaultText)
|
||||||
{
|
{
|
||||||
return new TextBox { Width = 160, Text = defaultText };
|
return new TextBox { Width = 160, Text = defaultText };
|
||||||
|
|||||||
@ -1,15 +1,24 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
|
using System.Linq;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
using CadParamPluging.Common;
|
||||||
|
|
||||||
namespace CadParamPluging.UI
|
namespace CadParamPluging.UI
|
||||||
{
|
{
|
||||||
public class SettingsForm : Form
|
public class SettingsForm : Form
|
||||||
{
|
{
|
||||||
public SettingsForm()
|
private readonly ListBox _lbDeliveryStatuses;
|
||||||
|
private readonly ListBox _lbProcessMethods;
|
||||||
|
private readonly ListBox _lbStructuralFeatures;
|
||||||
|
private readonly ListBox _lbSpecialConditions;
|
||||||
|
|
||||||
|
public DropdownOptions Result { get; private set; }
|
||||||
|
|
||||||
|
public SettingsForm(DropdownOptions current)
|
||||||
{
|
{
|
||||||
Text = "设置";
|
Text = "设置";
|
||||||
Size = new Size(400, 300);
|
Size = new Size(560, 420);
|
||||||
StartPosition = FormStartPosition.CenterScreen;
|
StartPosition = FormStartPosition.CenterScreen;
|
||||||
FormBorderStyle = FormBorderStyle.FixedDialog;
|
FormBorderStyle = FormBorderStyle.FixedDialog;
|
||||||
MaximizeBox = false;
|
MaximizeBox = false;
|
||||||
@ -26,20 +35,27 @@ namespace CadParamPluging.UI
|
|||||||
layout.RowStyles.Add(new RowStyle(SizeType.Percent, 100f)); // Content area
|
layout.RowStyles.Add(new RowStyle(SizeType.Percent, 100f)); // Content area
|
||||||
layout.RowStyles.Add(new RowStyle(SizeType.AutoSize)); // Buttons area
|
layout.RowStyles.Add(new RowStyle(SizeType.AutoSize)); // Buttons area
|
||||||
|
|
||||||
// Placeholder for future settings
|
_lbDeliveryStatuses = new ListBox();
|
||||||
var contentPanel = new GroupBox
|
_lbProcessMethods = new ListBox();
|
||||||
|
_lbStructuralFeatures = new ListBox();
|
||||||
|
_lbSpecialConditions = new ListBox();
|
||||||
|
|
||||||
|
var options = (current ?? DropdownOptions.CreateDefault()).Clone();
|
||||||
|
options.Normalize();
|
||||||
|
|
||||||
|
FillList(_lbDeliveryStatuses, options.DeliveryStatuses);
|
||||||
|
FillList(_lbProcessMethods, options.ProcessMethods);
|
||||||
|
FillList(_lbStructuralFeatures, options.StructuralFeatures);
|
||||||
|
FillList(_lbSpecialConditions, options.SpecialConditions);
|
||||||
|
|
||||||
|
var tabs = new TabControl
|
||||||
{
|
{
|
||||||
Text = "常规设置",
|
|
||||||
Dock = DockStyle.Fill
|
Dock = DockStyle.Fill
|
||||||
};
|
};
|
||||||
var lblPlaceholder = new Label
|
tabs.TabPages.Add(BuildEditableTab("交付状态", _lbDeliveryStatuses));
|
||||||
{
|
tabs.TabPages.Add(BuildEditableTab("工艺方法", _lbProcessMethods));
|
||||||
Text = "更多设置项即将推出...",
|
tabs.TabPages.Add(BuildEditableTab("结构特征", _lbStructuralFeatures));
|
||||||
AutoSize = true,
|
tabs.TabPages.Add(BuildEditableTab("特殊条件", _lbSpecialConditions));
|
||||||
Location = new Point(20, 30),
|
|
||||||
ForeColor = Color.Gray
|
|
||||||
};
|
|
||||||
contentPanel.Controls.Add(lblPlaceholder);
|
|
||||||
|
|
||||||
// Bottom buttons
|
// Bottom buttons
|
||||||
var flowButtons = new FlowLayoutPanel
|
var flowButtons = new FlowLayoutPanel
|
||||||
@ -50,12 +66,13 @@ namespace CadParamPluging.UI
|
|||||||
};
|
};
|
||||||
|
|
||||||
var btnCancel = new Button { Text = "取消", DialogResult = DialogResult.Cancel };
|
var btnCancel = new Button { Text = "取消", DialogResult = DialogResult.Cancel };
|
||||||
var btnOk = new Button { Text = "确定", DialogResult = DialogResult.OK };
|
var btnOk = new Button { Text = "确定" };
|
||||||
|
btnOk.Click += (_, __) => OnOk();
|
||||||
|
|
||||||
flowButtons.Controls.Add(btnCancel);
|
flowButtons.Controls.Add(btnCancel);
|
||||||
flowButtons.Controls.Add(btnOk);
|
flowButtons.Controls.Add(btnOk);
|
||||||
|
|
||||||
layout.Controls.Add(contentPanel, 0, 0);
|
layout.Controls.Add(tabs, 0, 0);
|
||||||
layout.Controls.Add(flowButtons, 0, 1);
|
layout.Controls.Add(flowButtons, 0, 1);
|
||||||
|
|
||||||
Controls.Add(layout);
|
Controls.Add(layout);
|
||||||
@ -63,5 +80,192 @@ namespace CadParamPluging.UI
|
|||||||
AcceptButton = btnOk;
|
AcceptButton = btnOk;
|
||||||
CancelButton = btnCancel;
|
CancelButton = btnCancel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void FillList(ListBox listBox, System.Collections.Generic.IEnumerable<string> items)
|
||||||
|
{
|
||||||
|
listBox.Items.Clear();
|
||||||
|
if (items == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var item in items)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(item))
|
||||||
|
{
|
||||||
|
listBox.Items.Add(item.Trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (listBox.Items.Count > 0)
|
||||||
|
{
|
||||||
|
listBox.SelectedIndex = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private TabPage BuildEditableTab(string title, ListBox listBox)
|
||||||
|
{
|
||||||
|
var page = new TabPage(title);
|
||||||
|
|
||||||
|
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));
|
||||||
|
|
||||||
|
listBox.Dock = DockStyle.Fill;
|
||||||
|
|
||||||
|
var actions = new FlowLayoutPanel
|
||||||
|
{
|
||||||
|
Dock = DockStyle.Fill,
|
||||||
|
FlowDirection = FlowDirection.TopDown,
|
||||||
|
WrapContents = false,
|
||||||
|
AutoSize = true
|
||||||
|
};
|
||||||
|
|
||||||
|
var btnAdd = new Button { Text = "新增", AutoSize = true };
|
||||||
|
btnAdd.Click += (_, __) => OnAddItem(title, listBox);
|
||||||
|
|
||||||
|
var btnEdit = new Button { Text = "修改", AutoSize = true };
|
||||||
|
btnEdit.Click += (_, __) => OnEditItem(title, listBox);
|
||||||
|
|
||||||
|
var btnDelete = new Button { Text = "删除", AutoSize = true };
|
||||||
|
btnDelete.Click += (_, __) => OnDeleteItem(title, listBox);
|
||||||
|
|
||||||
|
actions.Controls.Add(btnAdd);
|
||||||
|
actions.Controls.Add(btnEdit);
|
||||||
|
actions.Controls.Add(btnDelete);
|
||||||
|
|
||||||
|
layout.Controls.Add(listBox, 0, 0);
|
||||||
|
layout.Controls.Add(actions, 1, 0);
|
||||||
|
page.Controls.Add(layout);
|
||||||
|
return page;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnAddItem(string category, ListBox listBox)
|
||||||
|
{
|
||||||
|
if (!TextInputForm.TryGetValue(this, $"{category} - 新增", "请输入内容:", string.Empty, out var value))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
value = (value ?? string.Empty).Trim();
|
||||||
|
if (value.Length == 0)
|
||||||
|
{
|
||||||
|
MessageBox.Show(this, "内容不能为空。", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
listBox.Items.Add(value);
|
||||||
|
listBox.SelectedIndex = listBox.Items.Count - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnEditItem(string category, ListBox listBox)
|
||||||
|
{
|
||||||
|
var selected = listBox.SelectedItem as string;
|
||||||
|
if (string.IsNullOrWhiteSpace(selected))
|
||||||
|
{
|
||||||
|
MessageBox.Show(this, "请先选择一项再修改。", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!TextInputForm.TryGetValue(this, $"{category} - 修改", "请输入内容:", selected, out var value))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
value = (value ?? string.Empty).Trim();
|
||||||
|
if (value.Length == 0)
|
||||||
|
{
|
||||||
|
MessageBox.Show(this, "内容不能为空。", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var idx = listBox.SelectedIndex;
|
||||||
|
listBox.Items[idx] = value;
|
||||||
|
listBox.SelectedIndex = idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDeleteItem(string category, ListBox listBox)
|
||||||
|
{
|
||||||
|
var selected = listBox.SelectedItem as string;
|
||||||
|
if (string.IsNullOrWhiteSpace(selected))
|
||||||
|
{
|
||||||
|
MessageBox.Show(this, "请先选择一项再删除。", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var confirm = MessageBox.Show(
|
||||||
|
this,
|
||||||
|
$"确认删除:{selected}?",
|
||||||
|
$"{category} - 删除",
|
||||||
|
MessageBoxButtons.YesNo,
|
||||||
|
MessageBoxIcon.Warning);
|
||||||
|
|
||||||
|
if (confirm != DialogResult.Yes)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var idx = listBox.SelectedIndex;
|
||||||
|
listBox.Items.RemoveAt(idx);
|
||||||
|
if (listBox.Items.Count > 0)
|
||||||
|
{
|
||||||
|
listBox.SelectedIndex = Math.Min(idx, listBox.Items.Count - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnOk()
|
||||||
|
{
|
||||||
|
var result = new DropdownOptions
|
||||||
|
{
|
||||||
|
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()
|
||||||
|
};
|
||||||
|
|
||||||
|
result.Normalize();
|
||||||
|
|
||||||
|
var err = ValidateResult(result);
|
||||||
|
if (err != null)
|
||||||
|
{
|
||||||
|
MessageBox.Show(this, err, "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result = result;
|
||||||
|
DialogResult = DialogResult.OK;
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ValidateResult(DropdownOptions options)
|
||||||
|
{
|
||||||
|
if (options.DeliveryStatuses == null || options.DeliveryStatuses.Count == 0)
|
||||||
|
{
|
||||||
|
return "交付状态至少需要 1 项。";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.ProcessMethods == null || options.ProcessMethods.Count == 0)
|
||||||
|
{
|
||||||
|
return "工艺方法至少需要 1 项。";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.StructuralFeatures == null || options.StructuralFeatures.Count == 0)
|
||||||
|
{
|
||||||
|
return "结构特征至少需要 1 项。";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.SpecialConditions == null || options.SpecialConditions.Count == 0)
|
||||||
|
{
|
||||||
|
return "特殊条件至少需要 1 项。";
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
84
UI/TextInputForm.cs
Normal file
84
UI/TextInputForm.cs
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
using System;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace CadParamPluging.UI
|
||||||
|
{
|
||||||
|
public class TextInputForm : Form
|
||||||
|
{
|
||||||
|
private readonly TextBox _txt;
|
||||||
|
|
||||||
|
public string Value => _txt.Text?.Trim();
|
||||||
|
|
||||||
|
public TextInputForm(string title, string label, string initialValue)
|
||||||
|
{
|
||||||
|
Text = title;
|
||||||
|
Size = new Size(360, 150);
|
||||||
|
StartPosition = FormStartPosition.CenterParent;
|
||||||
|
FormBorderStyle = FormBorderStyle.FixedDialog;
|
||||||
|
MaximizeBox = false;
|
||||||
|
MinimizeBox = false;
|
||||||
|
ShowInTaskbar = false;
|
||||||
|
|
||||||
|
var layout = new TableLayoutPanel
|
||||||
|
{
|
||||||
|
Dock = DockStyle.Fill,
|
||||||
|
Padding = new Padding(10),
|
||||||
|
RowCount = 3,
|
||||||
|
ColumnCount = 1
|
||||||
|
};
|
||||||
|
layout.RowStyles.Add(new RowStyle(SizeType.AutoSize));
|
||||||
|
layout.RowStyles.Add(new RowStyle(SizeType.AutoSize));
|
||||||
|
layout.RowStyles.Add(new RowStyle(SizeType.AutoSize));
|
||||||
|
|
||||||
|
var lbl = new Label
|
||||||
|
{
|
||||||
|
Text = label,
|
||||||
|
AutoSize = true,
|
||||||
|
Dock = DockStyle.Fill
|
||||||
|
};
|
||||||
|
|
||||||
|
_txt = new TextBox
|
||||||
|
{
|
||||||
|
Dock = DockStyle.Fill,
|
||||||
|
Text = initialValue ?? string.Empty
|
||||||
|
};
|
||||||
|
|
||||||
|
var buttons = new FlowLayoutPanel
|
||||||
|
{
|
||||||
|
Dock = DockStyle.Fill,
|
||||||
|
FlowDirection = FlowDirection.RightToLeft,
|
||||||
|
AutoSize = true
|
||||||
|
};
|
||||||
|
|
||||||
|
var btnCancel = new Button { Text = "取消", DialogResult = DialogResult.Cancel };
|
||||||
|
var btnOk = new Button { Text = "确定", DialogResult = DialogResult.OK };
|
||||||
|
buttons.Controls.Add(btnCancel);
|
||||||
|
buttons.Controls.Add(btnOk);
|
||||||
|
|
||||||
|
layout.Controls.Add(lbl, 0, 0);
|
||||||
|
layout.Controls.Add(_txt, 0, 1);
|
||||||
|
layout.Controls.Add(buttons, 0, 2);
|
||||||
|
Controls.Add(layout);
|
||||||
|
|
||||||
|
AcceptButton = btnOk;
|
||||||
|
CancelButton = btnCancel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool TryGetValue(IWin32Window owner, string title, string label, string initialValue, out string value)
|
||||||
|
{
|
||||||
|
using (var form = new TextInputForm(title, label, initialValue))
|
||||||
|
{
|
||||||
|
var result = form.ShowDialog(owner);
|
||||||
|
if (result == DialogResult.OK)
|
||||||
|
{
|
||||||
|
value = form.Value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
value = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user