等待找用户确认图纸匹配等问题

This commit is contained in:
sladro 2025-12-15 11:54:32 +08:00
parent 7987c4ee43
commit 3daac3648a
13 changed files with 2304 additions and 42 deletions

View 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;
}
}
}

View File

@ -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;
}
} }
} }

View 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;
// 通常图纸在 PaperSpaceModel 不作为候选。
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));
}
}
}

View 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;
}
}
}

View File

@ -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
View 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;
}
}
}

View 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;
}
}
}
}

View File

@ -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
View 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
View 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;
}
}
}

View File

@ -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 };

View File

@ -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
View 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;
}
}
}