我是C#编程的新手(以及一般的编程),但是我正在为使用中的项目使用AutoDesk .NET API进行AutoCAD开发。
AutoCAD开发人员中有一些重复性任务,我一直在创建帮助程序方法以简化代码。为了通过.API在AutoCAD中创建对象(线,折线,注释等),程序员必须编写相当复杂的语句来访问AutoCAD环境,获取当前图形,获取对象的数据库。当前的图形文件,开始与数据库//do work
进行事务,然后将创建的实体附加到数据库,最后提交并结束事务。
因此,我编写了以下代码来简化此任务:
public static void CreateObjectActionWithinTransaction(Action<Transaction, Database, BlockTable, BlockTableRecord> action)
{
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;
action(transaction, database, blocktable, blockTableRecord);
transaction.Commit();
}
}
然后我的Lambda表达式创建一个通用的MText
并为其设置一些参数:
public static void createMtext(Point3d location, AttachmentPoint attachmentpoint, string contents, double height, short color, bool usebackgroundmask, bool usebackgroundcolor, double backgroundscale)
{
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);
});
}
最后,当我实际上在某个地方创建MText
时,我可以在一行中创建它,并为所有参数传递值,而不必为此编写巨大的事务代码:>
Helpers.createMtext(insertpoint, AttachmentPoint.MiddleLeft, "hello world", .08, colors.AutoCAD_Red, true, true, 1.2);
所以这很棒,当我想自己创建一个MText
并将其放在某个地方时,它可以工作。但是,在某些其他情况下,我不仅要创建MText
并将其放置在图形中,还希望使用与上述相同的基本前提来创建MText
,但是将其作为值返回给在其他地方使用。
AutoCAD具有一个称为Multileaders
的注释对象,与上面一样,它们实际上只是一个MText
,但附加到一些线和一个箭头以指向图形中的某物。在API中,您需要定义一个MText
并将其附加到Multileader
对象。但是我上面的代码无法使用,因为它不返回任何内容。
所以我的问题归结为,如何创建像上面那样的方法来创建对象,但不仅仅是创建该对象,而是让它返回该对象以供另一段代码使用?
对于Lambda表达式的初学者来说还有什么好的资源?书籍,网站,YouTube?
答案 0 :(得分:2)
如Miiir在评论中所述,不要返回对象,而是返回ObjectId
。一个对象实例属于一个事务,因此,如果使用某个事务打开该对象,提交该事务并尝试在另一个事务中使用该对象,则AutoCAD基本上只会崩溃。
始终使用AutoCAD API 遵循以下基本模式:
ObjectID
或循环遍历表并查找您感兴趣的任何属性(即BlockTable
,BlockTableRecord
,LayerTable
等)来完成。 / li>
如果您尝试绕过步骤1和步骤2,效果将不太理想。因此,返回ObjectID
,然后使用ID在另一个事务中获取对象。
如果要使用委托返回值,Action<T>
不是您的朋友。 Action
不返回值,仅“操作”,因此返回名称。如果要使用委托返回值,则有两个选项:
定义自定义委托类型。
使用.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.
最后,要了解lambda表达式,您需要先了解委托(如果尚未了解)。所有的lambda都是语法糖,用于实例化您在示例中所做的指向方法或匿名方法的委托对象。 This tutorial看起来不错。请记住,诸如Action
,Func
和Predicate
之类的代表,还是没有区别。因此,无论您定义自己的委托还是使用开箱即用的解决方案,lambda表达式都不在乎。
有关lambda概述,请check out this tutorial。
不要将自己局限于我提供的两个来源。只是Google吧,排名前10的热门歌曲都是相当不错的信息。您也可以签出Pluralsight。我在那里学习很多。
答案 1 :(得分:2)
我宁愿使用从事务中的调用方法中调用的扩展方法,也不使用委托。
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();
}
}
答案 2 :(得分:0)
我不熟悉AutoCad API,但看来“ transaction.Commit()”是实际执行将MText放置在模型上的动作的行。
如果是这种情况;我会做类似以下的事情:
public MText CreateMTextObject({parameters})
{
//code
Return textObject
}
public PlaceTextObject({parameters})
{
CreateTextObject(parameters).Commit()
}
这样,您可以选择保留文本对象以进行进一步操作,同时仍然允许一次性应用它。它还只有一个用于制作对象的代码块,以确保两种方法之间的实现没有差异