为了账号安全,请及时绑定邮箱和手机立即绑定

如何通过 Lambda 表达式创建 AutoCAD 对象并返回

如何通过 Lambda 表达式创建 AutoCAD 对象并返回

C#
眼眸繁星 2022-06-12 10:29:32
我是 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();    });}最后,当我实际在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?
查看完整描述

3 回答

?
qq_笑_17

TA贡献1818条经验 获得超7个赞

对于 AutoCAD 部分:

正如 Miiir 在评论中所说,不要返回对象,而是ObjectId. 一个对象实例属于一个事务,因此如果您使用某个事务打开该对象,提交该事务并尝试在另一个事务中使用该对象,AutoCAD 基本上只会崩溃。

使用 AutoCAD API始终遵循以下基本模式:

  1. 开始交易

  2. 创建新对象或使用事务来获取现有对象。这可以通过使用ObjectID或 遍历表并查找您感兴趣的任何属性(即BlockTableBlockTableRecordLayerTable等)来完成。

  3. 对对象做一些事情。

  4. 提交或中止事务。

如果您尝试绕过第 1 步和第 2 步,则效果不会很好。所以, return ObjectID,然后使用 id 在另一个事务中获取对象。

至于 C# 部分:

如果您希望使用委托返回值,那Action<T>不是您的朋友。Action不返回值,它只是“行动”,因此得名。如果您想使用委托返回一个值,您有 2 个选项:

  1. 定义自定义委托类型。

  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.


查看完整回答
反对 回复 2022-06-12
?
斯蒂芬大帝

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();

        }

    }


查看完整回答
反对 回复 2022-06-12
?
largeQ

TA贡献2039条经验 获得超8个赞

我不熟悉 AutoCad API,但似乎“transaction.Commit()”是实际执行将 MText 放置在模型上的操作的行。


如果是这种情况;我会做如下的事情:


public MText CreateMTextObject({parameters})

{

//code

  Return textObject

}


public PlaceTextObject({parameters})

{

  CreateTextObject(parameters).Commit()

}

这样,您可以选择保留文本对象以供进一步操作,同时仍然允许选择一次性应用它。它也将只有一个用于制作对象的代码块,确保两种方法的实现没有差异


查看完整回答
反对 回复 2022-06-12
  • 3 回答
  • 0 关注
  • 167 浏览

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号