3 回答

TA贡献1818条经验 获得超7个赞
对于 AutoCAD 部分:
正如 Miiir 在评论中所说,不要返回对象,而是ObjectId
. 一个对象实例属于一个事务,因此如果您使用某个事务打开该对象,提交该事务并尝试在另一个事务中使用该对象,AutoCAD 基本上只会崩溃。
使用 AutoCAD API始终遵循以下基本模式:
开始交易
创建新对象或使用事务来获取现有对象。这可以通过使用
ObjectID
或 遍历表并查找您感兴趣的任何属性(即BlockTable
、BlockTableRecord
、LayerTable
等)来完成。对对象做一些事情。
提交或中止事务。
如果您尝试绕过第 1 步和第 2 步,则效果不会很好。所以, return ObjectID
,然后使用 id 在另一个事务中获取对象。
至于 C# 部分:
如果您希望使用委托返回值,那Action<T>
不是您的朋友。Action
不返回值,它只是“行动”,因此得名。如果您想使用委托返回一个值,您有 2 个选项:
定义自定义委托类型。
使用 .NET 框架提供的通用委托
Func<T1,T2,T3,T4,TResult>
。
你应该使用哪一个?在您的情况下,我可能会选择选项 1,原因很简单,因为您的代码将更加简洁且易于维护。我将在这个例子中使用它。使用Func
的工作方式完全相同,只是你的函数签名看起来有点难看。
自定义委托:
//somewhere in your code inside a namespace (rather than a class)
public delegate ObjectId MyCreateDelegate(Transaction transaction, Database db,
BlockTable blockTable, BlockTableRecord blockTableRecord);
然后你的一般方法
public static ObjectId CreateObjectActionWithinTransaction(MyCreateDelegate createDel)
{
ObjectId ret;
var document = Application.DocumentManager.MdiActiveDocument;
var database = document.Database;
using (var transaction = document.TransactionManager.StartTransaction())
{
BlockTable blocktable = transaction.GetObject(database.BlockTableId, OpenMode.ForRead) as BlockTable;
BlockTableRecord blockTableRecord = transaction.GetObject(blocktable[BlockTableRecord.ModelSpace], OpenMode.ForWrite) as BlockTableRecord;
//here createMtext will get called in this case, and return ObjectID
ret = createDel(transaction, database, blocktable, blockTableRecord);
transaction.Commit();
}
return ret;
}
以及使用 lambda 的具体方法:
public ObjectId createMtext(Point3d location, AttachmentPoint attachmentpoint, string contents, double height, short color, bool usebackgroundmask, bool usebackgroundcolor, double backgroundscale)
{
//here you can return the result the general function
return CreateObjectActionWithinTransaction((transaction, database, blocktable, blocktablerecord) =>
{
MText mt = new MText();
mt.SetDatabaseDefaults();
mt.Location = location;
mt.Attachment = attachmentpoint;
mt.Contents = contents;
mt.Height = height;
mt.Color = Color.FromColorIndex(ColorMethod.ByAci, color);
mt.BackgroundFill = usebackgroundmask;
mt.UseBackgroundColor = usebackgroundcolor;
mt.BackgroundScaleFactor = backgroundscale;
blocktablerecord.AppendEntity(mt);
transaction.AddNewlyCreatedDBObject(mt, true);
//make sure to get ObjectId only AFTER adding to db.
return mt.ObjectId;
});
}
最后,像这样使用它
ObjectId mtId = Helpers.createMtext(insertpoint, AttachmentPoint.MiddleLeft, "hello world", .08, colors.AutoCAD_Red, true, true, 1.2);
//now use another transaction to open the object and do stuff to it.

TA贡献1827条经验 获得超8个赞
我宁愿使用从调用方法中的事务中调用的扩展方法,而不是使用委托。
static class ExtensionMethods
{
public static BlockTableRecord GetModelSpace(this Database db, OpenMode mode = OpenMode.ForRead)
{
var tr = db.TransactionManager.TopTransaction;
if (tr == null)
throw new Autodesk.AutoCAD.Runtime.Exception(ErrorStatus.NoActiveTransactions);
return (BlockTableRecord)tr.GetObject(SymbolUtilityServices.GetBlockModelSpaceId(db), mode);
}
public static void Add(this BlockTableRecord btr, Entity entity)
{
var tr = btr.Database.TransactionManager.TopTransaction;
if (tr == null)
throw new Autodesk.AutoCAD.Runtime.Exception(ErrorStatus.NoActiveTransactions);
btr.AppendEntity(entity);
tr.AddNewlyCreatedDBObject(entity, true);
}
public static MText AddMtext(this BlockTableRecord btr,
Point3d location,
AttachmentPoint attachmentpoint,
string contents,
double height,
short color = 256,
bool usebackgroundmask = false,
bool usebackgroundcolor = false,
double backgroundscale = 1.5)
{
MText mt = new MText();
mt.SetDatabaseDefaults();
mt.Location = location;
mt.Attachment = attachmentpoint;
mt.Contents = contents;
mt.Height = height;
mt.ColorIndex = color;
mt.BackgroundFill = usebackgroundmask;
mt.UseBackgroundColor = usebackgroundcolor;
mt.BackgroundScaleFactor = backgroundscale;
btr.Add(mt);
return mt;
}
}
使用示例:
public static void Test()
{
var doc = AcAp.DocumentManager.MdiActiveDocument;
var db = doc.Database;
using (var tr = db.TransactionManager.StartTransaction())
{
var ms = db.GetModelSpace(OpenMode.ForWrite);
var mt = ms.AddMtext(Point3d.Origin, AttachmentPoint.TopLeft, "foobar", 2.5);
// do what you want with 'mt'
tr.Commit();
}
}

TA贡献2039条经验 获得超8个赞
我不熟悉 AutoCad API,但似乎“transaction.Commit()”是实际执行将 MText 放置在模型上的操作的行。
如果是这种情况;我会做如下的事情:
public MText CreateMTextObject({parameters})
{
//code
Return textObject
}
public PlaceTextObject({parameters})
{
CreateTextObject(parameters).Commit()
}
这样,您可以选择保留文本对象以供进一步操作,同时仍然允许选择一次性应用它。它也将只有一个用于制作对象的代码块,确保两种方法的实现没有差异
- 3 回答
- 0 关注
- 167 浏览
添加回答
举报