使用.NET在AutoCAD表中嵌入字段

这篇文章是对我收到的一些如何在表中嵌入字段请求的回应。从上一篇文章开始,我们研究了如何在表中嵌入块预览图标。不过,我在这篇文章中展示的技术远不是针对表的。您可以很容易地将其用于支持AutoCAD内部字段(多行文字、文本、属性等)的任何其他文本对象。

字段是非常酷的对象:它们允许您访问各种各样的信息数组,并将它们嵌入到AutoCAD中的文本对象中。字段是一个对象,但可以由包含尖括号等的文本字符串定义。在执行过程中(在重新生成、图形加载等时),AutoCAD会在不同的时间解释和重新生成字段。有关详细信息,请查看FIELDEVAL 系统变量。在应用程序中创建字段对象的最简单方法就是简单地嵌入一个格式正确的字符串:如果一切正常,AutoCAD将理解它来表示字段,并在幕后创建相应的对象。

为了详细了解字段机制的工作原理,我建议查看ObjectARX SDK上的textfileField示例。这是一个C++示例,当第一次引入字段时(2005,如果我记得正确的话),我会创建它来显示如何实现自己的自定义字段。这个特殊的链接到一个文本文件,并将文件的内容嵌入到容器对象中。

在AutoCAD中有各种标准字段可用。您可以访问文档级信息(例如图形的作者)或系统级信息(例如当前日期)。要开始使用字段,我建议使用FIELD 命令打开“字段定义”对话框(也可以从mtext工具栏和其他位置访问)。此对话框允许您配置绝大多数字段,格式代码(包括未定义的)。

在AutoCAD附带的各种标准字段中,作为程序员,我觉得最吸引人的是acobjprop字段,它提供了从字段对象访问COM属性的能力。这真的很酷——你基本上传递了你想要访问的对象的ObjectID,以及你想要读取的COM自动化属性,而字段则完成了剩下的工作。这为我们大开方便之门,因为它最终允许您访问任何对象属性,假设它是通过COM公开的(开发人员经常通过COM公开他们的自定义对象,因为它允许与属性面板集成,并且能够通过VBA操作这些对象)。

让我们看看要添加到代码中的字符串。计划是向我们的表中添加一列,其中包含一个布尔值(是/否),指示块定义是否是动态的。

下面是一个可以直接嵌入表单元格的字符串示例:

%<\AcObjProp Object(%<_ObjId 2130399456>%).IsDynamicBlock>%

分解如下:

ObjectID仅对特定会话有效,因此不能硬编码此数字。AutoCAD非常聪明,可以在加载图形时在整个图形中重新映射这些图形,所以当重新打开图形时,属性仍然可以由字段访问。

如果要看到嵌入在文本对象中的此字段,它将显示一个灰色框,其中包含“0”或“1”,这是为特定块定义调用IsDynamicBlock 属性的结果(顺便说一下,这是该特定对象ID需要指出的结果)。这不太理想,因为我们想使用文本字符串。您可以通过指定/f将格式代码应用于AcObjProp 字段的结果。前面提到的特定代码没有文档记录,但是FIELD 命令允许您找出它们应该是什么。诀窍是找到与您的属性数据类型相同的属性,然后复制格式化代码。

例如,我使用MText.BackgroundFill属性(也是一个布尔值)来帮助我确定我的属性所需的格式代码是%bl2。以下是显示此信息的字段对话框:

因此,我现在知道我们要为每个块添加以下字符串(ObjectID应做适当更改):

%<\AcObjProp Object(%<_ObjId 2130399456>%).IsDynamicBlock \f “%bl2”>%

好了,我们准备好了一些代码…-)

这是更新后的C#代码。我没有对行进行编号,这一次,因为这会使我丢失一些有价值的宽度列,但是更改后的代码应该很容易识别-它只是在上一个表中添加一个额外的列(尽管我确实添加了一些行以添加一些列标题):

using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;

namespace TableCreation {
    public class Commands {
        [CommandMethod("CRT")]
        static public void CreateTable()
        {
            Document doc =Application.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;
            Editor ed = doc.Editor;
            PromptPointResult pr =ed.GetPoint("\nEnter table insertion point: ");
            if (pr.Status == PromptStatus.OK)
            {
                Transaction tr =doc.TransactionManager.StartTransaction();
                using (tr)
                {
                    BlockTable bt =(BlockTable)tr.GetObject(doc.Database.BlockTableId,OpenMode.ForRead);
                    Table tb = new Table();
                    tb.TableStyle = db.Tablestyle;
                    tb.NumRows = 5;
                    // Added an additional column for the block image
                    // and one for the "is dynamic" flag
                    tb.NumColumns = 5;
                    tb.SetRowHeight(3);
                    tb.SetColumnWidth(15);
                    tb.Position = pr.Value;
                    // Create a 2-dimensional array
                    // of our table contents
                    string[,] str = new string[5, 4];
                    str[0, 0] = "Part No.";
                    str[0, 1] = "Name ";
                    str[0, 2] = "Material ";
                    str[1, 0] = "1876-1";
                    str[1, 1] = "Flange";
                    str[1, 2] = "Perspex";
                    str[2, 0] = "0985-4";
                    str[2, 1] = "Bolt";
                    str[2, 2] = "Steel";
                    str[3, 0] = "3476-K";
                    str[3, 1] = "Tile";
                    str[3, 2] = "Ceramic";
                    str[4, 0] = "8734-3";
                    str[4, 1] = "Kean";
                    str[4, 2] = "Mostly water";
                    // Use a nested loop to add and format each cell
                    for (int i = 0; i < 5; i++)
                    {
                        for (int j = 0; j < 3; j++)
                        {
                            tb.SetTextHeight(i, j, 1);
                            tb.SetTextString(i, j, str[i, j]);
                            tb.SetAlignment(i, j, CellAlignment.MiddleCenter);
                        }
                        // Adding title information for additional columns
                        if (i == 0)
                        {
                            tb.SetTextHeight(i, 3, 1);
                            tb.SetTextString(i, 3, "Block Preview");
                            tb.SetAlignment(i, 3, CellAlignment.MiddleCenter);
                            tb.SetTextHeight(i, 4, 1);
                            tb.SetTextString(i, 4, "Is Dynamic?");
                            tb.SetAlignment(i, 4, CellAlignment.MiddleCenter);
                        }
                        // If a block definition exists for a block of our
                        // "name" field, then let's set it in the 4th column
                        if (bt.Has(str[i, 1])){
                            ObjectId objId = bt[str[i, 1]];
                            tb.SetBlockTableRecordId(i, 3, objId, true);
                            // And then we use a field to check on whether
                            // it's a dynamic block or not
                            string strObjId = objId.ToString();
                            strObjId = strObjId.Trim(new char[] { '(', ')' });
                            tb.SetTextHeight(i, 4, 1);
                            tb.SetTextString(i,4,"%<\\AcObjProp Object(%<\\_ObjId "+ strObjId+ ">%).IsDynamicBlock \\f \"%bl2\">%");
                            tb.SetAlignment(i, 4, CellAlignment.MiddleCenter);
                        }
                    }
                    tb.GenerateLayout();
                    BlockTableRecord btr =(BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace],OpenMode.ForWrite);
                    btr.AppendEntity(tb);
                    tr.AddNewlyCreatedDBObject(tb, true);
                    tr.Commit();
                }
            }
        }
    }
}

下面是运行CRT 命令的结果,假设kean块是四个块中唯一的动态块: